js-dos 7.4.9 → 8.1.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 (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 +179 -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 +133 -0
  66. package/src/sockdrive/js/src/sockdrive/drive.ts +457 -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 +288 -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 +186 -0
  82. package/src/store.tsx +108 -0
  83. package/src/ui.tsx +104 -0
  84. package/src/v8/changes.ts +56 -0
  85. package/src/v8/config.ts +18 -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 +84 -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 +101 -0
  110. package/src/ws/ws-transport-layer.ts +455 -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,314 @@
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
+ }
@@ -0,0 +1,135 @@
1
+ import { useT } from "../i18n";
2
+ import { SockdriveBackend } from "../components/dos-option-select";
3
+ import { useDispatch, useSelector } from "react-redux";
4
+ import { sockdriveBackend } from "../store/init";
5
+ import { State } from "../store";
6
+ import { useEffect, useState } from "preact/hooks";
7
+ import { dispatchLoginAction, uiSlice } from "../store/ui";
8
+ import { LockBadge } from "../components/lock";
9
+
10
+ interface Drive {
11
+ name: string,
12
+ owner: string
13
+ fork_of: string[],
14
+ template: string,
15
+ }
16
+
17
+ export function FatDrivesFrame() {
18
+ const t = useT();
19
+ const account = useSelector((state: State) => state.auth.account);
20
+ const { sockdriveEndpoint } = useSelector((state: State) =>
21
+ sockdriveBackend[state.init.sockdriveBackendName] ??
22
+ sockdriveBackend["js-dos"]);
23
+ const [myDrives, setMyDrives] = useState<Drive[]>([]);
24
+ const dispatch = useDispatch();
25
+ const preimium = account?.premium ?? false;
26
+ const me = account?.email ?? "";
27
+ const [filter, setFilter] = useState<string>("");
28
+
29
+ function reloadDrives() {
30
+ if (!account || !account.token) {
31
+ setMyDrives([]);
32
+ } else {
33
+ fetch(sockdriveEndpoint + "/list/drives/" + account.token.access_token)
34
+ .then((r) => r.json())
35
+ .then((drives: Drive[]) => {
36
+ setMyDrives(drives.sort((a, b) => a.name.localeCompare(b.name)));
37
+ })
38
+ .catch(console.error);
39
+ }
40
+ }
41
+
42
+ useEffect(() => {
43
+ reloadDrives();
44
+ }, [account?.token, sockdriveEndpoint]);
45
+
46
+ return <div class="frame-root items-start gap-3 px-4">
47
+ <SockdriveBackend multiline={true} class="w-full " />
48
+ <input class="input input-bordered input-sm w-full" placeholder={t("filter")}
49
+ value={filter} onChange={(e) => setFilter(e.currentTarget.value ?? "")}></input>
50
+ <div class="w-full">
51
+ <table class="table w-full">
52
+ <thead>
53
+ <tr>
54
+ <th>{t("net_drives")}</th>
55
+ <th></th>
56
+ </tr>
57
+ </thead>
58
+ <tbody>
59
+ {myDrives
60
+ .filter(({ owner, name }) => filter.length === 0 ||
61
+ owner.indexOf(filter) >= 0 || name.indexOf(filter) >= 0)
62
+ // eslint-disable-next-line camelcase
63
+ .map(({ name, owner, fork_of }, index) => {
64
+ return <>
65
+ <tr>
66
+ <td onClick={() => { }}>
67
+ <p class="font-bold">{name}</p>
68
+ {
69
+ // eslint-disable-next-line camelcase
70
+ fork_of.map((path) => {
71
+ return <p class="text-xs">&nbsp;&nbsp;&nbsp;&nbsp;{path}</p>;
72
+ })}
73
+ {owner !== account?.email &&
74
+ <p class="text-xs">{owner}</p>}
75
+ </td>
76
+ {preimium && <td>
77
+ {account?.token && <div class="btn btn-ghost btn-xs"
78
+ onClick={() => {
79
+ const newName = prompt("Enter drive name");
80
+ if (newName && newName.length > 0) {
81
+ // eslint-disable-next-line max-len
82
+ fetch(`${sockdriveEndpoint}/fork/drive/${owner}/${name}/${newName}/${account.token.access_token}`, {
83
+ method: "POST",
84
+ })
85
+ .then((r) => r.json())
86
+ .then((response) => {
87
+ if (response.error) {
88
+ throw new Error(response.error);
89
+ } else {
90
+ setMyDrives([]);
91
+ reloadDrives();
92
+ }
93
+ })
94
+ .catch((e) => {
95
+ console.error(e.message);
96
+ dispatch(uiSlice.actions
97
+ .showToast({ message: e.message, intent: "error" }));
98
+ });
99
+ }
100
+ }}>Fork</div>}
101
+ {account?.token && owner === me && <div class="btn btn-xs btn-ghost"
102
+ onClick={() => {
103
+ // eslint-disable-next-line max-len
104
+ fetch(`${sockdriveEndpoint}/fork/delete/${owner}/${name}/${account.token.access_token}`, { method: "POST" })
105
+ .then((r) => r.json())
106
+ .then((response) => {
107
+ if (response.error) {
108
+ throw new Error(response.error);
109
+ } else {
110
+ const newDrives = [...myDrives];
111
+ newDrives.splice(index, 1);
112
+ setMyDrives(newDrives);
113
+ }
114
+ })
115
+ .catch((e) => {
116
+ console.error(e.message);
117
+ dispatch(uiSlice.actions
118
+ .showToast({ message: e.message, intent: "error" }));
119
+ });
120
+ }}>{t("delete")}</div>}
121
+ </td>}
122
+ {!preimium && <td>
123
+ <div class="cursor-pointer"
124
+ onClick={() => dispatchLoginAction(account, dispatch)}>
125
+ <LockBadge class="text-error" />
126
+ </div>
127
+ </td>}
128
+ </tr>
129
+ </>;
130
+ })}
131
+ </tbody>
132
+ </table>
133
+ </div>
134
+ </div>;
135
+ }
@@ -0,0 +1,119 @@
1
+ @import "./editor/editor-frame.css";
2
+
3
+ .jsdos-rso {
4
+ .frame-root {
5
+ @apply flex flex-col;
6
+ }
7
+
8
+ .frame {
9
+ @apply absolute left-0 top-0 pl-12 overflow-auto h-full w-96 py-4;
10
+ background-color: hsl(var(--b3));
11
+ }
12
+
13
+ .frame-md {
14
+ width: 100% !important;
15
+ }
16
+
17
+ .frame-xs {
18
+ width: calc(var(--sidebar-width) * 2) !important;
19
+ }
20
+
21
+ .premium-plan-root {
22
+ @apply bg-white rounded-xl px-4 pt-4 pb-2 w-64;
23
+ }
24
+
25
+ .premium-plan-root.have-premium {
26
+ @apply bg-green-200;
27
+ }
28
+
29
+ .premium-plan-head {
30
+ @apply text-blue-400;
31
+ }
32
+
33
+ .premium-plan-cost {
34
+ @apply text-blue-600 text-5xl;
35
+ }
36
+
37
+ .premium-plan-cost-expl {
38
+ @apply text-gray-600 ml-4 flex flex-col;
39
+ }
40
+
41
+ .premium-plan-highlight {
42
+ @apply flex flex-row text-xs items-center py-2 border-b border-b-gray-200;
43
+ @apply text-gray-600;
44
+ }
45
+
46
+ .premium-plan-root.have-premium .premium-plan-highlight {
47
+ @apply border-b-green-300;
48
+ }
49
+
50
+ .settings-frame {
51
+ @apply px-6 -mt-2;
52
+
53
+ .label {
54
+ @apply p-0;
55
+ }
56
+
57
+ .label-text {
58
+ font-size: inherit;
59
+ }
60
+
61
+ .option {
62
+ @apply w-full justify-between;
63
+ }
64
+
65
+ .slider {
66
+ .touch {
67
+ @apply w-40 flex-grow-0;
68
+ }
69
+ }
70
+ }
71
+
72
+ .image-rendering-frame {
73
+ @apply px-6 -mt-2;
74
+
75
+ .option {
76
+ @apply w-full justify-between;
77
+ }
78
+ }
79
+
80
+ .account-frame {
81
+ .account-img {
82
+ @apply w-24 h-24 my-8 rounded-full;
83
+ }
84
+
85
+ .account-email {
86
+ @apply text-xs mt-4;
87
+ }
88
+ }
89
+
90
+ .network-frame {
91
+ @apply w-full;
92
+
93
+ .option {
94
+ @apply w-full;
95
+ }
96
+
97
+ .error {
98
+ .label-text {
99
+ @apply text-error;
100
+ }
101
+
102
+ input {
103
+ border-color: hsl(var(--er) / var(--tw-border-opacity));
104
+ --tw-border-opacity: 0.1;
105
+ --tw-bg-opacity: 1;
106
+ background-color: hsl(var(--er) / var(--tw-bg-opacity));
107
+ --tw-text-opacity: 1;
108
+ color: hsl(var(--erc, var(--nc)) / var(--tw-text-opacity));
109
+ }
110
+ }
111
+ }
112
+
113
+ .quick-save-frame {
114
+ .text-badge {
115
+ @apply absolute left-0 top-0 w-3 h-3 font-bold rounded-full flex items-center justify-center;
116
+ font-size: 0.5rem;
117
+ }
118
+ }
119
+ }
@@ -0,0 +1,34 @@
1
+ import { useSelector } from "react-redux";
2
+ import { State } from "../store";
3
+ import { AccountFrame } from "./account-frame";
4
+ import { EditorConf } from "./editor/editor-conf-frame";
5
+ import { EditorFsFrame } from "./editor/editor-fs-frame";
6
+ import { FatDrivesFrame } from "./fat-drives-frame";
7
+ import { NetworkFrame } from "./network-frame";
8
+ import { QuickSaveFrame } from "./quick-save-frame";
9
+ import { SettingsFrame } from "./settings-frame";
10
+ import { StatsFrame } from "./stats-frame";
11
+ import { ImageRenderingFrame } from "./image-rendering-frame";
12
+
13
+ export function Frame(props: {}) {
14
+ const frame = useSelector((state: State) => state.ui.frame);
15
+ const frameXs = useSelector((state: State) => state.ui.frameXs);
16
+ const wideScreen = useSelector((state: State) => state.ui.wideScreen);
17
+ if (frame === "none") {
18
+ return null;
19
+ }
20
+
21
+
22
+ return <div class={"frame " + (frameXs ? " frame-xs " : "") +
23
+ (frameXs || wideScreen ? "" : " frame-md")}>
24
+ { frame === "account" && <AccountFrame /> }
25
+ { frame === "settings" && <SettingsFrame /> }
26
+ { frame === "editor-conf" && <EditorConf /> }
27
+ { frame === "editor-fs" && <EditorFsFrame /> }
28
+ { frame === "network" && <NetworkFrame /> }
29
+ { frame === "stats" && <StatsFrame /> }
30
+ { frame === "fat-drives" && <FatDrivesFrame /> }
31
+ { frame === "quick-save" && <QuickSaveFrame /> }
32
+ { frame === "image-rendering" && <ImageRenderingFrame /> }
33
+ </div>;
34
+ };
@@ -0,0 +1,8 @@
1
+ import { ImageRenderingSelect, RenderAspectSelect } from "../components/dos-option-select";
2
+
3
+ export function ImageRenderingFrame(props: {}) {
4
+ return <div class="image-rendering-frame frame-root items-start pl-4">
5
+ <RenderAspectSelect multiline={true} />
6
+ <ImageRenderingSelect multiline={true} />
7
+ </div>;
8
+ }