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,257 @@
1
+ /*
2
+ * smartport-tools.js - SmartPort hard drive operation tools
3
+ *
4
+ * Written by
5
+ * Shawn Bullock <shawn@agenticexpert.ai>
6
+ */
7
+
8
+ import {
9
+ getRecentImages,
10
+ loadRecentImage,
11
+ clearRecentImages,
12
+ saveImageToStorage,
13
+ addToRecentImages,
14
+ } from "../disk-manager/hard-drive-persistence.js";
15
+
16
+ function requireSmartPort() {
17
+ const wasmModule = window.emulator?.wasmModule;
18
+ if (!wasmModule) {
19
+ throw new Error("WASM module not available");
20
+ }
21
+ if (!wasmModule._isSmartPortCardInstalled ||
22
+ !wasmModule._isSmartPortCardInstalled()) {
23
+ throw new Error(
24
+ "SmartPort card is not installed. Use slotsInstallCard to install it in a compatible slot."
25
+ );
26
+ }
27
+ return wasmModule;
28
+ }
29
+
30
+ export const smartportTools = {
31
+ /**
32
+ * Insert a SmartPort image into a device by path
33
+ */
34
+ smartportInsertImage: async (args) => {
35
+ const { deviceNum = 1, path } = args;
36
+
37
+ if (!path) {
38
+ throw new Error("path parameter is required");
39
+ }
40
+
41
+ if (deviceNum !== 1 && deviceNum !== 2) {
42
+ throw new Error("deviceNum must be 1 or 2");
43
+ }
44
+
45
+ const supportedFormats = [".hdv", ".po", ".2mg"];
46
+ const extension = path.toLowerCase().match(/\.(hdv|po|2mg)$/);
47
+
48
+ if (!extension) {
49
+ throw new Error(
50
+ `Unsupported SmartPort format. Supported formats: ${supportedFormats.join(", ")}`
51
+ );
52
+ }
53
+
54
+ const deviceIndex = deviceNum - 1;
55
+
56
+ requireSmartPort();
57
+
58
+ const hardDriveManager = window.emulator?.hardDriveManager;
59
+ if (!hardDriveManager) {
60
+ throw new Error("Hard drive manager not available");
61
+ }
62
+
63
+ try {
64
+ const agentManager = window.emulator?.agentManager;
65
+ if (!agentManager) {
66
+ throw new Error("Agent manager not available");
67
+ }
68
+
69
+ const result = await agentManager.callMCPTool("load_smartport_image", { path });
70
+
71
+ if (!result || !result.success) {
72
+ throw new Error(result?.error || "Failed to load SmartPort image");
73
+ }
74
+
75
+ const binaryString = atob(result.data);
76
+ const data = new Uint8Array(binaryString.length);
77
+ for (let i = 0; i < binaryString.length; i++) {
78
+ data[i] = binaryString.charCodeAt(i);
79
+ }
80
+
81
+ const filename = result.filename;
82
+
83
+ const success = hardDriveManager.loadImageFromData(deviceIndex, filename, data);
84
+ if (!success) {
85
+ throw new Error("Failed to insert SmartPort image into emulator");
86
+ }
87
+
88
+ await saveImageToStorage(deviceIndex, filename, data);
89
+ await addToRecentImages(deviceIndex, filename, data);
90
+
91
+ return {
92
+ success: true,
93
+ device: deviceNum,
94
+ filename: filename,
95
+ size: data.length,
96
+ message: `SmartPort image inserted into device ${deviceNum}: ${filename}`,
97
+ };
98
+ } catch (error) {
99
+ throw new Error(`Error loading SmartPort image: ${error.message}`);
100
+ }
101
+ },
102
+
103
+ /**
104
+ * Get list of recent images for a device
105
+ */
106
+ smartportRecentsList: async (args) => {
107
+ const { deviceNum = 1 } = args;
108
+
109
+ if (deviceNum !== 1 && deviceNum !== 2) {
110
+ throw new Error("deviceNum must be 1 or 2");
111
+ }
112
+
113
+ const deviceIndex = deviceNum - 1;
114
+
115
+ try {
116
+ const recentImages = await getRecentImages(deviceIndex);
117
+
118
+ const imageList = recentImages.map(img => ({
119
+ filename: img.filename,
120
+ accessedAt: img.accessedAt,
121
+ id: img.id,
122
+ }));
123
+
124
+ return {
125
+ success: true,
126
+ device: deviceNum,
127
+ count: imageList.length,
128
+ images: imageList,
129
+ message: `Found ${imageList.length} recent image(s) for device ${deviceNum}`,
130
+ };
131
+ } catch (error) {
132
+ throw new Error(`Error getting recent images: ${error.message}`);
133
+ }
134
+ },
135
+
136
+ /**
137
+ * Insert a recent image by name
138
+ */
139
+ smartportInsertRecent: async (args) => {
140
+ const { deviceNum = 1, name } = args;
141
+
142
+ if (!name) {
143
+ throw new Error("name parameter is required");
144
+ }
145
+
146
+ if (deviceNum !== 1 && deviceNum !== 2) {
147
+ throw new Error("deviceNum must be 1 or 2");
148
+ }
149
+
150
+ const deviceIndex = deviceNum - 1;
151
+
152
+ requireSmartPort();
153
+
154
+ const hardDriveManager = window.emulator?.hardDriveManager;
155
+ if (!hardDriveManager) {
156
+ throw new Error("Hard drive manager not available");
157
+ }
158
+
159
+ try {
160
+ const recentImages = await getRecentImages(deviceIndex);
161
+
162
+ const imageEntry = recentImages.find(img => img.filename === name);
163
+ if (!imageEntry) {
164
+ throw new Error(`Image "${name}" not found in recent list for device ${deviceNum}`);
165
+ }
166
+
167
+ const imageData = await loadRecentImage(imageEntry.id);
168
+ if (!imageData) {
169
+ throw new Error(`Failed to load image data for "${name}"`);
170
+ }
171
+
172
+ const success = hardDriveManager.loadImageFromData(deviceIndex, imageData.filename, imageData.data);
173
+ if (!success) {
174
+ throw new Error("Failed to insert SmartPort image into emulator");
175
+ }
176
+
177
+ await saveImageToStorage(deviceIndex, imageData.filename, imageData.data);
178
+ await addToRecentImages(deviceIndex, imageData.filename, imageData.data);
179
+
180
+ return {
181
+ success: true,
182
+ device: deviceNum,
183
+ filename: imageData.filename,
184
+ size: imageData.data.length,
185
+ message: `Recent image inserted into device ${deviceNum}: ${imageData.filename}`,
186
+ };
187
+ } catch (error) {
188
+ throw new Error(`Error inserting recent image: ${error.message}`);
189
+ }
190
+ },
191
+
192
+ /**
193
+ * Clear all recent images for a device
194
+ */
195
+ smartportClearRecent: async (args) => {
196
+ const { deviceNum = 1 } = args;
197
+
198
+ if (deviceNum !== 1 && deviceNum !== 2) {
199
+ throw new Error("deviceNum must be 1 or 2");
200
+ }
201
+
202
+ const deviceIndex = deviceNum - 1;
203
+
204
+ try {
205
+ await clearRecentImages(deviceIndex);
206
+
207
+ return {
208
+ success: true,
209
+ device: deviceNum,
210
+ message: `Cleared all recent images for device ${deviceNum}`,
211
+ };
212
+ } catch (error) {
213
+ throw new Error(`Error clearing recent images: ${error.message}`);
214
+ }
215
+ },
216
+
217
+ /**
218
+ * Eject a SmartPort image from a device
219
+ */
220
+ smartportEject: async (args) => {
221
+ const { deviceNum = 1 } = args;
222
+
223
+ if (deviceNum !== 1 && deviceNum !== 2) {
224
+ throw new Error("deviceNum must be 1 or 2");
225
+ }
226
+
227
+ const deviceIndex = deviceNum - 1;
228
+
229
+ const wasmModule = requireSmartPort();
230
+
231
+ const hardDriveManager = window.emulator?.hardDriveManager;
232
+ if (!hardDriveManager) {
233
+ throw new Error("Hard drive manager not available");
234
+ }
235
+
236
+ try {
237
+ // Eject image via WASM
238
+ wasmModule._ejectSmartPortImage(deviceIndex);
239
+
240
+ // Update UI
241
+ hardDriveManager.setImageName(deviceIndex, null);
242
+
243
+ // Notify if callback exists
244
+ if (hardDriveManager.onImageEjected) {
245
+ hardDriveManager.onImageEjected(deviceIndex);
246
+ }
247
+
248
+ return {
249
+ success: true,
250
+ device: deviceNum,
251
+ message: `SmartPort image ejected from device ${deviceNum}`,
252
+ };
253
+ } catch (error) {
254
+ throw new Error(`Error ejecting SmartPort image: ${error.message}`);
255
+ }
256
+ },
257
+ };
@@ -0,0 +1,80 @@
1
+ /*
2
+ * window-tools.js - Window management tools
3
+ *
4
+ * Written by
5
+ * Shawn Bullock <shawn@agenticexpert.ai>
6
+ */
7
+
8
+ export const windowTools = {
9
+ /**
10
+ * Show or focus a window
11
+ */
12
+ showWindow: async (args) => {
13
+ const { windowId } = args;
14
+
15
+ if (!windowId) {
16
+ throw new Error("windowId parameter is required");
17
+ }
18
+
19
+ const windowManager = window.emulator?.windowManager;
20
+ if (!windowManager) {
21
+ throw new Error("Window manager not available");
22
+ }
23
+
24
+ windowManager.showWindow(windowId);
25
+
26
+ return {
27
+ success: true,
28
+ windowId: windowId,
29
+ message: `Window '${windowId}' is now visible`,
30
+ };
31
+ },
32
+
33
+ /**
34
+ * Hide a window
35
+ */
36
+ hideWindow: async (args) => {
37
+ const { windowId } = args;
38
+
39
+ if (!windowId) {
40
+ throw new Error("windowId parameter is required");
41
+ }
42
+
43
+ const windowManager = window.emulator?.windowManager;
44
+ if (!windowManager) {
45
+ throw new Error("Window manager not available");
46
+ }
47
+
48
+ windowManager.hideWindow(windowId);
49
+
50
+ return {
51
+ success: true,
52
+ windowId: windowId,
53
+ message: `Window '${windowId}' is now hidden`,
54
+ };
55
+ },
56
+
57
+ /**
58
+ * Focus a window (bring to front)
59
+ */
60
+ focusWindow: async (args) => {
61
+ const { windowId } = args;
62
+
63
+ if (!windowId) {
64
+ throw new Error("windowId parameter is required");
65
+ }
66
+
67
+ const windowManager = window.emulator?.windowManager;
68
+ if (!windowManager) {
69
+ throw new Error("Window manager not available");
70
+ }
71
+
72
+ windowManager.bringToFront(windowId);
73
+
74
+ return {
75
+ success: true,
76
+ windowId: windowId,
77
+ message: `Window '${windowId}' is now focused`,
78
+ };
79
+ },
80
+ };