@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/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): void {
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 = (kind: 'k' | 'v' | 's' | 'c' | 'e', index: number): string =>
680
- `\0${kind}${index}`;
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 Error holds in place of properties (keys and values,
687
- * members, `cause` and `errors`), each with the segment standing for it in
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
- const BLOCK_WIDGET = /\{widget\s+["']?(\w+)["']?[^}]*\}([\s\S]*?)\{\/widget\}/g;
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(BLOCK_WIDGET)) {
56
- if (/\{@children\}/.test(match[2]!)) names.push(match[1]!);
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;