js-dos 7.5.0 → 8.1.1

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 (155) hide show
  1. package/.github/workflows/build.yml +51 -0
  2. package/.gitmodules +4 -0
  3. package/README.deployment.md +11 -0
  4. package/README.md +29 -89
  5. package/index.html +106 -0
  6. package/package.json +46 -71
  7. package/postcss.config.cjs +8 -0
  8. package/src/app.css +14 -0
  9. package/src/base.css +10 -0
  10. package/src/components/checkbox.tsx +41 -0
  11. package/src/components/close-button.tsx +17 -0
  12. package/src/components/components.css +28 -0
  13. package/src/components/dos-option-checkbox.tsx +94 -0
  14. package/src/components/dos-option-select.tsx +108 -0
  15. package/src/components/dos-option-slider.tsx +35 -0
  16. package/src/components/error.tsx +16 -0
  17. package/src/components/loading.tsx +26 -0
  18. package/src/components/lock.tsx +10 -0
  19. package/src/components/select.tsx +33 -0
  20. package/src/components/slider.tsx +69 -0
  21. package/src/download-file.ts +18 -0
  22. package/src/frame/account-frame.tsx +156 -0
  23. package/src/frame/editor/defaults.ts +419 -0
  24. package/src/frame/editor/editor-conf-frame.tsx +226 -0
  25. package/src/frame/editor/editor-frame.css +55 -0
  26. package/src/frame/editor/editor-fs-frame.tsx +314 -0
  27. package/src/frame/fat-drives-frame.tsx +135 -0
  28. package/src/frame/frame.css +119 -0
  29. package/src/frame/frame.tsx +34 -0
  30. package/src/frame/image-rendering-frame.tsx +8 -0
  31. package/src/frame/network-frame.tsx +157 -0
  32. package/src/frame/quick-save-frame.tsx +96 -0
  33. package/src/frame/settings-frame.tsx +14 -0
  34. package/src/frame/stats-frame.tsx +87 -0
  35. package/src/host/bundle-storage.ts +91 -0
  36. package/src/host/fullscreen.ts +35 -0
  37. package/src/host/lcache.ts +203 -0
  38. package/src/host/lstorage.ts +156 -0
  39. package/src/i18n.ts +183 -0
  40. package/src/index.css +7 -0
  41. package/src/load.ts +132 -0
  42. package/src/login/login.css +9 -0
  43. package/src/login/login.tsx +77 -0
  44. package/src/main.tsx +197 -0
  45. package/src/public/types.ts +41 -0
  46. package/src/sidebar/account-button.tsx +33 -0
  47. package/src/sidebar/cloud-save-button.tsx +89 -0
  48. package/src/sidebar/diskette-icon.tsx +14 -0
  49. package/src/sidebar/fullscreen-button.tsx +44 -0
  50. package/src/sidebar/network-button.tsx +39 -0
  51. package/src/sidebar/sidebar-button.tsx +244 -0
  52. package/src/sidebar/sidebar.css +47 -0
  53. package/src/sidebar/sidebar.tsx +30 -0
  54. package/src/sockdrive/LICENSE +674 -0
  55. package/src/sockdrive/README.md +45 -0
  56. package/src/sockdrive/include/sockdrive.h +23 -0
  57. package/src/sockdrive/js/.eslintrc.json +43 -0
  58. package/src/sockdrive/js/package.json +29 -0
  59. package/src/sockdrive/js/src/fatfs/chains.js +318 -0
  60. package/src/sockdrive/js/src/fatfs/dir.js +385 -0
  61. package/src/sockdrive/js/src/fatfs/helpers.js +210 -0
  62. package/src/sockdrive/js/src/fatfs/index.js +606 -0
  63. package/src/sockdrive/js/src/fatfs/structs.js +255 -0
  64. package/src/sockdrive/js/src/fatfs/vol.js +166 -0
  65. package/src/sockdrive/js/src/sockdrive/cache.ts +56 -0
  66. package/src/sockdrive/js/src/sockdrive/drive.ts +467 -0
  67. package/src/sockdrive/js/src/sockdrive/lru.ts +221 -0
  68. package/src/sockdrive/js/src/sockdrive/types.ts +14 -0
  69. package/src/sockdrive/js/src/sockdrive-fat.d.ts +64 -0
  70. package/src/sockdrive/js/src/sockdrive-fat.ts +226 -0
  71. package/src/sockdrive/js/src/sockdrive-native.ts +100 -0
  72. package/src/sockdrive/js/src/test/test.ts +289 -0
  73. package/src/sockdrive/js/tsconfig.json +11 -0
  74. package/src/sockdrive/js/webpack.config.js +68 -0
  75. package/src/sockdrive/js/yarn.lock +1808 -0
  76. package/src/store/auth.ts +115 -0
  77. package/src/store/dos.ts +305 -0
  78. package/src/store/editor.ts +61 -0
  79. package/src/store/init.ts +48 -0
  80. package/src/store/storage.ts +32 -0
  81. package/src/store/ui.ts +191 -0
  82. package/src/store.tsx +108 -0
  83. package/src/ui.tsx +140 -0
  84. package/src/v8/changes.ts +56 -0
  85. package/src/v8/config.ts +20 -0
  86. package/src/v8/subscriptions.ts +62 -0
  87. package/src/vite-env.d.ts +2 -0
  88. package/src/window/dos/controls/keyboard.ts +32 -0
  89. package/src/window/dos/controls/keys.ts +331 -0
  90. package/src/window/dos/controls/mouse/mount.ts +99 -0
  91. package/src/window/dos/controls/mouse/mouse-locked.ts +51 -0
  92. package/src/window/dos/controls/mouse/mouse-not-locked.ts +84 -0
  93. package/src/window/dos/controls/mouse/mouse-swipe.ts +57 -0
  94. package/src/window/dos/controls/mouse/pointer.ts +85 -0
  95. package/src/window/dos/controls/mouse.ts +21 -0
  96. package/src/window/dos/dos-dhry2.tsx +191 -0
  97. package/src/window/dos/dos-runtime.tsx +214 -0
  98. package/src/window/dos/dos-window.tsx +91 -0
  99. package/src/window/dos/render/canvas.ts +65 -0
  100. package/src/window/dos/render/resize.ts +31 -0
  101. package/src/window/dos/render/webgl.ts +173 -0
  102. package/src/window/dos/sound/audio-node.ts +148 -0
  103. package/src/window/error-window.tsx +9 -0
  104. package/src/window/loading-window.tsx +54 -0
  105. package/src/window/prerun-window.tsx +123 -0
  106. package/src/window/select-window.tsx +118 -0
  107. package/src/window/window.css +78 -0
  108. package/src/window/window.tsx +49 -0
  109. package/src/ws/ws-sockdrive.ts +123 -0
  110. package/src/ws/ws-transport-layer.ts +468 -0
  111. package/tailwind.config.cjs +48 -0
  112. package/tsconfig.json +32 -0
  113. package/tsconfig.node.json +9 -0
  114. package/vite.config.ts +22 -0
  115. package/LICENSE +0 -339
  116. package/dist/emulators-ui-loader.png +0 -0
  117. package/dist/js-dos.css +0 -1690
  118. package/dist/js-dos.js +0 -17
  119. package/dist/js-dos.js.map +0 -1
  120. package/dist/types/src/backend/v7/latency.d.ts +0 -8
  121. package/dist/types/src/backend/v7/personal.d.ts +0 -2
  122. package/dist/types/src/backend/v7/v7-config.d.ts +0 -16
  123. package/dist/types/src/components/action-bar.d.ts +0 -2
  124. package/dist/types/src/components/action-hide.d.ts +0 -4
  125. package/dist/types/src/components/action-save-or-exit.d.ts +0 -4
  126. package/dist/types/src/components/client.d.ts +0 -6
  127. package/dist/types/src/components/controls.d.ts +0 -8
  128. package/dist/types/src/components/cpu-control.d.ts +0 -2
  129. package/dist/types/src/components/horizontal-slider.d.ts +0 -13
  130. package/dist/types/src/components/region.d.ts +0 -6
  131. package/dist/types/src/components/scale-control.d.ts +0 -3
  132. package/dist/types/src/components/sensitivity-control.d.ts +0 -3
  133. package/dist/types/src/components/sidebar/latency-info.d.ts +0 -7
  134. package/dist/types/src/components/sidebar/main.d.ts +0 -2
  135. package/dist/types/src/components/sidebar/networking.d.ts +0 -2
  136. package/dist/types/src/components/sidebar/token/token-add-time.d.ts +0 -2
  137. package/dist/types/src/components/sidebar/token/token-select.d.ts +0 -2
  138. package/dist/types/src/components/sidebar/token/token.d.ts +0 -17
  139. package/dist/types/src/components/sidebar.d.ts +0 -2
  140. package/dist/types/src/components/sync-control.d.ts +0 -2
  141. package/dist/types/src/components/tip.d.ts +0 -2
  142. package/dist/types/src/components/vertical-slider.d.ts +0 -12
  143. package/dist/types/src/components/volume-control.d.ts +0 -3
  144. package/dist/types/src/dom.d.ts +0 -8
  145. package/dist/types/src/hardware-transport-layer.d.ts +0 -18
  146. package/dist/types/src/icons.d.ts +0 -95
  147. package/dist/types/src/player-app.d.ts +0 -50
  148. package/dist/types/src/player.d.ts +0 -29
  149. package/dist/types/src/request.d.ts +0 -1
  150. package/dist/types/src/xhr.d.ts +0 -6
  151. package/dist/types/v7-services/src/personal.d.ts +0 -3
  152. package/dist/wdosbox.js +0 -55
  153. package/dist/wdosbox.js.symbols +0 -3174
  154. package/dist/wdosbox.wasm +0 -0
  155. package/examples/digger/index.html +0 -71
@@ -0,0 +1,468 @@
1
+ import {
2
+ TransportLayer, ClientMessage, MessageHandler,
3
+ ServerMessage, DataChunk, FsNode,
4
+ } from "emulators/dist/types/protocol/protocol";
5
+
6
+ import { createSockdrive } from "./ws-sockdrive";
7
+
8
+ const sockdriveConfirmWrite = false;
9
+
10
+ export interface Hardware {
11
+ readConfig(): string;
12
+ sendMessage(payload: string): void;
13
+ addKey(key: number, pressed: number, timeMs: number): void;
14
+ mouseMove(x: number, y: number, relative: boolean, timeMs: number): void;
15
+ mouseButton(button: number, pressed: number, timeMs: number): void;
16
+ getFramePayload(): string;
17
+
18
+ createFile(path: string): string;
19
+ appendFile(blob: string): string;
20
+ writeFile(path: string): string;
21
+ closeFile(): string;
22
+ }
23
+
24
+ const textDecoder = new TextDecoder();
25
+ const textEncoder = new TextEncoder();
26
+ const fMultiplier = 200000000;
27
+
28
+ // eslint-disable-next-line max-len
29
+ const clientMessageValues: ClientMessage[] = [
30
+ "wc-install", "wc-run", "wc-pack-fs-to-bundle", "wc-add-key", "wc-mouse-move", "wc-mouse-button", "wc-mouse-sync",
31
+ "wc-exit", "wc-sync-sleep", "wc-pause", "wc-resume", "wc-mute", "wc-unmute", "wc-connect", "wc-disconnect",
32
+ "wc-backend-event", "wc-asyncify-stats", "wc-fs-tree", "wc-fs-get-file", "wc-send-data-chunk",
33
+ ];
34
+ const clientMessageEnum: { [msg: string]: number } = {};
35
+ clientMessageValues.forEach((v, i) => clientMessageEnum[v] = i);
36
+
37
+ const serverMessageValues: ServerMessage[] = [
38
+ "ws-extract-progress", "ws-ready", "ws-server-ready", "ws-frame-set-size",
39
+ "ws-update-lines", "ws-log", "ws-warn", "ws-err",
40
+ "ws-stdout", "ws-exit", "ws-persist", "ws-sound-init", "ws-sound-push",
41
+ "ws-config", "ws-sync-sleep", "ws-connected", "ws-disconnected",
42
+ "ws-asyncify-stats", "ws-fs-tree", "ws-send-data-chunk",
43
+ ];
44
+ const serverMessageEnum: { [num: string]: ServerMessage } = {};
45
+ serverMessageValues.forEach((v, i) => serverMessageEnum[i] = v);
46
+
47
+ export class WsTransportLayer implements TransportLayer {
48
+ socket: WebSocket;
49
+ sessionId: string = Date.now() + "";
50
+ hardware: Hardware;
51
+ onInit: (version: number) => void = {/**/};
52
+
53
+ private cycles = 0;
54
+ private sockdrive = createSockdrive(this.onSockdriveOpen.bind(this),
55
+ this.onSockdriveError.bind(this), this.onSockdrivePreloadProgress.bind(this));
56
+
57
+ private handler: MessageHandler = () => {/**/};
58
+
59
+ private writeUint32(container: Uint8Array, value: number, offset: number) {
60
+ container[offset] = value & 0xFF;
61
+ container[offset + 1] = (value & 0x0000FF00) >> 8;
62
+ container[offset + 2] = (value & 0x00FF0000) >> 16;
63
+ container[offset + 3] = (value & 0xFF000000) >> 24;
64
+ return offset + 4;
65
+ }
66
+
67
+ private readUint32(container: Uint8Array, offset: number) {
68
+ return (container[offset] & 0x000000FF) |
69
+ ((container[offset + 1] << 8) & 0x0000FF00) |
70
+ ((container[offset + 2] << 16) & 0x00FF0000) |
71
+ ((container[offset + 3] << 24) & 0xFF000000);
72
+ }
73
+
74
+ private readUint64(container: Uint8Array, offset: number) {
75
+ return this.readUint32(container, offset) + this.readUint32(container, offset + 4) * 2 ** 32;
76
+ }
77
+
78
+ private sendMessageToSocket(id: ClientMessage | number, ...payload: (Uint8Array | null)[]) {
79
+ let length = 1;
80
+ for (const next of payload) {
81
+ length += 4 + (next?.length ?? 0);
82
+ }
83
+
84
+ const encoded = new Uint8Array(length);
85
+ encoded[0] = typeof id === "string" ? clientMessageEnum[id] : id;
86
+ let position = 1;
87
+ for (const next of payload) {
88
+ this.writeUint32(encoded, next?.length ?? 0, position);
89
+ position += 4;
90
+ if (next) {
91
+ encoded.set(next, position);
92
+ position += next.length;
93
+ }
94
+ }
95
+
96
+ this.socket.send(encoded);
97
+ }
98
+
99
+ private readMessage(payload: Uint8Array): { id: number, payload: (Uint8Array | null)[] } {
100
+ const out: Uint8Array[] = [];
101
+ let position = 1;
102
+ while (position + 4 <= payload.length) {
103
+ const length = this.readUint32(payload, position);
104
+ position += 4;
105
+
106
+ if (position + length > payload.length) {
107
+ throw new Error("read_message out of bounds");
108
+ } else {
109
+ out.push(payload.slice(position, position + length));
110
+ }
111
+
112
+ position += length;
113
+ }
114
+
115
+ return {
116
+ id: payload[0],
117
+ payload: out,
118
+ };
119
+ }
120
+
121
+ private onMessage(msg: Uint8Array) {
122
+ const { id, payload } = this.readMessage(msg);
123
+ const message = serverMessageEnum[id];
124
+ switch (message) {
125
+ case "ws-ready": {
126
+ this.onInit((payload && payload[0] && payload.length > 0) ? payload[0][0] : 0);
127
+ this.handler(message, {});
128
+ } break;
129
+ case "ws-server-ready":
130
+ case "ws-exit": {
131
+ // delay ws-server-ready until ws-sound-init
132
+ if (message !== "ws-server-ready") {
133
+ this.handler(message, {});
134
+ }
135
+ } break;
136
+ case "ws-stdout":
137
+ case "ws-log":
138
+ case "ws-warn":
139
+ case "ws-err": {
140
+ this.handler(message, {
141
+ tag: textDecoder.decode(payload[0]!),
142
+ message: textDecoder.decode(payload[1]!),
143
+ });
144
+ } break;
145
+ case "ws-send-data-chunk": {
146
+ const chunk: DataChunk = {
147
+ type: textDecoder.decode(payload[0]!) as any,
148
+ name: textDecoder.decode(payload[1]!),
149
+ data: payload[2],
150
+ };
151
+ this.handler(message, { chunk });
152
+ } break;
153
+ case "ws-config": {
154
+ this.handler(message, {
155
+ dosboxConf: payload[0]!,
156
+ jsdosConf: textDecoder.decode(payload[1]!),
157
+ });
158
+ } break;
159
+ case "ws-sound-init": {
160
+ this.handler(message, {
161
+ freq: this.readUint32(payload[0]!, 0),
162
+ });
163
+ this.handler("ws-server-ready", {});
164
+ } break;
165
+ case "ws-sound-push": {
166
+ this.handler(message, {
167
+ samples: new Float32Array(payload[0]!.buffer),
168
+ });
169
+ } break;
170
+ case "ws-frame-set-size": {
171
+ this.handler(message, {
172
+ width: this.readUint32(payload[0]!, 0),
173
+ height: this.readUint32(payload[0]!, 4),
174
+ });
175
+ } break;
176
+ case "ws-update-lines": {
177
+ if (payload.length > 0) {
178
+ const lines: { start: number, heapu8: Uint8Array }[] = [];
179
+ for (const next of payload) {
180
+ lines.push({
181
+ start: this.readUint32(next!, 0),
182
+ heapu8: next!.slice(4),
183
+ });
184
+ }
185
+ this.handler(message, { lines });
186
+ }
187
+ } break;
188
+ case "ws-asyncify-stats": {
189
+ this.cycles += this.readUint32(payload[0]!, 0);
190
+ const stats = {
191
+ messageSent: this.readUint32(payload[0]!, 4),
192
+ messageReceived: this.readUint32(payload[0]!, 8),
193
+ messageFrame: this.readUint32(payload[0]!, 12),
194
+ messageSound: this.readUint32(payload[0]!, 16),
195
+ nonSkippableSleepCount: 0,
196
+ sleepCount: 0,
197
+ sleepTime: 0,
198
+ cycles: this.cycles,
199
+ netSent: 0,
200
+ netRecv: 0,
201
+ driveSent: this.sockdrive.stats.write,
202
+ driveRecv: this.sockdrive.stats.read,
203
+ driveRecvTime: this.sockdrive.stats.readTotalTime,
204
+ driveCacheHit: this.sockdrive.stats.cacheHit,
205
+ driveCacheMiss: this.sockdrive.stats.cacheMiss,
206
+ driveCacheUsed: this.sockdrive.stats.cacheUsed,
207
+ };
208
+ this.handler(message, stats);
209
+ } break;
210
+ case "ws-connected": {
211
+ this.handler(message, { networkType: payload[0]![0], address: textDecoder.decode(payload[1]!) });
212
+ } break;
213
+ case "ws-disconnected": {
214
+ this.handler(message, { networkType: payload[0]![0] });
215
+ } break;
216
+ case "ws-fs-tree": {
217
+ const info: { name: string, size: number | null }[] = [];
218
+ for (let i = 0; i < payload.length - 1; ++i) {
219
+ info.push({ name: textDecoder.decode(payload[i]!), size: null });
220
+ }
221
+
222
+ const sizes = payload[payload.length - 1]!;
223
+ for (let i = 0; i < info.length; ++i) {
224
+ const size = this.readUint32(sizes, i * 4);
225
+ info[i].size = size < 0 ? null : size;
226
+ }
227
+
228
+ const fsTree: FsNode = {
229
+ name: ".",
230
+ nodes: [],
231
+ size: null,
232
+ };
233
+
234
+ function lookupNode(name: string): [FsNode, string] {
235
+ const parts = name.split("/");
236
+ let node: FsNode | undefined = fsTree;
237
+ for (let i = 1; i < parts.length - 1; ++i) {
238
+ const dir = parts[i];
239
+ node = node!.nodes?.find((n) => n.name == dir);
240
+ if (!node) {
241
+ throw new Error("Unable to find node " + name + ", subdir " + dir);
242
+ }
243
+ }
244
+ return [node, parts[parts.length - 1]];
245
+ };
246
+
247
+ for (const { name, size } of info) {
248
+ const [node, basename] = lookupNode(name);
249
+ node.nodes?.push({
250
+ name: basename,
251
+ size,
252
+ nodes: size === null ? [] : null,
253
+ });
254
+ }
255
+
256
+ this.handler("ws-fs-tree", {
257
+ fsTree,
258
+ });
259
+ } break;
260
+ case "ws-persist": {
261
+ this.handler("ws-persist", {
262
+ bundle: payload.length > 0 ? payload[0]! : null,
263
+ });
264
+ } break;
265
+ default: {
266
+ if (message === undefined) { // not standard messages
267
+ (async () => {
268
+ switch (id) {
269
+ case 100/* ws-sockdrive-open */: {
270
+ const url = textDecoder.decode(payload[0]!);
271
+ const owner = textDecoder.decode(payload[1]!);
272
+ const name = textDecoder.decode(payload[2]!);
273
+ const token = textDecoder.decode(payload[3]!);
274
+ const { handle, aheadRange } = await this.sockdrive.open(url, owner, name, token);
275
+ const template = this.sockdrive.template(handle);
276
+ const packet = new Uint8Array(4 * 7);
277
+ let offset = 0;
278
+ offset = this.writeUint32(packet, handle, offset);
279
+ offset = this.writeUint32(packet, template.size, offset);
280
+ offset = this.writeUint32(packet, template.heads, offset);
281
+ offset = this.writeUint32(packet, template.cylinders, offset);
282
+ offset = this.writeUint32(packet, template.sectors, offset);
283
+ offset = this.writeUint32(packet, template.sectorSize, offset);
284
+ this.writeUint32(packet, aheadRange, offset);
285
+ this.sendMessageToSocket(100, packet);
286
+ } break;
287
+ case 101/* ws-sockdrive-read */: {
288
+ const handle = this.readUint32(payload[0]!, 0);
289
+ const sector = this.readUint32(payload[0]!, 4);
290
+ const response = await this.sockdrive.read(handle, sector);
291
+ const packet = new Uint8Array(4);
292
+ this.writeUint32(packet, response.code, 0);
293
+ this.sendMessageToSocket(101, packet, response.buffer ?? null);
294
+ } break;
295
+ case 102/* ws-sockdrive-write */: {
296
+ const handle = this.readUint32(payload[0]!, 0);
297
+ const sector = this.readUint32(payload[0]!, 4);
298
+ const code = this.sockdrive.write(handle, sector, payload[1]!);
299
+ const packet = new Uint8Array(4);
300
+ this.writeUint32(packet, code, 0);
301
+ if (sockdriveConfirmWrite) {
302
+ this.sendMessageToSocket(102, packet);
303
+ }
304
+ } break;
305
+ case 103/* ws-sockdrive-close */: {
306
+ this.sockdrive.close(this.readUint32(payload[0]!, 0));
307
+ } break;
308
+ default:
309
+ console.log("WARN! Unhandled server non standard message", id, payload);
310
+ }
311
+ })().catch((e) => {
312
+ this.handler("ws-err", {
313
+ tag: "panic",
314
+ message: "sockdrive error: " + (e.message ?? "???"),
315
+ });
316
+ });
317
+ } else {
318
+ console.warn("WARN! Unhandled server message", message);
319
+ }
320
+ }
321
+ }
322
+ }
323
+
324
+ constructor(socket: WebSocket, onInit: (version: number) => void) {
325
+ this.socket = socket;
326
+ this.socket.addEventListener("error", (e) => {
327
+ this.handler("ws-err", { tag: "ws", message: (e as any).message ?? "Unknown transport layer error" });
328
+ this.handler("ws-exit", {});
329
+ this.socket.close();
330
+ });
331
+ this.socket.addEventListener("message", async (ev) => {
332
+ const blob: Blob = ev.data;
333
+ this.onMessage(new Uint8Array(await blob.arrayBuffer()));
334
+ });
335
+ this.sendMessageToSocket("wc-install");
336
+ this.hardware = (null) as any;
337
+ this.onInit = onInit;
338
+ }
339
+
340
+ async sendMessageToServer(name: ClientMessage, props?: { [key: string]: any; }) {
341
+ if (props === undefined || props?.sessionId !== this.sessionId) {
342
+ return;
343
+ }
344
+
345
+ const messageId = clientMessageEnum[name];
346
+ switch (name) {
347
+ case "wc-run": {
348
+ const token = props.token ?? "";
349
+ this.sendMessageToSocket(messageId, textEncoder.encode(token));
350
+ } break;
351
+ case "wc-send-data-chunk": {
352
+ const chunk: DataChunk = props.chunk;
353
+ this.sendMessageToSocket(messageId,
354
+ textEncoder.encode(chunk.type),
355
+ textEncoder.encode(chunk.name),
356
+ chunk.data ? new Uint8Array(chunk.data) : null);
357
+ } break;
358
+ case "wc-asyncify-stats": {
359
+ this.sendMessageToSocket(messageId);
360
+ } break;
361
+ case "wc-add-key": {
362
+ const payload = new Uint8Array(3 * 4);
363
+ let offset = 0;
364
+ offset = this.writeUint32(payload, props.key, offset);
365
+ offset = this.writeUint32(payload, props.pressed ? 1 : 0, offset);
366
+ this.writeUint32(payload, props.timeMs, offset);
367
+ this.sendMessageToSocket(messageId, payload);
368
+ } break;
369
+ case "wc-mouse-move": {
370
+ const payload = new Uint8Array(3 * 4 + 3);
371
+ let offset = 0;
372
+ offset = this.writeUint32(payload, Math.abs(props.x) * fMultiplier, offset);
373
+ offset = this.writeUint32(payload, Math.abs(props.y) * fMultiplier, offset);
374
+ offset = this.writeUint32(payload, props.timeMs, offset);
375
+ payload[offset] = props.relative ? 1 : 0;
376
+ payload[offset + 1] = props.x >= 0 ? 0 : 1;
377
+ payload[offset + 2] = props.y >= 0 ? 0 : 1;
378
+ this.sendMessageToSocket(messageId, payload);
379
+ } break;
380
+ case "wc-mouse-button": {
381
+ const payload = new Uint8Array(4 + 2);
382
+ payload[0] = props.button;
383
+ payload[1] = props.pressed ? 1 : 0;
384
+ this.writeUint32(payload, props.timeMs, 2);
385
+ this.sendMessageToSocket(messageId, payload);
386
+ } break;
387
+ case "wc-mouse-sync": {
388
+ const payload = new Uint8Array(4);
389
+ this.writeUint32(payload, props.timeMs, 0);
390
+ this.sendMessageToSocket(messageId, payload);
391
+ } break;
392
+ case "wc-connect": {
393
+ this.sendMessageToSocket(messageId, new Uint8Array([props.networkType]),
394
+ textEncoder.encode(props.address));
395
+ } break;
396
+ case "wc-fs-tree": {
397
+ this.sendMessageToSocket(messageId);
398
+ } break;
399
+ case "wc-pack-fs-to-bundle": {
400
+ this.sendMessageToSocket(messageId, new Uint8Array([props.onlyChanges ? 1 : 0]));
401
+ } break;
402
+ default: {
403
+ console.log("Unhandled client message (wc):", name, messageId, props);
404
+ } break;
405
+ }
406
+ }
407
+
408
+ initMessageHandler(handler: MessageHandler) {
409
+ this.handler = (message, props) => {
410
+ props.sessionId = this.sessionId;
411
+ handler(message, props);
412
+ };
413
+ }
414
+
415
+ exit() {
416
+ this.sendMessageToSocket("wc-exit");
417
+ }
418
+
419
+ onSockdriveOpen(drive: string, read: boolean, write: boolean, imageSize: number, preloadQueue: number[]) {
420
+ this.handler("ws-log", {
421
+ tag: "worker",
422
+ message: "sockdrive: " + drive + ", read=" + read + ", write=" + write +
423
+ ", imageSize=" + Math.round(imageSize / 1024 / 1024) + "Mb" + ", preloadQueue=" + preloadQueue.length,
424
+ });
425
+ }
426
+
427
+ onSockdriveError(e: Error) {
428
+ this.handler("ws-err", {
429
+ tag: "panic",
430
+ message: e.message ?? "unable to open sockdrive",
431
+ });
432
+ }
433
+
434
+ onSockdrivePreloadProgress(drive: string, restBytes: number) {
435
+ this.handler("ws-log", {
436
+ tag: "worker",
437
+ message: "sockdrive: " + drive + ", preload=" + restBytes,
438
+ });
439
+ }
440
+ }
441
+
442
+ export function createWsTransportLayer(url: string, onInit: (version: number) => void): Promise<TransportLayer> {
443
+ return new Promise<TransportLayer>((resolve) => {
444
+ let locked = false;
445
+ const inervalId = setInterval(() => {
446
+ if (locked) {
447
+ return;
448
+ }
449
+
450
+ locked = true;
451
+ const ws = new WebSocket(url);
452
+ const onSuccess = () => {
453
+ clearInterval(inervalId);
454
+ console.log("Connected to", url);
455
+ resolve(new WsTransportLayer(ws, onInit));
456
+ };
457
+
458
+ ws.addEventListener("error", (error) => {
459
+ console.error("Can't conect to ", url, error);
460
+ ws.removeEventListener("open", onSuccess);
461
+ ws.close();
462
+ locked = false;
463
+ });
464
+
465
+ ws.addEventListener("open", onSuccess);
466
+ }, 1000);
467
+ });
468
+ }
@@ -0,0 +1,48 @@
1
+ /** @type {import('tailwindcss').Config} */
2
+ module.exports = {
3
+ content: [
4
+ "./index.html",
5
+ "./src/**/*.{js,ts,jsx,tsx}",
6
+ ],
7
+ theme: {
8
+ extend: {},
9
+ },
10
+ plugins: [require("daisyui")],
11
+ safelist: [
12
+ "alert-success",
13
+ "alert-error",
14
+ ],
15
+ daisyui: {
16
+ themes: [
17
+ "light",
18
+ "dark",
19
+ "cupcake",
20
+ "bumblebee",
21
+ "emerald",
22
+ "corporate",
23
+ "synthwave",
24
+ "retro",
25
+ "cyberpunk",
26
+ "valentine",
27
+ "halloween",
28
+ "garden",
29
+ "forest",
30
+ "aqua",
31
+ "lofi",
32
+ "pastel",
33
+ "fantasy",
34
+ "wireframe",
35
+ "black",
36
+ "luxury",
37
+ "dracula",
38
+ "cmyk",
39
+ "autumn",
40
+ "business",
41
+ "acid",
42
+ "lemonade",
43
+ "night",
44
+ "coffee",
45
+ "winter",
46
+ ],
47
+ }
48
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "compilerOptions": {
3
+ "paths": {
4
+ "react": ["./node_modules/preact/compat/"],
5
+ "react-dom": ["./node_modules/preact/compat/"]
6
+ },
7
+ "target": "ESNext",
8
+ "useDefineForClassFields": true,
9
+ "lib": ["DOM", "DOM.Iterable", "ESNext"],
10
+ "allowJs": false,
11
+ "skipLibCheck": true,
12
+ "esModuleInterop": false,
13
+ "allowSyntheticDefaultImports": true,
14
+ "strict": true,
15
+ "forceConsistentCasingInFileNames": true,
16
+ "module": "ESNext",
17
+ "moduleResolution": "Node",
18
+ "resolveJsonModule": true,
19
+ "isolatedModules": true,
20
+ "noEmit": true,
21
+ "jsx": "react-jsx",
22
+ "jsxImportSource": "preact"
23
+ },
24
+ "include": ["src"],
25
+ "exclude": [
26
+ "src/sockdrive/js/src/test",
27
+ "src/sockdrive/js/src/sockdrive-fat.ts",
28
+ "src/sockdrive/js/src/sockdrive-native.ts",
29
+ "src/sockdrive/js/src/fatfs"
30
+ ],
31
+ "references": [{ "path": "./tsconfig.node.json" }]
32
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "compilerOptions": {
3
+ "composite": true,
4
+ "module": "ESNext",
5
+ "moduleResolution": "Node",
6
+ "allowSyntheticDefaultImports": true
7
+ },
8
+ "include": ["vite.config.ts"]
9
+ }
package/vite.config.ts ADDED
@@ -0,0 +1,22 @@
1
+ import { defineConfig } from "vite";
2
+ import preact from "@preact/preset-vite";
3
+
4
+ // https://vitejs.dev/config/
5
+ export default defineConfig({
6
+ plugins: [preact()],
7
+ server: {
8
+ port: 3000,
9
+ host: "0.0.0.0",
10
+ cors: true,
11
+ },
12
+ build: {
13
+ rollupOptions: {
14
+ output: {
15
+ entryFileNames: "js-dos.js",
16
+ assetFileNames: (info) => {
17
+ return info.name === "index.css" ? "js-dos.css" : info.name;
18
+ },
19
+ },
20
+ },
21
+ },
22
+ });