@rohal12/spindle 0.52.5 → 0.52.6

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.
Files changed (41) hide show
  1. package/dist/pkg/format.js +1 -1
  2. package/dist/pkg/headless.js +651 -544
  3. package/dist/pkg/macro-registry.json +255 -0
  4. package/package.json +1 -1
  5. package/src/components/macros/Button.tsx +2 -4
  6. package/src/components/macros/Checkbox.tsx +6 -13
  7. package/src/components/macros/Computed.tsx +38 -55
  8. package/src/components/macros/Cycle.tsx +1 -0
  9. package/src/components/macros/Dialog.tsx +7 -8
  10. package/src/components/macros/Do.tsx +2 -1
  11. package/src/components/macros/For.tsx +27 -78
  12. package/src/components/macros/Goto.tsx +6 -9
  13. package/src/components/macros/Include.tsx +10 -57
  14. package/src/components/macros/Listbox.tsx +1 -0
  15. package/src/components/macros/MacroError.tsx +37 -0
  16. package/src/components/macros/MacroLink.tsx +9 -24
  17. package/src/components/macros/Meter.tsx +19 -37
  18. package/src/components/macros/Numberbox.tsx +5 -32
  19. package/src/components/macros/PassageDisplay.tsx +37 -47
  20. package/src/components/macros/Print.tsx +1 -0
  21. package/src/components/macros/Radiobutton.tsx +8 -38
  22. package/src/components/macros/Repeat.tsx +3 -3
  23. package/src/components/macros/Set.tsx +3 -29
  24. package/src/components/macros/Switch.tsx +36 -37
  25. package/src/components/macros/Textarea.tsx +2 -28
  26. package/src/components/macros/Textbox.tsx +2 -29
  27. package/src/components/macros/Type.tsx +3 -3
  28. package/src/components/macros/Unset.tsx +17 -40
  29. package/src/components/macros/Watch.tsx +23 -91
  30. package/src/components/macros/WidgetInvocation.tsx +9 -71
  31. package/src/components/macros/detached-body.tsx +2 -2
  32. package/src/components/macros/input-macro.ts +48 -0
  33. package/src/components/macros/locals-scope.tsx +60 -0
  34. package/src/components/macros/macro-args.ts +280 -0
  35. package/src/components/macros/option-utils.ts +7 -12
  36. package/src/define-macro.ts +47 -30
  37. package/src/hooks/use-render-options.ts +26 -0
  38. package/src/markup/render.tsx +2 -4
  39. package/src/registry.ts +48 -0
  40. package/types/index.d.ts +69 -6
  41. package/types/tooling.d.ts +33 -1
@@ -3,6 +3,13 @@
3
3
  "name": "set",
4
4
  "block": false,
5
5
  "subMacros": [],
6
+ "parameters": [
7
+ {
8
+ "name": "code",
9
+ "type": "expression",
10
+ "required": true
11
+ }
12
+ ],
6
13
  "source": "builtin"
7
14
  },
8
15
  {
@@ -11,6 +18,13 @@
11
18
  "subMacros": [],
12
19
  "interpolate": true,
13
20
  "merged": true,
21
+ "parameters": [
22
+ {
23
+ "name": "expression",
24
+ "type": "expression",
25
+ "required": true
26
+ }
27
+ ],
14
28
  "source": "builtin"
15
29
  },
16
30
  {
@@ -18,6 +32,22 @@
18
32
  "block": false,
19
33
  "subMacros": [],
20
34
  "merged": true,
35
+ "parameters": [
36
+ {
37
+ "name": "target",
38
+ "type": "variable",
39
+ "required": true
40
+ },
41
+ {
42
+ "name": "=",
43
+ "type": "separator"
44
+ },
45
+ {
46
+ "name": "expression",
47
+ "type": "expression",
48
+ "required": true
49
+ }
50
+ ],
21
51
  "source": "builtin"
22
52
  },
23
53
  {
@@ -26,6 +56,22 @@
26
56
  "subMacros": [],
27
57
  "interpolate": true,
28
58
  "merged": true,
59
+ "parameters": [
60
+ {
61
+ "name": "current",
62
+ "type": "expression",
63
+ "required": true
64
+ },
65
+ {
66
+ "name": "max",
67
+ "type": "expression",
68
+ "required": true
69
+ },
70
+ {
71
+ "name": "label",
72
+ "type": "string"
73
+ }
74
+ ],
29
75
  "source": "builtin"
30
76
  },
31
77
  {
@@ -42,6 +88,22 @@
42
88
  "subMacros": [],
43
89
  "interpolate": true,
44
90
  "merged": true,
91
+ "parameters": [
92
+ {
93
+ "name": "variables",
94
+ "type": "names",
95
+ "required": true
96
+ },
97
+ {
98
+ "name": "of",
99
+ "type": "separator"
100
+ },
101
+ {
102
+ "name": "list",
103
+ "type": "expression",
104
+ "required": true
105
+ }
106
+ ],
45
107
  "source": "builtin"
46
108
  },
47
109
  {
@@ -55,6 +117,13 @@
55
117
  "block": true,
56
118
  "subMacros": [],
57
119
  "interpolate": true,
120
+ "parameters": [
121
+ {
122
+ "name": "delay",
123
+ "type": "delay",
124
+ "required": true
125
+ }
126
+ ],
58
127
  "source": "builtin"
59
128
  },
60
129
  {
@@ -62,6 +131,13 @@
62
131
  "block": true,
63
132
  "subMacros": [],
64
133
  "interpolate": true,
134
+ "parameters": [
135
+ {
136
+ "name": "label",
137
+ "type": "text",
138
+ "required": true
139
+ }
140
+ ],
65
141
  "source": "builtin"
66
142
  },
67
143
  {
@@ -69,6 +145,17 @@
69
145
  "block": true,
70
146
  "subMacros": [],
71
147
  "interpolate": true,
148
+ "parameters": [
149
+ {
150
+ "name": "text",
151
+ "type": "string",
152
+ "required": true
153
+ },
154
+ {
155
+ "name": "passage",
156
+ "type": "string"
157
+ }
158
+ ],
72
159
  "source": "builtin"
73
160
  },
74
161
  {
@@ -79,6 +166,13 @@
79
166
  "default"
80
167
  ],
81
168
  "merged": true,
169
+ "parameters": [
170
+ {
171
+ "name": "expression",
172
+ "type": "expression",
173
+ "required": true
174
+ }
175
+ ],
82
176
  "source": "builtin"
83
177
  },
84
178
  {
@@ -101,6 +195,13 @@
101
195
  "block": true,
102
196
  "subMacros": [],
103
197
  "interpolate": true,
198
+ "parameters": [
199
+ {
200
+ "name": "delay",
201
+ "type": "delay",
202
+ "required": true
203
+ }
204
+ ],
104
205
  "source": "builtin"
105
206
  },
106
207
  {
@@ -162,6 +263,17 @@
162
263
  "block": true,
163
264
  "subMacros": [],
164
265
  "interpolate": true,
266
+ "parameters": [
267
+ {
268
+ "name": "label",
269
+ "type": "text",
270
+ "required": true
271
+ },
272
+ {
273
+ "name": "noclose",
274
+ "type": "flag"
275
+ }
276
+ ],
165
277
  "source": "builtin"
166
278
  },
167
279
  {
@@ -182,6 +294,17 @@
182
294
  "subMacros": [],
183
295
  "interpolate": true,
184
296
  "merged": true,
297
+ "parameters": [
298
+ {
299
+ "name": "inline",
300
+ "type": "flag"
301
+ },
302
+ {
303
+ "name": "passage",
304
+ "type": "expression",
305
+ "required": true
306
+ }
307
+ ],
185
308
  "source": "builtin"
186
309
  },
187
310
  {
@@ -195,12 +318,26 @@
195
318
  "block": false,
196
319
  "subMacros": [],
197
320
  "merged": true,
321
+ "parameters": [
322
+ {
323
+ "name": "passage",
324
+ "type": "expression",
325
+ "required": true
326
+ }
327
+ ],
198
328
  "source": "builtin"
199
329
  },
200
330
  {
201
331
  "name": "unset",
202
332
  "block": false,
203
333
  "subMacros": [],
334
+ "parameters": [
335
+ {
336
+ "name": "variable",
337
+ "type": "variable",
338
+ "required": true
339
+ }
340
+ ],
204
341
  "source": "builtin"
205
342
  },
206
343
  {
@@ -208,6 +345,17 @@
208
345
  "block": false,
209
346
  "subMacros": [],
210
347
  "storeVar": true,
348
+ "parameters": [
349
+ {
350
+ "name": "variable",
351
+ "type": "variable",
352
+ "required": true
353
+ },
354
+ {
355
+ "name": "placeholder",
356
+ "type": "string"
357
+ }
358
+ ],
211
359
  "source": "builtin"
212
360
  },
213
361
  {
@@ -215,6 +363,17 @@
215
363
  "block": false,
216
364
  "subMacros": [],
217
365
  "storeVar": true,
366
+ "parameters": [
367
+ {
368
+ "name": "variable",
369
+ "type": "variable",
370
+ "required": true
371
+ },
372
+ {
373
+ "name": "placeholder",
374
+ "type": "string"
375
+ }
376
+ ],
218
377
  "source": "builtin"
219
378
  },
220
379
  {
@@ -222,6 +381,17 @@
222
381
  "block": false,
223
382
  "subMacros": [],
224
383
  "storeVar": true,
384
+ "parameters": [
385
+ {
386
+ "name": "variable",
387
+ "type": "variable",
388
+ "required": true
389
+ },
390
+ {
391
+ "name": "placeholder",
392
+ "type": "string"
393
+ }
394
+ ],
225
395
  "source": "builtin"
226
396
  },
227
397
  {
@@ -229,6 +399,17 @@
229
399
  "block": false,
230
400
  "subMacros": [],
231
401
  "storeVar": true,
402
+ "parameters": [
403
+ {
404
+ "name": "variable",
405
+ "type": "variable",
406
+ "required": true
407
+ },
408
+ {
409
+ "name": "label",
410
+ "type": "text"
411
+ }
412
+ ],
232
413
  "source": "builtin"
233
414
  },
234
415
  {
@@ -236,6 +417,22 @@
236
417
  "block": false,
237
418
  "subMacros": [],
238
419
  "storeVar": true,
420
+ "parameters": [
421
+ {
422
+ "name": "variable",
423
+ "type": "variable",
424
+ "required": true
425
+ },
426
+ {
427
+ "name": "value",
428
+ "type": "text",
429
+ "required": true
430
+ },
431
+ {
432
+ "name": "label",
433
+ "type": "text"
434
+ }
435
+ ],
239
436
  "source": "builtin"
240
437
  },
241
438
  {
@@ -245,6 +442,13 @@
245
442
  "option"
246
443
  ],
247
444
  "storeVar": true,
445
+ "parameters": [
446
+ {
447
+ "name": "variable",
448
+ "type": "variable",
449
+ "required": true
450
+ }
451
+ ],
248
452
  "source": "builtin"
249
453
  },
250
454
  {
@@ -252,6 +456,13 @@
252
456
  "block": true,
253
457
  "subMacros": [],
254
458
  "storeVar": true,
459
+ "parameters": [
460
+ {
461
+ "name": "variable",
462
+ "type": "variable",
463
+ "required": true
464
+ }
465
+ ],
255
466
  "source": "builtin"
256
467
  },
257
468
  {
@@ -271,12 +482,56 @@
271
482
  "name": "watch",
272
483
  "block": false,
273
484
  "subMacros": [],
485
+ "parameters": [
486
+ {
487
+ "name": "condition",
488
+ "type": "string",
489
+ "required": true
490
+ },
491
+ {
492
+ "name": "options",
493
+ "type": "options",
494
+ "parameters": [
495
+ {
496
+ "name": "goto",
497
+ "type": "string"
498
+ },
499
+ {
500
+ "name": "dialog",
501
+ "type": "string"
502
+ },
503
+ {
504
+ "name": "run",
505
+ "type": "string"
506
+ },
507
+ {
508
+ "name": "name",
509
+ "type": "string"
510
+ },
511
+ {
512
+ "name": "priority",
513
+ "type": "number"
514
+ },
515
+ {
516
+ "name": "once",
517
+ "type": "flag"
518
+ }
519
+ ]
520
+ }
521
+ ],
274
522
  "source": "builtin"
275
523
  },
276
524
  {
277
525
  "name": "unwatch",
278
526
  "block": false,
279
527
  "subMacros": [],
528
+ "parameters": [
529
+ {
530
+ "name": "name",
531
+ "type": "text",
532
+ "required": true
533
+ }
534
+ ],
280
535
  "source": "builtin"
281
536
  }
282
537
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rohal12/spindle",
3
- "version": "0.52.5",
3
+ "version": "0.52.6",
4
4
  "type": "module",
5
5
  "description": "A Preact-based story format for Twine 2.",
6
6
  "license": "Unlicense",
@@ -1,15 +1,13 @@
1
1
  import { defineMacro } from '../../define-macro';
2
- import { readWholeQuoted } from './arg-utils';
3
2
  import { useDetachedBody } from './detached-body';
4
3
 
5
4
  defineMacro({
6
5
  name: 'button',
7
6
  block: true,
8
7
  interpolate: true,
8
+ parameters: [{ name: 'label', type: 'text', required: true }],
9
9
  render({ rawArgs, children = [] }, ctx) {
10
- const text =
11
- readWholeQuoted(rawArgs.trim()) ?? rawArgs.replace(/^["']|["']$/g, '');
12
- const label = ctx.resolve?.(text) ?? rawArgs;
10
+ const label = ctx.resolve?.(ctx.args.label ?? '') ?? rawArgs;
13
11
  const runBody = useDetachedBody();
14
12
 
15
13
  // Run the body outside the passage tree: all macro side effects ({set},
@@ -1,21 +1,14 @@
1
1
  import { defineMacro } from '../../define-macro';
2
- import { readWholeQuoted, stripLooseQuotes } from './arg-utils';
3
-
4
- export function parseCheckboxLabel(rawArgs: string): string {
5
- // `s`: a quoted label may span lines. Its trailing whitespace is trimmed
6
- // here, not by the regex: `(.+?)\s*$` tries every position of a run of
7
- // spaces in the label, taking time quadratic in its length.
8
- const match = rawArgs.match(/^\s*["']?\$?[\w.]+["']?\s+(.+)$/s);
9
- if (!match) return '';
10
- const rest = match[1]!.trimEnd() || match[1]!;
11
- return readWholeQuoted(rest) ?? stripLooseQuotes(rest);
12
- }
13
2
 
14
3
  defineMacro({
15
4
  name: 'checkbox',
16
5
  storeVar: true,
17
- render({ rawArgs }, ctx) {
18
- const label = parseCheckboxLabel(rawArgs);
6
+ parameters: [
7
+ { name: 'variable', type: 'variable', required: true },
8
+ { name: 'label', type: 'text' },
9
+ ],
10
+ render(_props, ctx) {
11
+ const label = ctx.args.label ?? '';
19
12
 
20
13
  ctx.useAction({
21
14
  type: 'checkbox',
@@ -2,9 +2,8 @@ import { useStoryStore } from '../../store';
2
2
  import { evaluate } from '../../expression';
3
3
  import { readState } from '../../execute-mutation';
4
4
  import { deepEqual } from '../../class-registry';
5
- import { currentSourceLocation } from '../../utils/source-location';
6
5
  import { defineMacro } from '../../define-macro';
7
- import { MacroError } from './MacroError';
6
+ import { MacroError, logMacroError } from './MacroError';
8
7
  import { checkVariableName } from '../../utils/namespace';
9
8
 
10
9
  /**
@@ -14,38 +13,26 @@ import { checkVariableName } from '../../utils/namespace';
14
13
  */
15
14
  const UNSET: unique symbol = Symbol('unset');
16
15
 
17
- function parseComputedArgs(rawArgs: string): { target: string; expr: string } {
18
- const trimmed = rawArgs.trim();
19
-
20
- let depth = 0;
21
- for (let i = 0; i < trimmed.length; i++) {
22
- const ch = trimmed[i];
23
- if (ch === '(' || ch === '[' || ch === '{') depth++;
24
- else if (ch === ')' || ch === ']' || ch === '}') depth--;
25
- else if (ch === '=' && depth === 0) {
26
- if (trimmed[i + 1] === '=') {
27
- i++;
28
- continue;
29
- }
30
- if (i > 0 && trimmed[i - 1] === '!') continue;
31
-
32
- const target = trimmed.slice(0, i).trim();
33
- const expr = trimmed.slice(i + 1).trim();
34
-
35
- if (!target.match(/^[$_@]\w+$/)) {
36
- throw new Error(
37
- `{computed}: target must be $name, _name, or @name, got "${target}"`,
38
- );
39
- }
16
+ /** Check the arguments of "target = expression". */
17
+ function assignmentArgs(
18
+ rawArgs: string,
19
+ args: { target?: string; '=': boolean; expression?: string },
20
+ ): { target: string; expr: string } {
21
+ if (!args['=']) {
22
+ throw new Error(
23
+ `{computed}: expected "target = expression", got "${rawArgs}"`,
24
+ );
25
+ }
40
26
 
41
- checkVariableName(target.slice(1), target);
42
- return { target, expr };
43
- }
27
+ const { target = '', expression: expr = '' } = args;
28
+ if (!target.match(/^[$_@]\w+$/)) {
29
+ throw new Error(
30
+ `{computed}: target must be $name, _name, or @name, got "${target}"`,
31
+ );
44
32
  }
45
33
 
46
- throw new Error(
47
- `{computed}: expected "target = expression", got "${rawArgs}"`,
48
- );
34
+ checkVariableName(target.slice(1), target);
35
+ return { target, expr };
49
36
  }
50
37
 
51
38
  /**
@@ -73,10 +60,7 @@ function computeAndApply(
73
60
  const { variables, temporary, transient } = readState();
74
61
  newValue = evaluate(expr, variables, temporary, getLocals(), transient);
75
62
  } catch (err) {
76
- console.error(
77
- `spindle: Error in {computed ${rawArgs}}${currentSourceLocation()}:`,
78
- err,
79
- );
63
+ logMacroError(`computed ${rawArgs}`, err);
80
64
  return;
81
65
  }
82
66
 
@@ -86,10 +70,7 @@ function computeAndApply(
86
70
  try {
87
71
  localsUpdate!(name, newValue);
88
72
  } catch (err) {
89
- console.error(
90
- `spindle: Error in {computed ${rawArgs}}${currentSourceLocation()}:`,
91
- err,
92
- );
73
+ logMacroError(`computed ${rawArgs}`, err);
93
74
  }
94
75
  } else {
95
76
  const state = useStoryStore.getState();
@@ -102,13 +83,18 @@ function computeAndApply(
102
83
  defineMacro({
103
84
  name: 'computed',
104
85
  merged: true,
86
+ parameters: [
87
+ { name: 'target', type: 'variable', required: true },
88
+ { name: '=', type: 'separator' },
89
+ { name: 'expression', type: 'expression', required: true },
90
+ ],
105
91
  render({ rawArgs }, ctx) {
106
92
  const [mergedVars, mergedTemps, mergedLocals, mergedTrans] = ctx.merged!;
107
93
 
108
94
  let target: string;
109
95
  let expr: string;
110
96
  try {
111
- ({ target, expr } = parseComputedArgs(rawArgs));
97
+ ({ target, expr } = assignmentArgs(rawArgs, ctx.args));
112
98
  } catch (err) {
113
99
  return (
114
100
  <MacroError
@@ -124,9 +110,7 @@ defineMacro({
124
110
 
125
111
  const prevOutput = ctx.hooks.useRef<unknown>(UNSET);
126
112
 
127
- const ran = ctx.hooks.useRef(false);
128
- if (!ran.current) {
129
- ran.current = true;
113
+ const compute = () =>
130
114
  computeAndApply(
131
115
  expr,
132
116
  name,
@@ -137,22 +121,21 @@ defineMacro({
137
121
  prevOutput,
138
122
  localsUpdate,
139
123
  );
124
+
125
+ const ran = ctx.hooks.useRef(false);
126
+ if (!ran.current) {
127
+ ran.current = true;
128
+ compute();
140
129
  }
141
130
 
142
131
  // The merged values only decide when to recompute; computeAndApply reads
143
132
  // the current ones.
144
- ctx.hooks.useLayoutEffect(() => {
145
- computeAndApply(
146
- expr,
147
- name,
148
- isTemp,
149
- isLocal,
150
- ctx.getValues,
151
- rawArgs,
152
- prevOutput,
153
- localsUpdate,
154
- );
155
- }, [mergedVars, mergedTemps, mergedLocals, mergedTrans]);
133
+ ctx.hooks.useLayoutEffect(compute, [
134
+ mergedVars,
135
+ mergedTemps,
136
+ mergedLocals,
137
+ mergedTrans,
138
+ ]);
156
139
 
157
140
  return null;
158
141
  },
@@ -4,6 +4,7 @@ defineMacro({
4
4
  name: 'cycle',
5
5
  block: true,
6
6
  storeVar: true,
7
+ parameters: [{ name: 'variable', type: 'variable', required: true }],
7
8
  render({ children = [] }, ctx) {
8
9
  const options = ctx.extractOptions(children);
9
10
 
@@ -1,20 +1,19 @@
1
1
  import { defineMacro } from '../../define-macro';
2
2
  import { PassageDialog } from '../PassageDialog';
3
- import { readWholeQuoted } from './arg-utils';
4
3
 
5
4
  defineMacro({
6
5
  name: 'dialog',
7
6
  block: true,
8
7
  interpolate: true,
9
- render({ rawArgs, children = [] }, ctx) {
8
+ parameters: [
9
+ { name: 'label', type: 'text', required: true },
10
+ { name: 'noclose', type: 'flag' },
11
+ ],
12
+ render({ children = [] }, ctx) {
10
13
  const [open, setOpen] = ctx.hooks.useState(false);
11
14
 
12
- const noclose = /\bnoclose\s*$/.test(rawArgs);
13
- const labelRaw = rawArgs.replace(/\bnoclose\s*$/, '').trim();
14
- const label =
15
- ctx.resolve?.(
16
- readWholeQuoted(labelRaw) ?? labelRaw.replace(/^["']|["']$/g, ''),
17
- ) ?? labelRaw;
15
+ const { noclose } = ctx.args;
16
+ const label = ctx.resolve!(ctx.args.label ?? '');
18
17
  const passageName = ctx
19
18
  .collectText(children)
20
19
  .trim()
@@ -1,4 +1,5 @@
1
1
  import { defineMacro } from '../../define-macro';
2
+ import { logMacroError } from './MacroError';
2
3
 
3
4
  defineMacro({
4
5
  name: 'do',
@@ -11,7 +12,7 @@ defineMacro({
11
12
  try {
12
13
  ctx.mutate(code);
13
14
  } catch (err) {
14
- console.error(`spindle: Error in {do}${ctx.sourceLocation()}:`, err);
15
+ logMacroError('do', err);
15
16
  }
16
17
  }, []);
17
18