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.
- package/.github/workflows/build.yml +51 -0
- package/.gitmodules +4 -0
- package/README.deployment.md +11 -0
- package/README.md +29 -89
- package/index.html +106 -0
- package/package.json +46 -71
- package/postcss.config.cjs +8 -0
- package/src/app.css +14 -0
- package/src/base.css +10 -0
- package/src/components/checkbox.tsx +41 -0
- package/src/components/close-button.tsx +17 -0
- package/src/components/components.css +28 -0
- package/src/components/dos-option-checkbox.tsx +94 -0
- package/src/components/dos-option-select.tsx +108 -0
- package/src/components/dos-option-slider.tsx +35 -0
- package/src/components/error.tsx +16 -0
- package/src/components/loading.tsx +26 -0
- package/src/components/lock.tsx +10 -0
- package/src/components/select.tsx +33 -0
- package/src/components/slider.tsx +69 -0
- package/src/download-file.ts +18 -0
- package/src/frame/account-frame.tsx +156 -0
- package/src/frame/editor/defaults.ts +419 -0
- package/src/frame/editor/editor-conf-frame.tsx +226 -0
- package/src/frame/editor/editor-frame.css +55 -0
- package/src/frame/editor/editor-fs-frame.tsx +314 -0
- package/src/frame/fat-drives-frame.tsx +135 -0
- package/src/frame/frame.css +119 -0
- package/src/frame/frame.tsx +34 -0
- package/src/frame/image-rendering-frame.tsx +8 -0
- package/src/frame/network-frame.tsx +157 -0
- package/src/frame/quick-save-frame.tsx +96 -0
- package/src/frame/settings-frame.tsx +14 -0
- package/src/frame/stats-frame.tsx +87 -0
- package/src/host/bundle-storage.ts +91 -0
- package/src/host/fullscreen.ts +35 -0
- package/src/host/lcache.ts +203 -0
- package/src/host/lstorage.ts +156 -0
- package/src/i18n.ts +179 -0
- package/src/index.css +7 -0
- package/src/load.ts +132 -0
- package/src/login/login.css +9 -0
- package/src/login/login.tsx +77 -0
- package/src/main.tsx +197 -0
- package/src/public/types.ts +41 -0
- package/src/sidebar/account-button.tsx +33 -0
- package/src/sidebar/cloud-save-button.tsx +89 -0
- package/src/sidebar/diskette-icon.tsx +14 -0
- package/src/sidebar/fullscreen-button.tsx +44 -0
- package/src/sidebar/network-button.tsx +39 -0
- package/src/sidebar/sidebar-button.tsx +244 -0
- package/src/sidebar/sidebar.css +47 -0
- package/src/sidebar/sidebar.tsx +30 -0
- package/src/sockdrive/LICENSE +674 -0
- package/src/sockdrive/README.md +45 -0
- package/src/sockdrive/include/sockdrive.h +23 -0
- package/src/sockdrive/js/.eslintrc.json +43 -0
- package/src/sockdrive/js/package.json +29 -0
- package/src/sockdrive/js/src/fatfs/chains.js +318 -0
- package/src/sockdrive/js/src/fatfs/dir.js +385 -0
- package/src/sockdrive/js/src/fatfs/helpers.js +210 -0
- package/src/sockdrive/js/src/fatfs/index.js +606 -0
- package/src/sockdrive/js/src/fatfs/structs.js +255 -0
- package/src/sockdrive/js/src/fatfs/vol.js +166 -0
- package/src/sockdrive/js/src/sockdrive/cache.ts +133 -0
- package/src/sockdrive/js/src/sockdrive/drive.ts +457 -0
- package/src/sockdrive/js/src/sockdrive/lru.ts +221 -0
- package/src/sockdrive/js/src/sockdrive/types.ts +14 -0
- package/src/sockdrive/js/src/sockdrive-fat.d.ts +64 -0
- package/src/sockdrive/js/src/sockdrive-fat.ts +226 -0
- package/src/sockdrive/js/src/sockdrive-native.ts +100 -0
- package/src/sockdrive/js/src/test/test.ts +288 -0
- package/src/sockdrive/js/tsconfig.json +11 -0
- package/src/sockdrive/js/webpack.config.js +68 -0
- package/src/sockdrive/js/yarn.lock +1808 -0
- package/src/store/auth.ts +115 -0
- package/src/store/dos.ts +305 -0
- package/src/store/editor.ts +61 -0
- package/src/store/init.ts +48 -0
- package/src/store/storage.ts +32 -0
- package/src/store/ui.ts +186 -0
- package/src/store.tsx +108 -0
- package/src/ui.tsx +104 -0
- package/src/v8/changes.ts +56 -0
- package/src/v8/config.ts +18 -0
- package/src/v8/subscriptions.ts +62 -0
- package/src/vite-env.d.ts +2 -0
- package/src/window/dos/controls/keyboard.ts +32 -0
- package/src/window/dos/controls/keys.ts +331 -0
- package/src/window/dos/controls/mouse/mount.ts +99 -0
- package/src/window/dos/controls/mouse/mouse-locked.ts +51 -0
- package/src/window/dos/controls/mouse/mouse-not-locked.ts +84 -0
- package/src/window/dos/controls/mouse/mouse-swipe.ts +57 -0
- package/src/window/dos/controls/mouse/pointer.ts +85 -0
- package/src/window/dos/controls/mouse.ts +21 -0
- package/src/window/dos/dos-dhry2.tsx +191 -0
- package/src/window/dos/dos-runtime.tsx +214 -0
- package/src/window/dos/dos-window.tsx +84 -0
- package/src/window/dos/render/canvas.ts +65 -0
- package/src/window/dos/render/resize.ts +31 -0
- package/src/window/dos/render/webgl.ts +173 -0
- package/src/window/dos/sound/audio-node.ts +148 -0
- package/src/window/error-window.tsx +9 -0
- package/src/window/loading-window.tsx +54 -0
- package/src/window/prerun-window.tsx +123 -0
- package/src/window/select-window.tsx +118 -0
- package/src/window/window.css +78 -0
- package/src/window/window.tsx +49 -0
- package/src/ws/ws-sockdrive.ts +101 -0
- package/src/ws/ws-transport-layer.ts +455 -0
- package/tailwind.config.cjs +48 -0
- package/tsconfig.json +32 -0
- package/tsconfig.node.json +9 -0
- package/vite.config.ts +22 -0
- package/LICENSE +0 -339
- package/dist/emulators-ui-loader.png +0 -0
- package/dist/js-dos.css +0 -1690
- package/dist/js-dos.js +0 -17
- package/dist/js-dos.js.map +0 -1
- package/dist/types/src/backend/v7/latency.d.ts +0 -8
- package/dist/types/src/backend/v7/personal.d.ts +0 -2
- package/dist/types/src/backend/v7/v7-config.d.ts +0 -16
- package/dist/types/src/components/action-bar.d.ts +0 -2
- package/dist/types/src/components/action-hide.d.ts +0 -4
- package/dist/types/src/components/action-save-or-exit.d.ts +0 -4
- package/dist/types/src/components/client.d.ts +0 -6
- package/dist/types/src/components/controls.d.ts +0 -8
- package/dist/types/src/components/cpu-control.d.ts +0 -2
- package/dist/types/src/components/horizontal-slider.d.ts +0 -13
- package/dist/types/src/components/region.d.ts +0 -6
- package/dist/types/src/components/scale-control.d.ts +0 -3
- package/dist/types/src/components/sensitivity-control.d.ts +0 -3
- package/dist/types/src/components/sidebar/latency-info.d.ts +0 -7
- package/dist/types/src/components/sidebar/main.d.ts +0 -2
- package/dist/types/src/components/sidebar/networking.d.ts +0 -2
- package/dist/types/src/components/sidebar/token/token-add-time.d.ts +0 -2
- package/dist/types/src/components/sidebar/token/token-select.d.ts +0 -2
- package/dist/types/src/components/sidebar/token/token.d.ts +0 -17
- package/dist/types/src/components/sidebar.d.ts +0 -2
- package/dist/types/src/components/sync-control.d.ts +0 -2
- package/dist/types/src/components/tip.d.ts +0 -2
- package/dist/types/src/components/vertical-slider.d.ts +0 -12
- package/dist/types/src/components/volume-control.d.ts +0 -3
- package/dist/types/src/dom.d.ts +0 -8
- package/dist/types/src/hardware-transport-layer.d.ts +0 -18
- package/dist/types/src/icons.d.ts +0 -95
- package/dist/types/src/player-app.d.ts +0 -50
- package/dist/types/src/player.d.ts +0 -29
- package/dist/types/src/request.d.ts +0 -1
- package/dist/types/src/xhr.d.ts +0 -6
- package/dist/types/v7-services/src/personal.d.ts +0 -3
- package/dist/wdosbox.js +0 -55
- package/dist/wdosbox.js.symbols +0 -3174
- package/dist/wdosbox.wasm +0 -0
- package/examples/digger/index.html +0 -71
package/src/main.tsx
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { render } from "preact";
|
|
2
|
+
import "./index.css";
|
|
3
|
+
|
|
4
|
+
import { Provider } from "react-redux";
|
|
5
|
+
import { Ui } from "./ui";
|
|
6
|
+
import { dosSlice } from "./store/dos";
|
|
7
|
+
import { initEmulators } from "./store/dos";
|
|
8
|
+
// eslint-disable-next-line
|
|
9
|
+
import { uiSlice } from "./store/ui";
|
|
10
|
+
import { i18nSlice } from "./i18n";
|
|
11
|
+
import { getCache } from "./host/lcache";
|
|
12
|
+
import { loadBundleFromConfg, loadBundleFromUrl } from "./load";
|
|
13
|
+
|
|
14
|
+
import { DosOptions, DosProps, DosFn } from "./public/types";
|
|
15
|
+
import { browserSetFullScreen } from "./host/fullscreen";
|
|
16
|
+
import { NonSerializableStore, Store, makeNonSerializableStore, makeStore, postJsDosEvent } from "./store";
|
|
17
|
+
|
|
18
|
+
export const Dos: DosFn = (element: HTMLDivElement,
|
|
19
|
+
options: Partial<DosOptions> = {}): DosProps => {
|
|
20
|
+
const nonSerializableStore = makeNonSerializableStore(options);
|
|
21
|
+
const store = makeStore(nonSerializableStore);
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
if (store.getState().auth.account?.email === "dz.caiiiycuk@gmail.com" ||
|
|
25
|
+
store.getState().auth.account?.email === "caiiiycuk@gmail.com") {
|
|
26
|
+
store.dispatch(dosSlice.actions.setSockdriveWrite(false));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
setupRootElement(element, nonSerializableStore, store);
|
|
30
|
+
|
|
31
|
+
let pollStep = "none";
|
|
32
|
+
function pollEvents() {
|
|
33
|
+
(async () => {
|
|
34
|
+
const state = store.getState();
|
|
35
|
+
const step = state.dos.step;
|
|
36
|
+
|
|
37
|
+
if (step === pollStep) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
pollStep = step;
|
|
41
|
+
|
|
42
|
+
switch (state.dos.step) {
|
|
43
|
+
case "emu-ready": {
|
|
44
|
+
const cachedEmail = state.auth.account?.email;
|
|
45
|
+
nonSerializableStore.cache = await getCache(cachedEmail ?? "guest");
|
|
46
|
+
|
|
47
|
+
if (nonSerializableStore.options.url) {
|
|
48
|
+
try {
|
|
49
|
+
await loadBundleFromUrl(nonSerializableStore.options.url, store);
|
|
50
|
+
} catch (e: any) {
|
|
51
|
+
store.dispatch(dosSlice.actions.bndError(e.message));
|
|
52
|
+
}
|
|
53
|
+
} else if (nonSerializableStore.options.dosboxConf) {
|
|
54
|
+
loadBundleFromConfg({
|
|
55
|
+
dosboxConf: nonSerializableStore.options.dosboxConf,
|
|
56
|
+
jsdosConf: {
|
|
57
|
+
version: "8",
|
|
58
|
+
},
|
|
59
|
+
}, store);
|
|
60
|
+
} else {
|
|
61
|
+
store.dispatch(uiSlice.actions.windowSelect());
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
postJsDosEvent(nonSerializableStore, "emu-ready");
|
|
65
|
+
} break;
|
|
66
|
+
};
|
|
67
|
+
})().catch(console.error);
|
|
68
|
+
}
|
|
69
|
+
store.subscribe(pollEvents);
|
|
70
|
+
|
|
71
|
+
initEmulators(store, options.pathPrefix ?? "https://v8.js-dos.com/latest/emulators/");
|
|
72
|
+
|
|
73
|
+
function setTheme(theme: DosOptions["theme"]) {
|
|
74
|
+
store.dispatch(uiSlice.actions.theme(theme));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function setLang(lang: DosOptions["lang"]) {
|
|
78
|
+
store.dispatch(i18nSlice.actions.setLang(lang));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function setBackend(backend: DosOptions["backend"]) {
|
|
82
|
+
store.dispatch(dosSlice.actions.dosBackend(backend));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function setBackendLocked(locked: boolean) {
|
|
86
|
+
store.dispatch(dosSlice.actions.dosBackendLocked(locked));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function setWorkerThread(workerThread: DosOptions["workerThread"]) {
|
|
90
|
+
store.dispatch(dosSlice.actions.dosWorker(workerThread));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function setMouseCapture(capture: DosOptions["mouseCapture"]) {
|
|
94
|
+
store.dispatch(dosSlice.actions.mouseCapture(capture));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function setServer(server: DosOptions["server"]) {
|
|
98
|
+
store.dispatch(dosSlice.actions.setServer(server));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function setRoom(room: DosOptions["room"]) {
|
|
102
|
+
store.dispatch(dosSlice.actions.setRoom(room));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function setFrame(frame: "network") {
|
|
106
|
+
store.dispatch(uiSlice.actions.frameNetwork());
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function setBackground(background: string) {
|
|
110
|
+
store.dispatch(uiSlice.actions.background(background));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function setFullScreen(fullScreen: boolean) {
|
|
114
|
+
browserSetFullScreen(fullScreen, store);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (options.theme) {
|
|
118
|
+
setTheme(options.theme);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (options.lang) {
|
|
122
|
+
setLang(options.lang);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (options.backend) {
|
|
126
|
+
setBackend(options.backend);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (options.backendLocked) {
|
|
130
|
+
setBackendLocked(options.backendLocked);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (options.workerThread !== undefined) {
|
|
134
|
+
setWorkerThread(options.workerThread);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (options.mouseCapture !== undefined) {
|
|
138
|
+
setMouseCapture(options.mouseCapture);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (options.server) {
|
|
142
|
+
setServer(options.server);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (options.room) {
|
|
146
|
+
setRoom(options.room);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (options.background) {
|
|
150
|
+
setBackground(options.background);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (options.fullScreen !== undefined) {
|
|
154
|
+
setFullScreen(options.fullScreen);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
render(
|
|
158
|
+
<Provider store={store}>
|
|
159
|
+
{<Ui /> as any}
|
|
160
|
+
</Provider>,
|
|
161
|
+
element,
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
setTheme,
|
|
166
|
+
setLang,
|
|
167
|
+
setBackend,
|
|
168
|
+
setBackendLocked,
|
|
169
|
+
setWorkerThread,
|
|
170
|
+
setMouseCapture,
|
|
171
|
+
setServer,
|
|
172
|
+
setRoom,
|
|
173
|
+
setFrame,
|
|
174
|
+
setBackground,
|
|
175
|
+
setFullScreen,
|
|
176
|
+
};
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
function setupRootElement(root: HTMLDivElement, nonSerializableStore: NonSerializableStore, store: Store) {
|
|
180
|
+
nonSerializableStore.root = root;
|
|
181
|
+
root.classList.add("jsdos-rso");
|
|
182
|
+
root.addEventListener("contextmenu", (e) => {
|
|
183
|
+
if (e.target !== null &&
|
|
184
|
+
(e.target as HTMLElement).classList?.contains("contextmenu")) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
e.stopPropagation();
|
|
188
|
+
e.preventDefault();
|
|
189
|
+
return false;
|
|
190
|
+
});
|
|
191
|
+
document.addEventListener("fullscreenchange", () => {
|
|
192
|
+
const fullscreen = document.fullscreenElement === root;
|
|
193
|
+
store.dispatch(uiSlice.actions.setFullScreen(fullscreen));
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
(window as any).Dos = Dos;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export type DosEvent = "emu-ready" | "ci-ready" | "bnd-play";
|
|
2
|
+
|
|
3
|
+
export interface DosOptions {
|
|
4
|
+
url: string,
|
|
5
|
+
dosboxConf: string,
|
|
6
|
+
background: string,
|
|
7
|
+
pathPrefix: string,
|
|
8
|
+
theme: "light" | "dark" | "cupcake" | "bumblebee" | "emerald" | "corporate" |
|
|
9
|
+
"synthwave" | "retro" | "cyberpunk" | "valentine" | "halloween" | "garden" |
|
|
10
|
+
"forest" | "aqua" | "lofi" | "pastel" | "fantasy" | "wireframe" | "black" |
|
|
11
|
+
"luxury" | "dracula" | "cmyk" | "autumn" | "business" | "acid" | "lemonade" |
|
|
12
|
+
"night" | "coffee" | "winter",
|
|
13
|
+
lang: "ru" | "en",
|
|
14
|
+
backend: "dosbox" | "dosboxX",
|
|
15
|
+
backendLocked: boolean,
|
|
16
|
+
backendHardware: ((backend: "dosbox" | "dosboxX") => Promise<string | null>),
|
|
17
|
+
workerThread: boolean,
|
|
18
|
+
mouseCapture: boolean,
|
|
19
|
+
onEvent: (event: DosEvent, ci?: any /* CommandInterface */) => void,
|
|
20
|
+
server: "netherlands" | "newyork" | "singapore",
|
|
21
|
+
room: string,
|
|
22
|
+
fullScreen: boolean,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface DosProps {
|
|
26
|
+
setTheme(theme: DosOptions["theme"]): void;
|
|
27
|
+
setLang(lang: DosOptions["lang"]): void;
|
|
28
|
+
setBackend(backend: DosOptions["backend"]): void;
|
|
29
|
+
setBackendLocked(locked: boolean): void;
|
|
30
|
+
setWorkerThread(capture: DosOptions["workerThread"]): void;
|
|
31
|
+
setMouseCapture(capture: DosOptions["mouseCapture"]): void;
|
|
32
|
+
setServer(server: DosOptions["server"]): void;
|
|
33
|
+
setRoom(room: DosOptions["room"]): void;
|
|
34
|
+
setFrame(frame: "network"): void;
|
|
35
|
+
setBackground(background: string | null): void;
|
|
36
|
+
setFullScreen(fullScreen: boolean): void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type DosFn = (element: HTMLDivElement, options: Partial<DosOptions>) => DosProps;
|
|
40
|
+
|
|
41
|
+
// declare const Dos: DosFn;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { useDispatch, useSelector } from "react-redux";
|
|
2
|
+
import { dispatchLoginAction } from "../store/ui";
|
|
3
|
+
import { State } from "../store";
|
|
4
|
+
|
|
5
|
+
export function AccountButton(props: {
|
|
6
|
+
class?: string,
|
|
7
|
+
}) {
|
|
8
|
+
const account = useSelector((state: State) => state.auth.account);
|
|
9
|
+
const loggedIn = account !== null;
|
|
10
|
+
const hightlight = useSelector((state: State) => state.ui.frame) === "account";
|
|
11
|
+
const dispatch = useDispatch();
|
|
12
|
+
|
|
13
|
+
function onClick() {
|
|
14
|
+
dispatchLoginAction(account, dispatch);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return <div class={"sidebar-button " + (hightlight ? "sidebar-highlight " : "") +
|
|
18
|
+
(account?.email === "dz.caiiiycuk@gmail.com" ? "bg-accent " : "") +
|
|
19
|
+
props.class} onClick={onClick}>
|
|
20
|
+
|
|
21
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none"
|
|
22
|
+
viewBox="0 0 24 24" stroke-width="1.5"
|
|
23
|
+
stroke="currentColor" class="w-full h-full">
|
|
24
|
+
<path stroke-linecap="round"
|
|
25
|
+
stroke-linejoin="round"
|
|
26
|
+
d="M17.982 18.725A7.488 7.488 0 0012 15.75a7.488 7.488 0 00-5.982 2.975m11.963 0a9 9 0 10-11.963
|
|
27
|
+
0m11.963 0A8.966 8.966 0 0112 21a8.966
|
|
28
|
+
8.966 0 01-5.982-2.275M15 9.75a3 3 0 11-6 0 3 3 0 016 0z" />
|
|
29
|
+
</svg>
|
|
30
|
+
|
|
31
|
+
{loggedIn === false && <div class="sidebar-badge" />}
|
|
32
|
+
</div>;
|
|
33
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { useState } from "preact/hooks";
|
|
2
|
+
import { useDispatch, useSelector } from "react-redux";
|
|
3
|
+
import { LockBadge } from "../components/lock";
|
|
4
|
+
import { State, useNonSerializableStore } from "../store";
|
|
5
|
+
import { uiSlice } from "../store/ui";
|
|
6
|
+
import { DisketteIcon } from "./diskette-icon";
|
|
7
|
+
import { useT } from "../i18n";
|
|
8
|
+
import { putChanges } from "../v8/changes";
|
|
9
|
+
|
|
10
|
+
export function CloudSaveButton(props: {
|
|
11
|
+
class?: string,
|
|
12
|
+
}) {
|
|
13
|
+
const [busy, setBusy] = useState<boolean>(false);
|
|
14
|
+
const account = useSelector((state: State) => state.auth.account);
|
|
15
|
+
const disabled = account === null;
|
|
16
|
+
const dispatch = useDispatch();
|
|
17
|
+
const t = useT();
|
|
18
|
+
const cloudSaves = useSelector((state: State) => state.ui.cloudSaves);
|
|
19
|
+
const nonSerializableStore = useNonSerializableStore();
|
|
20
|
+
|
|
21
|
+
if (!cloudSaves ||
|
|
22
|
+
nonSerializableStore.loadedBundle === null ||
|
|
23
|
+
nonSerializableStore.loadedBundle.bundleChangesUrl === null) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function onClick() {
|
|
28
|
+
if (disabled) {
|
|
29
|
+
dispatch(uiSlice.actions.modalLogin());
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (busy) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const ci = nonSerializableStore.ci;
|
|
38
|
+
const changesUrl = nonSerializableStore.loadedBundle?.bundleChangesUrl;
|
|
39
|
+
|
|
40
|
+
if (ci === null || !changesUrl) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
setBusy(true);
|
|
45
|
+
try {
|
|
46
|
+
const changes = await ci.persist(true);
|
|
47
|
+
if (changes !== null) {
|
|
48
|
+
if (account === null || !account.premium) {
|
|
49
|
+
await nonSerializableStore.cache.put(changesUrl, changes);
|
|
50
|
+
} else {
|
|
51
|
+
await Promise.all([
|
|
52
|
+
putChanges(changesUrl, changes),
|
|
53
|
+
nonSerializableStore.cache.put(changesUrl, changes),
|
|
54
|
+
]);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
dispatch(uiSlice.actions.showToast({
|
|
59
|
+
message: t("success"),
|
|
60
|
+
intent: "success",
|
|
61
|
+
}));
|
|
62
|
+
} catch (e: any) {
|
|
63
|
+
dispatch(uiSlice.actions.showToast({
|
|
64
|
+
message: t("unable_to_save"),
|
|
65
|
+
intent: "error",
|
|
66
|
+
}));
|
|
67
|
+
console.error(e);
|
|
68
|
+
} finally {
|
|
69
|
+
setBusy(false);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return <div class={"save-button sidebar-button overflow-hidden " +
|
|
74
|
+
(busy ? " sidebar-highlight " : "") + props.class +
|
|
75
|
+
(disabled ? " opacity-50" : "")} onClick={onClick}>
|
|
76
|
+
<div class="w-full h-full flex justify-center">
|
|
77
|
+
<DisketteIcon />
|
|
78
|
+
{busy && <div class="sidebar-badge" />}
|
|
79
|
+
{disabled && <LockBadge />}
|
|
80
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
|
81
|
+
stroke-width="1.5" stroke="currentColor"
|
|
82
|
+
class="absolute bottom-1 w-3 h-3 text-primary-content">
|
|
83
|
+
<path stroke-linecap="round" stroke-linejoin="round"
|
|
84
|
+
d="M12 16.5V9.75m0 0l3 3m-3-3l-3 3M6.75 19.5a4.5 4.5 0 01-1.41-8.775
|
|
85
|
+
5.25 5.25 0 0110.233-2.33 3 3 0 013.758 3.848A3.752 3.752 0 0118 19.5H6.75z" />
|
|
86
|
+
</svg>
|
|
87
|
+
</div>
|
|
88
|
+
</div >;
|
|
89
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function DisketteIcon(props: {
|
|
2
|
+
class?: string,
|
|
3
|
+
}) {
|
|
4
|
+
return <svg xmlns="http://www.w3.org/2000/svg"
|
|
5
|
+
style="padding: 2px"
|
|
6
|
+
viewBox="0 0 16 16" enable-background="new 0 0 16 16"
|
|
7
|
+
fill="currentColor" class={ "w-full h-full rounded-lg " + props.class }>
|
|
8
|
+
<path fill-rule="evenodd" clip-rule="evenodd"
|
|
9
|
+
d="M15.71,2.29l-2-2C13.53,0.11,13.28,0,13,0h-1v6H4V0H1C0.45,0,0,0.45,0,1v14
|
|
10
|
+
c0,0.55,0.45,1,1,1h14c0.55,0,1-0.45,1-1V3C16,2.72,15.89,2.47,15.71,2.29z
|
|
11
|
+
M14,15H2V9c0-0.55,0.45-1,1-1h10c0.55,0,1,0.45,1,1V15
|
|
12
|
+
z M11,1H9v4h2V1z"/>
|
|
13
|
+
</svg>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { useSelector, useStore } from "react-redux";
|
|
2
|
+
import { State, Store } from "../store";
|
|
3
|
+
import { browserSetFullScreen } from "../host/fullscreen";
|
|
4
|
+
|
|
5
|
+
export function FullscreenButton(props: {
|
|
6
|
+
class?: string,
|
|
7
|
+
}) {
|
|
8
|
+
const fullScreen = useSelector((state: State) => state.ui.fullScreen);
|
|
9
|
+
const store = useStore() as Store;
|
|
10
|
+
|
|
11
|
+
function onClick() {
|
|
12
|
+
browserSetFullScreen(!fullScreen, store);
|
|
13
|
+
}
|
|
14
|
+
/* eslint-disable max-len */
|
|
15
|
+
return <div class={"fullscreen-button sidebar-button " + props.class} onClick={onClick}>
|
|
16
|
+
<div class={"w-full h-full scale-75 hover:scale-90"}>
|
|
17
|
+
{!fullScreen &&
|
|
18
|
+
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
|
|
19
|
+
viewBox="0 0 16 16" fill="currentColor" stroke="none" enable-background="new 0 0 16 16" >
|
|
20
|
+
<g>
|
|
21
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.99,8.99c-0.28,0-0.53,0.11-0.71,0.29l-3.29,3.29v-1.59c0-0.55-0.45-1-1-1
|
|
22
|
+
s-1,0.45-1,1v4c0,0.55,0.45,1,1,1h4c0.55,0,1-0.45,1-1s-0.45-1-1-1H3.41L6.7,10.7c0.18-0.18,0.29-0.43,0.29-0.71
|
|
23
|
+
C6.99,9.44,6.54,8.99,5.99,8.99z M14.99-0.01h-4c-0.55,0-1,0.45-1,1s0.45,1,1,1h1.59L9.28,5.29C9.1,5.47,8.99,5.72,8.99,5.99
|
|
24
|
+
c0,0.55,0.45,1,1,1c0.28,0,0.53-0.11,0.71-0.29l3.29-3.29v1.59c0,0.55,0.45,1,1,1s1-0.45,1-1v-4C15.99,0.44,15.54-0.01,14.99-0.01
|
|
25
|
+
z"/>
|
|
26
|
+
</g>
|
|
27
|
+
</svg>
|
|
28
|
+
}
|
|
29
|
+
{fullScreen &&
|
|
30
|
+
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
|
|
31
|
+
viewBox="0 0 16 16" fill="currentColor" stroke="none" enable-background="new 0 0 16 16">
|
|
32
|
+
<g>
|
|
33
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.99,0.99c0-0.55-0.45-1-1-1c-0.28,0-0.53,0.11-0.71,0.29l-3.29,3.29V1.99
|
|
34
|
+
c0-0.55-0.45-1-1-1s-1,0.45-1,1v4c0,0.55,0.45,1,1,1h4c0.55,0,1-0.45,1-1s-0.45-1-1-1h-1.59L15.7,1.7
|
|
35
|
+
C15.88,1.52,15.99,1.27,15.99,0.99z M5.99,8.99h-4c-0.55,0-1,0.45-1,1s0.45,1,1,1h1.59l-3.29,3.29c-0.18,0.18-0.29,0.43-0.29,0.71
|
|
36
|
+
c0,0.55,0.45,1,1,1c0.28,0,0.53-0.11,0.71-0.29l3.29-3.29v1.59c0,0.55,0.45,1,1,1s1-0.45,1-1v-4C6.99,9.44,6.54,8.99,5.99,8.99z"
|
|
37
|
+
/>
|
|
38
|
+
</g>
|
|
39
|
+
</svg>
|
|
40
|
+
}
|
|
41
|
+
</div>
|
|
42
|
+
</div >;
|
|
43
|
+
/* eslint-enable max-len */
|
|
44
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useDispatch, useSelector } from "react-redux";
|
|
2
|
+
import { State } from "../store";
|
|
3
|
+
import { uiSlice } from "../store/ui";
|
|
4
|
+
|
|
5
|
+
export function NetworkButton(props: {
|
|
6
|
+
class?: string,
|
|
7
|
+
}) {
|
|
8
|
+
const hightlight = useSelector((state: State) => state.ui.frame) === "network";
|
|
9
|
+
const inactive = useSelector((state: State) => state.dos.network.ipx !== "connected");
|
|
10
|
+
const dispatch = useDispatch();
|
|
11
|
+
|
|
12
|
+
function onClick() {
|
|
13
|
+
if (hightlight) {
|
|
14
|
+
dispatch(uiSlice.actions.frameNone());
|
|
15
|
+
} else {
|
|
16
|
+
dispatch(uiSlice.actions.frameNetwork());
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return <div class={"network-button sidebar-button " +
|
|
21
|
+
(hightlight ? " sidebar-highlight " : "") +
|
|
22
|
+
(inactive ? " inactive " : "") +
|
|
23
|
+
props.class} onClick={onClick}>
|
|
24
|
+
<div class="w-full h-full">
|
|
25
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
|
26
|
+
stroke-width="1.5" stroke="currentColor" class="w-full h-full">
|
|
27
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M8.288 15.038a5.25 5.25 0
|
|
28
|
+
017.424 0M5.106 11.856c3.807-3.808 9.98-3.808 13.788 0M1.924 8.674c5.565-5.565
|
|
29
|
+
14.587-5.565 20.152 0M12.53 18.22l-.53.53-.53-.53a.75.75 0 011.06 0z" />
|
|
30
|
+
</svg>
|
|
31
|
+
{ inactive && <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
|
32
|
+
stroke="currentColor" class="w-full text-error h-full absolute top-0 left-0">
|
|
33
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M4 4L18 18" />
|
|
34
|
+
</svg> }
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
</div >;
|
|
39
|
+
}
|