@rohal12/spindle 0.59.6 → 0.59.8
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/dist/pkg/format.js +1 -1
- package/dist/pkg/headless.js +197 -71
- package/dist/pkg/story-variables.js +13 -2
- package/package.json +1 -1
- package/src/class-registry.ts +12 -0
- package/src/components/App.tsx +7 -9
- package/src/components/macros/Computed.tsx +3 -2
- package/src/components/macros/MenubarButtons.tsx +8 -5
- package/src/components/macros/PassageDisplay.tsx +18 -7
- package/src/components/macros/SaveManager.tsx +18 -7
- package/src/components/macros/detached-body.tsx +16 -3
- package/src/execute-mutation.ts +3 -3
- package/src/expression.ts +9 -21
- package/src/hooks/use-modal-focus.ts +15 -1
- package/src/index.tsx +22 -7
- package/src/quick-actions.ts +23 -0
- package/src/saves/save-manager.ts +23 -6
- package/src/saves/storage.ts +10 -8
- package/src/settings.ts +5 -1
- package/src/store.ts +8 -4
- package/src/structural.ts +11 -5
- package/src/styles.css +5 -0
- package/src/widgets/widget-def.ts +15 -3
package/src/store.ts
CHANGED
|
@@ -259,7 +259,7 @@ let sessionMoments: SaveHistoryMoment[] = [];
|
|
|
259
259
|
* Write the game to the session: the whole payload, encoded in one piece
|
|
260
260
|
* on every navigation.
|
|
261
261
|
*/
|
|
262
|
-
function persistSession(get: () => StoryState):
|
|
262
|
+
function persistSession(get: () => StoryState): { error: unknown } | undefined {
|
|
263
263
|
const {
|
|
264
264
|
storyData,
|
|
265
265
|
currentPassage,
|
|
@@ -269,7 +269,7 @@ function persistSession(get: () => StoryState): void {
|
|
|
269
269
|
visitCounts,
|
|
270
270
|
renderCounts,
|
|
271
271
|
} = get();
|
|
272
|
-
if (!storyData) return;
|
|
272
|
+
if (!storyData) return undefined;
|
|
273
273
|
|
|
274
274
|
// Trim cache when history shrank (navigate() drops discarded forward
|
|
275
275
|
// moments itself, since a replacement branch may keep the same length)
|
|
@@ -294,7 +294,7 @@ function persistSession(get: () => StoryState): void {
|
|
|
294
294
|
}
|
|
295
295
|
}
|
|
296
296
|
|
|
297
|
-
saveSession(storyData.ifid, {
|
|
297
|
+
return saveSession(storyData.ifid, {
|
|
298
298
|
passage: currentPassage,
|
|
299
299
|
variables,
|
|
300
300
|
history: sessionMoments,
|
|
@@ -324,7 +324,11 @@ const NAVIGATION_ERROR_CONTEXT = 'The story could not go to another passage:';
|
|
|
324
324
|
function persistSessionThen(get: () => StoryState, after?: () => void): void {
|
|
325
325
|
let failure: { error: unknown } | undefined;
|
|
326
326
|
try {
|
|
327
|
-
persistSession(get);
|
|
327
|
+
const storageFailure = persistSession(get);
|
|
328
|
+
// The story goes on, but a reload would go back to the last copy
|
|
329
|
+
if (storageFailure) {
|
|
330
|
+
showRuntimeError(SESSION_ERROR_CONTEXT, storageFailure.error);
|
|
331
|
+
}
|
|
328
332
|
} catch (error) {
|
|
329
333
|
failure = { error };
|
|
330
334
|
// The player sees it too: a reload would go back to the last moment
|
package/src/structural.ts
CHANGED
|
@@ -674,17 +674,20 @@ export function underChange(
|
|
|
674
674
|
|
|
675
675
|
/**
|
|
676
676
|
* The segment standing for the entry `index` of a Map or Set in a path, or
|
|
677
|
-
* for the `cause` or `errors` of an Error ('c', 'e')
|
|
677
|
+
* for the `cause` or `errors` of an Error ('c', 'e'), or for the buffer a
|
|
678
|
+
* typed array or DataView views ('b').
|
|
678
679
|
*/
|
|
679
|
-
const entryKey = (
|
|
680
|
-
|
|
680
|
+
const entryKey = (
|
|
681
|
+
kind: 'k' | 'v' | 's' | 'c' | 'e' | 'b',
|
|
682
|
+
index: number,
|
|
683
|
+
): string => `\0${kind}${index}`;
|
|
681
684
|
|
|
682
685
|
export const isEntryKey = (segment: string): boolean =>
|
|
683
686
|
segment.startsWith('\0');
|
|
684
687
|
|
|
685
688
|
/**
|
|
686
|
-
* What a Map, Set or
|
|
687
|
-
* members, `cause` and `errors
|
|
689
|
+
* What a Map, Set, Error or view holds in place of properties (keys and
|
|
690
|
+
* values, members, `cause` and `errors`, the buffer), each with the segment standing for it in
|
|
688
691
|
* a path (see entryKey); undefined for any other value.
|
|
689
692
|
*/
|
|
690
693
|
function entryChildren(value: object): [string, unknown][] | undefined {
|
|
@@ -707,6 +710,9 @@ function entryChildren(value: object): [string, unknown][] | undefined {
|
|
|
707
710
|
]);
|
|
708
711
|
}
|
|
709
712
|
}
|
|
713
|
+
} else if (ArrayBuffer.isView(value)) {
|
|
714
|
+
// Views of one buffer share it, which an alias of the buffer changes
|
|
715
|
+
children.push([entryKey('b', 0), value.buffer]);
|
|
710
716
|
} else {
|
|
711
717
|
return undefined;
|
|
712
718
|
}
|
package/src/styles.css
CHANGED
|
@@ -261,6 +261,11 @@ tw-storydata {
|
|
|
261
261
|
max-width: 400px;
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
+
/* Keep the first control clear of the close button above it */
|
|
265
|
+
.dialog-settings .dialog-body {
|
|
266
|
+
padding-top: 2.25em;
|
|
267
|
+
}
|
|
268
|
+
|
|
264
269
|
.dialog-close {
|
|
265
270
|
position: absolute;
|
|
266
271
|
top: 0.75em;
|
|
@@ -36,7 +36,14 @@ export function parseWidgetDef(rawArgs: string): WidgetDef {
|
|
|
36
36
|
return widgetDef(parseMacroArgs(rawArgs, WIDGET_PARAMETERS));
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
/**
|
|
40
|
+
* A definition: its arguments (read like a {widget} macro's, see
|
|
41
|
+
* parseWidgetDef) and its body. Macro names are not case-sensitive.
|
|
42
|
+
*/
|
|
43
|
+
const WIDGET_DEFINITION = /\{widget\s+([^}]*)\}([\s\S]*?)\{\/widget\}/gi;
|
|
44
|
+
|
|
45
|
+
/** `{@children}`, also with selectors such as `{.highlight @children}`. */
|
|
46
|
+
const CHILDREN_PLACEHOLDER = /\{[^{}]*@children\s*\}/;
|
|
40
47
|
|
|
41
48
|
/**
|
|
42
49
|
* The names of the block widgets (those whose body renders `{@children}`)
|
|
@@ -52,8 +59,13 @@ export function blockWidgetNames(
|
|
|
52
59
|
if (passage.name !== 'StoryInit' && !passage.tags?.includes('widget')) {
|
|
53
60
|
continue;
|
|
54
61
|
}
|
|
55
|
-
for (const match of passage.content.matchAll(
|
|
56
|
-
if (
|
|
62
|
+
for (const match of passage.content.matchAll(WIDGET_DEFINITION)) {
|
|
63
|
+
if (!CHILDREN_PLACEHOLDER.test(match[2]!)) continue;
|
|
64
|
+
try {
|
|
65
|
+
names.push(parseWidgetDef(match[1]!).name);
|
|
66
|
+
} catch {
|
|
67
|
+
// The {widget} macro reports a definition it cannot read
|
|
68
|
+
}
|
|
57
69
|
}
|
|
58
70
|
}
|
|
59
71
|
return names;
|