web-a2e 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (295) hide show
  1. package/.clangd +5 -0
  2. package/.mcp.json +12 -0
  3. package/CLAUDE.md +362 -0
  4. package/CMakeLists.txt +774 -0
  5. package/LICENSE +21 -0
  6. package/README.md +392 -0
  7. package/build-wasm/generated/roms.cpp +2447 -0
  8. package/docker-compose.staging.yml +9 -0
  9. package/docs/basic-rom-disassembly.md +6663 -0
  10. package/docs/softswitch-comparison.md +273 -0
  11. package/docs/thunderclock-debug.md +89 -0
  12. package/examples/cube.bas +72 -0
  13. package/examples/hello.s +55 -0
  14. package/examples/scroll.s +140 -0
  15. package/package.json +18 -0
  16. package/public/assets/apple-logo-old.png +0 -0
  17. package/public/assets/apple-logo.png +0 -0
  18. package/public/assets/drive-closed-light-on.png +0 -0
  19. package/public/assets/drive-closed.png +0 -0
  20. package/public/assets/drive-open-light-on.png +0 -0
  21. package/public/assets/drive-open.png +0 -0
  22. package/public/audio-worklet.js +82 -0
  23. package/public/disks/Apple DOS 3.3 January 1983.dsk +0 -0
  24. package/public/disks/ProDOS 2.4.3.po +0 -0
  25. package/public/disks/h32mb.2mg +0 -0
  26. package/public/disks/library.json +26 -0
  27. package/public/docs/llms/llm-assembler.txt +90 -0
  28. package/public/docs/llms/llm-basic-program.txt +256 -0
  29. package/public/docs/llms/llm-disk-drives.txt +72 -0
  30. package/public/docs/llms/llm-file-explorer.txt +50 -0
  31. package/public/docs/llms/llm-hard-drives.txt +80 -0
  32. package/public/docs/llms/llm-main.txt +51 -0
  33. package/public/docs/llms/llm-slot-configuration.txt +66 -0
  34. package/public/icons/icon-192.svg +4 -0
  35. package/public/icons/icon-512.svg +4 -0
  36. package/public/index.html +661 -0
  37. package/public/llms.txt +49 -0
  38. package/public/manifest.json +29 -0
  39. package/public/shaders/burnin.glsl +22 -0
  40. package/public/shaders/crt.glsl +706 -0
  41. package/public/shaders/edge.glsl +109 -0
  42. package/public/shaders/vertex.glsl +8 -0
  43. package/public/sw.js +186 -0
  44. package/roms/341-0027.bin +0 -0
  45. package/roms/341-0160-A-US-UK.bin +0 -0
  46. package/roms/341-0160-A.bin +0 -0
  47. package/roms/342-0273-A-US-UK.bin +0 -0
  48. package/roms/342-0349-B-C0-FF.bin +0 -0
  49. package/roms/Apple Mouse Interface Card ROM - 342-0270-C.bin +0 -0
  50. package/roms/Thunderclock Plus ROM.bin +0 -0
  51. package/scripts/generate_roms.sh +69 -0
  52. package/src/bindings/wasm_interface.cpp +1940 -0
  53. package/src/core/assembler/assembler.cpp +1239 -0
  54. package/src/core/assembler/assembler.hpp +115 -0
  55. package/src/core/audio/audio.cpp +160 -0
  56. package/src/core/audio/audio.hpp +81 -0
  57. package/src/core/basic/basic_detokenizer.cpp +436 -0
  58. package/src/core/basic/basic_detokenizer.hpp +41 -0
  59. package/src/core/basic/basic_tokenizer.cpp +286 -0
  60. package/src/core/basic/basic_tokenizer.hpp +26 -0
  61. package/src/core/basic/basic_tokens.hpp +295 -0
  62. package/src/core/cards/disk2_card.cpp +568 -0
  63. package/src/core/cards/disk2_card.hpp +316 -0
  64. package/src/core/cards/expansion_card.hpp +185 -0
  65. package/src/core/cards/mockingboard/ay8910.cpp +616 -0
  66. package/src/core/cards/mockingboard/ay8910.hpp +159 -0
  67. package/src/core/cards/mockingboard/via6522.cpp +530 -0
  68. package/src/core/cards/mockingboard/via6522.hpp +163 -0
  69. package/src/core/cards/mockingboard_card.cpp +312 -0
  70. package/src/core/cards/mockingboard_card.hpp +159 -0
  71. package/src/core/cards/mouse_card.cpp +654 -0
  72. package/src/core/cards/mouse_card.hpp +190 -0
  73. package/src/core/cards/smartport/block_device.cpp +202 -0
  74. package/src/core/cards/smartport/block_device.hpp +60 -0
  75. package/src/core/cards/smartport/smartport_card.cpp +603 -0
  76. package/src/core/cards/smartport/smartport_card.hpp +120 -0
  77. package/src/core/cards/thunderclock_card.cpp +237 -0
  78. package/src/core/cards/thunderclock_card.hpp +122 -0
  79. package/src/core/cpu/cpu6502.cpp +1609 -0
  80. package/src/core/cpu/cpu6502.hpp +203 -0
  81. package/src/core/debug/condition_evaluator.cpp +470 -0
  82. package/src/core/debug/condition_evaluator.hpp +87 -0
  83. package/src/core/disassembler/disassembler.cpp +552 -0
  84. package/src/core/disassembler/disassembler.hpp +171 -0
  85. package/src/core/disk-image/disk_image.hpp +267 -0
  86. package/src/core/disk-image/dsk_disk_image.cpp +827 -0
  87. package/src/core/disk-image/dsk_disk_image.hpp +204 -0
  88. package/src/core/disk-image/gcr_encoding.cpp +147 -0
  89. package/src/core/disk-image/gcr_encoding.hpp +78 -0
  90. package/src/core/disk-image/woz_disk_image.cpp +1049 -0
  91. package/src/core/disk-image/woz_disk_image.hpp +343 -0
  92. package/src/core/emulator.cpp +2126 -0
  93. package/src/core/emulator.hpp +434 -0
  94. package/src/core/filesystem/dos33.cpp +178 -0
  95. package/src/core/filesystem/dos33.hpp +66 -0
  96. package/src/core/filesystem/pascal.cpp +262 -0
  97. package/src/core/filesystem/pascal.hpp +87 -0
  98. package/src/core/filesystem/prodos.cpp +369 -0
  99. package/src/core/filesystem/prodos.hpp +119 -0
  100. package/src/core/input/keyboard.cpp +227 -0
  101. package/src/core/input/keyboard.hpp +111 -0
  102. package/src/core/mmu/mmu.cpp +1387 -0
  103. package/src/core/mmu/mmu.hpp +236 -0
  104. package/src/core/types.hpp +196 -0
  105. package/src/core/video/video.cpp +680 -0
  106. package/src/core/video/video.hpp +156 -0
  107. package/src/css/assembler-editor.css +1617 -0
  108. package/src/css/base.css +470 -0
  109. package/src/css/basic-debugger.css +791 -0
  110. package/src/css/basic-editor.css +792 -0
  111. package/src/css/controls.css +783 -0
  112. package/src/css/cpu-debugger.css +1413 -0
  113. package/src/css/debug-base.css +160 -0
  114. package/src/css/debug-windows.css +6455 -0
  115. package/src/css/disk-drives.css +406 -0
  116. package/src/css/documentation.css +392 -0
  117. package/src/css/file-explorer.css +867 -0
  118. package/src/css/hard-drive.css +180 -0
  119. package/src/css/layout.css +217 -0
  120. package/src/css/memory-windows.css +798 -0
  121. package/src/css/modals.css +510 -0
  122. package/src/css/monitor.css +425 -0
  123. package/src/css/release-notes.css +101 -0
  124. package/src/css/responsive.css +400 -0
  125. package/src/css/rule-builder.css +340 -0
  126. package/src/css/save-states.css +201 -0
  127. package/src/css/settings-windows.css +1231 -0
  128. package/src/css/window-switcher.css +150 -0
  129. package/src/js/agent/agent-manager.js +643 -0
  130. package/src/js/agent/agent-tools.js +293 -0
  131. package/src/js/agent/agent-version-tools.js +131 -0
  132. package/src/js/agent/assembler-tools.js +357 -0
  133. package/src/js/agent/basic-program-tools.js +894 -0
  134. package/src/js/agent/disk-tools.js +417 -0
  135. package/src/js/agent/file-explorer-tools.js +269 -0
  136. package/src/js/agent/index.js +13 -0
  137. package/src/js/agent/main-tools.js +222 -0
  138. package/src/js/agent/slot-tools.js +303 -0
  139. package/src/js/agent/smartport-tools.js +257 -0
  140. package/src/js/agent/window-tools.js +80 -0
  141. package/src/js/audio/audio-driver.js +417 -0
  142. package/src/js/audio/audio-worklet.js +85 -0
  143. package/src/js/audio/index.js +8 -0
  144. package/src/js/config/default-layout.js +34 -0
  145. package/src/js/config/version.js +8 -0
  146. package/src/js/data/apple2-rom-routines.js +577 -0
  147. package/src/js/debug/assembler-editor-window.js +2993 -0
  148. package/src/js/debug/basic-breakpoint-manager.js +529 -0
  149. package/src/js/debug/basic-program-parser.js +436 -0
  150. package/src/js/debug/basic-program-window.js +2594 -0
  151. package/src/js/debug/basic-variable-inspector.js +447 -0
  152. package/src/js/debug/breakpoint-manager.js +472 -0
  153. package/src/js/debug/cpu-debugger-window.js +2396 -0
  154. package/src/js/debug/index.js +22 -0
  155. package/src/js/debug/label-manager.js +238 -0
  156. package/src/js/debug/memory-browser-window.js +416 -0
  157. package/src/js/debug/memory-heat-map-window.js +481 -0
  158. package/src/js/debug/memory-map-window.js +206 -0
  159. package/src/js/debug/mockingboard-window.js +882 -0
  160. package/src/js/debug/mouse-card-window.js +355 -0
  161. package/src/js/debug/rule-builder-window.js +648 -0
  162. package/src/js/debug/soft-switch-window.js +458 -0
  163. package/src/js/debug/stack-viewer-window.js +221 -0
  164. package/src/js/debug/symbols.js +416 -0
  165. package/src/js/debug/trace-panel.js +291 -0
  166. package/src/js/debug/zero-page-watch-window.js +297 -0
  167. package/src/js/disk-manager/disk-drives-window.js +212 -0
  168. package/src/js/disk-manager/disk-operations.js +284 -0
  169. package/src/js/disk-manager/disk-persistence.js +301 -0
  170. package/src/js/disk-manager/disk-surface-renderer.js +388 -0
  171. package/src/js/disk-manager/drive-sounds.js +139 -0
  172. package/src/js/disk-manager/hard-drive-manager.js +481 -0
  173. package/src/js/disk-manager/hard-drive-persistence.js +187 -0
  174. package/src/js/disk-manager/hard-drive-window.js +57 -0
  175. package/src/js/disk-manager/index.js +890 -0
  176. package/src/js/display/display-settings-window.js +383 -0
  177. package/src/js/display/index.js +10 -0
  178. package/src/js/display/screen-window.js +342 -0
  179. package/src/js/display/webgl-renderer.js +705 -0
  180. package/src/js/file-explorer/disassembler.js +574 -0
  181. package/src/js/file-explorer/dos33.js +266 -0
  182. package/src/js/file-explorer/file-viewer.js +359 -0
  183. package/src/js/file-explorer/index.js +1261 -0
  184. package/src/js/file-explorer/prodos.js +549 -0
  185. package/src/js/file-explorer/utils.js +67 -0
  186. package/src/js/help/documentation-window.js +1096 -0
  187. package/src/js/help/index.js +10 -0
  188. package/src/js/help/release-notes-window.js +85 -0
  189. package/src/js/help/release-notes.js +612 -0
  190. package/src/js/input/gamepad-handler.js +176 -0
  191. package/src/js/input/index.js +12 -0
  192. package/src/js/input/input-handler.js +396 -0
  193. package/src/js/input/joystick-window.js +404 -0
  194. package/src/js/input/mouse-handler.js +99 -0
  195. package/src/js/input/text-selection.js +462 -0
  196. package/src/js/main.js +653 -0
  197. package/src/js/state/index.js +15 -0
  198. package/src/js/state/save-states-window.js +393 -0
  199. package/src/js/state/state-manager.js +409 -0
  200. package/src/js/state/state-persistence.js +218 -0
  201. package/src/js/ui/confirm.js +43 -0
  202. package/src/js/ui/disk-drive-positioner.js +347 -0
  203. package/src/js/ui/reminder-controller.js +129 -0
  204. package/src/js/ui/slot-configuration-window.js +560 -0
  205. package/src/js/ui/theme-manager.js +61 -0
  206. package/src/js/ui/toast.js +44 -0
  207. package/src/js/ui/ui-controller.js +897 -0
  208. package/src/js/ui/window-switcher.js +275 -0
  209. package/src/js/utils/basic-autocomplete.js +832 -0
  210. package/src/js/utils/basic-highlighting.js +473 -0
  211. package/src/js/utils/basic-tokenizer.js +153 -0
  212. package/src/js/utils/basic-tokens.js +117 -0
  213. package/src/js/utils/constants.js +28 -0
  214. package/src/js/utils/indexeddb-helper.js +225 -0
  215. package/src/js/utils/merlin-editor-support.js +905 -0
  216. package/src/js/utils/merlin-highlighting.js +551 -0
  217. package/src/js/utils/storage.js +125 -0
  218. package/src/js/utils/string-utils.js +19 -0
  219. package/src/js/utils/wasm-memory.js +54 -0
  220. package/src/js/windows/base-window.js +690 -0
  221. package/src/js/windows/index.js +9 -0
  222. package/src/js/windows/window-manager.js +375 -0
  223. package/tests/catch2/catch.hpp +17976 -0
  224. package/tests/common/basic_program_builder.cpp +119 -0
  225. package/tests/common/basic_program_builder.hpp +209 -0
  226. package/tests/common/disk_image_builder.cpp +444 -0
  227. package/tests/common/disk_image_builder.hpp +141 -0
  228. package/tests/common/test_helpers.hpp +118 -0
  229. package/tests/gcr/gcr-test.cpp +142 -0
  230. package/tests/integration/check-rom.js +70 -0
  231. package/tests/integration/compare-boot.js +239 -0
  232. package/tests/integration/crash-trace.js +102 -0
  233. package/tests/integration/disk-boot-test.js +264 -0
  234. package/tests/integration/memory-crash.js +108 -0
  235. package/tests/integration/nibble-read-test.js +249 -0
  236. package/tests/integration/phase-test.js +159 -0
  237. package/tests/integration/test_emulator.cpp +291 -0
  238. package/tests/integration/test_emulator_basic.cpp +91 -0
  239. package/tests/integration/test_emulator_debug.cpp +344 -0
  240. package/tests/integration/test_emulator_disk.cpp +153 -0
  241. package/tests/integration/test_emulator_state.cpp +163 -0
  242. package/tests/klaus/6502_functional_test.bin +0 -0
  243. package/tests/klaus/65C02_extended_opcodes_test.bin +0 -0
  244. package/tests/klaus/klaus_6502_test.cpp +184 -0
  245. package/tests/klaus/klaus_65c02_test.cpp +197 -0
  246. package/tests/thunderclock/thunderclock_mmu_test.cpp +304 -0
  247. package/tests/thunderclock/thunderclock_test.cpp +550 -0
  248. package/tests/unit/test_assembler.cpp +521 -0
  249. package/tests/unit/test_audio.cpp +196 -0
  250. package/tests/unit/test_ay8910.cpp +311 -0
  251. package/tests/unit/test_basic_detokenizer.cpp +265 -0
  252. package/tests/unit/test_basic_tokenizer.cpp +382 -0
  253. package/tests/unit/test_block_device.cpp +259 -0
  254. package/tests/unit/test_condition_evaluator.cpp +219 -0
  255. package/tests/unit/test_cpu6502.cpp +1301 -0
  256. package/tests/unit/test_cpu_addressing.cpp +361 -0
  257. package/tests/unit/test_cpu_cycle_counts.cpp +409 -0
  258. package/tests/unit/test_cpu_decimal.cpp +166 -0
  259. package/tests/unit/test_cpu_interrupts.cpp +285 -0
  260. package/tests/unit/test_disassembler.cpp +323 -0
  261. package/tests/unit/test_disk2_card.cpp +330 -0
  262. package/tests/unit/test_dos33.cpp +273 -0
  263. package/tests/unit/test_dsk_disk_image.cpp +315 -0
  264. package/tests/unit/test_expansion_card.cpp +178 -0
  265. package/tests/unit/test_gcr_encoding.cpp +232 -0
  266. package/tests/unit/test_keyboard.cpp +262 -0
  267. package/tests/unit/test_mmu.cpp +555 -0
  268. package/tests/unit/test_mmu_slots.cpp +323 -0
  269. package/tests/unit/test_mockingboard.cpp +352 -0
  270. package/tests/unit/test_mouse_card.cpp +386 -0
  271. package/tests/unit/test_pascal.cpp +248 -0
  272. package/tests/unit/test_prodos.cpp +259 -0
  273. package/tests/unit/test_smartport_card.cpp +321 -0
  274. package/tests/unit/test_thunderclock.cpp +354 -0
  275. package/tests/unit/test_via6522.cpp +323 -0
  276. package/tests/unit/test_video.cpp +319 -0
  277. package/tests/unit/test_woz_disk_image.cpp +257 -0
  278. package/vite.config.js +96 -0
  279. package/wiki/AI-Agent.md +372 -0
  280. package/wiki/Architecture-Overview.md +303 -0
  281. package/wiki/Audio-System.md +449 -0
  282. package/wiki/CPU-Emulation.md +477 -0
  283. package/wiki/Debugger.md +516 -0
  284. package/wiki/Disk-Drives.md +161 -0
  285. package/wiki/Disk-System-Internals.md +547 -0
  286. package/wiki/Display-Settings.md +88 -0
  287. package/wiki/Expansion-Slots.md +187 -0
  288. package/wiki/File-Explorer.md +259 -0
  289. package/wiki/Getting-Started.md +156 -0
  290. package/wiki/Home.md +69 -0
  291. package/wiki/Input-Devices.md +183 -0
  292. package/wiki/Keyboard-Shortcuts.md +158 -0
  293. package/wiki/Memory-System.md +364 -0
  294. package/wiki/Save-States.md +172 -0
  295. package/wiki/Video-Rendering.md +658 -0
@@ -0,0 +1,109 @@
1
+ // Edge overlay fragment shader
2
+ // Renders the screen edge highlight as a separate pass so it is
3
+ // completely unaffected by CRT effects (scanlines, shadow mask, etc.).
4
+ // Uses stable coordinates (no jitter / horizontal sync distortion).
5
+
6
+ precision highp float;
7
+
8
+ uniform float u_curvature;
9
+ uniform float u_cornerRadius;
10
+ uniform float u_edgeHighlight;
11
+ uniform vec2 u_textureSize;
12
+ uniform vec2 u_resolution;
13
+
14
+ varying vec2 v_texCoord;
15
+
16
+ // Screen curvature (must match crt.glsl)
17
+ vec2 curveUV(vec2 uv) {
18
+ if (u_curvature < 0.001) return uv;
19
+
20
+ vec2 cc = uv - 0.5;
21
+ float dist = dot(cc, cc);
22
+ float distortion = dist * u_curvature * 0.5;
23
+ return uv + cc * distortion;
24
+ }
25
+
26
+ // Rounded rectangle alpha (must match crt.glsl)
27
+ float roundedRectAlpha(vec2 uv, float radius) {
28
+ if (radius < 0.001) return 1.0;
29
+
30
+ vec2 centered = abs(uv - 0.5);
31
+ vec2 cornerDist = centered - (0.5 - radius);
32
+
33
+ if (cornerDist.x < 0.0 || cornerDist.y < 0.0) {
34
+ return 1.0;
35
+ }
36
+
37
+ float dist = length(cornerDist);
38
+ return 1.0 - smoothstep(radius - 0.005, radius + 0.005, dist);
39
+ }
40
+
41
+ // Edge highlight with uniform screen-pixel width and anti-aliasing.
42
+ // Computes the gradient direction of the distance field so that line
43
+ // width and AA are expressed in output pixels regardless of whether
44
+ // the fragment sits on a straight edge or a corner arc.
45
+ float edgeHighlightIntensity(vec2 uv, float radius) {
46
+ if (u_edgeHighlight < 0.001) return 0.0;
47
+
48
+ vec2 centered = abs(uv - 0.5);
49
+ vec2 cornerDist = centered - (0.5 - radius);
50
+
51
+ float distFromEdge;
52
+ vec2 gradDir; // unit-length gradient direction in UV space
53
+
54
+ if (cornerDist.x > 0.0 && cornerDist.y > 0.0) {
55
+ // Corner arc
56
+ float d = length(cornerDist);
57
+ distFromEdge = radius - d;
58
+ gradDir = cornerDist / d;
59
+ } else if (0.5 - centered.x < 0.5 - centered.y) {
60
+ // Nearest to a vertical edge (left / right)
61
+ distFromEdge = 0.5 - centered.x;
62
+ gradDir = vec2(1.0, 0.0);
63
+ } else {
64
+ // Nearest to a horizontal edge (top / bottom)
65
+ distFromEdge = 0.5 - centered.y;
66
+ gradDir = vec2(0.0, 1.0);
67
+ }
68
+
69
+ // Convert pixel counts to UV-space distances along the gradient.
70
+ // length(gradDir / u_resolution) gives UV units per output pixel
71
+ // in the direction perpendicular to the edge.
72
+ float uvPerPixel = length(gradDir / u_resolution);
73
+ float lineWidth = 2.5 * uvPerPixel;
74
+ float aa = 0.75 * uvPerPixel;
75
+
76
+ // Anti-aliased line: smoothstep at both inner and outer boundaries
77
+ float outer = smoothstep(-aa, aa, distFromEdge);
78
+ float inner = smoothstep(lineWidth + aa, lineWidth - aa, distFromEdge);
79
+
80
+ return outer * inner * u_edgeHighlight;
81
+ }
82
+
83
+ void main() {
84
+ // Use raw texture coordinates with only curvature applied.
85
+ // No jitter or horizontal sync — the edge stays perfectly stable.
86
+ vec2 curvedUV = curveUV(v_texCoord);
87
+
88
+ // Outside the rounded rectangle — fully transparent
89
+ float cornerAlpha = roundedRectAlpha(curvedUV, u_cornerRadius);
90
+ if (cornerAlpha < 0.001) {
91
+ gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
92
+ return;
93
+ }
94
+
95
+ // Edge highlight
96
+ float edgeGlow = edgeHighlightIntensity(curvedUV, u_cornerRadius);
97
+ if (edgeGlow < 0.001) {
98
+ gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
99
+ return;
100
+ }
101
+
102
+ // Separate the line shape (0–1) from the brightness.
103
+ // Use the shape as alpha so the line is fully opaque where it exists
104
+ // and fades only at its own anti-aliased boundaries — not at the
105
+ // rounded-rect corner alpha which would dim the corner arcs.
106
+ float lineAlpha = edgeGlow / u_edgeHighlight;
107
+ vec3 highlightColor = vec3(0.55, 0.55, 0.5) * u_edgeHighlight;
108
+ gl_FragColor = vec4(highlightColor, lineAlpha);
109
+ }
@@ -0,0 +1,8 @@
1
+ attribute vec2 a_position;
2
+ attribute vec2 a_texCoord;
3
+ varying vec2 v_texCoord;
4
+
5
+ void main() {
6
+ gl_Position = vec4(a_position, 0.0, 1.0);
7
+ v_texCoord = a_texCoord;
8
+ }
package/public/sw.js ADDED
@@ -0,0 +1,186 @@
1
+ // Service Worker for Apple //e Emulator
2
+ // Enables offline functionality by caching app assets
3
+
4
+ // IMPORTANT: Bump this version when WASM or core JS files change
5
+ const CACHE_VERSION = 3.3;
6
+ const CACHE_NAME = `a2e-cache-v${CACHE_VERSION}`;
7
+
8
+ // Files that should always be fetched fresh (network-first)
9
+ const NETWORK_FIRST_FILES = ["/a2e.js", "/a2e.wasm"];
10
+
11
+ // Assets to cache on install
12
+ const PRECACHE_ASSETS = [
13
+ "/",
14
+ "/index.html",
15
+ "/manifest.json",
16
+ "/a2e.js",
17
+ "/a2e.wasm",
18
+ "/audio-worklet.js",
19
+ "/css/base.css",
20
+ "/css/layout.css",
21
+ "/css/monitor.css",
22
+ "/css/disk-drives.css",
23
+ "/css/controls.css",
24
+ "/css/modals.css",
25
+ "/css/debug-windows.css",
26
+ "/css/file-explorer.css",
27
+ "/css/documentation.css",
28
+ "/css/responsive.css",
29
+ "/images/drive-open.png",
30
+ ];
31
+
32
+ // Install event - cache core assets
33
+ self.addEventListener("install", (event) => {
34
+ event.waitUntil(
35
+ caches
36
+ .open(CACHE_NAME)
37
+ .then((cache) => {
38
+ console.log("[SW] Precaching app assets");
39
+ return cache.addAll(PRECACHE_ASSETS);
40
+ })
41
+ .then(() => {
42
+ // Activate immediately without waiting
43
+ return self.skipWaiting();
44
+ })
45
+ .catch((error) => {
46
+ console.error("[SW] Precache failed:", error);
47
+ }),
48
+ );
49
+ });
50
+
51
+ // Activate event - clean up old caches
52
+ self.addEventListener("activate", (event) => {
53
+ event.waitUntil(
54
+ caches
55
+ .keys()
56
+ .then((cacheNames) => {
57
+ return Promise.all(
58
+ cacheNames
59
+ .filter((name) => name !== CACHE_NAME)
60
+ .map((name) => {
61
+ console.log("[SW] Deleting old cache:", name);
62
+ return caches.delete(name);
63
+ }),
64
+ );
65
+ })
66
+ .then(() => {
67
+ // Take control of all pages immediately
68
+ return self.clients.claim();
69
+ }),
70
+ );
71
+ });
72
+
73
+ // Check if URL should use network-first strategy
74
+ function isNetworkFirst(url) {
75
+ // Explicit network-first files
76
+ if (
77
+ NETWORK_FIRST_FILES.some(
78
+ (file) => url.pathname === file || url.pathname.endsWith(file),
79
+ )
80
+ ) {
81
+ return true;
82
+ }
83
+ // Also use network-first for Vite JS bundles (they have hashed names)
84
+ if (url.pathname.includes("/assets/") && url.pathname.endsWith(".js")) {
85
+ return true;
86
+ }
87
+ return false;
88
+ }
89
+
90
+ // Fetch event - serve from cache, fall back to network
91
+ self.addEventListener("fetch", (event) => {
92
+ const url = new URL(event.request.url);
93
+
94
+ // Skip non-GET requests
95
+ if (event.request.method !== "GET") {
96
+ return;
97
+ }
98
+
99
+ // Skip cross-origin requests (fonts, etc.)
100
+ if (url.origin !== self.location.origin) {
101
+ return;
102
+ }
103
+
104
+ // Use network-first for critical files (WASM, core JS)
105
+ if (isNetworkFirst(url)) {
106
+ event.respondWith(
107
+ fetch(event.request)
108
+ .then((networkResponse) => {
109
+ if (networkResponse && networkResponse.status === 200) {
110
+ // Update cache with fresh response
111
+ const responseToCache = networkResponse.clone();
112
+ caches.open(CACHE_NAME).then((cache) => {
113
+ cache.put(event.request, responseToCache);
114
+ });
115
+ }
116
+ return networkResponse;
117
+ })
118
+ .catch(() => {
119
+ // Network failed, try cache as fallback
120
+ return caches.match(event.request);
121
+ }),
122
+ );
123
+ return;
124
+ }
125
+
126
+ // Cache-first for other assets
127
+ event.respondWith(
128
+ caches.match(event.request).then((cachedResponse) => {
129
+ if (cachedResponse) {
130
+ // Return cached response
131
+ return cachedResponse;
132
+ }
133
+
134
+ // Not in cache - fetch from network
135
+ return fetch(event.request)
136
+ .then((networkResponse) => {
137
+ // Don't cache non-successful responses
138
+ if (!networkResponse || networkResponse.status !== 200) {
139
+ return networkResponse;
140
+ }
141
+
142
+ // Cache the new response for future use
143
+ // Clone because response can only be consumed once
144
+ const responseToCache = networkResponse.clone();
145
+
146
+ caches.open(CACHE_NAME).then((cache) => {
147
+ // Cache JS bundles and other assets dynamically
148
+ if (shouldCache(event.request.url)) {
149
+ cache.put(event.request, responseToCache);
150
+ }
151
+ });
152
+
153
+ return networkResponse;
154
+ })
155
+ .catch((error) => {
156
+ console.error("[SW] Fetch failed:", error);
157
+ // Could return an offline fallback page here
158
+ throw error;
159
+ });
160
+ }),
161
+ );
162
+ });
163
+
164
+ // Determine if a URL should be cached dynamically
165
+ function shouldCache(url) {
166
+ // Cache JS bundles (Vite generates hashed names)
167
+ if (url.includes("/assets/") && url.endsWith(".js")) {
168
+ return true;
169
+ }
170
+ // Cache images
171
+ if (url.match(/\.(png|jpg|jpeg|gif|svg|webp)$/)) {
172
+ return true;
173
+ }
174
+ // Cache WASM
175
+ if (url.endsWith(".wasm")) {
176
+ return true;
177
+ }
178
+ return false;
179
+ }
180
+
181
+ // Listen for messages from the main app
182
+ self.addEventListener("message", (event) => {
183
+ if (event.data === "skipWaiting") {
184
+ self.skipWaiting();
185
+ }
186
+ });
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,69 @@
1
+ #!/bin/bash
2
+
3
+ # Generate C++ source file containing ROM data as byte arrays
4
+ # Usage: generate_roms.sh <rom_directory> <output_file>
5
+
6
+ ROM_DIR="$1"
7
+ OUTPUT_FILE="$2"
8
+
9
+ if [ -z "$ROM_DIR" ] || [ -z "$OUTPUT_FILE" ]; then
10
+ echo "Usage: $0 <rom_directory> <output_file>"
11
+ exit 1
12
+ fi
13
+
14
+ # Start the output file
15
+ cat > "$OUTPUT_FILE" << 'EOF'
16
+ // Auto-generated ROM data - DO NOT EDIT
17
+ // Generated by generate_roms.sh
18
+
19
+ #include <cstdint>
20
+ #include <cstddef>
21
+
22
+ namespace roms {
23
+
24
+ EOF
25
+
26
+ # Function to convert a binary file to a C array
27
+ generate_array() {
28
+ local file="$1"
29
+ local name="$2"
30
+
31
+ if [ ! -f "$file" ]; then
32
+ echo "// ROM file not found: $file" >> "$OUTPUT_FILE"
33
+ echo "const uint8_t ${name}[] = {};" >> "$OUTPUT_FILE"
34
+ echo "const size_t ${name}_SIZE = 0;" >> "$OUTPUT_FILE"
35
+ echo "" >> "$OUTPUT_FILE"
36
+ return
37
+ fi
38
+
39
+ local size=$(wc -c < "$file" | tr -d ' ')
40
+
41
+ echo "// $(basename "$file")" >> "$OUTPUT_FILE"
42
+ echo "const uint8_t ${name}[] = {" >> "$OUTPUT_FILE"
43
+
44
+ xxd -i < "$file" | sed 's/^/ /' >> "$OUTPUT_FILE"
45
+
46
+ echo "};" >> "$OUTPUT_FILE"
47
+ echo "const size_t ${name}_SIZE = $size;" >> "$OUTPUT_FILE"
48
+ echo "" >> "$OUTPUT_FILE"
49
+ }
50
+
51
+ # Generate arrays for each ROM
52
+ # Prefer combined ROM if available
53
+ if [ -f "$ROM_DIR/342-0349-B-C0-FF.bin" ]; then
54
+ generate_array "$ROM_DIR/342-0349-B-C0-FF.bin" "ROM_SYSTEM"
55
+ elif [ -f "$ROM_DIR/342-0135-A-CD.bin" ] && [ -f "$ROM_DIR/342-0134-A-EF.bin" ]; then
56
+ # Fallback to separate CD/EF ROMs - combine them
57
+ generate_array "$ROM_DIR/342-0135-A-CD.bin" "ROM_CD"
58
+ generate_array "$ROM_DIR/342-0134-A-EF.bin" "ROM_EF"
59
+ fi
60
+
61
+ generate_array "$ROM_DIR/342-0273-A-US-UK.bin" "ROM_CHAR"
62
+ generate_array "$ROM_DIR/341-0027.bin" "ROM_DISK2"
63
+ generate_array "$ROM_DIR/Thunderclock Plus ROM.bin" "ROM_THUNDERCLOCK"
64
+ generate_array "$ROM_DIR/Apple Mouse Interface Card ROM - 342-0270-C.bin" "ROM_MOUSE"
65
+
66
+ # Close namespace
67
+ echo "} // namespace roms" >> "$OUTPUT_FILE"
68
+
69
+ echo "Generated $OUTPUT_FILE"