@monimesl/dot-simulator 0.1.0-alpha.7 → 0.1.0-alpha.9
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 +0 -5
- package/dist/client/service-bridge.js +101 -17
- package/dist/client/service-bridge.js.map +1 -1
- package/dist/client/simulator.js +164 -112
- package/dist/client/simulator.js.map +1 -1
- package/dist/client/webview-bridge.js.map +1 -1
- package/dist/node/index.d.ts +7 -7
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +385 -97
- package/dist/node/index.js.map +1 -1
- package/package.json +3 -3
package/dist/client/simulator.js
CHANGED
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
props: {
|
|
21
21
|
value: "number",
|
|
22
22
|
currency: "string",
|
|
23
|
-
|
|
23
|
+
minorUnits: "number",
|
|
24
|
+
unit: ["minor", "major"],
|
|
24
25
|
label: "string",
|
|
25
26
|
placeholder: "string",
|
|
26
27
|
hint: "string",
|
|
@@ -51,6 +52,22 @@
|
|
|
51
52
|
"medium",
|
|
52
53
|
"large"
|
|
53
54
|
];
|
|
55
|
+
const PADDING_PROPS = {
|
|
56
|
+
padding: SPACES,
|
|
57
|
+
paddingTop: SPACES,
|
|
58
|
+
paddingStart: SPACES,
|
|
59
|
+
paddingEnd: SPACES,
|
|
60
|
+
paddingBottom: SPACES
|
|
61
|
+
};
|
|
62
|
+
const ASPECTS = [
|
|
63
|
+
"1:1",
|
|
64
|
+
"4:3",
|
|
65
|
+
"3:2",
|
|
66
|
+
"16:9",
|
|
67
|
+
"3:4",
|
|
68
|
+
"2:3",
|
|
69
|
+
"9:16"
|
|
70
|
+
];
|
|
54
71
|
const TONES = [
|
|
55
72
|
"default",
|
|
56
73
|
"muted",
|
|
@@ -89,26 +106,33 @@
|
|
|
89
106
|
"download",
|
|
90
107
|
"edit",
|
|
91
108
|
"error",
|
|
109
|
+
"external-link",
|
|
92
110
|
"eye",
|
|
93
111
|
"eye-off",
|
|
94
112
|
"file",
|
|
95
113
|
"gift",
|
|
114
|
+
"globe",
|
|
96
115
|
"grid",
|
|
116
|
+
"headphones",
|
|
97
117
|
"heart",
|
|
98
118
|
"help",
|
|
99
119
|
"history",
|
|
100
120
|
"home",
|
|
101
121
|
"image",
|
|
102
122
|
"info",
|
|
123
|
+
"link",
|
|
103
124
|
"list",
|
|
104
125
|
"location",
|
|
105
126
|
"lock",
|
|
106
127
|
"logout",
|
|
107
128
|
"mail",
|
|
129
|
+
"microphone",
|
|
108
130
|
"minus",
|
|
109
131
|
"mobile",
|
|
110
132
|
"more",
|
|
133
|
+
"music",
|
|
111
134
|
"phone",
|
|
135
|
+
"playlist",
|
|
112
136
|
"plus",
|
|
113
137
|
"qr-code",
|
|
114
138
|
"receipt",
|
|
@@ -119,10 +143,12 @@
|
|
|
119
143
|
"share",
|
|
120
144
|
"star",
|
|
121
145
|
"success",
|
|
146
|
+
"ticket",
|
|
122
147
|
"trash",
|
|
123
148
|
"upload",
|
|
124
149
|
"user",
|
|
125
150
|
"users",
|
|
151
|
+
"video",
|
|
126
152
|
"wallet",
|
|
127
153
|
"warning",
|
|
128
154
|
"water",
|
|
@@ -137,7 +163,8 @@
|
|
|
137
163
|
indicator: ["dots", "none"],
|
|
138
164
|
gap: SPACES,
|
|
139
165
|
effect: ["slide", "scale"],
|
|
140
|
-
index: "number"
|
|
166
|
+
index: "number",
|
|
167
|
+
...PADDING_PROPS
|
|
141
168
|
},
|
|
142
169
|
events: ["change"],
|
|
143
170
|
children: "elements"
|
|
@@ -167,6 +194,21 @@
|
|
|
167
194
|
events: ["change", "complete"],
|
|
168
195
|
children: "none"
|
|
169
196
|
});
|
|
197
|
+
const DetailRow = defineComponent({
|
|
198
|
+
name: "DetailRow",
|
|
199
|
+
props: {
|
|
200
|
+
label: "string",
|
|
201
|
+
value: "string",
|
|
202
|
+
tone: TONES,
|
|
203
|
+
weight: [
|
|
204
|
+
"regular",
|
|
205
|
+
"medium",
|
|
206
|
+
"bold"
|
|
207
|
+
],
|
|
208
|
+
...PADDING_PROPS
|
|
209
|
+
},
|
|
210
|
+
children: "none"
|
|
211
|
+
});
|
|
170
212
|
const Divider = defineComponent({
|
|
171
213
|
name: "Divider",
|
|
172
214
|
children: "none"
|
|
@@ -176,7 +218,8 @@
|
|
|
176
218
|
props: {
|
|
177
219
|
icon: ICON_NAMES,
|
|
178
220
|
title: "string",
|
|
179
|
-
text: "string"
|
|
221
|
+
text: "string",
|
|
222
|
+
...PADDING_PROPS
|
|
180
223
|
},
|
|
181
224
|
children: "elements"
|
|
182
225
|
});
|
|
@@ -194,7 +237,8 @@
|
|
|
194
237
|
name: "Notice",
|
|
195
238
|
props: {
|
|
196
239
|
tone: STATUS_TONES,
|
|
197
|
-
title: "string"
|
|
240
|
+
title: "string",
|
|
241
|
+
...PADDING_PROPS
|
|
198
242
|
},
|
|
199
243
|
children: "text"
|
|
200
244
|
});
|
|
@@ -205,13 +249,15 @@
|
|
|
205
249
|
});
|
|
206
250
|
const Footer = defineComponent({
|
|
207
251
|
name: "Footer",
|
|
252
|
+
props: PADDING_PROPS,
|
|
208
253
|
children: "elements"
|
|
209
254
|
});
|
|
210
255
|
const Grid = defineComponent({
|
|
211
256
|
name: "Grid",
|
|
212
257
|
props: {
|
|
213
258
|
columns: "number",
|
|
214
|
-
gap: SPACES
|
|
259
|
+
gap: SPACES,
|
|
260
|
+
...PADDING_PROPS
|
|
215
261
|
},
|
|
216
262
|
children: "elements"
|
|
217
263
|
});
|
|
@@ -248,10 +294,16 @@
|
|
|
248
294
|
"rounded",
|
|
249
295
|
"square",
|
|
250
296
|
"circle"
|
|
251
|
-
]
|
|
297
|
+
],
|
|
298
|
+
aspect: ASPECTS
|
|
252
299
|
},
|
|
253
300
|
events: ["load", "error"],
|
|
254
|
-
children: "none"
|
|
301
|
+
children: "none",
|
|
302
|
+
overrides: [{
|
|
303
|
+
prop: "aspect",
|
|
304
|
+
ignores: "height",
|
|
305
|
+
because: "the aspect sets the height"
|
|
306
|
+
}]
|
|
255
307
|
});
|
|
256
308
|
const Input = defineComponent({
|
|
257
309
|
name: "Input",
|
|
@@ -276,9 +328,23 @@
|
|
|
276
328
|
events: ["change", "submit"],
|
|
277
329
|
children: "none"
|
|
278
330
|
});
|
|
331
|
+
const Link = defineComponent({
|
|
332
|
+
name: "Link",
|
|
333
|
+
props: {
|
|
334
|
+
href: "string",
|
|
335
|
+
tone: TONES
|
|
336
|
+
},
|
|
337
|
+
events: ["tap"],
|
|
338
|
+
children: "text",
|
|
339
|
+
inline: true
|
|
340
|
+
});
|
|
279
341
|
const List = defineComponent({
|
|
280
342
|
name: "List",
|
|
281
|
-
props: {
|
|
343
|
+
props: {
|
|
344
|
+
size: ["sm", "md"],
|
|
345
|
+
...PADDING_PROPS
|
|
346
|
+
},
|
|
347
|
+
events: ["endReached"],
|
|
282
348
|
children: "elements"
|
|
283
349
|
});
|
|
284
350
|
const ListItem = defineComponent({
|
|
@@ -288,7 +354,8 @@
|
|
|
288
354
|
subtitle: "string",
|
|
289
355
|
value: "string",
|
|
290
356
|
icon: ICON_NAMES,
|
|
291
|
-
image: "string"
|
|
357
|
+
image: "string",
|
|
358
|
+
...PADDING_PROPS
|
|
292
359
|
},
|
|
293
360
|
events: ["tap"],
|
|
294
361
|
children: "elements"
|
|
@@ -298,7 +365,8 @@
|
|
|
298
365
|
props: {
|
|
299
366
|
open: "boolean",
|
|
300
367
|
title: "string",
|
|
301
|
-
showCloseIcon: "boolean"
|
|
368
|
+
showCloseIcon: "boolean",
|
|
369
|
+
...PADDING_PROPS
|
|
302
370
|
},
|
|
303
371
|
events: ["close"],
|
|
304
372
|
children: "elements"
|
|
@@ -343,6 +411,21 @@
|
|
|
343
411
|
},
|
|
344
412
|
children: "none"
|
|
345
413
|
});
|
|
414
|
+
const Scroll = defineComponent({
|
|
415
|
+
name: "Scroll",
|
|
416
|
+
props: {
|
|
417
|
+
gap: SPACES,
|
|
418
|
+
snap: [
|
|
419
|
+
"none",
|
|
420
|
+
"start",
|
|
421
|
+
"center"
|
|
422
|
+
],
|
|
423
|
+
index: "number",
|
|
424
|
+
...PADDING_PROPS
|
|
425
|
+
},
|
|
426
|
+
events: ["change"],
|
|
427
|
+
children: "elements"
|
|
428
|
+
});
|
|
346
429
|
const Skeleton = defineComponent({
|
|
347
430
|
name: "Skeleton",
|
|
348
431
|
props: {
|
|
@@ -360,7 +443,8 @@
|
|
|
360
443
|
name: "Tabs",
|
|
361
444
|
props: {
|
|
362
445
|
value: "string",
|
|
363
|
-
variant: ["underline", "segmented"]
|
|
446
|
+
variant: ["underline", "segmented"],
|
|
447
|
+
...PADDING_PROPS
|
|
364
448
|
},
|
|
365
449
|
events: ["change"],
|
|
366
450
|
children: "elements"
|
|
@@ -393,7 +477,8 @@
|
|
|
393
477
|
"center",
|
|
394
478
|
"end"
|
|
395
479
|
],
|
|
396
|
-
maxLines: "number"
|
|
480
|
+
maxLines: "number",
|
|
481
|
+
...PADDING_PROPS
|
|
397
482
|
},
|
|
398
483
|
children: "text",
|
|
399
484
|
inline: true
|
|
@@ -451,6 +536,7 @@
|
|
|
451
536
|
Checkbox,
|
|
452
537
|
Chip,
|
|
453
538
|
CodeInput,
|
|
539
|
+
DetailRow,
|
|
454
540
|
Divider,
|
|
455
541
|
EmptyState,
|
|
456
542
|
Footer,
|
|
@@ -459,6 +545,7 @@
|
|
|
459
545
|
Icon,
|
|
460
546
|
Image,
|
|
461
547
|
Input,
|
|
548
|
+
Link,
|
|
462
549
|
List,
|
|
463
550
|
ListItem,
|
|
464
551
|
Notice,
|
|
@@ -475,6 +562,7 @@
|
|
|
475
562
|
children: "none"
|
|
476
563
|
}),
|
|
477
564
|
RadioGroup,
|
|
565
|
+
Scroll,
|
|
478
566
|
Select,
|
|
479
567
|
Sheet,
|
|
480
568
|
Skeleton,
|
|
@@ -504,11 +592,7 @@
|
|
|
504
592
|
],
|
|
505
593
|
wrap: "boolean",
|
|
506
594
|
grow: "boolean",
|
|
507
|
-
|
|
508
|
-
paddingTop: SPACES,
|
|
509
|
-
paddingStart: SPACES,
|
|
510
|
-
paddingEnd: SPACES,
|
|
511
|
-
paddingBottom: SPACES,
|
|
595
|
+
...PADDING_PROPS,
|
|
512
596
|
variant: ["plain", "card"]
|
|
513
597
|
},
|
|
514
598
|
events: ["tap"],
|
|
@@ -600,10 +684,6 @@
|
|
|
600
684
|
get top() {
|
|
601
685
|
return this.#pages.at(-1);
|
|
602
686
|
}
|
|
603
|
-
/**
|
|
604
|
-
* Applies a navigation, or refuses it and returns false: a push onto a full stack, or going back
|
|
605
|
-
* from the first page (which the host handles itself, e.g. by closing the dot).
|
|
606
|
-
*/
|
|
607
687
|
apply(navigation) {
|
|
608
688
|
switch (navigation.action) {
|
|
609
689
|
case "push":
|
|
@@ -642,10 +722,10 @@
|
|
|
642
722
|
}
|
|
643
723
|
};
|
|
644
724
|
function checkCompatibility(manifest, { dashVersion, minRuntimeVersion, reactVersions }) {
|
|
645
|
-
if (!isVersionAtLeast(manifest.runtime, minRuntimeVersion)) throw new IncompatibleDotError("runtimeTooOld", `This dot was built with Dot runtime ${manifest.runtime}
|
|
725
|
+
if (!isVersionAtLeast(manifest.runtime, minRuntimeVersion)) throw new IncompatibleDotError("runtimeTooOld", `This dot was built with an old Dot runtime, ${manifest.runtime}. Rebuild it with @monimesl/dot-runtime ${minRuntimeVersion} or newer.`);
|
|
646
726
|
const minDashVersion = manifest.dash?.minVersion;
|
|
647
|
-
if (minDashVersion !== void 0 && !isVersionAtLeast(dashVersion, minDashVersion)) throw new IncompatibleDotError("dashTooOld", `This dot needs Dash ${minDashVersion} or newer
|
|
648
|
-
if (reactVersions !== void 0 && !reactVersions.some((version) => isReactCompatible(version, manifest.react))) throw new IncompatibleDotError("reactUnavailable", `This dot needs React ${manifest.react}
|
|
727
|
+
if (minDashVersion !== void 0 && !isVersionAtLeast(dashVersion, minDashVersion)) throw new IncompatibleDotError("dashTooOld", `This dot needs Dash ${minDashVersion} or newer. This is Dash ${dashVersion}.`);
|
|
728
|
+
if (reactVersions !== void 0 && !reactVersions.some((version) => isReactCompatible(version, manifest.react))) throw new IncompatibleDotError("reactUnavailable", `This dot needs React ${manifest.react}, which this host doesn't have.`);
|
|
649
729
|
}
|
|
650
730
|
//#endregion
|
|
651
731
|
//#region src/host-calls.ts
|
|
@@ -667,7 +747,7 @@
|
|
|
667
747
|
id,
|
|
668
748
|
error: {
|
|
669
749
|
code: "unsupported",
|
|
670
|
-
message: `This host
|
|
750
|
+
message: `This host doesn't support "${method}"`
|
|
671
751
|
}
|
|
672
752
|
});
|
|
673
753
|
return;
|
|
@@ -690,7 +770,7 @@
|
|
|
690
770
|
this.#end(id)?.controller.abort();
|
|
691
771
|
}
|
|
692
772
|
dismissUI() {
|
|
693
|
-
for (const [id, call] of this.#running) if (call.method.startsWith(UI_METHOD_PREFIX)) this.#fail(id, new HostError("cancelled", "
|
|
773
|
+
for (const [id, call] of this.#running) if (call.method.startsWith(UI_METHOD_PREFIX)) this.#fail(id, new HostError("cancelled", "Its page is no longer on screen"));
|
|
694
774
|
}
|
|
695
775
|
async #invoke(id, entry, call) {
|
|
696
776
|
try {
|
|
@@ -752,7 +832,7 @@
|
|
|
752
832
|
}
|
|
753
833
|
async check(method, permission, signal) {
|
|
754
834
|
const reason = this.#declared[permission];
|
|
755
|
-
if (reason === void 0) throw new HostError("denied", `"${method}" needs the "${permission}" permission. Add it to dot.json
|
|
835
|
+
if (reason === void 0) throw new HostError("denied", `"${method}" needs the "${permission}" permission. Add it to permissions in dot.json, with a reason to show the person.`);
|
|
756
836
|
let allowed = this.#answers.get(permission);
|
|
757
837
|
if (allowed === void 0) {
|
|
758
838
|
allowed = await this.#ask({
|
|
@@ -762,7 +842,7 @@
|
|
|
762
842
|
}, signal);
|
|
763
843
|
this.#answers.set(permission, allowed);
|
|
764
844
|
}
|
|
765
|
-
if (!allowed) throw new HostError("denied", `The person
|
|
845
|
+
if (!allowed) throw new HostError("denied", `The person didn't allow "${permission}"`);
|
|
766
846
|
}
|
|
767
847
|
};
|
|
768
848
|
//#endregion
|
|
@@ -908,7 +988,9 @@
|
|
|
908
988
|
const CLIENT_PATH = "/__dot/";
|
|
909
989
|
const EVENTS_PATH = `${CLIENT_PATH}events`;
|
|
910
990
|
const HTTP_PATH = `${CLIENT_PATH}network/http/`;
|
|
911
|
-
const FILE_PATH = `${CLIENT_PATH}file/`;
|
|
991
|
+
const FILE_PATH = `${CLIENT_PATH}storage/file/`;
|
|
992
|
+
const CACHE_PATH = `${CLIENT_PATH}storage/cache/`;
|
|
993
|
+
const SECRET_PATH = `${CLIENT_PATH}storage/secret/`;
|
|
912
994
|
const DOT_PATH = "/dot/";
|
|
913
995
|
const WEBVIEW_URL = `${DOT_PATH}ui/index.html`;
|
|
914
996
|
const MANIFEST_URL = `${DOT_PATH}manifest.json`;
|
|
@@ -947,7 +1029,7 @@
|
|
|
947
1029
|
try {
|
|
948
1030
|
await navigator.clipboard.writeText(text);
|
|
949
1031
|
} catch (error) {
|
|
950
|
-
throw new HostError("failed", `The browser
|
|
1032
|
+
throw new HostError("failed", `The browser blocked the clipboard: ${error.message}`);
|
|
951
1033
|
}
|
|
952
1034
|
note(`copied: ${text}`);
|
|
953
1035
|
}
|
|
@@ -957,9 +1039,21 @@
|
|
|
957
1039
|
params.url,
|
|
958
1040
|
params.file && `file ${params.file}`
|
|
959
1041
|
].filter(Boolean);
|
|
960
|
-
if (parts.length === 0) throw new HostError("invalid", "
|
|
1042
|
+
if (parts.length === 0) throw new HostError("invalid", "share needs a text, a url or a file");
|
|
961
1043
|
note(`shared: ${parts.join(" · ")}`);
|
|
962
1044
|
}
|
|
1045
|
+
function openUrl(note, url) {
|
|
1046
|
+
if (!isHttps(url)) throw new HostError("invalid", `Dash only opens https addresses, not ${url}`);
|
|
1047
|
+
window.open(url, "_blank", "noopener");
|
|
1048
|
+
note(`opened: ${url}`);
|
|
1049
|
+
}
|
|
1050
|
+
function isHttps(url) {
|
|
1051
|
+
try {
|
|
1052
|
+
return new URL(url).protocol === "https:";
|
|
1053
|
+
} catch {
|
|
1054
|
+
return false;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
963
1057
|
function delay(milliseconds, signal) {
|
|
964
1058
|
if (!Number.isFinite(milliseconds) || milliseconds < 0) throw new HostError("invalid", `A delay is a number of milliseconds, 0 or more, not ${milliseconds}`);
|
|
965
1059
|
return new Promise((resolve) => {
|
|
@@ -1033,6 +1127,20 @@
|
|
|
1033
1127
|
"open",
|
|
1034
1128
|
"usage"
|
|
1035
1129
|
];
|
|
1130
|
+
const CACHE_OPERATIONS = [
|
|
1131
|
+
"get",
|
|
1132
|
+
"set",
|
|
1133
|
+
"remove",
|
|
1134
|
+
"exists",
|
|
1135
|
+
"keys",
|
|
1136
|
+
"clear"
|
|
1137
|
+
];
|
|
1138
|
+
const SECRET_OPERATIONS = [
|
|
1139
|
+
"get",
|
|
1140
|
+
"set",
|
|
1141
|
+
"remove",
|
|
1142
|
+
"clear"
|
|
1143
|
+
];
|
|
1036
1144
|
function dashHandlers(phone, note) {
|
|
1037
1145
|
return {
|
|
1038
1146
|
"dash.ui.alert": ({ params, signal }) => alert(phone, params, signal),
|
|
@@ -1040,9 +1148,12 @@
|
|
|
1040
1148
|
"dash.system.torch": ({ params }) => torch(phone, note, params.state),
|
|
1041
1149
|
"dash.system.copyText": ({ params }) => copyText(note, params.text),
|
|
1042
1150
|
"dash.system.share": ({ params }) => share(note, params),
|
|
1151
|
+
"dash.system.openUrl": ({ params }) => openUrl(note, params.url),
|
|
1043
1152
|
"dash.system.delay": ({ params, signal }) => delay(params.milliseconds, signal),
|
|
1044
1153
|
...devServerHandlers("dash.network.http", HTTP_PATH, HTTP_METHODS),
|
|
1045
|
-
...devServerHandlers("dash.file", FILE_PATH, FILE_OPERATIONS)
|
|
1154
|
+
...devServerHandlers("dash.storage.file", FILE_PATH, FILE_OPERATIONS),
|
|
1155
|
+
...devServerHandlers("dash.storage.cache", CACHE_PATH, CACHE_OPERATIONS),
|
|
1156
|
+
...devServerHandlers("dash.storage.secret", SECRET_PATH, SECRET_OPERATIONS)
|
|
1046
1157
|
};
|
|
1047
1158
|
}
|
|
1048
1159
|
function devServerHandlers(group, path, names) {
|
|
@@ -1101,21 +1212,17 @@
|
|
|
1101
1212
|
const log = element("#log");
|
|
1102
1213
|
const errorBanner = element("#error");
|
|
1103
1214
|
const phone = element(".phone");
|
|
1104
|
-
const platformSelect = element("#platform");
|
|
1105
|
-
const inputModeSelect = element("#input-mode");
|
|
1106
1215
|
const schemeSelect = element("#scheme");
|
|
1107
1216
|
const navigationBar = element(".navigation-bar");
|
|
1108
1217
|
const navigationTitle = element("#navigation-title");
|
|
1109
1218
|
const navigationBack = element("#navigation-back");
|
|
1110
1219
|
const tabBar = element(".tab-bar");
|
|
1111
1220
|
const browserDark = matchMedia("(prefers-color-scheme: dark)");
|
|
1112
|
-
|
|
1113
|
-
const EMULATED_TOUCH_STYLE = "data-dot-simulator-touch";
|
|
1114
|
-
let runningApp = null;
|
|
1221
|
+
let runningDot = null;
|
|
1115
1222
|
let launchCount = 0;
|
|
1116
1223
|
let hotReloads = 0;
|
|
1117
1224
|
async function launch(restorePages = []) {
|
|
1118
|
-
|
|
1225
|
+
stopRunningDot();
|
|
1119
1226
|
launchCount += 1;
|
|
1120
1227
|
const thisLaunch = launchCount;
|
|
1121
1228
|
if (log.childElementCount > 0) note("dot restarted");
|
|
@@ -1149,16 +1256,16 @@
|
|
|
1149
1256
|
});
|
|
1150
1257
|
worker.addEventListener("error", (event) => showError(`Service crashed: ${event.message}`));
|
|
1151
1258
|
screen.src = WEBVIEW_URL;
|
|
1152
|
-
|
|
1259
|
+
runningDot = {
|
|
1153
1260
|
host,
|
|
1154
1261
|
worker
|
|
1155
1262
|
};
|
|
1156
1263
|
}
|
|
1157
|
-
function
|
|
1158
|
-
if (!
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1264
|
+
function stopRunningDot() {
|
|
1265
|
+
if (!runningDot) return;
|
|
1266
|
+
runningDot.host.stop();
|
|
1267
|
+
runningDot.worker.terminate();
|
|
1268
|
+
runningDot = null;
|
|
1162
1269
|
}
|
|
1163
1270
|
function showBars(bars, pages) {
|
|
1164
1271
|
navigationBar.hidden = bars.navigationBarHidden;
|
|
@@ -1173,7 +1280,7 @@
|
|
|
1173
1280
|
else phone.style.removeProperty(property);
|
|
1174
1281
|
}
|
|
1175
1282
|
function goBack() {
|
|
1176
|
-
if (
|
|
1283
|
+
if (runningDot && !runningDot.host.back()) note("back on the first page: a device would close the dot");
|
|
1177
1284
|
}
|
|
1178
1285
|
const ARROWS = {
|
|
1179
1286
|
toHost: "both --> host",
|
|
@@ -1199,22 +1306,22 @@
|
|
|
1199
1306
|
while (log.childElementCount > MAX_LOG_ENTRIES) log.firstElementChild?.remove();
|
|
1200
1307
|
log.scrollTop = log.scrollHeight;
|
|
1201
1308
|
}
|
|
1202
|
-
function
|
|
1309
|
+
function restart(restorePages = []) {
|
|
1203
1310
|
launch(restorePages).catch((error) => {
|
|
1204
1311
|
showError(`Could not start the dot: ${String(error)}`);
|
|
1205
1312
|
});
|
|
1206
1313
|
}
|
|
1207
1314
|
function relaunch() {
|
|
1208
|
-
|
|
1315
|
+
restart(runningDot?.host.pages ?? []);
|
|
1209
1316
|
}
|
|
1210
1317
|
function hotReload() {
|
|
1211
|
-
if (!
|
|
1212
|
-
|
|
1318
|
+
if (!runningDot) {
|
|
1319
|
+
restart();
|
|
1213
1320
|
return;
|
|
1214
1321
|
}
|
|
1215
1322
|
hotReloads += 1;
|
|
1216
1323
|
const reload = { dotHotReload: hotReloads };
|
|
1217
|
-
|
|
1324
|
+
runningDot.worker.postMessage(reload);
|
|
1218
1325
|
screen.contentWindow?.postMessage(reload, location.origin);
|
|
1219
1326
|
note("dot updated in place");
|
|
1220
1327
|
}
|
|
@@ -1222,9 +1329,11 @@
|
|
|
1222
1329
|
errorBanner.hidden = message === null;
|
|
1223
1330
|
errorBanner.textContent = message ?? "";
|
|
1224
1331
|
}
|
|
1332
|
+
function syncSystemContext() {
|
|
1333
|
+
runningDot?.host.setSystemContext(systemContext());
|
|
1334
|
+
}
|
|
1225
1335
|
function systemContext() {
|
|
1226
1336
|
return {
|
|
1227
|
-
platform: platformSelect.value,
|
|
1228
1337
|
scheme: schemeSelect.value,
|
|
1229
1338
|
appState: appState()
|
|
1230
1339
|
};
|
|
@@ -1239,55 +1348,7 @@
|
|
|
1239
1348
|
function changeScheme(scheme) {
|
|
1240
1349
|
schemeSelect.value = scheme;
|
|
1241
1350
|
phone.dataset.scheme = scheme;
|
|
1242
|
-
|
|
1243
|
-
}
|
|
1244
|
-
function changeInputMode(mode) {
|
|
1245
|
-
inputModeSelect.value = mode;
|
|
1246
|
-
phone.dataset.inputMode = mode;
|
|
1247
|
-
applyInputMode();
|
|
1248
|
-
}
|
|
1249
|
-
function applyInputMode() {
|
|
1250
|
-
const document = screen.contentDocument;
|
|
1251
|
-
if (!document) return;
|
|
1252
|
-
document.documentElement.dataset.dotSimulatorInput = inputModeSelect.value;
|
|
1253
|
-
document.querySelector(`style[${EMULATED_TOUCH_STYLE}]`)?.remove();
|
|
1254
|
-
if (inputModeSelect.value !== "touch") return;
|
|
1255
|
-
const css = coarsePointerCss(document);
|
|
1256
|
-
if (!css) return;
|
|
1257
|
-
const style = document.createElement("style");
|
|
1258
|
-
style.setAttribute(EMULATED_TOUCH_STYLE, "");
|
|
1259
|
-
style.textContent = css;
|
|
1260
|
-
document.head.append(style);
|
|
1261
|
-
}
|
|
1262
|
-
function coarsePointerCss(document) {
|
|
1263
|
-
let css = "";
|
|
1264
|
-
for (const sheet of document.styleSheets) try {
|
|
1265
|
-
css += coarsePointerRules(sheet.cssRules);
|
|
1266
|
-
} catch {}
|
|
1267
|
-
return css;
|
|
1268
|
-
}
|
|
1269
|
-
function coarsePointerRules(rules) {
|
|
1270
|
-
let css = "";
|
|
1271
|
-
for (const rule of rules) {
|
|
1272
|
-
const nestedRules = rule.cssRules;
|
|
1273
|
-
if (!nestedRules) continue;
|
|
1274
|
-
if (rule.type === CSSRule.MEDIA_RULE) {
|
|
1275
|
-
const media = rule;
|
|
1276
|
-
COARSE_POINTER_MEDIA.lastIndex = 0;
|
|
1277
|
-
if (COARSE_POINTER_MEDIA.test(media.conditionText)) {
|
|
1278
|
-
COARSE_POINTER_MEDIA.lastIndex = 0;
|
|
1279
|
-
const condition = media.conditionText.replace(COARSE_POINTER_MEDIA, "(min-width: 0px)");
|
|
1280
|
-
const contents = Array.from(media.cssRules, (child) => child.cssText).join("\n");
|
|
1281
|
-
css += condition === "(min-width: 0px)" ? contents : `@media ${condition}{${contents}}`;
|
|
1282
|
-
continue;
|
|
1283
|
-
}
|
|
1284
|
-
}
|
|
1285
|
-
const contents = coarsePointerRules(nestedRules);
|
|
1286
|
-
if (!contents) continue;
|
|
1287
|
-
const openingBrace = rule.cssText.indexOf("{");
|
|
1288
|
-
if (openingBrace !== -1) css += `${rule.cssText.slice(0, openingBrace)}{${contents}}`;
|
|
1289
|
-
}
|
|
1290
|
-
return css;
|
|
1351
|
+
syncSystemContext();
|
|
1291
1352
|
}
|
|
1292
1353
|
const events = new EventSource(EVENTS_PATH);
|
|
1293
1354
|
events.addEventListener("reload", () => {
|
|
@@ -1299,24 +1360,15 @@
|
|
|
1299
1360
|
hotReload();
|
|
1300
1361
|
});
|
|
1301
1362
|
events.addEventListener("build-error", (event) => showError(String(event.data)));
|
|
1302
|
-
element("#reload").addEventListener("click", () =>
|
|
1363
|
+
element("#reload").addEventListener("click", () => restart());
|
|
1303
1364
|
element("#back").addEventListener("click", goBack);
|
|
1304
1365
|
navigationBack.addEventListener("click", goBack);
|
|
1305
1366
|
element("#clear").addEventListener("click", () => log.replaceChildren());
|
|
1306
1367
|
schemeSelect.addEventListener("change", () => changeScheme(schemeSelect.value));
|
|
1307
|
-
inputModeSelect.addEventListener("change", () => changeInputMode(inputModeSelect.value));
|
|
1308
|
-
platformSelect.addEventListener("change", () => {
|
|
1309
|
-
phone.dataset.platform = platformSelect.value;
|
|
1310
|
-
runningApp?.host.setSystemContext(systemContext());
|
|
1311
|
-
});
|
|
1312
|
-
screen.addEventListener("load", applyInputMode);
|
|
1313
1368
|
browserDark.addEventListener("change", () => changeScheme(browserScheme()));
|
|
1314
|
-
document.addEventListener("visibilitychange",
|
|
1315
|
-
runningApp?.host.setSystemContext(systemContext());
|
|
1316
|
-
});
|
|
1369
|
+
document.addEventListener("visibilitychange", syncSystemContext);
|
|
1317
1370
|
changeScheme(browserScheme());
|
|
1318
|
-
|
|
1319
|
-
syncLaunch();
|
|
1371
|
+
restart();
|
|
1320
1372
|
//#endregion
|
|
1321
1373
|
})();
|
|
1322
1374
|
|