From 1f6a95c2536ab54c6a765be845196fbb1fedb3a4 Mon Sep 17 00:00:00 2001 From: Alexander Bass Date: Wed, 6 Mar 2024 23:41:02 -0500 Subject: [PATCH] Webpack copy. add windows --- package-lock.json | 194 ++++++++++++ package.json | 1 + src/events.ts | 6 + src/include/explainer.png | Bin 0 -> 4441 bytes src/include/index.html | 38 +++ src/include/pencil.png | Bin 0 -> 4450 bytes src/include/texout.png | Bin 0 -> 4487 bytes src/include/tv.png | Bin 0 -> 4528 bytes src/index.ts | 2 +- src/style.scss | 310 ------------------- src/style/buttons.scss | 101 ++++++ src/style/memory_registers.scss | 69 +++++ src/style/style.scss | 93 ++++++ src/style/vars.scss | 8 + src/style/windows.scss | 108 +++++++ src/ui.ts | 28 +- src/ui/celledViewer.ts | 1 + src/ui/edit_button.ts | 41 +++ src/ui/editableHex.ts | 8 + src/ui/frequencyIndicator.ts | 3 + src/ui/pausePlay.ts | 2 +- src/ui/ribbon.ts | 60 ---- src/ui/uiComponent.ts | 2 + src/ui/windowBox.ts | 82 +++++ src/ui/{ => windows}/bankIndicator.ts | 6 +- src/ui/{ => windows}/instructionExplainer.ts | 22 +- src/ui/windows/printout.ts | 25 ++ src/ui/{ => windows}/screen.ts | 37 ++- svg.html | 74 +++++ todo.md | 4 - webpack.config.js | 8 + 31 files changed, 910 insertions(+), 423 deletions(-) create mode 100644 src/include/explainer.png create mode 100644 src/include/index.html create mode 100644 src/include/pencil.png create mode 100644 src/include/texout.png create mode 100644 src/include/tv.png delete mode 100644 src/style.scss create mode 100644 src/style/buttons.scss create mode 100644 src/style/memory_registers.scss create mode 100644 src/style/style.scss create mode 100644 src/style/vars.scss create mode 100644 src/style/windows.scss create mode 100644 src/ui/edit_button.ts delete mode 100644 src/ui/ribbon.ts create mode 100644 src/ui/windowBox.ts rename src/ui/{ => windows}/bankIndicator.ts (71%) rename src/ui/{ => windows}/instructionExplainer.ts (71%) create mode 100644 src/ui/windows/printout.ts rename src/ui/{ => windows}/screen.ts (59%) create mode 100644 svg.html 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 0000000000000000000000000000000000000000..7ed74b866f60b6c72ba4b40c38221606075f5c7e GIT binary patch literal 4441 zcmeHKdsGuw8XuqrnhHYIDk^8E1MY%2naN~Al951>SE2?Zusp1SlgtE0@*){X$cjZo zx2=^!&)QlaEiDM{aS^vg+U_a^k*a&N)ur9jqxHekuClAt>f*M#TKCTIKHGElaL)EW zl9SBb@BY5u{l4G*?mhRuWXwnpmM)M&5EQJ}rDTF9Ec}8b;NR`@4S~m?FFTLVq&=|H z<*;y87UnCQEX;a23j}#D?ObzlXAd6q$TRnOf5|3ety}-)iCu|UTt-~v&ZD~PA@K#> zKRnQOpO0CmDPE#{{?jMcWPK$$*jv|hQ+KIs-oKviPfu7kBW=Y7wkGbWWp}@AJbis- z|E>D1&9fIC=jW~c=fk__u!OpsH{f)*)Y|+>;>Tg3gZp(>SC3_UMmyC*$)mE3BI8p!cp{+aG zT0>f^;qL?bZ}lA*Ko)P_J!fr?_jKv3+m*M^Uhz~JF~ukUcrjqFec#`i<>#*F^jwIo z9%#_Kn6M}RR7+G--?ERs3G4g!`d5nXfAL7;`^)i={~AT9dc*{dap$wJ;ldvU=A1^0S!2Grktr z)&7>(e%Dg5`iE|3L195ZbvP{Q-Ktbw=sRz}9K4H;OgonG@;36#Uu=4Kvb7M|y}UIT zcb#kW4}R}v;Nz{=I@+bbnO%^n_TxWNe|kDP^wNw4%G37~%J-joGIW!qb?EiZ%s@+m z{z&`Lfv^jm!Ryw2K(ezwyzICAt(g~ht&h>IjGj~Vo#H|BBHa1ZP>Zj!|DiC!mdE@64t#1?UOEpq!OZShQK0(FVP}-1|yv8aIr8cBV{<6>gCD_BtZ(tyBG_V znUXd_0a{w5nCG1ohIu?5nMWaWxQZ~jMx()S0wV|%AW(OOou|F1-5n`V40EKgZnKMX z@|?pC3!JpcQO0W#1oXqB@!6aP!x+8YJ)#281M|{OOfJJQn+==j;pS7z0m(=}KkDJm z1`8U?WZjN3mzhm1XYG9CL<+_{=I<rmVk*J0y6ecsFz!^e0fl6<8^R(T}3RHkx#sMCdG~ujV4k&0s zg_58Jywwb9A}u(pF`49ynwUVb+QosYq^%R95~vtJMXOCFwT9K8auwhs&9n(sGYSUP zsN+;}HLg(6ghixc%v74gWuw7#ayGh%#hmscu|p7?N;2xT2qDAAEk-NNTY!TWS;g7Q zyyG3&oQ=)mX+cf73fHJ`@Kq`a4S}o1K{>3;4JuLKl;bi5DfS4{LV;v}u(VL8fIzf^ zT&QFhOY;s_w!>l7B0@;8;5pW90LzJ?c{+vWSwM;tB!w#|JT4oTQwkNOBvD*R;S=x< zhO<=sFSM|D;P~Mw*KuwzeuZcnUQt2PbfY30PF1jFK@plI`O3U0cbWyClE*Kn7) zn6?+OVD}g)*HJt7Bc-5Zl>})51t5=;D^OBR6DUop%qVMOOmf!Yly6P+WnI3#J?q3>!%p6ZVYJjN`HY(j;C4OxR?A z->?mAUSKc8#x}zd&4g{|C;W`e#ZNc_pq?INO8QRAH7(ba6qpity1J(2nvw!j0#8@h z|4lCG_;rf4gHwD1-BAxIV}{QSI4iRHi; z$m)TU2K%Fa7E zn41*R1qTImKkmG|z8v3?mQ)wU?9ZN#d-om|6sj)71VHbYVgtLb z_6ErSIW>e4SIHSAQ93Y zbzpA(!OogzlsaDVUR~Vr@QOK&`HKtxR-40gTnTQh{`H}**j&xC?R>beEfCU140&Ey temJ}9^_h*(+%iN{{bOfL=XS~ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..e36d1d602f61c27548532e1628bfcd26f5d5e9fc
GIT binary patch
literal 4450
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
q3|j*mJ_~rMsU~)7N5qWs5I?Q~55%M4zNx%n)
zg}TwXgu;cU;@!#Ugkm~-ZJ
zzk7e*y}x_Ed%o{?#)R4aev|z~B9Xsdr%8fuM7Vvt;nPu2&<$VT7bK_gNwgEO*{von
z2OxaD4Ise9nM5MjmAB@lTTUQjd)p_fifcqM0qMIgP5Jyrt?$;o2a*p2{{xIQ;kR9d
zwHI4_4{Xc{r&gRBwROiFwUJwTZ}aKT?!6l}t?+&p(;ibERR$9x%2WHO18Uk*CWmM;%ZU)
zOO5l~v|`bvj>)&qU%o2q@p_t{(j%IjoL5y_+qF_8@+jfdYNKAQeik{5cjv12RJ!_E
zzbS3qzfAjaz>9&ojmEOh3iRzGe%g=5Eh|ZRX|DK5;C3=3T>N%%c3s`>6CbaA^G{U)
zRmI3pUTvM%_jaJ6tG7&C*x)*q`@;QI_nW?T78xb7vv<~cZMW>W&BmR%mC|rwMsY`l
zVqMI(^plk#`>wzG<@bTtAHTLC^HIwaMZwpfpC~HQP0cLjCLEfOXuaayzN*b<#_8!@
zimp%flR{SC{H(2ON_$0<=lKm=w{=g=eLXGW=qDFXRr2S8->&SfuUdj`iL3I*?PvCQ
z`d8l_{d8^X7f1ZwpRhPd?!kX4KX)o@+?8KVrcOPI$=i9-cic+vs_xCTNuy0M`a?$!
zcLZLj^Kx6!nXgh0CCTZgP
zA>f}1&Ek2RQX+9WonogbJ)fP6??v`r!v;}Wx3GT6ewYx5vTze7K3;Yfxn
zT9O1D*5!5vX!C%D4;~D`G6VLu<@OwRI4mOpIlv569dK6ZkSS;D4aNZrfr3oVY;#*d
zvWIB$oas4PLwplP+~Ev%1ey=v4$*!VyIUD*84OB|m02#hr`M=ZVSXiRWjI#p{v=pA
zK@uhgBUnISBuO)vf+eLG%aU>m$Ylftz#u5S#lh1S1_)3PT+Bfn1)+!}l9#jz)BP_rMOr|y3K{OC}A*=SX!u42;i2(
zSd?lzpn0o3*=o&Ep@K_@U^y^sfXj)cd0IpB0D|HKsl;VUJTe)VDhat#8inDMvflv#
z&R}IZQ~v+b78VZ@4fSElb({mvpYIm+t*H4Rx9_j-H-~dCB?NIV3MI|-h2Ws`0PFS>
zV)YF%S+pe+!0ypsuFvG$&y)g1W#A+kNnnJ6g88Fh709Jga!g9oj7&~T*$fjg6y0Gp
z@lM(fVlyF+kSkc8Zmy6pccQ|FYCE%lkOfE>hRd+$gvt8}lk^8H5qie6h@&O{#YwbV
zVNjESc6~C~ykIYs3^c=j&V;t}3ts)X_yuQx&?A!!i{BBtM(7$A1H)1tsjd;ahQ+|J
zlt-%T|3;VJ^Wzk-z+I3N9+t$DbZg;3%R6JvY>oTKC3>VjUI@Q@ZMt-aNF)vxZV%VK
znR!q+n%5h&qdUC3y{C@X&WXPXMM1najaOUE!meE8-YfzZadLbXB5W2@*91(0XKr7;
zCN?>({%mUV_mUN$ByxTGw#)TjPr?hYUSzizZOHC}q8
z3H@ew#I1MUn11{H^q-D?aiS`I(Y-M&S+k%;#^h<20kg9Sq<!

literal 0
HcmV?d00001

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") {