js-dos 8.1.1 → 8.1.3

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/README.md CHANGED
@@ -14,9 +14,14 @@ The key features:
14
14
  * Cloud storage
15
15
  * WebAssembly and pure JS versions
16
16
 
17
+ ## Demo
17
18
 
18
-
19
- [![Watch the video](https://github.com/caiiiycuk/js-dos/assets/1727152/a2b481cb-43b1-44aa-8b71-2181d351bb1a)](https://youtu.be/lhFrAe5YrJE)
19
+ * [Win 95](https://v8.js-dos.com) - plain Windows 95 with D3D & 3Dfx
20
+ * [DOS.Zone](https://dos.zone) - community portal with 1900+ dos games
21
+ * [Multiplayer](https://dos.zone/multiplayer) - multiplayer games (Doom, Heroes 2, etc.)
22
+ * [Diablo I](https://dos.zone/diablo-1996/)
23
+ * Dune 2000
24
+ [![Watch the video](https://github.com/caiiiycuk/js-dos/assets/1727152/a2b481cb-43b1-44aa-8b71-2181d351bb1a)](https://youtu.be/lhFrAe5YrJE)
20
25
 
21
26
  ## Documentation
22
27
 
@@ -26,10 +31,6 @@ The key features:
26
31
  * [js-dos 3.xx](https://js-dos.com/index_v3.html)
27
32
  * [Repository](https://github.com/caiiiycuk/js-dos-docs)
28
33
 
29
- Demo:
30
-
31
- [DOS.Zone](https://dos.zone)
32
-
33
34
  ## Development
34
35
 
35
36
  1. You need to install node dependencies and put `emulators@beta` into `public/emulators`.
package/index.html CHANGED
@@ -42,6 +42,10 @@
42
42
  // console.log("Hardware backend for", backend);
43
43
  // return Promise.resolve("ws://127.0.0.1:8080");
44
44
  //},
45
+ // sockdriveBackend: {
46
+ // name: "makevm",
47
+ // host: "backend.make-vm.com:8001",
48
+ // },
45
49
  onEvent: (event) => {
46
50
  console.log("Event", event)
47
51
  if (event === "ci-ready") {
@@ -96,8 +100,8 @@ scaler=none
96
100
 
97
101
  [autoexec]
98
102
  echo off
99
- imgmount 2 sockdrive wss://backend.make-vm.com:8001 dos.zone diablo_109_c
100
- imgmount 3 sockdrive wss://backend.make-vm.com:8001 dos.zone diablo_109_d
103
+ imgmount 2 sockdrive wss://sockdrive.js-dos.com:8001 dos.zone diablo_109_c
104
+ imgmount 3 sockdrive wss://sockdrive.js-dos.com:8001 dos.zone diablo_109_d
101
105
  boot c:
102
106
  `;
103
107
  </script>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-dos",
3
- "version": "8.1.1",
3
+ "version": "8.1.3",
4
4
  "description": "Full-featured DOS player with multiple emulator backends",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -237,7 +237,7 @@ scaler=none
237
237
 
238
238
  [autoexec]
239
239
  echo off
240
- imgmount 2 sockdrive {wss-makevm} dos.zone dos7.1
240
+ imgmount 2 sockdrive {wss-makevm} system dos7.1-v1
241
241
  echo Please visit our website:
242
242
  echo
243
243
  echo _ __
@@ -312,7 +312,7 @@ scaler=none
312
312
 
313
313
  [autoexec]
314
314
  echo off
315
- imgmount 2 sockdrive {wss-makevm} dos.zone win95
315
+ imgmount 2 sockdrive {wss-makevm} system win95-v1
316
316
  echo Please visit our website:
317
317
  echo
318
318
  echo _ __
@@ -387,7 +387,7 @@ scaler=none
387
387
 
388
388
  [autoexec]
389
389
  echo off
390
- imgmount 2 sockdrive {wss-makevm} dos.zone win98
390
+ imgmount 2 sockdrive {wss-makevm} system win98-v1
391
391
  echo Please visit our website:
392
392
  echo
393
393
  echo _ __
@@ -24,7 +24,8 @@ export function EditorConf() {
24
24
  const account = useSelector((state: State) => state.auth.account);
25
25
  const backend = useSelector((state: State) => state.dos.backend);
26
26
  const { sockdriveEndpoint, sockdriveWssEndpoint } = useSelector((state: State) =>
27
- sockdriveBackend[state.init.sockdriveBackendName]);
27
+ sockdriveBackend[state.init.sockdriveBackendName] ??
28
+ sockdriveBackend["js-dos"]);
28
29
  const dispatch = useDispatch();
29
30
  const [myDrives, setMyDrives] = useState<{ name: string, owner: string }[]>([]);
30
31
 
@@ -3,7 +3,7 @@ import { SockdriveBackend } from "../components/dos-option-select";
3
3
  import { useDispatch, useSelector } from "react-redux";
4
4
  import { sockdriveBackend } from "../store/init";
5
5
  import { State } from "../store";
6
- import { useEffect, useState } from "preact/hooks";
6
+ import { useEffect, useRef, useState } from "preact/hooks";
7
7
  import { dispatchLoginAction, uiSlice } from "../store/ui";
8
8
  import { LockBadge } from "../components/lock";
9
9
 
@@ -25,17 +25,27 @@ export function FatDrivesFrame() {
25
25
  const preimium = account?.premium ?? false;
26
26
  const me = account?.email ?? "";
27
27
  const [filter, setFilter] = useState<string>("");
28
+ const [busy, setBusy] = useState<boolean>(true);
29
+ const forkDialogRef = useRef<HTMLDialogElement>(null);
30
+ const deleteDialogRef = useRef<HTMLDialogElement>(null);
31
+ const [forkName, setForkName] = useState<string>("");
32
+ const [deleteConfirm, setDeleteConfirm] = useState<string>("");
33
+ const [dialogError, setDialogError] = useState<string | null>(null);
34
+ const [dialogIndex, setDialogIndex] = useState<number>(-1);
35
+
28
36
 
29
37
  function reloadDrives() {
30
38
  if (!account || !account.token) {
31
39
  setMyDrives([]);
32
40
  } else {
41
+ setBusy(true);
33
42
  fetch(sockdriveEndpoint + "/list/drives/" + account.token.access_token)
34
43
  .then((r) => r.json())
35
44
  .then((drives: Drive[]) => {
36
45
  setMyDrives(drives.sort((a, b) => a.name.localeCompare(b.name)));
37
46
  })
38
- .catch(console.error);
47
+ .catch(console.error)
48
+ .finally(() => setBusy(false));
39
49
  }
40
50
  }
41
51
 
@@ -48,7 +58,7 @@ export function FatDrivesFrame() {
48
58
  <input class="input input-bordered input-sm w-full" placeholder={t("filter")}
49
59
  value={filter} onChange={(e) => setFilter(e.currentTarget.value ?? "")}></input>
50
60
  <div class="w-full">
51
- <table class="table w-full">
61
+ {!busy && <table class="table w-full">
52
62
  <thead>
53
63
  <tr>
54
64
  <th>{t("net_drives")}</th>
@@ -56,11 +66,13 @@ export function FatDrivesFrame() {
56
66
  </tr>
57
67
  </thead>
58
68
  <tbody>
59
- {myDrives
60
- .filter(({ owner, name }) => filter.length === 0 ||
61
- owner.indexOf(filter) >= 0 || name.indexOf(filter) >= 0)
69
+ {
62
70
  // eslint-disable-next-line camelcase
63
- .map(({ name, owner, fork_of }, index) => {
71
+ myDrives.map(({ name, owner, fork_of }, index) => {
72
+ if (filter.length > 0 &&
73
+ owner.indexOf(filter) === -1 && name.indexOf(filter) === -1) {
74
+ return null;
75
+ }
64
76
  return <>
65
77
  <tr>
66
78
  <td onClick={() => { }}>
@@ -76,47 +88,13 @@ export function FatDrivesFrame() {
76
88
  {preimium && <td>
77
89
  {account?.token && <div class="btn btn-ghost btn-xs"
78
90
  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
- }
91
+ setDialogIndex(index);
92
+ forkDialogRef.current?.showModal();
100
93
  }}>Fork</div>}
101
94
  {account?.token && owner === me && <div class="btn btn-xs btn-ghost"
102
95
  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
- });
96
+ setDialogIndex(index);
97
+ deleteDialogRef.current?.showModal();
120
98
  }}>{t("delete")}</div>}
121
99
  </td>}
122
100
  {!preimium && <td>
@@ -130,6 +108,92 @@ export function FatDrivesFrame() {
130
108
  })}
131
109
  </tbody>
132
110
  </table>
111
+ }
112
+ {busy && <span class="loading loading-spinner loading-lg"></span>}
133
113
  </div>
134
- </div>;
114
+ <dialog ref={forkDialogRef} class="modal">
115
+ {!busy && dialogIndex >= 0 && account !== null && <div class="modal-box">
116
+ <h3 class="font-bold text-lg">{t("enter_name_of_drive")}:</h3>
117
+ <p class="text-xs text-neutral-content mt-2">
118
+ fork of: {myDrives[dialogIndex].owner}/{myDrives[dialogIndex].name}
119
+ </p>
120
+ <input type="text" placeholder="name" value={forkName}
121
+ class="mt-6 input input-bordered input-primary w-full" onChange={(e) =>
122
+ setForkName(e.currentTarget.value)} />
123
+ {dialogError !== null &&
124
+ <p class="mt-2 text-error">{t("error")}: {dialogError}</p>}
125
+ <div class="modal-action">
126
+ {forkName.length > 0 && <button class="btn mr-2 btn-primary" onClick={() => {
127
+ setDialogIndex(-1);
128
+ setBusy(true);
129
+ // eslint-disable-next-line max-len
130
+ fetch(`${sockdriveEndpoint}/fork/drive/${myDrives[dialogIndex].owner}/${myDrives[dialogIndex].name}/${forkName}/${account.token.access_token}`, {
131
+ method: "POST",
132
+ })
133
+ .then((r) => r.json())
134
+ .then((response) => {
135
+ if (response.error) {
136
+ throw new Error(response.error);
137
+ } else {
138
+ setMyDrives([]);
139
+ reloadDrives();
140
+ }
141
+ forkDialogRef.current?.close();
142
+ })
143
+ .catch((e) => {
144
+ console.error(e.message);
145
+ dispatch(uiSlice.actions
146
+ .showToast({ message: e.message, intent: "error" }));
147
+ })
148
+ .finally(() => {
149
+ setBusy(false);
150
+ });
151
+ }}>{t("fork")}</button>}
152
+ <button class="btn" onClick={() => forkDialogRef.current?.close()}>{t("close")}</button>
153
+ </div>
154
+ </div>}
155
+ </dialog>
156
+ <dialog ref={deleteDialogRef} class="modal">
157
+ {!busy && dialogIndex >= 0 && account !== null && <div class="modal-box">
158
+ <h3 class="font-bold text-lg">{t("enter_name_of_drive")}:</h3>
159
+ <p class="text-xs text-neutral mt-2">{myDrives[dialogIndex].owner}/{myDrives[dialogIndex].name}</p>
160
+ <input type="text" placeholder="name" value={deleteConfirm}
161
+ class="mt-6 input input-bordered input-primary w-full"
162
+ onChange={(e) => setDeleteConfirm(e.currentTarget.value)} />
163
+ {dialogError !== null &&
164
+ <p class="mt-2 text-error">{t("error")}: {setDialogError}</p>}
165
+ <div class="modal-action">
166
+ {deleteConfirm === myDrives[dialogIndex].owner + "/" + myDrives[dialogIndex].name &&
167
+ <button class="btn mr-2 btn-primary" onClick={() => {
168
+ setDialogIndex(-1);
169
+ setBusy(true);
170
+ // eslint-disable-next-line max-len
171
+ fetch(`${sockdriveEndpoint}/fork/delete/${myDrives[dialogIndex].owner}/${myDrives[dialogIndex].name}/${account.token.access_token}`, { method: "POST" })
172
+ .then((r) => r.json())
173
+ .then((response) => {
174
+ if (response.error) {
175
+ throw new Error(response.error);
176
+ } else {
177
+ const newDrives = [...myDrives];
178
+ newDrives.splice(dialogIndex, 1);
179
+ setMyDrives(newDrives);
180
+ }
181
+ deleteDialogRef.current?.close();
182
+ })
183
+ .catch((e) => {
184
+ console.error(e.message);
185
+ setDialogError(e.message);
186
+ dispatch(uiSlice.actions
187
+ .showToast({ message: e.message, intent: "error" }));
188
+ })
189
+ .finally(() => {
190
+ setBusy(false);
191
+ });
192
+ }}>{t("delete")}</button>}
193
+ <button class="btn" onClick={() => deleteDialogRef.current?.close()}>{t("close")}</button>
194
+ </div>
195
+ </div>
196
+ }
197
+ </dialog >
198
+ </div >;
135
199
  }
@@ -6,20 +6,22 @@ import { State, useNonSerializableStore } from "../store";
6
6
  import { dispatchLoginAction } from "../store/ui";
7
7
 
8
8
  export function QuickSaveFrame() {
9
- const premium = useSelector((state: State) => state.auth.account)?.premium;
9
+ const premium = useSelector((state: State) => state.auth.account)?.premium && false;
10
10
  return <div class="quick-save-frame flex flex-col justify-center items-center -my-2">
11
11
  <CloudSaveButton />
12
- <SaveButton label={1} bgcolor="bg-primary" textcolor="text-primary-content" />
13
- <LoadButton label={1} bgcolor="bg-primary" />
14
- <SaveButton disabled={!premium} label={2} bgcolor="bg-secondary" textcolor="text-secondary-content" />
15
- <LoadButton disabled={!premium} label={2} bgcolor="bg-secondary" />
16
- <SaveButton disabled={!premium} label={3} bgcolor="bg-accent" textcolor="text-accent-content" />
17
- <LoadButton disabled={!premium} label={3} bgcolor="bg-accent" />
12
+ <SaveButton label="quick" bgcolor="bg-primary" textcolor="text-primary-content" />
13
+ <LoadButton label="quick" bgcolor="bg-primary" />
14
+ {premium && <>
15
+ <SaveButton disabled={!premium} label={2} bgcolor="bg-secondary" textcolor="text-secondary-content" />
16
+ <LoadButton disabled={!premium} label={2} bgcolor="bg-secondary" />
17
+ <SaveButton disabled={!premium} label={3} bgcolor="bg-accent" textcolor="text-accent-content" />
18
+ <LoadButton disabled={!premium} label={3} bgcolor="bg-accent" />
19
+ </>}
18
20
  </div>;
19
21
  }
20
22
 
21
23
  function SaveButton(props: {
22
- label: number,
24
+ label: number | string,
23
25
  bgcolor: string,
24
26
  textcolor: string,
25
27
  disabled?: boolean,
@@ -59,7 +61,7 @@ function SaveButton(props: {
59
61
  }
60
62
 
61
63
  function LoadButton(props: {
62
- label: number,
64
+ label: number | string,
63
65
  bgcolor: string,
64
66
  disabled?: boolean,
65
67
  }) {
@@ -84,7 +86,7 @@ function LoadButton(props: {
84
86
 
85
87
  return <div class={"sidebar-button flex justify-center " + (props.disabled ? " opacity-50" : "")}
86
88
  onClick={onClick}>
87
- <DisketteIcon class="opacity-10"/>
89
+ <DisketteIcon class="opacity-10" />
88
90
  {props.disabled === true && <LockBadge />}
89
91
  <svg xmlns="http://www.w3.org/2000/svg" fill="none"
90
92
  viewBox="0 0 24 24" stroke-width="1.5"
package/src/i18n.ts CHANGED
@@ -4,6 +4,7 @@ import { State } from "./store";
4
4
 
5
5
  const translations: {[lang: string]: {[key: string]: string} } = {
6
6
  ru: {
7
+ enter_name_of_drive: "Введите название диска",
7
8
  ws_outdated: "Необходимо обновить DOS Браузер",
8
9
  update: "Обновить",
9
10
  preloading_sockdrive: "Загрузка FAT",
@@ -77,6 +78,7 @@ const translations: {[lang: string]: {[key: string]: string} } = {
77
78
  fat_write: "FAT запись",
78
79
  },
79
80
  en: {
81
+ enter_name_of_drive: "Enter name of drive",
80
82
  ws_outdated: "Must update DOS Browser App",
81
83
  update: "Update",
82
84
  preloading_sockdrive: "Preloading FAT",
package/src/main.tsx CHANGED
@@ -18,7 +18,7 @@ import { NonSerializableStore, Store, makeNonSerializableStore, makeStore, postJ
18
18
  export const Dos: DosFn = (element: HTMLDivElement,
19
19
  options: Partial<DosOptions> = {}): DosProps => {
20
20
  const nonSerializableStore = makeNonSerializableStore(options);
21
- const store = makeStore(nonSerializableStore);
21
+ const store = makeStore(nonSerializableStore, options);
22
22
 
23
23
 
24
24
  if (store.getState().auth.account?.email === "dz.caiiiycuk@gmail.com" ||
@@ -20,6 +20,10 @@ export interface DosOptions {
20
20
  server: "netherlands" | "newyork" | "singapore",
21
21
  room: string,
22
22
  fullScreen: boolean,
23
+ sockdriveBackend: {
24
+ name: string,
25
+ host: string,
26
+ }
23
27
  }
24
28
 
25
29
  export interface DosProps {
@@ -12,7 +12,7 @@ export function CloudSaveButton(props: {
12
12
  }) {
13
13
  const [busy, setBusy] = useState<boolean>(false);
14
14
  const account = useSelector((state: State) => state.auth.account);
15
- const disabled = account === null;
15
+ const disabled = account === null && false;
16
16
  const dispatch = useDispatch();
17
17
  const t = useT();
18
18
  const cloudSaves = useSelector((state: State) => state.ui.cloudSaves);
@@ -31,7 +31,7 @@ export function ImageRenderingButton(props: {
31
31
  export function QuickSaveButton(props: {
32
32
  class?: string,
33
33
  }) {
34
- const disabled = useSelector((state: State) => state.auth.account) === null;
34
+ const disabled = useSelector((state: State) => state.auth.account) === null && false;
35
35
  const action = disabled ?
36
36
  uiSlice.actions.modalLogin() :
37
37
  uiSlice.actions.frameQuickSave();
package/src/store/init.ts CHANGED
@@ -10,8 +10,8 @@ export const sockdriveBackend: {
10
10
  }
11
11
  } = {
12
12
  "js-dos": {
13
- sockdriveEndpoint: "https://backend.make-vm.com:8001",
14
- sockdriveWssEndpoint: "wss://backend.make-vm.com:8001",
13
+ sockdriveEndpoint: "https://sockdrive.js-dos.com:8001",
14
+ sockdriveWssEndpoint: "wss://sockdrive.js-dos.com:8001",
15
15
  },
16
16
  "localhost": {
17
17
  sockdriveEndpoint: "http://localhost:8001",
package/src/store.tsx CHANGED
@@ -9,7 +9,7 @@ import { DosEvent, DosOptions } from "./public/types";
9
9
  import { CommandInterface, InitFs } from "emulators";
10
10
  import { useStore } from "react-redux";
11
11
  import { Cache, CacheNoop } from "./host/lcache";
12
- import { InitState, createInitSlice } from "./store/init";
12
+ import { InitState, createInitSlice, initSlice, sockdriveBackend, sockdriveBackendNames } from "./store/init";
13
13
 
14
14
  export interface LoadedBundle {
15
15
  bundleUrl: string | null,
@@ -53,7 +53,7 @@ export function makeNonSerializableStore(options: Partial<DosOptions>): NonSeria
53
53
  };
54
54
  }
55
55
 
56
- export function makeStore(nonSerializableStore: NonSerializableStore) {
56
+ export function makeStore(nonSerializableStore: NonSerializableStore, options: Partial<DosOptions>) {
57
57
  const { storeUid, slice } = createInitSlice();
58
58
  const store = configureStore({
59
59
  reducer: {
@@ -72,6 +72,14 @@ export function makeStore(nonSerializableStore: NonSerializableStore) {
72
72
  },
73
73
  });
74
74
  nonSerializableStoreMap[storeUid] = nonSerializableStore;
75
+ if (options.sockdriveBackend && sockdriveBackendNames.indexOf(options.sockdriveBackend.name) === -1) {
76
+ sockdriveBackendNames.push(options.sockdriveBackend.name);
77
+ sockdriveBackend[options.sockdriveBackend.name] = {
78
+ sockdriveEndpoint: "https://" + options.sockdriveBackend.host,
79
+ sockdriveWssEndpoint: "wss://" + options.sockdriveBackend.host,
80
+ };
81
+ store.dispatch(initSlice.actions.setSockdriveBackendName(options.sockdriveBackend.name));
82
+ }
75
83
  return store;
76
84
  };
77
85
 
package/src/v8/config.ts CHANGED
@@ -17,4 +17,4 @@ export const cancelSubscriptionPage = {
17
17
  ru: "https://v8.js-dos.com/ru/cancel-your-subscription/",
18
18
  };
19
19
 
20
- export const actualWsVersion = 1;
20
+ export const actualWsVersion = 2;