diff --git a/package-lock.json b/package-lock.json index 2b3b42f..ab8374f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "devDependencies": { "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", + "copy-webpack-plugin": "^12.0.2", "css-loader": "^6.10.0", "eslint": "^8.44.0", "sass": "^1.71.0", @@ -262,6 +263,18 @@ "node": ">= 8" } }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@types/eslint": { "version": "8.56.2", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.2.tgz", @@ -753,6 +766,45 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, "node_modules/ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -1028,6 +1080,127 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/copy-webpack-plugin": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-12.0.2.tgz", + "integrity": "sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA==", + "dev": true, + "dependencies": { + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.1", + "globby": "^14.0.0", + "normalize-path": "^3.0.0", + "schema-utils": "^4.2.0", + "serialize-javascript": "^6.0.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/copy-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/copy-webpack-plugin/node_modules/globby": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.1.tgz", + "integrity": "sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ==", + "dev": true, + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.2", + "ignore": "^5.2.4", + "path-type": "^5.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/copy-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/copy-webpack-plugin/node_modules/path-type": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", + "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/copy-webpack-plugin/node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/copy-webpack-plugin/node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2474,6 +2647,15 @@ "node": ">= 10.13.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -2985,6 +3167,18 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", diff --git a/package.json b/package.json index f2e6cbe..cca2efa 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "devDependencies": { "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", + "copy-webpack-plugin": "^12.0.2", "css-loader": "^6.10.0", "eslint": "^8.44.0", "sass": "^1.71.0", diff --git a/src/events.ts b/src/events.ts index 50e8113..68e184b 100644 --- a/src/events.ts +++ b/src/events.ts @@ -72,6 +72,12 @@ export enum UiEvent { // Ui Events EditOn, EditOff, + ConsoleOn, + ConsoleOff, + ExplainerOn, + ExplainerOff, + VideoOn, + VideoOff, } interface UiEventMap { diff --git a/src/include/explainer.png b/src/include/explainer.png new file mode 100644 index 0000000..7ed74b8 Binary files /dev/null and b/src/include/explainer.png differ diff --git a/src/include/index.html b/src/include/index.html new file mode 100644 index 0000000..58ad3e9 --- /dev/null +++ b/src/include/index.html @@ -0,0 +1,38 @@ + + + + + + + Virtual 8-Bit Computer + + +
+
+
VIRTUAL 8-BIT COMPUTER
+
+
+
←REGISTERS
+
MEMORY↯
+
+
+ +
+ + + + + +
+ +
+
+
+
+
+
+
+ +

+	
+
diff --git a/src/include/pencil.png b/src/include/pencil.png
new file mode 100644
index 0000000..e36d1d6
Binary files /dev/null and b/src/include/pencil.png differ
diff --git a/src/include/texout.png b/src/include/texout.png
new file mode 100644
index 0000000..7b8aab3
Binary files /dev/null and b/src/include/texout.png differ
diff --git a/src/include/tv.png b/src/include/tv.png
new file mode 100644
index 0000000..7c0aba0
Binary files /dev/null and b/src/include/tv.png differ
diff --git a/src/index.ts b/src/index.ts
index 01e474a..65f9d24 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -10,7 +10,7 @@ import { generate_isa } from "./isaGenerator";
 import { UI } from "./ui";
 import { u8 } from "./num";
 
-import "./style.scss";
+import "./style/style.scss";
 import { CpuEvent } from "./events";
 
 declare global {
diff --git a/src/style.scss b/src/style.scss
deleted file mode 100644
index 347237e..0000000
--- a/src/style.scss
+++ /dev/null
@@ -1,310 +0,0 @@
-* {
-	box-sizing: border-box;
-}
-
-pre {
-	font-size: 0.5em;
-}
-
-:root {
-	--Border: #ffff00;
-	--mem-instruction: #3af78f;
-	--mem-memory: #ff26a8;
-	--mem-register: #9e0ef7;
-	--mem-constant: #19f7f0;
-	--mem-invalid: #bf2e2e;
-}
-
-img {
-	image-rendering: pixelated;
-}
-
-body {
-	color: #808080;
-	background-color: black;
-	font-size: 2.5em;
-	font-family: monospace;
-}
-
-#labelcontainer {
-	column-gap: 18px;
-	font-size: 0.85em;
-	display: flex;
-	align-items: center;
-	user-select: none;
-	grid-area: regmemlabel;
-}
-
-#main {
-	justify-content: center;
-	display: grid;
-	grid-template-columns: min-content max-content max-content min-content 500px;
-	grid-template-rows: min-content 1.5fr 10px 2fr 2fr min-content;
-	grid-template-areas:
-		"cycles registers regmemlabel . explainer "
-		"title .         .           ribbon explainer "
-		"title .         .           ribbon ."
-		"title .         .           ribbon printout "
-		"title .         .           . printout "
-		".     buttons   buttons     . .";
-	#memory {
-		grid-column: 2/4;
-		grid-row: 2/6;
-	}
-	#cycles {
-		grid-area: cycles;
-		text-align: left;
-		align-self: center;
-		font-size: 0.48em;
-	}
-	#title {
-		grid-area: title;
-		writing-mode: vertical-lr;
-		text-align: left;
-		user-select: none;
-		transform: scale(-1, -1);
-	}
-	#ribbon_menu {
-		grid-area: ribbon;
-	}
-}
-
-#printout {
-	border: 4px dashed var(--Border);
-	grid-area: printout;
-	padding: 10px;
-	word-wrap: break-word;
-	word-break: break-all;
-	max-height: 200px;
-	overflow-x: scroll;
-}
-
-#instruction_explainer {
-	grid-area: explainer;
-	display: flex;
-	flex-direction: column;
-	gap: 5px;
-	border: 5px dashed var(--Border);
-	#expl_box {
-		padding-inline: 20px;
-		padding-block-start: 10px;
-	}
-
-	#expl_text {
-		font-size: 0.6em;
-		text-align: center;
-		vertical-align: center;
-	}
-
-	#expl_icon {
-		margin-inline-end: 0.5em;
-		font-size: 30px;
-		padding: 5px;
-		color: var(--color);
-		outline: 3px dashed var(--color);
-	}
-}
-
-.invalid {
-	--color: var(--mem-invalid);
-}
-.constant {
-	--color: var(--mem-constant);
-}
-.register {
-	--color: var(--mem-register);
-}
-.memory {
-	--color: var(--mem-memory);
-}
-.instruction {
-	--color: var(--mem-instruction);
-}
-
-div[contenteditable] {
-	&.caret_selected {
-		box-sizing: border-box;
-		outline: 2px solid red;
-	}
-	outline: none;
-}
-
-.pending_edit {
-	color: green;
-}
-#memory {
-	grid-area: memory;
-	display: grid;
-	grid-template-columns: repeat(16, min-content);
-	gap: 5px;
-	padding: 10px;
-	border: 5px solid yellow;
-
-	div {
-		user-select: none;
-		caret-color: transparent;
-		text-align: center;
-		color: var(--color);
-	}
-
-	.program_counter {
-		outline: 3px solid orange;
-	}
-
-	.instruction_argument,
-	.current_instruction {
-		outline: 3px dashed var(--color);
-	}
-	.recent_edit {
-		color: lime;
-	}
-	div.last_access {
-		color: orange;
-	}
-
-	.invalid {
-		&::after {
-			user-select: none;
-			float: right;
-			position: relative;
-			right: 0.5em;
-			width: 0px;
-			font-size: 0.5em;
-			content: "!";
-		}
-	}
-}
-
-div#main.editor {
-	#memory,
-	#registers {
-		border-style: dashed;
-		div {
-			cursor: text;
-		}
-	}
-}
-
-#ribbon_menu {
-	margin-inline: 8px;
-	.editor_toggle {
-		//TODO CHANGE COLORS WHen
-		&.off {
-		}
-		&.on {
-		}
-	}
-}
-
-#registers {
-	grid-area: registers;
-	border: 5px solid yellow;
-	border-bottom: none !important;
-
-	grid-template-columns: repeat(8, min-content);
-	max-width: fit-content;
-	display: grid;
-	column-gap: 5px;
-	color: lightgray;
-	padding: 10px;
-
-	div {
-		max-height: min-content;
-		// margin-block: auto;
-	}
-}
-
-button,
-label.button {
-	border: 4px solid yellow;
-	color: gray;
-	margin: 10px;
-	margin-inline: 0px;
-	padding: 10px;
-	font-size: 0.8em;
-	font-family: monospace;
-	background-color: transparent;
-	cursor: pointer;
-	user-select: none;
-}
-
-button:disabled {
-	border: 4px solid rgb(255, 255, 128);
-	color: orange;
-	cursor: default;
-}
-
-input[type="range"]:disabled {
-	cursor: default;
-}
-
-button.no_style {
-	border: none;
-	color: inherit;
-	margin: 0;
-	padding: 0;
-	font-size: inherit;
-	background-color: inherit;
-}
-
-button:hover,
-label.button:hover {
-	color: white;
-}
-
-#controls_bar {
-	grid-area: buttons;
-
-	display: flex;
-	gap: 10px;
-	#controls_buttons {
-		display: flex;
-		gap: inherit;
-	}
-}
-
-input[type="range"] {
-	background-color: transparent;
-	-webkit-appearance: none;
-	appearance: none;
-	margin: 18px 0;
-}
-
-input[type="range"]:focus {
-	outline: none;
-}
-// 2024 and we still have to do this
-input[type="range"]::-webkit-slider-runnable-track {
-	height: 0.5em;
-	cursor: pointer;
-	background: yellow;
-	border-radius: 0px;
-}
-input[type="range"]::-webkit-slider-thumb {
-	border: 4px solid yellow;
-	background-color: black;
-	height: 42px;
-	width: 20px;
-	border-radius: 0px;
-	cursor: pointer;
-	margin-top: -18px;
-	-webkit-appearance: none;
-}
-
-input[type="range"]:focus::-webkit-slider-runnable-track {
-	background: yellow;
-}
-input[type="range"]::-moz-range-track {
-	height: 0.5em;
-	cursor: pointer;
-	background: yellow;
-	border-radius: 0px;
-}
-input[type="range"]::-moz-range-thumb {
-	border: 4px solid yellow;
-	background-color: black;
-	height: 36px;
-	width: 16px;
-	border-radius: 0px;
-	cursor: pointer;
-}
diff --git a/src/style/buttons.scss b/src/style/buttons.scss
new file mode 100644
index 0000000..74b34d4
--- /dev/null
+++ b/src/style/buttons.scss
@@ -0,0 +1,101 @@
+@use "sass:math";
+
+button,
+label.button {
+	border: 4px solid yellow;
+	color: gray;
+	margin: 10px;
+	margin-inline: 0px;
+	padding: 10px;
+	font-size: 0.8em;
+	font-family: monospace;
+	background-color: transparent;
+	cursor: pointer;
+	user-select: none;
+}
+
+button:disabled {
+	border: 4px solid rgb(255, 255, 128);
+	color: orange;
+	cursor: default;
+}
+
+input[type="range"]:disabled {
+	cursor: default;
+}
+
+button.nostyle {
+	border: none;
+	color: inherit;
+	margin: 0;
+	padding: 0;
+	font-size: inherit;
+	background-color: inherit;
+}
+
+button:hover,
+label.button:hover {
+	color: white;
+}
+
+#controls_bar {
+	grid-area: buttons;
+
+	display: flex;
+	gap: 10px;
+	#controls_buttons {
+		display: flex;
+		gap: inherit;
+	}
+}
+
+input[type="range"] {
+	background-color: transparent;
+	-webkit-appearance: none;
+	appearance: none;
+	margin: 18px 0;
+	width: 100px;
+}
+
+input[type="range"]:focus {
+	outline: none;
+}
+
+// 2024 and we still have to do this
+
+@mixin sliderTrack {
+	height: 0.5em;
+	cursor: pointer;
+	background: yellow;
+	border-radius: 0px;
+}
+
+@mixin sliderThumb($height, $width) {
+	border: 4px solid yellow;
+	background-color: black;
+	height: $height;
+	width: $width;
+	border-radius: 0px;
+	cursor: pointer;
+}
+
+input[type="range"]::-webkit-slider-runnable-track {
+	@include sliderTrack;
+}
+input[type="range"]::-moz-range-track {
+	@include sliderTrack;
+}
+
+input[type="range"]::-webkit-slider-thumb {
+	@include sliderThumb(42px, 20px);
+	-webkit-appearance: none;
+	margin-top: -18px;
+}
+
+input[type="range"]::-moz-range-thumb {
+	@include sliderThumb(36px, 16px);
+}
+
+input[type="range"]:focus::-webkit-slider-runnable-track {
+	background: yellow;
+}
diff --git a/src/style/memory_registers.scss b/src/style/memory_registers.scss
new file mode 100644
index 0000000..8052540
--- /dev/null
+++ b/src/style/memory_registers.scss
@@ -0,0 +1,69 @@
+#memory {
+	grid-template-columns: repeat(16, min-content);
+
+	.program_counter {
+		outline: 3px solid orange;
+	}
+
+	.instruction_argument,
+	.current_instruction {
+		outline: 3px dashed var(--color);
+	}
+
+	.invalid {
+		&::after {
+			user-select: none;
+			float: right;
+			position: relative;
+			right: 0.5em;
+			width: 0px;
+			font-size: 0.5em;
+			content: "!";
+		}
+	}
+}
+#registers {
+	border-bottom: none !important;
+
+	grid-template-columns: repeat(8, min-content);
+	color: lightgray;
+}
+
+#labelcontainer {
+	column-gap: 18px;
+	font-size: 0.85em;
+	display: flex;
+	align-items: center;
+	user-select: none;
+}
+.celled_viewer {
+	display: grid;
+	max-width: fit-content;
+	gap: 5px;
+	border: 5px solid yellow;
+	padding: 10px;
+	user-select: none;
+
+	div {
+		.pending_edit {
+			color: green;
+		}
+		&.recent_edit {
+			color: lime;
+		}
+		&.last_access {
+			color: orange;
+		}
+		user-select: none;
+		caret-color: transparent;
+		text-align: center;
+		color: var(--color);
+		&[contenteditable] {
+			&.caret_selected {
+				box-sizing: border-box;
+				outline: 2px solid red;
+			}
+			outline: none;
+		}
+	}
+}
diff --git a/src/style/style.scss b/src/style/style.scss
new file mode 100644
index 0000000..64cd08d
--- /dev/null
+++ b/src/style/style.scss
@@ -0,0 +1,93 @@
+@use "memory_registers";
+@use "windows";
+@use "buttons";
+@use "vars";
+
+* {
+	box-sizing: border-box;
+}
+
+pre {
+	font-size: 0.5em;
+}
+
+img {
+	image-rendering: pixelated;
+}
+
+body {
+	color: #808080;
+	background-color: black;
+	font-size: 2.5em;
+	font-family: monospace;
+}
+
+main {
+	display: flex;
+	justify-content: center;
+}
+
+#grid {
+	justify-content: center;
+	display: grid;
+	grid-template-columns: min-content min-content min-content;
+	grid-template-rows: min-content min-content min-content;
+	grid-template-areas:
+		"cycles registers regmemlabel"
+		"title memory	 memory"
+		".     buttons   buttons     ";
+	#memory {
+		grid-area: memory;
+		// grid-column: 2/4;
+		// grid-row: 2/6;
+	}
+	#window_box {
+		grid-area: windowbox;
+	}
+	#registers {
+		grid-area: registers;
+	}
+	#labelcontainer {
+		grid-area: regmemlabel;
+	}
+	#cycles {
+		grid-area: cycles;
+		text-align: left;
+		align-self: center;
+		font-size: 0.48em;
+		user-select: none;
+	}
+	#title {
+		grid-area: title;
+		writing-mode: vertical-lr;
+		text-align: left;
+		user-select: none;
+		transform: scale(-1, -1);
+	}
+}
+
+.invalid {
+	--color: var(--mem-invalid);
+}
+.constant {
+	--color: var(--mem-constant);
+}
+.register {
+	--color: var(--mem-register);
+}
+.memory {
+	--color: var(--mem-memory);
+}
+.instruction {
+	--color: var(--mem-instruction);
+}
+
+div#main.editor {
+	#memory,
+	#registers {
+		border-style: dashed;
+		div {
+			cursor: text;
+		}
+	}
+}
diff --git a/src/style/vars.scss b/src/style/vars.scss
new file mode 100644
index 0000000..d47c6f9
--- /dev/null
+++ b/src/style/vars.scss
@@ -0,0 +1,8 @@
+:root {
+	--border: #ffff00;
+	--mem-instruction: #3af78f;
+	--mem-memory: #ff26a8;
+	--mem-register: #9e0ef7;
+	--mem-constant: #19f7f0;
+	--mem-invalid: #bf2e2e;
+}
diff --git a/src/style/windows.scss b/src/style/windows.scss
new file mode 100644
index 0000000..dc4b24d
--- /dev/null
+++ b/src/style/windows.scss
@@ -0,0 +1,108 @@
+#resize {
+	// background-color: red;
+	width: 100%;
+	height: 18px;
+	position: absolute;
+	bottom: 0;
+	border-bottom: 5px solid yellow;
+
+	cursor: s-resize;
+}
+
+#window_box {
+	display: flex;
+	flex-direction: column;
+	gap: 10px;
+	margin-left: 10px;
+	max-width: 500px;
+	.window {
+		overflow-y: hidden;
+		position: relative;
+		border: 5px Solid var(--border);
+		border-bottom: unset;
+		&.collapsed {
+			.window_title {
+				// border-bottom: unset;
+			}
+		}
+		.window_title {
+			position: sticky;
+			user-select: none;
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			font-size: 0.6em;
+			color: lightgray;
+			border-bottom: 5px solid var(--border);
+			background: repeating-linear-gradient(
+					to top,
+					transparent,
+					transparent 2px,
+					transparent 2px,
+					yellow 2px,
+					yellow 4px
+				),
+				repeating-linear-gradient(to right, transparent, transparent 2px, transparent 2px, yellow 2px, yellow 4px);
+
+			#text {
+				display: inline-block;
+				text-align: center;
+				height: 100%;
+				padding-inline: 10px;
+				background-color: black;
+			}
+			#collapse_button {
+				height: 23px !important;
+				aspect-ratio: 1;
+				border: 2px solid white;
+				background-color: black;
+				margin-right: 3px;
+			}
+		}
+	}
+}
+
+.window#tv {
+	height: unset;
+	#screen {
+		max-width: 100%;
+		aspect-ratio: 1;
+	}
+}
+
+#instruction_explainer {
+	grid-area: explainer;
+	display: flex;
+	flex-direction: column;
+	gap: 5px;
+	height: 400px;
+	#expl_box {
+		padding-inline: 20px;
+		padding-block-start: 10px;
+	}
+
+	#expl_text {
+		font-size: 0.6em;
+		text-align: center;
+		vertical-align: center;
+	}
+
+	#expl_icon {
+		margin-inline-end: 0.5em;
+		font-size: 30px;
+		padding: 5px;
+		color: var(--color);
+		outline: 3px dashed var(--color);
+	}
+}
+
+#printout {
+	grid-area: printout;
+	height: 500px;
+	word-wrap: break-word;
+	word-break: break-all;
+	overflow-x: hidden;
+	#printout_text {
+		padding-inline: 10px;
+	}
+}
diff --git a/src/ui.ts b/src/ui.ts
index 84450b0..d29a03b 100644
--- a/src/ui.ts
+++ b/src/ui.ts
@@ -1,19 +1,16 @@
 import { CpuEvent, CpuEventHandler, UiEvent, UiEventHandler } from "./events";
 import { $ } from "./etc";
-import { InstructionExplainer } from "./ui/instructionExplainer";
+import { InstructionExplainer } from "./ui/windows/instructionExplainer";
 import { MemoryView } from "./ui/memoryView";
 import { frequencyIndicator } from "./ui/frequencyIndicator";
 import { RegisterView } from "./ui/registerView";
-import { Screen } from "./ui/screen";
-import { Ribbon } from "./ui/ribbon";
+import { Screen } from "./ui/windows/screen";
+import { EditButton } from "./ui/edit_button";
 import { UiComponent, UiComponentConstructor } from "./ui/uiComponent";
 import { pausePlay } from "./ui/pausePlay";
+import { Printout } from "./ui/windows/printout";
 
 export class UI {
-	printout: HTMLElement;
-
-	auto_running: boolean;
-
 	events: UiEventHandler = new UiEventHandler();
 
 	private components: Array;
@@ -30,27 +27,23 @@ export class UI {
 		this.register_component(frequencyIndicator, $("cycles"));
 		this.register_component(InstructionExplainer, $("instruction_explainer"));
 		this.register_component(RegisterView, $("registers"));
-		this.register_component(Screen, $("screen") as HTMLCanvasElement);
-		this.register_component(Ribbon, $("ribbon_menu"));
+		this.register_component(Screen, $("tv"));
+		this.register_component(Printout, $("printout"));
+		this.register_component(EditButton, $("edit_button"));
 		this.register_component(pausePlay, $("controls_buttons"));
-		this.printout = $("printout");
 
-		this.auto_running = false;
 		const pp_button = $("pause_play_button");
 	}
-	private register_component(c: UiComponentConstructor, e: HTMLElement): void {
+	private register_component(ctor: UiComponentConstructor, e: HTMLElement): void {
 		if (e === undefined) {
-			console.log(c);
+			console.log(ctor);
 			throw new Error("Could not find HTML element while registering UI component");
 		}
-		const component = new c(e, this.events);
+		const component = new ctor(e, this.events);
 		this.components.push(component);
 	}
 
 	init_events(cpu_events: CpuEventHandler): void {
-		cpu_events.listen(CpuEvent.Print, (char) => {
-			this.printout.textContent = (this.printout.textContent ?? "") + char;
-		});
 		cpu_events.listen(CpuEvent.Reset, () => {
 			this.reset();
 		});
@@ -64,6 +57,5 @@ export class UI {
 		for (const c of this.components) {
 			c.reset();
 		}
-		this.printout.textContent = "";
 	}
 }
diff --git a/src/ui/celledViewer.ts b/src/ui/celledViewer.ts
index b75520a..0e80068 100644
--- a/src/ui/celledViewer.ts
+++ b/src/ui/celledViewer.ts
@@ -19,6 +19,7 @@ export abstract class CelledViewer {
 		this.element = element;
 		this.width = width;
 		this.height = height;
+		this.element.classList.add("celled_viewer");
 		for (let i = 0; i < this.width * this.height; i++) {
 			const mem_cell_el = el("div");
 			mem_cell_el.append("0", "0");
diff --git a/src/ui/edit_button.ts b/src/ui/edit_button.ts
new file mode 100644
index 0000000..c9ee99b
--- /dev/null
+++ b/src/ui/edit_button.ts
@@ -0,0 +1,41 @@
+import { el, $ } from "../etc";
+import { UiEventHandler, UiEvent } from "../events";
+import { UiComponent } from "./uiComponent";
+
+export class EditButton implements UiComponent {
+	element: HTMLElement;
+	events: UiEventHandler;
+	constructor(element: HTMLElement, event: UiEventHandler) {
+		this.element = element;
+		this.events = event;
+
+		const image = el("img");
+		image.src = "pencil.png";
+		image.style.width = "20px";
+		image.style.height = "20px";
+		this.element.classList.add("editor_toggle");
+		this.element.addEventListener("click", () => this.edit_toggle());
+		this.element.appendChild(image);
+	}
+	reset(): void {
+		const is_on = this.element.classList.contains("on");
+		if (is_on) {
+			this.edit_toggle();
+		}
+	}
+
+	edit_toggle(): void {
+		const is_on = this.element.classList.contains("on");
+		if (is_on) {
+			this.element.classList.remove("on");
+			$("root").classList.remove("editor");
+			this.element.classList.add("off");
+			this.events.dispatch(UiEvent.EditOff);
+		} else {
+			this.events.dispatch(UiEvent.EditOn);
+			$("root").classList.add("editor");
+			this.element.classList.add("on");
+			this.element.classList.remove("off");
+		}
+	}
+}
diff --git a/src/ui/editableHex.ts b/src/ui/editableHex.ts
index 3c83ead..32f7fb1 100644
--- a/src/ui/editableHex.ts
+++ b/src/ui/editableHex.ts
@@ -24,6 +24,14 @@ export class EditorContext {
 			cell.addEventListener("keydown", (e) => {
 				this.keydown(e, i);
 			});
+			cell.addEventListener("input", (e) => {
+				const target = e.target as HTMLElement;
+				if (target === null) return;
+				const text = target.textContent ?? "";
+				if (text.length !== 2) {
+					target.textContent = text.substring(0, 2);
+				}
+			});
 			cell.addEventListener("focus", () => {
 				if (!this.enabled) return;
 				this.current_cell_info.old = cell.textContent ?? "00";
diff --git a/src/ui/frequencyIndicator.ts b/src/ui/frequencyIndicator.ts
index 0006fc5..a0f7c53 100644
--- a/src/ui/frequencyIndicator.ts
+++ b/src/ui/frequencyIndicator.ts
@@ -37,6 +37,9 @@ export class frequencyIndicator implements UiComponent {
 			this.element.textContent = `${value}hz`;
 			this.last_value = value;
 		}
+		if (value === 0) {
+			this.element.textContent = "";
+		}
 		this.last_time = new_time;
 		this.count = 0;
 	}
diff --git a/src/ui/pausePlay.ts b/src/ui/pausePlay.ts
index 7973a25..66312f8 100644
--- a/src/ui/pausePlay.ts
+++ b/src/ui/pausePlay.ts
@@ -1,5 +1,5 @@
 import { el } from "../etc";
-import { UiEventHandler, CpuEventHandler, UiEvent } from "../events";
+import { UiEventHandler, UiEvent } from "../events";
 import { UiComponent } from "./uiComponent";
 
 const MAX_SLIDER = 1000;
diff --git a/src/ui/ribbon.ts b/src/ui/ribbon.ts
deleted file mode 100644
index 9c04d76..0000000
--- a/src/ui/ribbon.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-import { el, $ } from "../etc";
-import { UiEventHandler, UiEvent } from "../events";
-import { UiComponent } from "./uiComponent";
-
-function new_button(name: string, img_path: string, additional_class?: string): HTMLButtonElement {
-	const button = el("button", "", "no_style ribbon_button");
-	const image = el("img");
-	image.src = img_path;
-	image.width = 64;
-	image.height = 64;
-	if (additional_class !== undefined) {
-		button.classList.add(additional_class);
-	}
-	button.appendChild(image);
-	return button;
-}
-
-export class Ribbon implements UiComponent {
-	element: HTMLElement;
-	events: UiEventHandler;
-	edit_button: HTMLButtonElement;
-	console_button: HTMLButtonElement;
-	display_button: HTMLButtonElement;
-	explainer_button: HTMLButtonElement;
-	constructor(element: HTMLElement, event: UiEventHandler) {
-		this.element = element;
-		this.events = event;
-
-		this.edit_button = new_button("Edit", "pencil.png", "editor_toggle");
-		this.console_button = new_button("Console", "texout.png");
-		this.display_button = new_button("Video", "tv.png");
-		this.explainer_button = new_button("Explainer", "explainer.png");
-		this.edit_button.addEventListener("click", () => this.edit_toggle());
-		this.element.appendChild(this.edit_button);
-		this.element.appendChild(this.console_button);
-		this.element.appendChild(this.display_button);
-		this.element.appendChild(this.explainer_button);
-	}
-	reset(): void {
-		const is_on = this.edit_button.classList.contains("on");
-		if (is_on) {
-			this.edit_toggle();
-		}
-	}
-
-	edit_toggle(): void {
-		const is_on = this.edit_button.classList.contains("on");
-		if (is_on) {
-			this.edit_button.classList.remove("on");
-			$("main").classList.remove("editor");
-			this.edit_button.classList.add("off");
-			this.events.dispatch(UiEvent.EditOff);
-		} else {
-			this.events.dispatch(UiEvent.EditOn);
-			$("main").classList.add("editor");
-			this.edit_button.classList.add("on");
-			this.edit_button.classList.remove("off");
-		}
-	}
-}
diff --git a/src/ui/uiComponent.ts b/src/ui/uiComponent.ts
index 9b2a16a..d970ea2 100644
--- a/src/ui/uiComponent.ts
+++ b/src/ui/uiComponent.ts
@@ -9,7 +9,9 @@ export interface UiComponent {
 	element: HTMLElement;
 	/** Allows listening and emitting UiEvents*/
 	events: UiEventHandler;
+	/** Completely reset the state of the component */
 	reset: () => void;
+	soft_reset?: () => void;
 	/**  Allows listening CPUEvents*/
 	init_cpu_events?: (c: CpuEventHandler) => void;
 }
diff --git a/src/ui/windowBox.ts b/src/ui/windowBox.ts
new file mode 100644
index 0000000..32415d6
--- /dev/null
+++ b/src/ui/windowBox.ts
@@ -0,0 +1,82 @@
+import { el } from "../etc";
+export abstract class WindowBox {
+	element: HTMLElement;
+	readonly title: string;
+	title_bar: HTMLElement;
+	private resize: HTMLElement;
+	private collapse_button: HTMLButtonElement;
+	private collapsed: boolean = false;
+	private resize_func: (e: MouseEvent) => void;
+
+	constructor(element: HTMLElement, title: string, options?: { collapsed?: boolean }) {
+		this.element = element;
+		this.title = title;
+		this.element.classList.add("window");
+		this.title_bar = el("div", undefined, "window_title");
+		this.element.appendChild(this.title_bar);
+		const title_bar_text_box = el("div", "text");
+		title_bar_text_box.textContent = title;
+		this.title_bar.appendChild(title_bar_text_box);
+		this.resize = el("div", "resize");
+		this.element.appendChild(this.resize);
+		this.resize_func = this.resize_move.bind(this);
+		this.collapse_button = el("button", "collapse_button", "nostyle");
+		this.collapse_button.addEventListener("click", () => {
+			this.toggle_collapse();
+		});
+		this.title_bar.appendChild(this.collapse_button);
+		this.resize.addEventListener("mousedown", (e) => {
+			window.addEventListener("mousemove", this.resize_func);
+		});
+		window.addEventListener("mouseup", () => {
+			this.remove_resize_listeners();
+		});
+		window.addEventListener("mouseleave", () => {
+			this.remove_resize_listeners();
+		});
+		if (options?.collapsed) {
+			this.collapse();
+		}
+	}
+
+	collapse(): void {
+		this.element.classList.add("collapsed");
+		this.remove_resize_listeners();
+		this.resize.style.visibility = "hidden";
+		this.element.style.height = `${this.title_bar.offsetHeight + 4}px`;
+		this.collapsed = true;
+	}
+
+	toggle_collapse(): void {
+		if (this.collapsed) {
+			this.uncollapse();
+		} else {
+			this.collapse();
+		}
+	}
+
+	uncollapse(): void {
+		this.element.classList.remove("collapsed");
+		this.resize.style.visibility = "unset";
+		this.element.style.height = `${this.title_bar.offsetHeight + 10 + 200}px`;
+		this.collapsed = false;
+	}
+
+	remove_resize_listeners(): void {
+		window.removeEventListener("mousemove", this.resize_func);
+	}
+
+	resize_move(e: MouseEvent): void {
+		if (this.collapsed) {
+			this.uncollapse();
+			this.remove_resize_listeners();
+			return;
+		}
+		const distance_to_title = e.clientY - this.element.offsetTop - this.title_bar.offsetHeight + window.scrollY + 5;
+		if (distance_to_title <= 5) {
+			this.collapse();
+			return;
+		}
+		this.element.style.height = `${e.clientY - this.element.offsetTop + window.scrollY + 8}px`;
+	}
+}
diff --git a/src/ui/bankIndicator.ts b/src/ui/windows/bankIndicator.ts
similarity index 71%
rename from src/ui/bankIndicator.ts
rename to src/ui/windows/bankIndicator.ts
index 19f429e..3109465 100644
--- a/src/ui/bankIndicator.ts
+++ b/src/ui/windows/bankIndicator.ts
@@ -1,6 +1,6 @@
-import { UiEventHandler, CpuEventHandler, CpuEvent } from "../events";
-import { u1, u2 } from "../num";
-import { UiComponent } from "./uiComponent";
+import { UiEventHandler, CpuEventHandler, CpuEvent } from "../../events";
+import { u1, u2 } from "../../num";
+import { UiComponent } from "../uiComponent";
 
 class BankIndicator implements UiComponent {
 	element: HTMLElement;
diff --git a/src/ui/instructionExplainer.ts b/src/ui/windows/instructionExplainer.ts
similarity index 71%
rename from src/ui/instructionExplainer.ts
rename to src/ui/windows/instructionExplainer.ts
index 422d1cd..a83d334 100644
--- a/src/ui/instructionExplainer.ts
+++ b/src/ui/windows/instructionExplainer.ts
@@ -1,14 +1,14 @@
-import { el, format_hex } from "../etc";
-import { CpuEvent, CpuEventHandler, UiEventHandler } from "../events";
-import { Instruction, ParamType, ParameterType } from "../instructionSet";
-import { u8 } from "../num";
-import { UiComponent } from "./uiComponent";
+import { el, format_hex } from "../../etc";
+import { CpuEvent, CpuEventHandler, UiEventHandler } from "../../events";
+import { Instruction, ParamType, ParameterType } from "../../instructionSet";
+import { u8 } from "../../num";
+import { WindowBox } from "../windowBox";
+import { UiComponent } from "../uiComponent";
 
-export class InstructionExplainer implements UiComponent {
-	element: HTMLElement;
+export class InstructionExplainer extends WindowBox implements UiComponent {
 	events: UiEventHandler;
 	constructor(element: HTMLElement, e: UiEventHandler) {
-		this.element = element;
+		super(element, "Instruction Explainer");
 		this.events = e;
 	}
 	add_instruction(instr: Instruction, pos: u8, byte: u8): void {
@@ -29,7 +29,7 @@ export class InstructionExplainer implements UiComponent {
 		this.element.appendChild(instr_box);
 	}
 
-	add_param(param: ParameterType, pos: u8, byte: u8): void {
+	add_parameter(param: ParameterType, pos: u8, byte: u8): void {
 		const t = param.type;
 		let name;
 		if (t === ParamType.Const) {
@@ -51,7 +51,7 @@ export class InstructionExplainer implements UiComponent {
 
 	init_cpu_events(c: CpuEventHandler): void {
 		c.listen(CpuEvent.ParameterParsed, ({ param, code, pos }) => {
-			this.add_param(param, pos, code);
+			this.add_parameter(param, pos, code);
 		});
 		c.listen(CpuEvent.InstructionParsed, ({ instr, code, pos }) => {
 			this.add_instruction(instr, pos, code);
@@ -62,6 +62,6 @@ export class InstructionExplainer implements UiComponent {
 	}
 
 	reset(): void {
-		this.element.innerHTML = "";
+		this.element.querySelectorAll("#expl_box").forEach((e) => e.remove());
 	}
 }
diff --git a/src/ui/windows/printout.ts b/src/ui/windows/printout.ts
new file mode 100644
index 0000000..f24e0bc
--- /dev/null
+++ b/src/ui/windows/printout.ts
@@ -0,0 +1,25 @@
+import { el } from "../../etc";
+import { CpuEvent, CpuEventHandler, UiEventHandler } from "../../events";
+import { WindowBox } from "../windowBox";
+import { UiComponent } from "../uiComponent";
+
+export class Printout extends WindowBox implements UiComponent {
+	events: UiEventHandler;
+	text_box: HTMLElement;
+	constructor(element: HTMLElement, events: UiEventHandler) {
+		super(element, "Printout");
+		this.events = events;
+		this.text_box = el("div", "printout_text");
+		this.element.appendChild(this.text_box);
+	}
+
+	init_cpu_events(c: CpuEventHandler): void {
+		c.listen(CpuEvent.Print, (c) => {
+			this.text_box.textContent += c;
+		});
+	}
+
+	reset(): void {
+		this.text_box.textContent = "";
+	}
+}
diff --git a/src/ui/screen.ts b/src/ui/windows/screen.ts
similarity index 59%
rename from src/ui/screen.ts
rename to src/ui/windows/screen.ts
index 67d96b5..058f7dc 100644
--- a/src/ui/screen.ts
+++ b/src/ui/windows/screen.ts
@@ -1,32 +1,39 @@
-import { UiEventHandler, CpuEventHandler, CpuEvent } from "../events";
-import { u4, u8 } from "../num";
-import { UiComponent } from "./uiComponent";
-export class Screen implements UiComponent {
-	element: HTMLCanvasElement;
+import { el } from "../../etc";
+import { UiEventHandler, CpuEventHandler, CpuEvent } from "../../events";
+import { u4, u8 } from "../../num";
+import { UiComponent } from "../uiComponent";
+import { WindowBox } from "../windowBox";
+export class Screen extends WindowBox implements UiComponent {
 	events: UiEventHandler;
+	screen: HTMLCanvasElement;
 	ctx: CanvasRenderingContext2D;
 	scale: [number, number];
 	constructor(element: HTMLElement, event: UiEventHandler) {
-		this.element = element as HTMLCanvasElement;
+		super(element, "TV", { collapsed: true });
+		this.screen = el("canvas", "screen");
 		this.events = event;
 		const canvas_size = [512, 512];
 		const data_size = [16, 16];
 		this.scale = [canvas_size[0] / data_size[0], canvas_size[1] / data_size[1]];
-		[this.element.width, this.element.height] = canvas_size;
-		const ctx = this.element.getContext("2d");
+		[this.screen.width, this.screen.height] = canvas_size;
+		const ctx = this.screen.getContext("2d");
 		if (ctx === null) {
-			throw new Error("todo");
+			throw new Error("could not load screen");
 		}
 		this.ctx = ctx;
-		// for (let x = 0; x < 16; x++) {
-		// 	for (let y = 0; y < 16; y++) {
-		// 		this.setPixel(x as u4, y as u4, (x + 16 * y) as u8);
-		// 	}
-		// }
+		this.element.appendChild(this.screen);
+	}
+
+	private test_pattern(): void {
+		for (let x = 0; x < 16; x++) {
+			for (let y = 0; y < 16; y++) {
+				this.setPixel(x as u4, y as u4, (x + 16 * y) as u8);
+			}
+		}
 	}
 
 	reset(): void {
-		const ctx = this.element.getContext("2d");
+		const ctx = this.screen.getContext("2d");
 		if (ctx === null) {
 			throw new Error("todo");
 		}
diff --git a/svg.html b/svg.html
new file mode 100644
index 0000000..e826c0c
--- /dev/null
+++ b/svg.html
@@ -0,0 +1,74 @@
+
+
+	
+		
+		
+		Document
+		
+	
+	
+		
+
CPU
+ + + + + + +
+
Main
+
Bank 1
+
Bank 2
+
VRAM
+
+
+ + diff --git a/todo.md b/todo.md index 75ce1d9..d859ee2 100644 --- a/todo.md +++ b/todo.md @@ -3,7 +3,6 @@ Edit Mode - Select where program counter is Implement HCF -Move start/stop/auto logic into computer Speed control slider behavior Speed control slider styling @@ -11,9 +10,6 @@ Overclock Box error in instruction when number out of range (fix new Error("todo")) -UI for screen (toggling (click an icon?)) -UI for togging other UI elements - UI for showing which Memory bank is selected VRAM select instruction diff --git a/webpack.config.js b/webpack.config.js index 60de58d..bf6fb8d 100755 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,4 +1,7 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +/* eslint-disable no-undef */ const path = require("path"); +const CopyPlugin = require("copy-webpack-plugin"); const config = { entry: "./src/index.ts", @@ -33,6 +36,11 @@ const config = { filename: "main.js", path: path.resolve(__dirname, "dist"), }, + plugins: [ + new CopyPlugin({ + patterns: [{ from: "./src/include", to: "./" }], + }), + ], }; module.exports = (env, argv) => { if (argv.mode === "development") {