@rohal12/spindle 0.50.1 → 0.51.1
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 +418 -129
- package/dist/pkg/story-variables.js +13 -0
- package/package.json +1 -1
- package/src/class-registry.ts +63 -1
- package/src/components/macros/Computed.tsx +2 -18
- package/src/components/macros/Include.tsx +57 -2
- package/src/components/macros/MacroLink.tsx +4 -3
- package/src/components/macros/SaveManager.tsx +7 -0
- package/src/components/macros/VarDisplay.tsx +3 -2
- package/src/components/macros/Widget.tsx +7 -1
- package/src/components/macros/WidgetInvocation.tsx +4 -2
- package/src/execute-mutation.ts +8 -54
- package/src/expression.ts +99 -4
- package/src/index.tsx +12 -20
- package/src/interpolation.ts +2 -1
- package/src/markup/ast.ts +9 -1
- package/src/markup/render.tsx +2 -1
- package/src/preact-store.ts +50 -0
- package/src/saves/save-manager.ts +109 -55
- package/src/saves/storage.ts +18 -4
- package/src/saves/types.ts +56 -7
- package/src/store.ts +86 -55
- package/src/story-api.ts +9 -9
- package/src/story-init.ts +23 -0
- package/src/story-variables.ts +18 -0
- package/types/index.d.ts +16 -6
package/src/story-init.ts
CHANGED
|
@@ -4,6 +4,8 @@ import { tokenize } from './markup/tokenizer';
|
|
|
4
4
|
import { buildAST } from './markup/ast';
|
|
5
5
|
import { renderNodes } from './markup/render';
|
|
6
6
|
import { setSaveTitlePassage } from './saves/save-manager';
|
|
7
|
+
import { emit } from './event-emitter';
|
|
8
|
+
import type { SavePayload } from './saves/types';
|
|
7
9
|
|
|
8
10
|
/** Hidden container holding the currently mounted StoryInit tree. */
|
|
9
11
|
let storyInitContainer: HTMLElement | null = null;
|
|
@@ -60,3 +62,24 @@ export function executeStoryInit() {
|
|
|
60
62
|
setSaveTitlePassage(saveTitlePassage.content);
|
|
61
63
|
}
|
|
62
64
|
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Initialize a freshly booted story: run StoryInit, restore `session` (the
|
|
68
|
+
* state a page refresh left behind) if there is one, then fire `storyinit`
|
|
69
|
+
* once all state is settled. Without a session, what the `storyinit`
|
|
70
|
+
* handlers set synchronously belongs to the start moment, like StoryInit's
|
|
71
|
+
* changes; a restored session keeps the history it recorded.
|
|
72
|
+
*/
|
|
73
|
+
export function initializeStory(session?: SavePayload): void {
|
|
74
|
+
executeStoryInit();
|
|
75
|
+
|
|
76
|
+
if (session) {
|
|
77
|
+
useStoryStore.getState().loadFromPayload(session);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
emit('storyinit');
|
|
81
|
+
|
|
82
|
+
if (!session) {
|
|
83
|
+
recordStoryInitState();
|
|
84
|
+
}
|
|
85
|
+
}
|
package/src/story-variables.ts
CHANGED
|
@@ -26,6 +26,13 @@ const FOR_LOCAL_RE = /\{for\s+@(\w+)(?:\s*,\s*@(\w+))?\s+of\b/g;
|
|
|
26
26
|
|
|
27
27
|
const VALID_VAR_TYPES = new Set<string>(['number', 'string', 'boolean']);
|
|
28
28
|
|
|
29
|
+
/** Boxed sample values whose members a primitive of each type can access. */
|
|
30
|
+
const PRIMITIVE_SAMPLES: Partial<Record<VarType, object>> = {
|
|
31
|
+
string: Object(''),
|
|
32
|
+
number: Object(0),
|
|
33
|
+
boolean: Object(false),
|
|
34
|
+
};
|
|
35
|
+
|
|
29
36
|
function inferSchema(value: unknown): FieldSchema {
|
|
30
37
|
if (Array.isArray(value)) {
|
|
31
38
|
return { type: 'array' };
|
|
@@ -138,6 +145,17 @@ function validateRef(
|
|
|
138
145
|
// so any field access on an array is allowed.
|
|
139
146
|
if (current.type === 'array') return null;
|
|
140
147
|
|
|
148
|
+
// Primitives expose their wrapper's built-ins (length, toUpperCase,
|
|
149
|
+
// toFixed, etc.). Keep validating past properties of primitive type.
|
|
150
|
+
const sample = PRIMITIVE_SAMPLES[current.type];
|
|
151
|
+
if (sample && part in sample) {
|
|
152
|
+
const member: unknown = sample[part as keyof typeof sample];
|
|
153
|
+
const memberType = typeof member;
|
|
154
|
+
if (!VALID_VAR_TYPES.has(memberType)) return null;
|
|
155
|
+
current = { type: memberType as VarType };
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
|
|
141
159
|
if (current.type !== 'object' || !current.fields) {
|
|
142
160
|
return `Cannot access field "${part}" on $${parts.slice(0, i).join('.')} (type: ${current.type})`;
|
|
143
161
|
}
|
package/types/index.d.ts
CHANGED
|
@@ -541,11 +541,18 @@ export interface StoryAPI {
|
|
|
541
541
|
/** Restart the story from the beginning. */
|
|
542
542
|
restart(): void;
|
|
543
543
|
|
|
544
|
-
/**
|
|
545
|
-
|
|
544
|
+
/**
|
|
545
|
+
* Save the current state. Pass `slot` for a named save, `custom` for metadata.
|
|
546
|
+
* Resolves once the save is persisted (after `aftersave` handlers ran and
|
|
547
|
+
* `hasSave(slot)` is true); rejects if persisting fails.
|
|
548
|
+
*/
|
|
549
|
+
save(slot?: string, custom?: Record<string, unknown>): Promise<void>;
|
|
546
550
|
|
|
547
|
-
/**
|
|
548
|
-
|
|
551
|
+
/**
|
|
552
|
+
* Load a saved state (quick load). Resolves once the loaded state is applied
|
|
553
|
+
* (immediately if the slot is empty); rejects if loading fails.
|
|
554
|
+
*/
|
|
555
|
+
load(slot?: string): Promise<void>;
|
|
549
556
|
|
|
550
557
|
/** Check whether a save exists. */
|
|
551
558
|
hasSave(slot?: string): boolean;
|
|
@@ -556,8 +563,11 @@ export interface StoryAPI {
|
|
|
556
563
|
/** List metadata for all known save slots. */
|
|
557
564
|
listSaves(): Promise<SaveInfo[]>;
|
|
558
565
|
|
|
559
|
-
/**
|
|
560
|
-
|
|
566
|
+
/**
|
|
567
|
+
* Delete a save by slot name. Resolves once the save is removed and
|
|
568
|
+
* `hasSave(slot)` is false; rejects if deleting fails.
|
|
569
|
+
*/
|
|
570
|
+
deleteSave(slot?: string): Promise<void>;
|
|
561
571
|
|
|
562
572
|
/**
|
|
563
573
|
* Export the save in a slot as a portable object (plain JSON).
|