js-dos 8.1.5 → 8.1.6

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 (140) hide show
  1. package/dist/emulators/emulators.js +2 -0
  2. package/dist/emulators/emulators.js.map +1 -0
  3. package/dist/emulators/types/build.d.ts +28 -0
  4. package/dist/emulators/types/dos/bundle/dos-bundle.d.ts +20 -0
  5. package/dist/emulators/types/dos/dosbox/ts/direct.d.ts +3 -0
  6. package/dist/emulators/types/dos/dosbox/ts/worker.d.ts +3 -0
  7. package/dist/emulators/types/emulators.d.ts +76 -0
  8. package/dist/emulators/types/http.d.ts +9 -0
  9. package/dist/emulators/types/impl/ci-impl.d.ts +29 -0
  10. package/dist/emulators/types/impl/emulators-impl.d.ts +26 -0
  11. package/dist/emulators/types/impl/modules.d.ts +38 -0
  12. package/dist/emulators/types/libzip/libzip.d.ts +16 -0
  13. package/dist/emulators/types/protocol/messages-queue.d.ts +8 -0
  14. package/dist/emulators/types/protocol/protocol.d.ts +128 -0
  15. package/dist/emulators/wdosbox-x.js +55 -0
  16. package/dist/emulators/wdosbox-x.js.symbols +8760 -0
  17. package/dist/emulators/wdosbox-x.wasm +0 -0
  18. package/dist/emulators/wdosbox.js +55 -0
  19. package/dist/emulators/wdosbox.js.symbols +3001 -0
  20. package/dist/emulators/wdosbox.wasm +0 -0
  21. package/dist/emulators/wlibzip.js +21 -0
  22. package/dist/emulators/wlibzip.js.symbols +268 -0
  23. package/dist/emulators/wlibzip.wasm +0 -0
  24. package/dist/js-dos.css +1 -0
  25. package/dist/js-dos.js +22 -0
  26. package/dist/js-dos.js.map +1 -0
  27. package/package.json +1 -1
  28. package/release.zip +0 -0
  29. package/.eslintrc.json +0 -43
  30. package/.github/workflows/build.yml +0 -51
  31. package/.gitmodules +0 -4
  32. package/index.html +0 -110
  33. package/postcss.config.cjs +0 -8
  34. package/src/app.css +0 -14
  35. package/src/base.css +0 -10
  36. package/src/components/checkbox.tsx +0 -41
  37. package/src/components/close-button.tsx +0 -17
  38. package/src/components/components.css +0 -28
  39. package/src/components/dos-option-checkbox.tsx +0 -94
  40. package/src/components/dos-option-select.tsx +0 -108
  41. package/src/components/dos-option-slider.tsx +0 -35
  42. package/src/components/error.tsx +0 -16
  43. package/src/components/loading.tsx +0 -26
  44. package/src/components/lock.tsx +0 -10
  45. package/src/components/select.tsx +0 -33
  46. package/src/components/slider.tsx +0 -69
  47. package/src/download-file.ts +0 -18
  48. package/src/frame/account-frame.tsx +0 -156
  49. package/src/frame/editor/defaults.ts +0 -419
  50. package/src/frame/editor/editor-conf-frame.tsx +0 -227
  51. package/src/frame/editor/editor-frame.css +0 -55
  52. package/src/frame/editor/editor-fs-frame.tsx +0 -314
  53. package/src/frame/fat-drives-frame.tsx +0 -199
  54. package/src/frame/frame.css +0 -119
  55. package/src/frame/frame.tsx +0 -34
  56. package/src/frame/image-rendering-frame.tsx +0 -8
  57. package/src/frame/network-frame.tsx +0 -157
  58. package/src/frame/quick-save-frame.tsx +0 -98
  59. package/src/frame/settings-frame.tsx +0 -14
  60. package/src/frame/stats-frame.tsx +0 -87
  61. package/src/host/bundle-storage.ts +0 -91
  62. package/src/host/fullscreen.ts +0 -35
  63. package/src/host/lcache.ts +0 -203
  64. package/src/host/lstorage.ts +0 -156
  65. package/src/i18n.ts +0 -185
  66. package/src/index.css +0 -7
  67. package/src/load.ts +0 -132
  68. package/src/login/login.css +0 -9
  69. package/src/login/login.tsx +0 -77
  70. package/src/main.tsx +0 -197
  71. package/src/public/types.ts +0 -45
  72. package/src/sidebar/account-button.tsx +0 -33
  73. package/src/sidebar/cloud-save-button.tsx +0 -89
  74. package/src/sidebar/diskette-icon.tsx +0 -14
  75. package/src/sidebar/fullscreen-button.tsx +0 -44
  76. package/src/sidebar/network-button.tsx +0 -39
  77. package/src/sidebar/sidebar-button.tsx +0 -244
  78. package/src/sidebar/sidebar.css +0 -47
  79. package/src/sidebar/sidebar.tsx +0 -30
  80. package/src/sockdrive/LICENSE +0 -674
  81. package/src/sockdrive/README.md +0 -45
  82. package/src/sockdrive/include/sockdrive.h +0 -23
  83. package/src/sockdrive/js/.eslintrc.json +0 -43
  84. package/src/sockdrive/js/package.json +0 -29
  85. package/src/sockdrive/js/src/fatfs/chains.js +0 -318
  86. package/src/sockdrive/js/src/fatfs/dir.js +0 -385
  87. package/src/sockdrive/js/src/fatfs/helpers.js +0 -210
  88. package/src/sockdrive/js/src/fatfs/index.js +0 -606
  89. package/src/sockdrive/js/src/fatfs/structs.js +0 -255
  90. package/src/sockdrive/js/src/fatfs/vol.js +0 -166
  91. package/src/sockdrive/js/src/sockdrive/cache.ts +0 -56
  92. package/src/sockdrive/js/src/sockdrive/drive.ts +0 -471
  93. package/src/sockdrive/js/src/sockdrive/lru.ts +0 -221
  94. package/src/sockdrive/js/src/sockdrive/types.ts +0 -14
  95. package/src/sockdrive/js/src/sockdrive-fat.d.ts +0 -64
  96. package/src/sockdrive/js/src/sockdrive-fat.ts +0 -226
  97. package/src/sockdrive/js/src/sockdrive-native.ts +0 -100
  98. package/src/sockdrive/js/src/test/test.ts +0 -289
  99. package/src/sockdrive/js/tsconfig.json +0 -11
  100. package/src/sockdrive/js/webpack.config.js +0 -68
  101. package/src/sockdrive/js/yarn.lock +0 -1808
  102. package/src/store/auth.ts +0 -115
  103. package/src/store/dos.ts +0 -305
  104. package/src/store/editor.ts +0 -61
  105. package/src/store/init.ts +0 -48
  106. package/src/store/storage.ts +0 -32
  107. package/src/store/ui.ts +0 -191
  108. package/src/store.tsx +0 -116
  109. package/src/ui.tsx +0 -140
  110. package/src/v8/changes.ts +0 -56
  111. package/src/v8/config.ts +0 -20
  112. package/src/v8/subscriptions.ts +0 -62
  113. package/src/vite-env.d.ts +0 -5
  114. package/src/window/dos/controls/keyboard.ts +0 -32
  115. package/src/window/dos/controls/keys.ts +0 -331
  116. package/src/window/dos/controls/mouse/mount.ts +0 -99
  117. package/src/window/dos/controls/mouse/mouse-locked.ts +0 -51
  118. package/src/window/dos/controls/mouse/mouse-not-locked.ts +0 -84
  119. package/src/window/dos/controls/mouse/mouse-swipe.ts +0 -57
  120. package/src/window/dos/controls/mouse/pointer.ts +0 -85
  121. package/src/window/dos/controls/mouse.ts +0 -21
  122. package/src/window/dos/dos-dhry2.tsx +0 -191
  123. package/src/window/dos/dos-runtime.tsx +0 -216
  124. package/src/window/dos/dos-window.tsx +0 -91
  125. package/src/window/dos/render/canvas.ts +0 -65
  126. package/src/window/dos/render/resize.ts +0 -31
  127. package/src/window/dos/render/webgl.ts +0 -173
  128. package/src/window/dos/sound/audio-node.ts +0 -148
  129. package/src/window/error-window.tsx +0 -9
  130. package/src/window/loading-window.tsx +0 -54
  131. package/src/window/prerun-window.tsx +0 -121
  132. package/src/window/select-window.tsx +0 -118
  133. package/src/window/window.css +0 -78
  134. package/src/window/window.tsx +0 -49
  135. package/src/ws/ws-sockdrive.ts +0 -123
  136. package/src/ws/ws-transport-layer.ts +0 -468
  137. package/tailwind.config.cjs +0 -48
  138. package/tsconfig.json +0 -32
  139. package/tsconfig.node.json +0 -9
  140. package/vite.config.ts +0 -25
@@ -1,227 +0,0 @@
1
- import { useDispatch, useSelector } from "react-redux";
2
- import { useT } from "../../i18n";
3
- import { State } from "../../store";
4
- import { editorSlice } from "../../store/editor";
5
- import { dosboxconf } from "./defaults";
6
- import { dosSlice } from "../../store/dos";
7
- import { useEffect, useState } from "preact/hooks";
8
- import { sockdriveBackend } from "../../store/init";
9
- import { uiSlice } from "../../store/ui";
10
-
11
- const imgmount = "imgmount\\s+(\\d+)\\s+sockdrive\\s+([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)\\s*$";
12
- const cleanup = "imgmount\\s+(\\d+)\\s+sockdrive\\s+.*$";
13
-
14
- interface Sockdrive {
15
- num: string,
16
- backend: string,
17
- owner: string,
18
- drive: string,
19
- }
20
-
21
- export function EditorConf() {
22
- const t = useT();
23
- const bundleConfig = useSelector((state: State) => state.editor.bundleConfig);
24
- const account = useSelector((state: State) => state.auth.account);
25
- const backend = useSelector((state: State) => state.dos.backend);
26
- const { sockdriveEndpoint, sockdriveWssEndpoint } = useSelector((state: State) =>
27
- sockdriveBackend[state.init.sockdriveBackendName] ??
28
- sockdriveBackend["js-dos"]);
29
- const dispatch = useDispatch();
30
- const [myDrives, setMyDrives] = useState<{ name: string, owner: string }[]>([]);
31
-
32
- useEffect(() => {
33
- if (!account || !account.token) {
34
- setMyDrives([]);
35
- } else {
36
- fetch(sockdriveEndpoint + "/list/drives/" + account.token.access_token)
37
- .then((r) => r.json())
38
- .then(setMyDrives)
39
- .catch(console.error);
40
- }
41
- }, [account?.token, sockdriveEndpoint]);
42
-
43
- function parseSockDrives(conf: string) {
44
- const drives: { [num: string]: Sockdrive } = {};
45
- const re = new RegExp(imgmount, "gm");
46
- let m: RegExpExecArray | null;
47
- while (m = re.exec(conf)) {
48
- /* eslint-disable-next-line no-unused-vars */
49
- const [_, num, backend, owner, drive] = m;
50
- drives[num] = {
51
- num,
52
- backend,
53
- owner,
54
- drive,
55
- };
56
- }
57
- return drives;
58
- }
59
-
60
- const [sockdrives, setSockdrives] = useState<{ [num: string]: Sockdrive }>(
61
- parseSockDrives(bundleConfig?.dosboxConf ?? ""));
62
-
63
- function setSockdrivesAndUpdateConf(sockdrives: { [num: string]: Sockdrive }) {
64
- setSockdrives(sockdrives);
65
- if (bundleConfig) {
66
- const parts: string[] = [];
67
- for (const next of Object.values(sockdrives)) {
68
- if (next.owner.length > 0 && next.drive.length > 0) {
69
- parts.push(`imgmount ${next.num} sockdrive ${next.backend} ${next.owner} ${next.drive}`);
70
- }
71
- }
72
-
73
- let contents = bundleConfig.dosboxConf;
74
- contents = contents.replaceAll(new RegExp(cleanup, "gm"), "");
75
- if (parts.length > 0) {
76
- let start = contents.indexOf("[autoexec]");
77
- if (start === -1) {
78
- start = contents.length;
79
- } else {
80
- start += 10;
81
- }
82
- const footer = contents.substring(start);
83
- contents = contents.substring(0, start);
84
- contents += "\n" + parts.join("\n") + "\n" + footer.trim();
85
- }
86
- updateDosboxConf(contents);
87
- }
88
- }
89
-
90
- function changeConfig(contents: string) {
91
- setSockdrives(parseSockDrives(contents));
92
- updateDosboxConf(contents);
93
- }
94
-
95
- function updateDosboxConf(newConf: string) {
96
- dispatch(dosSlice.actions.mouseCapture(newConf.indexOf("autolock=true") > 0));
97
- dispatch(editorSlice.actions.dosboxConf(newConf));
98
- dispatch(dosSlice.actions.dosBackendLocked(newConf.indexOf("sockdrive") >= 0));
99
- }
100
-
101
- if (bundleConfig === null) {
102
- return null;
103
- }
104
-
105
- return <div class="editor-conf-frame flex flex-col flex-grow w-full gap-2">
106
- <div class="">{t("dosboxconf_template")}</div>
107
- <div class="flex flex-row flex-wrap items-center gap-2">
108
- {dosboxconf
109
- .map(({ name, backend, contents }) => {
110
- return <button class="btn btn-sm"
111
- onClick={() => {
112
- dispatch(dosSlice.actions.dosBackend(backend === "dosboxX" ? "dosboxX" : "dosbox"));
113
- changeConfig(contents.replaceAll("{wss-makevm}", sockdriveWssEndpoint));
114
- }}>
115
- {name}
116
- </button>;
117
- })}
118
- </div>
119
- {backend === "dosboxX" && <>
120
- <button class="btn btn-ghost btn-xs self-start" target="_blank"
121
- onClick={() => dispatch(uiSlice.actions.frameFatDrives())}>
122
- {t("net_drives")}:
123
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
124
- stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
125
- <path stroke-linecap="round" stroke-linejoin="round" d="m12.75 15 3-3m0 0-3-3m3
126
- 3h-7.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
127
- </svg>
128
- </button>
129
- {["2", "3"].map((num) => {
130
- return <div class="flex flex-row justify-start items-center gap-3 w-full">
131
- <p>{num === "2" ? "C:" : "D:"}</p>
132
- <div class="flex flex-col gap-2">
133
- <div class="flex flex-row gap-1 items-center">
134
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
135
- stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
136
- <path stroke-linecap="round" stroke-linejoin="round" d="M17.982 18.725A7.488 7.488
137
- 0 0 0 12 15.75a7.488 7.488 0 0 0-5.982 2.975m11.963 0a9 9 0 1 0-11.963 0m11.963
138
- 0A8.966 8.966 0 0 1 12 21a8.966 8.966 0 0 1-5.982-2.275M15 9.75a3
139
- 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
140
- </svg>
141
- <input class="input input-xs" type="text" value={sockdrives[num]?.owner ?? ""}
142
- onChange={(e) => {
143
- const newSockdrives = { ...sockdrives };
144
- newSockdrives[num] = {
145
- num,
146
- backend: sockdrives[num]?.backend ?? sockdriveWssEndpoint,
147
- owner: e.currentTarget.value,
148
- drive: sockdrives[num]?.drive ?? "",
149
- };
150
- setSockdrivesAndUpdateConf(newSockdrives);
151
- }}></input>
152
- </div>
153
- <div class="flex flex-row gap-1 items-center">
154
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
155
- stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
156
- <path stroke-linecap="round" stroke-linejoin="round" d="M20.25 6.375c0 2.278-3.694
157
- 4.125-8.25 4.125S3.75 8.653 3.75 6.375m16.5 0c0-2.278-3.694-4.125-8.25-4.125S3.75
158
- 4.097 3.75 6.375m16.5 0v11.25c0 2.278-3.694 4.125-8.25
159
- 4.125s-8.25-1.847-8.25-4.125V6.375m16.5 0v3.75m-16.5-3.75v3.75m16.5
160
- 0v3.75C20.25 16.153
161
- 16.556 18 12 18s-8.25-1.847-8.25-4.125v-3.75m16.5 0c0 2.278-3.694 4.125-8.25
162
- 4.125s-8.25-1.847-8.25-4.125" />
163
- </svg>
164
-
165
- <input class="input input-xs" type="text" value={sockdrives[num]?.drive ?? ""}
166
- onChange={(e) => {
167
- const newSockdrives = { ...sockdrives };
168
- newSockdrives[num] = {
169
- num,
170
- backend: sockdrives[num]?.backend ?? sockdriveWssEndpoint,
171
- owner: sockdrives[num]?.owner ?? "",
172
- drive: e.currentTarget.value,
173
- };
174
- setSockdrivesAndUpdateConf(newSockdrives);
175
- }}></input>
176
- </div>
177
- </div>
178
- <div class="dropdown dropdown-bottom dropdown-end">
179
- <button tabIndex={0} role="button" class="btn btn-sm">
180
- <svg xmlns="http://www.w3.org/2000/svg" fill="none"
181
- viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
182
- <path stroke-linecap="round" stroke-linejoin="round"
183
- d="M3.75 9.776c.112-.017.227-.026.344-.026h15.812c.117 0 .232.009.344.026m-16.5
184
- 0a2.25 2.25 0 0 0-1.883 2.542l.857 6a2.25 2.25 0 0 0 2.227 1.932H19.05a2.25
185
- 2.25 0 0 0 2.227-1.932l.857-6a2.25 2.25 0 0 0-1.883-2.542m-16.5 0V6A2.25 2.25
186
- 0 0 1 6 3.75h3.879a1.5 1.5 0 0 1 1.06.44l2.122 2.12a1.5 1.5 0 0 0
187
- 1.06.44H18A2.25 2.25 0 0 1 20.25 9v.776" />
188
- </svg>
189
- </button>
190
- <ul tabIndex={0} class="dropdown-content z-[1] menu p-2 shadow bg-base-100
191
- rounded-box w-52 max-h-80 overflow-auto">
192
- {myDrives.map(({ name, owner }) => {
193
- return <li onClick={(e) => {
194
- const newSockdrives = { ...sockdrives };
195
- newSockdrives[num] = {
196
- num,
197
- owner,
198
- backend: sockdriveWssEndpoint,
199
- drive: name,
200
- };
201
- setSockdrivesAndUpdateConf(newSockdrives);
202
- e.currentTarget.parentElement!.blur();
203
- }}><a>{name}</a></li>;
204
- })}
205
- </ul>
206
- </div>
207
- <button class="btn btn-sm" onClick={() => {
208
- const newSockdrives = { ...sockdrives };
209
- delete newSockdrives[num];
210
- setSockdrivesAndUpdateConf(newSockdrives);
211
- }}>
212
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
213
- stroke="currentColor" class="w-4 h-4">
214
- <path stroke-linecap="round" stroke-linejoin="round"
215
- d="M12 9.75 14.25 12m0 0 2.25 2.25M14.25 12l2.25-2.25M14.25 12 12 14.25m-2.58
216
- 4.92-6.374-6.375a1.125 1.125 0 0 1 0-1.59L9.42 4.83c.21-.211.497-.33.795-.33H19.5a2.25
217
- 2.25 0 0 1 2.25 2.25v10.5a2.25 2.25 0 0 1-2.25 2.25h-9.284c-.298 0-.585-.119-.795-.33Z"
218
- />
219
- </svg>
220
- </button>
221
- </div>;
222
- })}
223
- </>}
224
- <textarea class="flex-grow" value={bundleConfig.dosboxConf}
225
- onChange={(e) => changeConfig(e.currentTarget?.value ?? "")} />
226
- </div>;
227
- }
@@ -1,55 +0,0 @@
1
- .jsdos-rso {
2
- .editor-conf-frame {
3
- @apply w-full overflow-hidden flex-grow flex flex-col items-start justify-center h-full px-4;
4
-
5
- textarea {
6
- @apply w-full textarea;
7
- resize: none;
8
- }
9
- }
10
-
11
- .editor-fs-frame {
12
- @apply h-full;
13
-
14
- .fs-tree-view {
15
- @apply form-control bg-base-100 rounded w-full h-full;
16
-
17
- .fs-tree {
18
-
19
- ol {
20
- @apply ml-2;
21
- }
22
-
23
- li {
24
- @apply my-2;
25
- }
26
-
27
- button {
28
- border: none;
29
- background: none;
30
- filter: none;
31
- min-height: auto;
32
- height: auto;
33
- @apply m-0 p-0;
34
- }
35
-
36
- svg {
37
- @apply text-accent;
38
- }
39
-
40
- input {
41
- @apply checkbox checkbox-accent mr-2 w-4 h-4;
42
- }
43
-
44
- .rct-text,
45
- .rct-bare-label,
46
- label {
47
- display: flex;
48
- flex-direction: row;
49
- justify-content: start;
50
- align-items: center;
51
- }
52
- }
53
- }
54
- }
55
- }
@@ -1,314 +0,0 @@
1
- import { FsNode } from "emulators/dist/types/protocol/protocol";
2
- import { useEffect, useRef, useState } from "preact/hooks";
3
- import CheckboxTree, { Node, OnCheckNode, OnExpandNode } from "react-checkbox-tree";
4
- import { useSelector, useStore } from "react-redux";
5
- import { useT } from "../../i18n";
6
- import { loadBundle } from "../../load";
7
- import { State, useNonSerializableStore } from "../../store";
8
- import { downloadArrayToFs } from "../../download-file";
9
-
10
- interface NodeExt extends Node {
11
- fsNode: FsNode,
12
- }
13
-
14
- export function EditorFsFrame() {
15
- const t = useT();
16
- const haveCi = useSelector((state: State) => state.dos.ci);
17
- const nonSerializableStore = useNonSerializableStore();
18
- const [fsSize, setFsSize] = useState<number>(0);
19
- const [fsTree, _setFsTree] = useState<NodeExt[] | null>(null);
20
- const [expanded, setExpanded] = useState<string[]>([]);
21
- const [checked, setChecked] = useState<string[]>([]);
22
- const [uploadingFile, setUploadingFile] = useState<string | null>(null);
23
- const [makingBundle, setMakingBundle] = useState<boolean>(false);
24
-
25
- function setFsTree(fsTree: NodeExt[] | null) {
26
- _setFsTree(fsTree);
27
- setFsSize(Math.round(calcFsSize(fsTree) / 1024 / 1024 * 100) / 100);
28
- }
29
-
30
- useEffect(() => {
31
- if (nonSerializableStore.ci === null || !haveCi) {
32
- return;
33
- }
34
-
35
- let cancled = false;
36
- nonSerializableStore.ci.fsTree()
37
- .then((tree) => {
38
- if (!cancled) {
39
- setFsTree(toNodes(tree));
40
- }
41
- })
42
- .catch((e) => {
43
- console.error(e);
44
- if (!cancled) {
45
- setFsTree(null);
46
- }
47
- });
48
-
49
- return () => {
50
- cancled = true;
51
- };
52
- }, [haveCi]);
53
-
54
-
55
- function onExpand(expanded: Array<string>, node: OnExpandNode) {
56
- setExpanded(expanded);
57
- }
58
-
59
- function onCheck(checked: Array<string>, node: OnCheckNode) {
60
- setChecked(checked);
61
- }
62
-
63
- async function onRefresh() {
64
- setFsTree(null);
65
- if (nonSerializableStore.ci === null || !haveCi) {
66
- return;
67
- }
68
- const tree = await nonSerializableStore.ci.fsTree();
69
- setFsTree(toNodes(tree));
70
- }
71
-
72
- // const check = <input type="checkbox" class="checkbox" />;
73
- return <div class="editor-fs-frame frame-root items-start px-4">
74
- {(makingBundle || uploadingFile !== null) &&
75
- <div class="card card-bordered bg-base-100 shadow-xl w-full">
76
- <div class="card-body">
77
- <div class="card-title">
78
- {t(makingBundle ? "please_wait" : "uploading_file")}
79
- </div>
80
- <span class="break-words">
81
- {makingBundle ? t("making_bundle") : uploadingFile}
82
- </span>
83
- </div>
84
- </div>}
85
- {!makingBundle && uploadingFile === null && fsTree &&
86
- <div className="fs-tree-view">
87
- <Actions
88
- onRefresh={onRefresh}
89
- onUploadingFile={setUploadingFile}
90
- onMakingBundle={setMakingBundle} />
91
- <div class="bg-base-200 px-2 py-1 text-right">
92
- {t("size")}: {fsSize} Mb
93
- </div>
94
- <div class="fs-tree">
95
- <CheckboxTree
96
- icons={{
97
- expandOpen: <FolderOpened /> as any,
98
- expandClose: <FolderClosed /> as any,
99
- // expandAll: <div class="bg-red-100 w-4 h-4"></div> as any,
100
- // parentOpen: <div class="bg-blue-100 w-4 h-4"></div> as any,
101
- // parentClose: <div class="bg-purple-100 w-4 h-4"></div> as any,
102
- }}
103
- iconsClass="icon"
104
- nativeCheckboxes={false}
105
- onlyLeafCheckboxes={true}
106
- nodes={fsTree}
107
- checked={checked}
108
- onCheck={onCheck}
109
- expanded={expanded}
110
- onExpand={onExpand} />
111
- </div>
112
- </div>}
113
- </div>;
114
- }
115
-
116
- function toNodes(fsTree: FsNode): NodeExt[] {
117
- const fs: NodeExt[] = [];
118
- for (const next of (fsTree.nodes?.sort(nodeSorter) || [])) {
119
- fs.push(toNode(next, "."));
120
- }
121
- return fs;
122
- }
123
-
124
- function toNode(fsNode: FsNode, path: string): NodeExt {
125
- const fullPath = path + "/" + fsNode.name;
126
- const nodeExt: NodeExt = {
127
- label: fsNode.name,
128
- value: fullPath,
129
- fsNode,
130
- };
131
-
132
- if (fsNode.nodes !== null) {
133
- const children = fsNode.nodes.sort(nodeSorter)
134
- .map((next) => toNode(next, fullPath));
135
- nodeExt.children = children;
136
- }
137
-
138
- return nodeExt;
139
- }
140
-
141
- function Actions(props: {
142
- onRefresh: () => void,
143
- onUploadingFile: (file: string | null) => void,
144
- onMakingBundle: (making: boolean) => void,
145
- }) {
146
- const t = useT();
147
- const uploadDirRef = useRef<HTMLInputElement>(null);
148
- const uploadFileRef = useRef<HTMLInputElement>(null);
149
- const store = useStore();
150
- const nonSerializableStore = useNonSerializableStore();
151
- const { onRefresh, onUploadingFile, onMakingBundle } = props;
152
-
153
- useEffect(() => {
154
- if (uploadDirRef.current !== null) {
155
- uploadDirRef.current.setAttribute("directory", "");
156
- uploadDirRef.current.setAttribute("webkitdirectory", "");
157
- }
158
- }, [uploadDirRef]);
159
-
160
- function onUploadClick(file: boolean) {
161
- const input = (file ? uploadFileRef : uploadDirRef).current;
162
- if (input === null) {
163
- return;
164
- }
165
-
166
- input.click();
167
- }
168
-
169
- async function onUpload(event: any) {
170
- const ci = nonSerializableStore.ci;
171
- const files: FileList = event.target.files;
172
- if (ci === null || files === null) {
173
- return;
174
- }
175
-
176
- try {
177
- for (const file of files) {
178
- onUploadingFile(file.name);
179
- const parts = (file.webkitRelativePath ?? "").split("/");
180
- parts.shift();
181
- const relPath = parts.join("/");
182
- await ci.fsWriteFile(relPath.length == 0 ? file.name : relPath, file.stream());
183
- }
184
- } finally {
185
- onUploadingFile(null);
186
- }
187
-
188
- await onRefresh();
189
- }
190
-
191
- async function onRestart() {
192
- const ci = nonSerializableStore.ci;
193
- if (ci === null || !window.confirm(t("fs_restart"))) {
194
- return;
195
- }
196
-
197
- onMakingBundle(true);
198
- try {
199
- const bundle = await ci.persist(false);
200
- if (bundle) {
201
- loadBundle(bundle, true, store);
202
- }
203
- } finally {
204
- onMakingBundle(false);
205
- }
206
- }
207
-
208
- async function onDownload() {
209
- const ci = nonSerializableStore.ci;
210
- if (ci === null) {
211
- return;
212
- }
213
-
214
- onMakingBundle(true);
215
- try {
216
- const bundle = await ci.persist(false);
217
- if (bundle) {
218
- downloadArrayToFs("bundle.jsdos", bundle);
219
- }
220
- } finally {
221
- onMakingBundle(false);
222
- }
223
- }
224
-
225
- return <div class="h-6 flex flex-row">
226
- <input class="hidden" type="file" multiple ref={uploadDirRef} onChange={onUpload} />
227
- <input class="hidden" type="file" multiple ref={uploadFileRef} onChange={onUpload} />
228
- <button class="refresh btn-xs rounded-none" onClick={onRefresh}>
229
- <svg xmlns="http://www.w3.org/2000/svg" fill="none"
230
- viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
231
- <path stroke-linecap="round" stroke-linejoin="round"
232
- d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181
233
- 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181
234
- 3.182m0-4.991v4.99" />
235
- </svg>
236
- </button>
237
- <button class="add-file btn-xs rounded-none" onClick={() => onUploadClick(true)}>
238
- <svg xmlns="http://www.w3.org/2000/svg" fill="none"
239
- viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
240
- <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375
241
- 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0
242
- 00-3.375-3.375H8.25m3.75 9v6m3-3H9m1.5-12H5.625c-.621 0-1.125.504-1.125
243
- 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9
244
- 9 0 00-9-9z" />
245
- </svg>
246
- </button>
247
- <button class="add-dir btn-xs rounded-none" onClick={() => onUploadClick(false)}>
248
- <svg xmlns="http://www.w3.org/2000/svg" fill="none"
249
- viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
250
- <path stroke-linecap="round" stroke-linejoin="round"
251
- d="M12 10.5v6m3-3H9m4.06-7.19l-2.12-2.12a1.5 1.5 0 00-1.061-.44H4.5A2.25
252
- 2.25 0 002.25 6v12a2.25 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75
253
- 18V9a2.25 2.25 0 00-2.25-2.25h-5.379a1.5 1.5 0 01-1.06-.44z" />
254
- </svg>
255
- </button>
256
- <div class="flex-grow"></div>
257
- <button class="download btn-xs rounded-none" onClick={onDownload}>
258
- <svg xmlns="http://www.w3.org/2000/svg" fill="none"
259
- viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
260
- <path stroke-linecap="round" stroke-linejoin="round" d="M9 13.5l3 3m0 0l3-3m-3
261
- 3v-6m1.06-4.19l-2.12-2.12a1.5 1.5 0 00-1.061-.44H4.5A2.25 2.25 0 002.25 6v12a2.25 2.25
262
- 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V9a2.25 2.25 0 00-2.25-2.25h-5.379a1.5 1.5
263
- 0 01-1.06-.44z" />
264
- </svg>
265
- </button>
266
- <button class="restart btn-xs rounded-none" onClick={onRestart}>
267
- <svg xmlns="http://www.w3.org/2000/svg" fill="none"
268
- viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
269
- <path stroke-linecap="round" stroke-linejoin="round" d="M21 7.5V18M15 7.5V18M3
270
- 16.811V8.69c0-.864.933-1.406 1.683-.977l7.108 4.061a1.125 1.125 0 010 1.954l-7.108
271
- 4.061A1.125 1.125 0 013 16.811z" />
272
- </svg>
273
- </button>
274
- </div>;
275
- }
276
-
277
- function nodeSorter(a: FsNode, b: FsNode) {
278
- if (a.nodes !== null && b.nodes !== null) {
279
- return b.name.localeCompare(a.name);
280
- }
281
-
282
- return a.nodes === null ? 1 : -1;
283
- }
284
-
285
- function FolderOpened() {
286
- return <svg xmlns="http://www.w3.org/2000/svg" fill="none"
287
- viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 mr-2">
288
- <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" />
289
- </svg>;
290
- }
291
-
292
- function FolderClosed() {
293
- return <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
294
- stroke-width="1.5" stroke="currentColor" class="w-4 h-4 mr-2">
295
- <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
296
- </svg>;
297
- }
298
-
299
- function calcFsSize(fsTree: NodeExt[] | null) {
300
- if (!fsTree) {
301
- return 0;
302
- }
303
-
304
- let size = 0;
305
- for (const next of fsTree) {
306
- if (next.fsNode.size !== null) {
307
- size += next.fsNode.size!;
308
- } else {
309
- size += calcFsSize(next.children as NodeExt[]);
310
- }
311
- }
312
-
313
- return size;
314
- }