@rohal12/spindle 0.15.0 → 0.16.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.
Files changed (40) hide show
  1. package/dist/pkg/format.js +1 -1
  2. package/package.json +1 -1
  3. package/src/components/macros/Back.tsx +5 -6
  4. package/src/components/macros/Button.tsx +5 -9
  5. package/src/components/macros/Checkbox.tsx +5 -7
  6. package/src/components/macros/Computed.tsx +5 -5
  7. package/src/components/macros/Cycle.tsx +5 -9
  8. package/src/components/macros/Do.tsx +5 -6
  9. package/src/components/macros/For.tsx +5 -8
  10. package/src/components/macros/Forward.tsx +5 -6
  11. package/src/components/macros/Goto.tsx +5 -5
  12. package/src/components/macros/If.tsx +5 -5
  13. package/src/components/macros/Include.tsx +5 -7
  14. package/src/components/macros/Listbox.tsx +6 -9
  15. package/src/components/macros/MacroLink.tsx +6 -9
  16. package/src/components/macros/Meter.tsx +5 -7
  17. package/src/components/macros/Numberbox.tsx +5 -7
  18. package/src/components/macros/PassageDisplay.tsx +5 -6
  19. package/src/components/macros/Print.tsx +5 -7
  20. package/src/components/macros/QuickLoad.tsx +5 -6
  21. package/src/components/macros/QuickSave.tsx +5 -6
  22. package/src/components/macros/Radiobutton.tsx +5 -7
  23. package/src/components/macros/Repeat.tsx +6 -10
  24. package/src/components/macros/Restart.tsx +5 -6
  25. package/src/components/macros/Saves.tsx +5 -6
  26. package/src/components/macros/Set.tsx +5 -5
  27. package/src/components/macros/SettingsButton.tsx +5 -6
  28. package/src/components/macros/Stop.tsx +5 -1
  29. package/src/components/macros/StoryTitle.tsx +5 -6
  30. package/src/components/macros/Switch.tsx +7 -6
  31. package/src/components/macros/Textarea.tsx +5 -7
  32. package/src/components/macros/Textbox.tsx +5 -7
  33. package/src/components/macros/Timed.tsx +22 -32
  34. package/src/components/macros/Type.tsx +5 -9
  35. package/src/components/macros/Unset.tsx +5 -5
  36. package/src/components/macros/Widget.tsx +5 -7
  37. package/src/index.tsx +1 -0
  38. package/src/macros/register-builtins.ts +35 -0
  39. package/src/markup/render.tsx +36 -404
  40. package/src/registry.ts +13 -1
@@ -1,7 +1,9 @@
1
1
  import { useLayoutEffect, useContext } from 'preact/hooks';
2
2
  import { RepeatContext } from './Repeat';
3
+ import { registerMacro } from '../../registry';
4
+ import type { MacroProps } from '../../registry';
3
5
 
4
- export function Stop() {
6
+ export function Stop(_props: MacroProps) {
5
7
  const { stop } = useContext(RepeatContext);
6
8
 
7
9
  useLayoutEffect(() => {
@@ -10,3 +12,5 @@ export function Stop() {
10
12
 
11
13
  return null;
12
14
  }
15
+
16
+ registerMacro('stop', Stop);
@@ -1,11 +1,8 @@
1
1
  import { useStoryStore } from '../../store';
2
+ import { registerMacro } from '../../registry';
3
+ import type { MacroProps } from '../../registry';
2
4
 
3
- interface StoryTitleProps {
4
- className?: string;
5
- id?: string;
6
- }
7
-
8
- export function StoryTitle({ className, id }: StoryTitleProps) {
5
+ export function StoryTitle({ className, id }: MacroProps) {
9
6
  const name = useStoryStore((s) => s.storyData?.name || '');
10
7
  const cls = className ? `story-title ${className}` : 'story-title';
11
8
 
@@ -18,3 +15,5 @@ export function StoryTitle({ className, id }: StoryTitleProps) {
18
15
  </span>
19
16
  );
20
17
  }
18
+
19
+ registerMacro('story-title', StoryTitle);
@@ -2,14 +2,11 @@ import { evaluate } from '../../expression';
2
2
  import { renderNodes } from '../../markup/render';
3
3
  import { useMergedLocals } from '../../hooks/use-merged-locals';
4
4
  import { currentSourceLocation } from '../../utils/source-location';
5
+ import { registerMacro, registerSubMacro } from '../../registry';
6
+ import type { MacroProps } from '../../registry';
5
7
  import type { Branch } from '../../markup/ast';
6
8
 
7
- interface SwitchProps {
8
- rawArgs: string;
9
- branches: Branch[];
10
- }
11
-
12
- export function Switch({ rawArgs, branches }: SwitchProps) {
9
+ export function Switch({ rawArgs, branches = [] }: MacroProps) {
13
10
  const [mergedVars, mergedTemps, mergedLocals] = useMergedLocals();
14
11
 
15
12
  let switchValue: unknown;
@@ -65,3 +62,7 @@ export function Switch({ rawArgs, branches }: SwitchProps) {
65
62
 
66
63
  return null;
67
64
  }
65
+
66
+ registerMacro('switch', Switch);
67
+ registerSubMacro('case');
68
+ registerSubMacro('default');
@@ -1,11 +1,7 @@
1
1
  import { useStoryStore } from '../../store';
2
2
  import { useAction } from '../../hooks/use-action';
3
-
4
- interface TextareaProps {
5
- rawArgs: string;
6
- className?: string;
7
- id?: string;
8
- }
3
+ import { registerMacro } from '../../registry';
4
+ import type { MacroProps } from '../../registry';
9
5
 
10
6
  function parseArgs(rawArgs: string): { varName: string; placeholder: string } {
11
7
  const match = rawArgs.match(/^\s*(["']?\$\w+["']?)\s*(?:["'](.*)["'])?\s*$/);
@@ -17,7 +13,7 @@ function parseArgs(rawArgs: string): { varName: string; placeholder: string } {
17
13
  return { varName, placeholder };
18
14
  }
19
15
 
20
- export function Textarea({ rawArgs, className, id }: TextareaProps) {
16
+ export function Textarea({ rawArgs, className, id }: MacroProps) {
21
17
  const { varName, placeholder } = parseArgs(rawArgs);
22
18
  const name = varName.startsWith('$') ? varName.slice(1) : varName;
23
19
 
@@ -48,3 +44,5 @@ export function Textarea({ rawArgs, className, id }: TextareaProps) {
48
44
  />
49
45
  );
50
46
  }
47
+
48
+ registerMacro('textarea', Textarea);
@@ -1,11 +1,7 @@
1
1
  import { useStoryStore } from '../../store';
2
2
  import { useAction } from '../../hooks/use-action';
3
-
4
- interface TextboxProps {
5
- rawArgs: string;
6
- className?: string;
7
- id?: string;
8
- }
3
+ import { registerMacro } from '../../registry';
4
+ import type { MacroProps } from '../../registry';
9
5
 
10
6
  function parseArgs(rawArgs: string): { varName: string; placeholder: string } {
11
7
  const match = rawArgs.match(/^\s*(["']?\$\w+["']?)\s*(?:["'](.*)["'])?\s*$/);
@@ -17,7 +13,7 @@ function parseArgs(rawArgs: string): { varName: string; placeholder: string } {
17
13
  return { varName, placeholder };
18
14
  }
19
15
 
20
- export function Textbox({ rawArgs, className, id }: TextboxProps) {
16
+ export function Textbox({ rawArgs, className, id }: MacroProps) {
21
17
  const { varName, placeholder } = parseArgs(rawArgs);
22
18
  const name = varName.startsWith('$') ? varName.slice(1) : varName;
23
19
 
@@ -47,3 +43,5 @@ export function Textbox({ rawArgs, className, id }: TextboxProps) {
47
43
  />
48
44
  );
49
45
  }
46
+
47
+ registerMacro('textbox', Textbox);
@@ -2,37 +2,21 @@ import { useState, useEffect, useMemo } from 'preact/hooks';
2
2
  import { renderNodes } from '../../markup/render';
3
3
  import { parseDelay } from '../../utils/parse-delay';
4
4
  import { useInterpolate } from '../../hooks/use-interpolate';
5
- import type { ASTNode, Branch } from '../../markup/ast';
5
+ import { registerMacro, registerSubMacro } from '../../registry';
6
+ import type { MacroProps } from '../../registry';
6
7
 
7
- interface TimedProps {
8
- rawArgs: string;
9
- children: ASTNode[];
10
- branches: Branch[];
11
- className?: string;
12
- id?: string;
13
- }
14
-
15
- export function Timed({
16
- rawArgs,
17
- children,
18
- branches,
19
- className,
20
- id,
21
- }: TimedProps) {
8
+ export function Timed({ branches = [] }: MacroProps) {
22
9
  const resolve = useInterpolate();
23
- className = resolve(className);
24
- id = resolve(id);
25
- // Section 0 = initial children, sections 1..N = {next} branches
26
- // Each section has its own delay
10
+ // For branching blocks, className/id from the opening tag goes on branches[0],
11
+ // so the component-level className/id is unused. Each branch carries its own.
27
12
  const sections = useMemo(() => {
28
- const result: { delay: number; nodes: ASTNode[] }[] = [];
29
- result.push({ delay: parseDelay(rawArgs), nodes: children });
30
- for (const branch of branches) {
31
- const delay = branch.rawArgs ? parseDelay(branch.rawArgs) : 0;
32
- result.push({ delay, nodes: branch.children });
33
- }
34
- return result;
35
- }, [rawArgs, children, branches]);
13
+ return branches.map((branch) => ({
14
+ delay: branch.rawArgs ? parseDelay(branch.rawArgs) : 0,
15
+ nodes: branch.children,
16
+ className: branch.className,
17
+ id: branch.id,
18
+ }));
19
+ }, [branches]);
36
20
 
37
21
  const [visibleIndex, setVisibleIndex] = useState(-1);
38
22
 
@@ -51,16 +35,22 @@ export function Timed({
51
35
 
52
36
  if (visibleIndex < 0) return null;
53
37
 
54
- const content = renderNodes(sections[visibleIndex]!.nodes);
38
+ const section = sections[visibleIndex]!;
39
+ const content = renderNodes(section.nodes);
40
+ const sectionClass = resolve(section.className);
41
+ const sectionId = resolve(section.id);
55
42
 
56
- if (className || id)
43
+ if (sectionClass || sectionId)
57
44
  return (
58
45
  <span
59
- id={id}
60
- class={className}
46
+ id={sectionId}
47
+ class={sectionClass}
61
48
  >
62
49
  {content}
63
50
  </span>
64
51
  );
65
52
  return <>{content}</>;
66
53
  }
54
+
55
+ registerMacro('timed', Timed);
56
+ registerSubMacro('next');
@@ -2,16 +2,10 @@ import { useState, useEffect, useRef } from 'preact/hooks';
2
2
  import { renderInlineNodes } from '../../markup/render';
3
3
  import { parseDelay } from '../../utils/parse-delay';
4
4
  import { useInterpolate } from '../../hooks/use-interpolate';
5
- import type { ASTNode } from '../../markup/ast';
5
+ import { registerMacro } from '../../registry';
6
+ import type { MacroProps } from '../../registry';
6
7
 
7
- interface TypeProps {
8
- rawArgs: string;
9
- children: ASTNode[];
10
- className?: string;
11
- id?: string;
12
- }
13
-
14
- export function Type({ rawArgs, children, className, id }: TypeProps) {
8
+ export function Type({ rawArgs, children = [], className, id }: MacroProps) {
15
9
  const resolve = useInterpolate();
16
10
  className = resolve(className);
17
11
  id = resolve(id);
@@ -78,3 +72,5 @@ export function Type({ rawArgs, children, className, id }: TypeProps) {
78
72
  </span>
79
73
  );
80
74
  }
75
+
76
+ registerMacro('type', Type);
@@ -1,11 +1,9 @@
1
1
  import { useRef } from 'preact/hooks';
2
2
  import { useStoryStore } from '../../store';
3
+ import { registerMacro } from '../../registry';
4
+ import type { MacroProps } from '../../registry';
3
5
 
4
- interface UnsetProps {
5
- rawArgs: string;
6
- }
7
-
8
- export function Unset({ rawArgs }: UnsetProps) {
6
+ export function Unset({ rawArgs }: MacroProps) {
9
7
  const ran = useRef(false);
10
8
 
11
9
  if (!ran.current) {
@@ -26,3 +24,5 @@ export function Unset({ rawArgs }: UnsetProps) {
26
24
 
27
25
  return null;
28
26
  }
27
+
28
+ registerMacro('unset', Unset);
@@ -1,11 +1,7 @@
1
1
  import { useLayoutEffect } from 'preact/hooks';
2
2
  import { registerWidget } from '../../widgets/widget-registry';
3
- import type { ASTNode } from '../../markup/ast';
4
-
5
- interface WidgetProps {
6
- rawArgs: string;
7
- children: ASTNode[];
8
- }
3
+ import { registerMacro } from '../../registry';
4
+ import type { MacroProps } from '../../registry';
9
5
 
10
6
  /**
11
7
  * Parse widget definition args: "WidgetName" or "WidgetName" $param1 $param2
@@ -17,7 +13,7 @@ function parseWidgetDef(rawArgs: string): { name: string; params: string[] } {
17
13
  return { name, params };
18
14
  }
19
15
 
20
- export function Widget({ rawArgs, children }: WidgetProps) {
16
+ export function Widget({ rawArgs, children = [] }: MacroProps) {
21
17
  const { name, params } = parseWidgetDef(rawArgs);
22
18
 
23
19
  useLayoutEffect(() => {
@@ -26,3 +22,5 @@ export function Widget({ rawArgs, children }: WidgetProps) {
26
22
 
27
23
  return null;
28
24
  }
25
+
26
+ registerMacro('widget', Widget);
package/src/index.tsx CHANGED
@@ -14,6 +14,7 @@ import { tokenize } from './markup/tokenizer';
14
14
  import { buildAST } from './markup/ast';
15
15
  import { registerWidget } from './widgets/widget-registry';
16
16
  import type { ASTNode } from './markup/ast';
17
+ import './macros/register-builtins';
17
18
  import './styles.css';
18
19
 
19
20
  function renderErrors(root: HTMLElement, errors: string[]) {
@@ -0,0 +1,35 @@
1
+ // Import all built-in macro components to trigger their self-registration.
2
+ import '../components/macros/Set';
3
+ import '../components/macros/Print';
4
+ import '../components/macros/Computed';
5
+ import '../components/macros/Meter';
6
+ import '../components/macros/If';
7
+ import '../components/macros/For';
8
+ import '../components/macros/Do';
9
+ import '../components/macros/Button';
10
+ import '../components/macros/MacroLink';
11
+ import '../components/macros/Switch';
12
+ import '../components/macros/Timed';
13
+ import '../components/macros/Repeat';
14
+ import '../components/macros/Stop';
15
+ import '../components/macros/Type';
16
+ import '../components/macros/Widget';
17
+ import '../components/macros/StoryTitle';
18
+ import '../components/macros/Back';
19
+ import '../components/macros/Forward';
20
+ import '../components/macros/Restart';
21
+ import '../components/macros/QuickSave';
22
+ import '../components/macros/QuickLoad';
23
+ import '../components/macros/SettingsButton';
24
+ import '../components/macros/Saves';
25
+ import '../components/macros/Include';
26
+ import '../components/macros/Goto';
27
+ import '../components/macros/Unset';
28
+ import '../components/macros/Textbox';
29
+ import '../components/macros/Numberbox';
30
+ import '../components/macros/Textarea';
31
+ import '../components/macros/Checkbox';
32
+ import '../components/macros/Radiobutton';
33
+ import '../components/macros/Listbox';
34
+ import '../components/macros/Cycle';
35
+ import '../components/macros/PassageDisplay';