Added logic sim app

This commit is contained in:
Prozilla 2024-06-02 18:28:51 +02:00
parent 35c06deb3c
commit a9a7c78128
No known key found for this signature in database
GPG key ID: 5858DFE71CAF31EE
22 changed files with 684 additions and 28 deletions

View file

@ -0,0 +1,4 @@
.Canvas {
width: 100%;
height: 100%;
}

View file

@ -0,0 +1,21 @@
import { useEffect, useRef, useState } from "react";
import { Circuit } from "../../../features/apps/logic-sim/circuit";
import styles from "./CircuitView.module.css";
export function CircuitView() {
const [circuit, setCircuit] = useState(new Circuit("Chip", "#000", 2, 1));
const canvasRef = useRef(null);
useEffect(() => {
if (canvasRef.current == null && circuit.canvas != null)
return;
circuit.init(canvasRef.current as HTMLCanvasElement);
return () => {
circuit.cleanup();
};
}, [canvasRef, circuit]);
return <canvas ref={canvasRef} className={styles.Canvas}/>;
}

View file

@ -0,0 +1,4 @@
.Container {
width: 100%;
height: 100%;
}

View file

@ -0,0 +1,8 @@
import { CircuitView } from "./CircuitView";
import styles from "./LogicSim.module.css";
export function LogicSim() {
return <div className={styles.Container}>
<CircuitView/>
</div>;
}

View file

@ -16,7 +16,7 @@ import WindowsManager from "../../../../features/windows/windowsManager";
export function MarkdownImage({ currentFile, alt, src, modalsManager, windowsManager, app, ...props }) {
const source = useMemo(() => {
if (src.startsWith("public")) {
return src.replace(/^public\//g, `${process.env.PUBLIC_URL}/`);
return src.replace(/^public\//g, `${(process.env as NodeJS.ProcessEnv).PUBLIC_URL}/`);
}
return src;

View file

@ -145,7 +145,7 @@ export const Taskbar = memo(() => {
className={`${styles["Menu-button"]} ${styles["Home-button"]}`}
onClick={() => { updateShowHome(!showHome); }}
>
<ReactSVG src={process.env.PUBLIC_URL + "/assets/logo.svg"}/>
<ReactSVG src={(process.env as NodeJS.ProcessEnv).PUBLIC_URL + "/assets/logo.svg"}/>
</button>
<HomeMenu active={showHome} setActive={updateShowHome} search={search}/>
</OutsideClickListener>

View file

@ -77,7 +77,7 @@ export const AppButton: FC<AppButtonProps> = memo(({ app, windowsManager, pins,
}}
title={app.name}
>
<ReactSVG src={`${process.env.PUBLIC_URL}/assets/apps/icons/${app.id}.svg`}/>
<ReactSVG src={`${(process.env as NodeJS.ProcessEnv).PUBLIC_URL}/assets/apps/icons/${app.id}.svg`}/>
</button>
);
});

View file

@ -25,7 +25,7 @@ export const WindowsView: FC = memo(() => {
useEffect(() => {
const resetViewportTitleAndIcon = () => {
setViewportTitle(`${NAME} | ${TAG_LINE}`);
setViewportIcon(`${process.env.PUBLIC_URL}/favicon.ico`);
setViewportIcon(`${(process.env as NodeJS.ProcessEnv).PUBLIC_URL}/favicon.ico`);
};
if (sortedWindows.length === 0 || sortedWindows[sortedWindows.length - 1].minimized)

View file

@ -19,10 +19,10 @@ export const APP_NAMES = {
};
export const APP_ICONS = {
TERMINAL: `${process.env.PUBLIC_URL}/assets/apps/icons/${APPS.TERMINAL}.svg`,
SETTINGS: `${process.env.PUBLIC_URL}/assets/apps/icons/${APPS.SETTINGS}.svg`,
MEDIA_VIEWER: `${process.env.PUBLIC_URL}/assets/apps/icons/${APPS.MEDIA_VIEWER}.svg`,
TEXT_EDITOR: `${process.env.PUBLIC_URL}/assets/apps/icons/${APPS.TEXT_EDITOR}.svg`,
FILE_EXPLORER: `${process.env.PUBLIC_URL}/assets/apps/icons/${APPS.FILE_EXPLORER}.svg`,
CALCULATOR: `${process.env.PUBLIC_URL}/assets/apps/icons/${APPS.CALCULATOR}.svg`,
TERMINAL: `${(process.env as NodeJS.ProcessEnv).PUBLIC_URL}/assets/apps/icons/${APPS.TERMINAL}.svg`,
SETTINGS: `${(process.env as NodeJS.ProcessEnv).PUBLIC_URL}/assets/apps/icons/${APPS.SETTINGS}.svg`,
MEDIA_VIEWER: `${(process.env as NodeJS.ProcessEnv).PUBLIC_URL}/assets/apps/icons/${APPS.MEDIA_VIEWER}.svg`,
TEXT_EDITOR: `${(process.env as NodeJS.ProcessEnv).PUBLIC_URL}/assets/apps/icons/${APPS.TEXT_EDITOR}.svg`,
FILE_EXPLORER: `${(process.env as NodeJS.ProcessEnv).PUBLIC_URL}/assets/apps/icons/${APPS.FILE_EXPLORER}.svg`,
CALCULATOR: `${(process.env as NodeJS.ProcessEnv).PUBLIC_URL}/assets/apps/icons/${APPS.CALCULATOR}.svg`,
};

View file

@ -0,0 +1,47 @@
export const CURSORS = {
default: "default",
pointer: "pointer"
};
export const BACKGROUND = {
padding: 40,
borderWidth: 8,
};
export const INPUT_OUTPUT = {
radius: 24,
borderWidth: 4,
pinOffset: 40,
connectorWidth: 10,
};
export const WIRE = {
width: 4,
};
export const PIN = {
radius: 10,
};
export const COLORS = {
pin: {
fill: "dark-grey-b",
fillHover: "dark-grey-a"
},
inputOutput: {
connector: "dark-grey-ca",
on: "red-b",
onHover: "red-a",
off: "dark-grey-d",
offHover: "dark-grey-ca",
stroke: "dark-grey-b"
},
background: {
outer: "dark-grey-e",
inner: "dark-grey-f",
border: "dark-grey-d",
},
wire: {
placing: "dark-grey-ca",
},
};

View file

@ -10,6 +10,7 @@ import Vector2 from "../math/vector2";
import { APPS, APP_NAMES } from "../../config/apps.config";
import { Browser } from "../../components/apps/browser/Browser";
import { IMAGE_FORMATS } from "../../config/apps/mediaViewer.config";
import { LogicSim } from "../../components/apps/logic-sim/LogicSim";
export default class AppsManager {
static APPS: App[] = [
@ -35,6 +36,7 @@ export default class AppsManager {
new App(APP_NAMES.BROWSER, APPS.BROWSER, Browser, {
size: new Vector2(700, 500)
}),
new App("Logic Sim", "logic-sim", LogicSim),
];
static getAppById(id: string): App | null {
@ -73,9 +75,9 @@ export default class AppsManager {
*/
static getAppIconUrl(appId: string, iconName?: string): string {
if (iconName == null) {
return `${process.env.PUBLIC_URL}/assets/apps/icons/${appId}.svg`;
return `${(process.env as NodeJS.ProcessEnv).PUBLIC_URL}/assets/apps/icons/${appId}.svg`;
} else {
return `${process.env.PUBLIC_URL}/assets/apps/${appId}/icons/${iconName}.svg`;
return `${(process.env as NodeJS.ProcessEnv).PUBLIC_URL}/assets/apps/${appId}/icons/${iconName}.svg`;
}
}
}

View file

@ -0,0 +1,65 @@
import Vector2 from "../../math/vector2";
import { Circuit } from "./circuit";
import { Pin } from "./pin";
import { State } from "./state";
export class Chip {
color: string;
name: string;
position: Vector2;
circuit: Circuit;
inputCount: number;
outputCount: number;
inputPins: Pin[];
outputPins: Pin[];
logic: (inputStates: State[]) => State[];
constructor(circuit: Circuit | null, name: string, color: string, inputCount: number, outputCount: number) {
Object.assign(this, { circuit, name, color, inputCount, outputCount });
if (this.circuit == null)
this.circuit = this as unknown as Circuit;
this.inputPins = [];
for (let i = 0; i < inputCount; i++) {
this.inputPins.push(new Pin(this.circuit, "IN " + i, true, this, i));
}
this.outputPins = [];
for (let i = 0; i < outputCount; i++) {
this.outputPins.push(new Pin(this.circuit, "OUT " + i, false, this, i));
}
}
setInputState(index: number, state: State) {
if (this.inputPins[index].state.isEqual(state))
return;
this.inputPins[index].state = state;
this.update();
}
update() {
const inputStates: State[] = [];
for (let i = 0; i < this.inputCount; i++) {
const state = this.inputPins[i].state ?? State.LOW;
inputStates.push(state);
}
const outputStates: State[] = this.logic(inputStates);
for (let i = 0; i < this.outputCount; i++) {
this.outputPins[i].state = outputStates[i];
}
}
drawPins() {
this.inputPins.forEach((pin) => pin.draw());
this.outputPins.forEach((pin) => pin.draw());
}
draw() {
this.drawPins();
}
}

View file

@ -0,0 +1,370 @@
import { BACKGROUND, COLORS, CURSORS, INPUT_OUTPUT, PIN, WIRE } from "../../../config/apps/logicSim.config";
import Vector2 from "../../math/vector2";
import { Chip } from "./chip";
import { Pin } from "./pin";
import { State } from "./state";
import { Wire } from "./wire";
export class Circuit extends Chip {
canvas: HTMLCanvasElement;
size = Vector2.ZERO;
context: CanvasRenderingContext2D;
colors: { [key: string]: string } = {};
mousePosition = Vector2.ZERO;
isPlacing = false;
snapping = false;
placingWire: Wire;
wires: Wire[] = [];
cursor = CURSORS.default;
constructor(name: string, color: string, inputCount: number, outputCount: number) {
super(null, name, color, inputCount, outputCount);
}
resize() {
this.size.x = this.canvas.clientWidth;
this.size.y = this.canvas.clientHeight;
// Set pin positions to the side of the screen
this.inputPins.forEach((pin, index) => {
const positionX = BACKGROUND.padding + BACKGROUND.borderWidth / 2 + INPUT_OUTPUT.pinOffset;
const positionY = BACKGROUND.padding * 2 + (INPUT_OUTPUT.radius * 2 + 15) * index;
pin.position = new Vector2(positionX, positionY);
});
this.outputPins.forEach((pin, index) => {
const positionX = this.size.x - (BACKGROUND.padding + BACKGROUND.borderWidth / 2 + INPUT_OUTPUT.pinOffset);
const positionY = BACKGROUND.padding * 2 + (INPUT_OUTPUT.radius * 2 + 15) * index;
pin.position = new Vector2(positionX, positionY);
});
}
setMousePosition(event: MouseEvent) {
const rect = this.canvas.getBoundingClientRect();
this.mousePosition.x = event.clientX - rect.left;
this.mousePosition.y = event.clientY - rect.top;
}
init(canvas: HTMLCanvasElement) {
this.canvas = canvas;
this.context = this.canvas.getContext("2d");
this.resize();
this.mousePosition = Vector2.ZERO;
// Detect size changes of canvas
const observer = new ResizeObserver((entries) => {
entries.forEach(({ target }) => {
if (target === this.canvas && (target.clientWidth != this.size.x || target.clientHeight != this.size.y)) {
this.resize();
}
});
});
observer.observe(this.canvas);
this.canvas.addEventListener("mousemove", this.onMouseMove);
this.canvas.addEventListener("click", this.onClick);
this.canvas.addEventListener("contextmenu", this.onClick);
window.addEventListener("keydown", this.onKeyDown);
window.addEventListener("keyup", this.onKeyUp);
this.render();
}
cleanup() {
this.canvas.removeEventListener("mousemove", this.onMouseMove);
this.canvas.removeEventListener("click", this.onClick);
this.canvas.removeEventListener("contextmenu", this.onClick);
window.removeEventListener("keydown", this.onKeyDown);
window.removeEventListener("keyup", this.onKeyUp);
}
onMouseMove = (event?: MouseEvent) => {
if (event != null)
this.setMousePosition(event);
if (this.placingWire != null) {
const anchorCount = this.placingWire.anchorPoints.length;
const lastAnchorPoint = this.placingWire.anchorPoints[anchorCount - 1];
if (this.snapping) {
const previousAnchorPoint = anchorCount >= 2
? this.placingWire.anchorPoints[anchorCount - 2]
: this.placingWire.inputPin.position;
const deltaX = Math.abs(this.mousePosition.x - previousAnchorPoint.x);
const deltaY = Math.abs(this.mousePosition.y - previousAnchorPoint.y);
if (deltaX > deltaY) {
lastAnchorPoint.x = this.mousePosition.x;
lastAnchorPoint.y = previousAnchorPoint.y;
} else {
lastAnchorPoint.x = previousAnchorPoint.x;
lastAnchorPoint.y = this.mousePosition.y;
}
} else {
lastAnchorPoint.x = this.mousePosition.x;
lastAnchorPoint.y = this.mousePosition.y;
}
}
};
onClickPin(pin: Pin) {
if (this.placingWire != null) {
if (!pin.isInput) {
this.placingWire.outputPin = pin;
this.placingWire.anchorPoints.pop();
this.placingWire.inputPin.setOutputWire(this.placingWire);
this.placingWire.inputPin.update();
this.placingWire = null;
this.isPlacing = false;
}
return;
}
const inputConnection = pin.isInput ? pin : null;
const outputConnection = pin.isInput ? null : pin;
const anchorPoint = this.mousePosition.clone;
this.placingWire = new Wire("blue", inputConnection, outputConnection, [anchorPoint]);
this.wires.push(this.placingWire);
}
onClick = (event: MouseEvent) => {
event.preventDefault();
this.setMousePosition(event);
if (event.button === 2) {
if (this.placingWire != null) {
this.placingWire = null;
this.isPlacing = false;
this.wires.pop();
return;
}
} else if (event.button === 0) {
let eventComplete = false;
this.inputPins.forEach((pin: Pin) => {
if (this.mousePosition.getDistance(pin.position.x - INPUT_OUTPUT.pinOffset, pin.position.y) <= INPUT_OUTPUT.radius) {
pin.setState(State.invert(pin.state));
eventComplete = true;
} else if (this.mousePosition.getDistance(pin.position.x, pin.position.y) <= PIN.radius) {
this.onClickPin(pin);
eventComplete = true;
}
});
if (eventComplete)
return;
this.outputPins.forEach((pin: Pin) => {
if (this.mousePosition.getDistance(pin.position.x, pin.position.y) <= PIN.radius) {
this.onClickPin(pin);
eventComplete = true;
}
});
if (eventComplete)
return;
if (this.placingWire != null) {
this.placingWire.anchorPoints.push(this.mousePosition.clone);
}
}
};
onKeyDown = (event: KeyboardEvent) => {
if (event.key === "Shift") {
event.preventDefault();
this.snapping = true;
this.onMouseMove();
return;
}
};
onKeyUp = (event: KeyboardEvent) => {
if (event.key === "Shift") {
event.preventDefault();
this.snapping = false;
this.onMouseMove();
return;
}
};
getColor(key: string) {
if (this.colors[key] != null)
return this.colors[key];
const color = getComputedStyle(this.canvas).getPropertyValue("--" + key);
this.colors[key] = color;
return color;
}
drawRect(style: string, positionX: number, positionY: number, sizeX: number, sizeY: number) {
this.context.fillStyle = style;
this.context.fillRect(positionX, positionY, sizeX, sizeY);
}
drawCircle(style: string, positionX: number, positionY: number, radius: number) {
this.context.beginPath();
this.context.arc(positionX, positionY, radius, 0, 2 * Math.PI);
this.context.fillStyle = style;
this.context.fill();
}
drawLine(style: string, positions: Vector2[], width: number) {
if (positions.length < 2)
return;
this.context.lineWidth = width;
this.context.lineJoin = "round";
this.context.lineCap = "round";
this.context.beginPath();
this.context.moveTo(positions[0].x, positions[0].y);
for (let i = 1; i < positions.length; i++) {
this.context.lineTo(positions[i].x, positions[i].y);
}
this.context.strokeStyle = style;
this.context.stroke();
}
drawBackground() {
let offset = 0;
this.drawRect(this.getColor(COLORS.background.outer), offset, offset, this.size.x, this.size.y);
offset = BACKGROUND.padding - BACKGROUND.borderWidth;
this.drawRect(
this.getColor(COLORS.background.border),
offset, offset,
this.size.x - offset * 2, this.size.y - offset * 2
);
offset = BACKGROUND.padding;
this.drawRect(
this.getColor(COLORS.background.inner),
offset, offset,
this.size.x - offset * 2, this.size.y - offset * 2
);
}
drawWires() {
this.wires.forEach((wire, index) => {
const positions = [...wire.anchorPoints];
if (wire.inputPin != null)
positions.unshift(wire.inputPin.position);
if (wire.outputPin != null)
positions.push(wire.outputPin.position);
let color: string;
const isPlacingWire = this.placingWire != null && index == this.wires.length - 1;
if (isPlacingWire) {
color = COLORS.wire.placing;
} else if (wire.state.value === 1) {
color = `${wire.color}-a`;
} else {
color = `${wire.color}-b`;
}
this.drawLine(this.getColor(color), positions, WIRE.width);
});
}
drawInputOutput(state: State, isInteractable: boolean, positionX: number, positionY: number) {
let color: string;
if (isInteractable && this.mousePosition.getDistance(positionX, positionY) <= INPUT_OUTPUT.radius) {
if (state.value === 1) {
color = COLORS.inputOutput.onHover;
} else {
color = COLORS.inputOutput.offHover;
}
this.cursor = CURSORS.pointer;
} else {
if (state.value === 1) {
color = COLORS.inputOutput.on;
} else {
color = COLORS.inputOutput.off;
}
}
this.drawCircle(
this.getColor(COLORS.inputOutput.stroke),
positionX, positionY,
INPUT_OUTPUT.radius
);
this.drawCircle(
this.getColor(color),
positionX, positionY,
INPUT_OUTPUT.radius - INPUT_OUTPUT.borderWidth
);
}
drawPins() {
this.inputPins.forEach((pin) => {
const positionX = pin.position.x - INPUT_OUTPUT.pinOffset;
const positionY = pin.position.y;
this.drawRect(
this.getColor(COLORS.inputOutput.connector),
positionX, positionY - INPUT_OUTPUT.connectorWidth / 2,
INPUT_OUTPUT.pinOffset, INPUT_OUTPUT.connectorWidth
);
this.drawInputOutput(pin.state, true, positionX, positionY);
});
this.outputPins.forEach((pin) => {
const positionX = pin.position.x + INPUT_OUTPUT.pinOffset;
const positionY = pin.position.y;
this.drawRect(
this.getColor(COLORS.inputOutput.connector),
positionX - INPUT_OUTPUT.pinOffset, positionY - INPUT_OUTPUT.connectorWidth / 2,
INPUT_OUTPUT.pinOffset, INPUT_OUTPUT.connectorWidth
);
this.drawInputOutput(pin.state, false, positionX, pin.position.y);
});
super.drawPins();
}
draw() {
this.drawBackground();
this.drawWires();
this.drawPins();
}
render() {
if (this.canvas.width != this.size.x)
this.canvas.width = this.size.x;
if (this.canvas.height != this.size.y)
this.canvas.height = this.size.y;
this.cursor = CURSORS.default;
this.draw();
if (this.isPlacing) {
this.canvas.style.cursor = CURSORS.default;
} else {
this.canvas.style.cursor = this.cursor;
}
window.requestAnimationFrame(() => {
this.render();
});
}
}

View file

@ -0,0 +1,56 @@
import { COLORS, CURSORS, PIN } from "../../../config/apps/logicSim.config";
import Vector2 from "../../math/vector2";
import { Chip } from "./chip";
import { Circuit } from "./circuit";
import { State } from "./state";
import { Wire } from "./wire";
export class Pin {
name: string;
position: Vector2;
attachedChip: Chip;
index: number;
circuit: Circuit;
state = State.LOW;
isInput: boolean;
outputWire: Wire;
constructor(circuit: Circuit, name: string, isInput: boolean, attachedChip: Chip, index: number) {
Object.assign(this, { circuit, name, isInput, attachedChip, index });
}
setOutputWire(wire: Wire) {
this.outputWire = wire;
this.outputWire.setState(this.state);
}
setState(state: State) {
if (this.state.isEqual(state))
return;
this.state = state;
this.update();
}
update() {
if (this.isInput) {
this.outputWire?.setState(this.state);
}
}
draw() {
let color = COLORS.pin.fill;
if (this.circuit.mousePosition.getDistance(this.position.x, this.position.y) <= PIN.radius) {
this.circuit.cursor = CURSORS.pointer;
color = COLORS.pin.fillHover;
}
this.circuit.drawCircle(
this.circuit.getColor(color),
this.position.x, this.position.y,
PIN.radius
);
}
}

View file

@ -0,0 +1,18 @@
export class State {
value: number;
static LOW = new State(0);
static HIGH = new State(1);
constructor(value: number) {
this.value = value;
}
static invert(state: State) {
return new State(1 - state.value);
}
isEqual(state: State) {
return this.value === state.value;
}
}

View file

@ -0,0 +1,30 @@
import Vector2 from "../../math/vector2";
import { Pin } from "./pin";
import { State } from "./state";
export class Wire {
color: string;
state = State.LOW;
inputPin: Pin;
outputPin: Pin;
anchorPoints: Vector2[];
constructor(color: string, inputPin?: Pin, outputPin?: Pin, anchorPoints?: Vector2[]) {
Object.assign(this, { color, inputPin, outputPin, anchorPoints });
}
setState(state: State) {
if (this.state.isEqual(state))
return;
this.state = state;
this.update();
}
update() {
if (this.outputPin == null)
return;
this.outputPin.setState(this.state);
}
}

View file

@ -2,9 +2,15 @@ export default class Vector2 {
x: number;
y: number;
static ZERO = new Vector2(0, 0);
static get ZERO() {
return new Vector2(0, 0);
};
constructor(x: number, y: number) {
get clone() {
return new Vector2(this.x, this.y);
}
constructor(x: number, y?: number) {
this.x = x;
this.y = y ?? x;
}
@ -14,4 +20,22 @@ export default class Vector2 {
this.y = Math.round(this.y);
return this;
}
getDistance(x: number, y?: number): number;
getDistance(vector2: Vector2): number;
getDistance(x: unknown, y?: unknown): number {
let deltaX = 0, deltaY = 0;
if (x instanceof Vector2) {
const vector2 = x;
deltaX = this.x - vector2.x;
deltaY = this.y - vector2.y;
} else {
deltaX = this.x - (x as number);
deltaY = this.y - (y as number);
}
return Math.sqrt(deltaX * deltaX + deltaY * deltaY);
}
}

View file

@ -67,6 +67,6 @@ export default class ModalsManager {
}
static getModalIconUrl(name: string): string {
return `${process.env.PUBLIC_URL}/assets/modals/icons/${name}.svg`;
return `${(process.env as NodeJS.ProcessEnv).PUBLIC_URL}/assets/modals/icons/${name}.svg`;
}
}

View file

@ -110,7 +110,7 @@ export function loadDefaultData(virtualRoot: VirtualRoot) {
folder.createFolders(["outfit", "roboto-mono"]);
}).createFolder("screenshots", (folder) => {
folder.createFile("screenshot", "png", (file) => {
file.setSource(`${process.env.PUBLIC_URL}/assets/screenshots/screenshot-files-settings-taskbar-desktop.png`);
file.setSource(`${(process.env as NodeJS.ProcessEnv).PUBLIC_URL}/assets/screenshots/screenshot-files-settings-taskbar-desktop.png`);
});
}).createFolder("wallpapers", (folder) => {
folder.setProtected(true);
@ -122,34 +122,34 @@ export function loadDefaultData(virtualRoot: VirtualRoot) {
});
}
}).createFile("banner", "png", (file) => {
file.setSource(`${process.env.PUBLIC_URL}/assets/banner-logo-title.png`);
file.setSource(`${(process.env as NodeJS.ProcessEnv).PUBLIC_URL}/assets/banner-logo-title.png`);
}).createFile("logo", "svg", (file) => {
file.setSource(`${process.env.PUBLIC_URL}/assets/logo.svg`);
file.setSource(`${(process.env as NodeJS.ProcessEnv).PUBLIC_URL}/assets/logo.svg`);
});
}).createFolder("config", (folder) => {
folder.createFile("apps", "xml", (file) => {
file.setSource(`${process.env.PUBLIC_URL}/config/apps.xml`);
file.setSource(`${(process.env as NodeJS.ProcessEnv).PUBLIC_URL}/config/apps.xml`);
}).createFile("desktop", "xml", (file) => {
file.setSource(`${process.env.PUBLIC_URL}/config/desktop.xml`);
file.setSource(`${(process.env as NodeJS.ProcessEnv).PUBLIC_URL}/config/desktop.xml`);
}).createFile("taskbar", "xml", (file) => {
file.setSource(`${process.env.PUBLIC_URL}/config/taskbar.xml`);
file.setSource(`${(process.env as NodeJS.ProcessEnv).PUBLIC_URL}/config/taskbar.xml`);
}).createFile("theme", "xml", (file) => {
file.setSource(`${process.env.PUBLIC_URL}/config/theme.xml`);
file.setSource(`${(process.env as NodeJS.ProcessEnv).PUBLIC_URL}/config/theme.xml`);
});
}).createFolder("documents", (folder) => {
folder.createFile("info", "md", (file) => {
file.setSource(`${process.env.PUBLIC_URL}/documents/info.md`);
file.setSource(`${(process.env as NodeJS.ProcessEnv).PUBLIC_URL}/documents/info.md`);
}).createFile("links", "md", (file) => {
file.setSource(`${process.env.PUBLIC_URL}/documents/links.md`);
file.setSource(`${(process.env as NodeJS.ProcessEnv).PUBLIC_URL}/documents/links.md`);
});
}).createFile("favicon", "ico", (file) => {
file.setSource(`${process.env.PUBLIC_URL}/favicon.ico`);
file.setSource(`${(process.env as NodeJS.ProcessEnv).PUBLIC_URL}/favicon.ico`);
}).createFile("index", "html", (file) => {
file.setSource(`${process.env.PUBLIC_URL}/index.html`);
file.setSource(`${(process.env as NodeJS.ProcessEnv).PUBLIC_URL}/index.html`);
}).createFile("robots", "txt", (file) => {
file.setSource(`${process.env.PUBLIC_URL}/robots.txt`);
file.setSource(`${(process.env as NodeJS.ProcessEnv).PUBLIC_URL}/robots.txt`);
}).createFile("sitemap", "xml", (file) => {
file.setSource(`${process.env.PUBLIC_URL}/sitemap.xml`);
file.setSource(`${(process.env as NodeJS.ProcessEnv).PUBLIC_URL}/sitemap.xml`);
});
});

View file

@ -26,6 +26,7 @@
--dark-grey-ca: hsl(212, 27%, 12%);
--dark-grey-d: hsl(212, 14%, 10%);
--dark-grey-e: hsl(212, 12%, 8%);
--dark-grey-f: hsl(212, 10%, 6%);
--foreground-color-a: var(--white-a);
--foreground-color-b: var(--grey-a);

6
src/types/environment.d.ts vendored Normal file
View file

@ -0,0 +1,6 @@
declare namespace NodeJS {
interface ProcessEnv {
NODE_ENV: "development" | "production" | "test";
PUBLIC_URL: string;
}
}