@rohal12/spindle 0.51.3 → 0.52.0
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 +4833 -1603
- package/dist/pkg/macro-registry.json +7 -7
- package/dist/pkg/story-variables.js +1658 -189
- package/package.json +5 -2
- package/src/automation/runner.ts +2 -1
- package/src/class-registry.ts +277 -90
- package/src/components/Passage.tsx +2 -2
- package/src/components/PassageDialog.tsx +2 -5
- package/src/components/StoryInterface.tsx +2 -4
- package/src/components/macros/Button.tsx +9 -32
- package/src/components/macros/Checkbox.tsx +10 -4
- package/src/components/macros/Computed.tsx +19 -13
- package/src/components/macros/Dialog.tsx +4 -1
- package/src/components/macros/For.tsx +33 -4
- package/src/components/macros/If.tsx +8 -0
- package/src/components/macros/Include.tsx +15 -24
- package/src/components/macros/MacroError.tsx +2 -1
- package/src/components/macros/MacroLink.tsx +14 -46
- package/src/components/macros/Meter.tsx +26 -40
- package/src/components/macros/Nobr.tsx +1 -0
- package/src/components/macros/PassageDisplay.tsx +3 -0
- package/src/components/macros/Print.tsx +4 -0
- package/src/components/macros/Radiobutton.tsx +27 -2
- package/src/components/macros/SaveManager.tsx +39 -14
- package/src/components/macros/Span.tsx +1 -0
- package/src/components/macros/StoryTitle.tsx +1 -0
- package/src/components/macros/Switch.tsx +13 -0
- package/src/components/macros/Unset.tsx +30 -10
- package/src/components/macros/VarDisplay.tsx +21 -4
- package/src/components/macros/Watch.tsx +88 -40
- package/src/components/macros/Widget.tsx +20 -1
- package/src/components/macros/WidgetInvocation.tsx +20 -158
- package/src/components/macros/arg-utils.ts +226 -0
- package/src/components/macros/detached-body.tsx +68 -0
- package/src/components/macros/option-utils.ts +10 -5
- package/src/define-macro.ts +44 -38
- package/src/execute-mutation.ts +499 -28
- package/src/expression.ts +88 -272
- package/src/hooks/use-action.ts +18 -3
- package/src/hooks/use-interpolate.ts +36 -5
- package/src/index.tsx +10 -1
- package/src/interpolation.ts +394 -96
- package/src/js-lexer.ts +1460 -0
- package/src/markup/code-attributes.ts +64 -0
- package/src/markup/markdown.ts +188 -9
- package/src/markup/render.tsx +552 -113
- package/src/markup/tokenizer.ts +601 -119
- package/src/prng.ts +8 -8
- package/src/registry.ts +35 -0
- package/src/saves/save-manager.ts +368 -153
- package/src/saves/storage.ts +24 -7
- package/src/saves/types.ts +20 -6
- package/src/store.ts +549 -137
- package/src/story-api.ts +46 -81
- package/src/story-init.ts +1 -1
- package/src/story-variables.ts +98 -102
- package/src/triggers.ts +6 -2
- package/src/utils/error-message.ts +12 -0
- package/src/utils/live-locals.ts +10 -3
- package/src/utils/namespace.ts +71 -0
- package/src/utils/object-path.ts +194 -0
- package/src/utils/stable-key.ts +82 -0
- package/src/widgets/widget-registry.ts +9 -0
- package/types/index.d.ts +43 -7
- package/types/tooling.d.ts +1 -0
package/src/define-macro.ts
CHANGED
|
@@ -15,13 +15,17 @@ import {
|
|
|
15
15
|
LocalsUpdateContext,
|
|
16
16
|
LocalsValuesContext,
|
|
17
17
|
NobrContext,
|
|
18
|
+
InlineContext,
|
|
19
|
+
RawTextContext,
|
|
18
20
|
renderNodes as _renderNodes,
|
|
19
21
|
renderInlineNodes,
|
|
20
22
|
} from './markup/render';
|
|
21
23
|
import type { ASTNode } from './markup/ast';
|
|
22
|
-
import { executeMutation } from './execute-mutation';
|
|
24
|
+
import { executeMutation, readState } from './execute-mutation';
|
|
23
25
|
import { evaluate } from './expression';
|
|
24
26
|
import { useStoryStore } from './store';
|
|
27
|
+
import { getByPath, setByPath } from './utils/object-path';
|
|
28
|
+
import { RESERVED_NAME } from './utils/namespace';
|
|
25
29
|
import { useAction } from './hooks/use-action';
|
|
26
30
|
import type { UseActionOptions } from './hooks/use-action';
|
|
27
31
|
import { collectText } from './utils/extract-text';
|
|
@@ -29,12 +33,15 @@ import { currentSourceLocation } from './utils/source-location';
|
|
|
29
33
|
import { parseVarArgs, extractOptions } from './components/macros/option-utils';
|
|
30
34
|
import {
|
|
31
35
|
registerMacro,
|
|
36
|
+
registerMacroText,
|
|
32
37
|
registerSubMacro,
|
|
33
38
|
registerMacroMetadata,
|
|
34
39
|
} from './registry';
|
|
35
|
-
import type { MacroProps, ParameterDef } from './registry';
|
|
40
|
+
import type { MacroProps, MacroTextContext, ParameterDef } from './registry';
|
|
36
41
|
import { registerBlockMacro } from './markup/ast';
|
|
37
42
|
|
|
43
|
+
export type { MacroTextContext };
|
|
44
|
+
|
|
38
45
|
export function macroClass(type: string, className?: string): string {
|
|
39
46
|
const base = `macro-${type}`;
|
|
40
47
|
return className ? `${base} ${className}` : base;
|
|
@@ -89,6 +96,12 @@ export interface MacroDefinition {
|
|
|
89
96
|
description?: string;
|
|
90
97
|
parameters?: ParameterDef[];
|
|
91
98
|
render: (props: MacroProps, ctx: MacroContext) => ComponentChildren;
|
|
99
|
+
/**
|
|
100
|
+
* The macro's text form, used where markup becomes a string: HTML
|
|
101
|
+
* attribute values, image alt text and link titles, macro labels. Without
|
|
102
|
+
* one, the macro can't be used there and is reported as an error.
|
|
103
|
+
*/
|
|
104
|
+
text?: (props: MacroProps, ctx: MacroTextContext) => string;
|
|
92
105
|
}
|
|
93
106
|
|
|
94
107
|
const sharedHooks = {
|
|
@@ -101,33 +114,6 @@ const sharedHooks = {
|
|
|
101
114
|
useContext,
|
|
102
115
|
};
|
|
103
116
|
|
|
104
|
-
/** Traverse a dot-path on an object, returning the nested value. */
|
|
105
|
-
function getByPath(obj: Record<string, unknown>, segments: string[]): unknown {
|
|
106
|
-
let current: unknown = obj;
|
|
107
|
-
for (const seg of segments) {
|
|
108
|
-
if (current == null || typeof current !== 'object') return undefined;
|
|
109
|
-
current = (current as Record<string, unknown>)[seg];
|
|
110
|
-
}
|
|
111
|
-
return current;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/** Set a nested value by dot-path segments on an (Immer draft) object. */
|
|
115
|
-
function setByPath(
|
|
116
|
-
obj: Record<string, unknown>,
|
|
117
|
-
segments: string[],
|
|
118
|
-
value: unknown,
|
|
119
|
-
): void {
|
|
120
|
-
let target: Record<string, unknown> = obj;
|
|
121
|
-
for (let i = 0; i < segments.length - 1; i++) {
|
|
122
|
-
const seg = segments[i]!;
|
|
123
|
-
if (target[seg] == null || typeof target[seg] !== 'object') {
|
|
124
|
-
target[seg] = {};
|
|
125
|
-
}
|
|
126
|
-
target = target[seg] as Record<string, unknown>;
|
|
127
|
-
}
|
|
128
|
-
target[segments[segments.length - 1]!] = value;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
117
|
export function defineMacro(
|
|
132
118
|
config: MacroDefinition,
|
|
133
119
|
source: 'builtin' | 'user' = 'builtin',
|
|
@@ -146,11 +132,25 @@ export function defineMacro(
|
|
|
146
132
|
// Always-on: cssClass + mutation
|
|
147
133
|
const { update, getValues } = useContext(LocalsUpdateContext);
|
|
148
134
|
const nobr = useContext(NobrContext);
|
|
135
|
+
const inline = useContext(InlineContext);
|
|
136
|
+
const raw = useContext(RawTextContext);
|
|
149
137
|
const localsValues = useContext(LocalsValuesContext);
|
|
150
138
|
const renderNodes = (
|
|
151
139
|
nodes: ASTNode[],
|
|
152
|
-
options?: {
|
|
153
|
-
|
|
140
|
+
options?: {
|
|
141
|
+
nobr?: boolean;
|
|
142
|
+
locals?: Record<string, unknown>;
|
|
143
|
+
inline?: boolean;
|
|
144
|
+
raw?: boolean;
|
|
145
|
+
},
|
|
146
|
+
) =>
|
|
147
|
+
_renderNodes(nodes, {
|
|
148
|
+
nobr,
|
|
149
|
+
inline,
|
|
150
|
+
raw,
|
|
151
|
+
locals: localsValues,
|
|
152
|
+
...options,
|
|
153
|
+
});
|
|
154
154
|
const ctx: MacroContext = {
|
|
155
155
|
collectText,
|
|
156
156
|
sourceLocation: currentSourceLocation,
|
|
@@ -196,17 +196,22 @@ export function defineMacro(
|
|
|
196
196
|
|
|
197
197
|
const varExpr = firstToken.replace(/["']/g, '').replace(/^\$/, '');
|
|
198
198
|
const segments = varExpr.split('.');
|
|
199
|
+
if (segments.includes(RESERVED_NAME)) {
|
|
200
|
+
return h(
|
|
201
|
+
'span',
|
|
202
|
+
{ class: 'error' },
|
|
203
|
+
`{${config.name}}: "$${varExpr}" cannot be bound: ${RESERVED_NAME} is reserved`,
|
|
204
|
+
);
|
|
205
|
+
}
|
|
199
206
|
ctx.varName = varExpr;
|
|
200
207
|
ctx.value = useStoryStore((s) => getByPath(s.variables, segments));
|
|
201
|
-
|
|
202
|
-
|
|
208
|
+
// In program order, also when mutation code performs the input
|
|
209
|
+
ctx.getValue = () => getByPath(readState().variables, segments);
|
|
203
210
|
ctx.setValue = (value: unknown) => {
|
|
204
211
|
useStoryStore.setState((state) => {
|
|
205
|
-
setByPath(
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
value,
|
|
209
|
-
);
|
|
212
|
+
setByPath(state.variables, segments, value, {
|
|
213
|
+
createMissing: true,
|
|
214
|
+
});
|
|
210
215
|
});
|
|
211
216
|
};
|
|
212
217
|
}
|
|
@@ -215,6 +220,7 @@ export function defineMacro(
|
|
|
215
220
|
}
|
|
216
221
|
|
|
217
222
|
registerMacro(config.name, Wrapper);
|
|
223
|
+
registerMacroText(config.name, config.text);
|
|
218
224
|
|
|
219
225
|
// Store metadata for tooling API
|
|
220
226
|
const isBlock =
|