@rohal12/spindle 0.14.0 → 0.16.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/package.json +1 -1
- package/src/components/macros/Back.tsx +5 -6
- package/src/components/macros/Button.tsx +6 -11
- package/src/components/macros/Checkbox.tsx +5 -7
- package/src/components/macros/Computed.tsx +5 -5
- package/src/components/macros/Cycle.tsx +5 -9
- package/src/components/macros/Do.tsx +6 -8
- package/src/components/macros/For.tsx +10 -9
- package/src/components/macros/Forward.tsx +5 -6
- package/src/components/macros/Goto.tsx +5 -5
- package/src/components/macros/If.tsx +5 -5
- package/src/components/macros/Include.tsx +5 -7
- package/src/components/macros/Listbox.tsx +6 -9
- package/src/components/macros/MacroLink.tsx +8 -12
- package/src/components/macros/Meter.tsx +5 -7
- package/src/components/macros/Numberbox.tsx +5 -7
- package/src/components/macros/PassageDisplay.tsx +5 -6
- package/src/components/macros/Print.tsx +5 -7
- package/src/components/macros/QuickLoad.tsx +5 -6
- package/src/components/macros/QuickSave.tsx +5 -6
- package/src/components/macros/Radiobutton.tsx +5 -7
- package/src/components/macros/Repeat.tsx +6 -10
- package/src/components/macros/Restart.tsx +5 -6
- package/src/components/macros/Saves.tsx +5 -6
- package/src/components/macros/Set.tsx +6 -7
- package/src/components/macros/SettingsButton.tsx +5 -6
- package/src/components/macros/Stop.tsx +5 -1
- package/src/components/macros/StoryTitle.tsx +5 -6
- package/src/components/macros/Switch.tsx +7 -6
- package/src/components/macros/Textarea.tsx +5 -7
- package/src/components/macros/Textbox.tsx +5 -7
- package/src/components/macros/Timed.tsx +12 -14
- package/src/components/macros/Type.tsx +5 -9
- package/src/components/macros/Unset.tsx +5 -5
- package/src/components/macros/VarDisplay.tsx +1 -2
- package/src/components/macros/Widget.tsx +5 -7
- package/src/components/macros/WidgetInvocation.tsx +1 -1
- package/src/execute-mutation.ts +1 -1
- package/src/hooks/use-merged-locals.ts +3 -20
- package/src/index.tsx +1 -0
- package/src/macros/register-builtins.ts +35 -0
- package/src/markup/render.tsx +36 -404
- package/src/registry.ts +13 -1
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import { useRef, useContext } from 'preact/hooks';
|
|
2
2
|
import { LocalsUpdateContext } from '../../markup/render';
|
|
3
|
-
import { stripLocalsPrefix } from '../../hooks/use-merged-locals';
|
|
4
3
|
import { executeMutation } from '../../execute-mutation';
|
|
5
4
|
import { currentSourceLocation } from '../../utils/source-location';
|
|
5
|
+
import { registerMacro } from '../../registry';
|
|
6
|
+
import type { MacroProps } from '../../registry';
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
rawArgs: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function Set({ rawArgs }: SetProps) {
|
|
8
|
+
export function Set({ rawArgs }: MacroProps) {
|
|
12
9
|
const { update, getValues } = useContext(LocalsUpdateContext);
|
|
13
10
|
const ran = useRef(false);
|
|
14
11
|
|
|
@@ -16,7 +13,7 @@ export function Set({ rawArgs }: SetProps) {
|
|
|
16
13
|
ran.current = true;
|
|
17
14
|
|
|
18
15
|
try {
|
|
19
|
-
executeMutation(rawArgs,
|
|
16
|
+
executeMutation(rawArgs, getValues(), update);
|
|
20
17
|
} catch (err) {
|
|
21
18
|
console.error(
|
|
22
19
|
`spindle: Error in {set ${rawArgs}}${currentSourceLocation()}:`,
|
|
@@ -28,3 +25,5 @@ export function Set({ rawArgs }: SetProps) {
|
|
|
28
25
|
|
|
29
26
|
return null;
|
|
30
27
|
}
|
|
28
|
+
|
|
29
|
+
registerMacro('set', Set);
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import { useState } from 'preact/hooks';
|
|
2
2
|
import { settings } from '../../settings';
|
|
3
3
|
import { SettingsDialog } from '../SettingsDialog';
|
|
4
|
+
import { registerMacro } from '../../registry';
|
|
5
|
+
import type { MacroProps } from '../../registry';
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
className?: string;
|
|
7
|
-
id?: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function SettingsButton({ className, id }: SettingsButtonProps) {
|
|
7
|
+
export function SettingsButton({ className, id }: MacroProps) {
|
|
11
8
|
const [open, setOpen] = useState(false);
|
|
12
9
|
|
|
13
10
|
if (!settings.hasAny()) return null;
|
|
@@ -27,3 +24,5 @@ export function SettingsButton({ className, id }: SettingsButtonProps) {
|
|
|
27
24
|
</>
|
|
28
25
|
);
|
|
29
26
|
}
|
|
27
|
+
|
|
28
|
+
registerMacro('settings', SettingsButton);
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 }:
|
|
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
|
-
|
|
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 }:
|
|
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,26 +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
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
rawArgs: string;
|
|
9
|
-
children: ASTNode[];
|
|
10
|
-
branches: Branch[];
|
|
11
|
-
className?: string;
|
|
12
|
-
id?: string;
|
|
13
|
-
}
|
|
5
|
+
import { registerMacro, registerSubMacro } from '../../registry';
|
|
6
|
+
import type { MacroProps } from '../../registry';
|
|
7
|
+
import type { ASTNode } from '../../markup/ast';
|
|
14
8
|
|
|
15
9
|
export function Timed({
|
|
16
10
|
rawArgs,
|
|
17
|
-
children,
|
|
18
|
-
branches,
|
|
11
|
+
children = [],
|
|
12
|
+
branches = [],
|
|
19
13
|
className,
|
|
20
14
|
id,
|
|
21
|
-
}:
|
|
15
|
+
}: MacroProps) {
|
|
22
16
|
const resolve = useInterpolate();
|
|
23
|
-
|
|
24
|
-
|
|
17
|
+
const firstBranch = branches[0];
|
|
18
|
+
className = resolve(className ?? firstBranch?.className);
|
|
19
|
+
id = resolve(id ?? firstBranch?.id);
|
|
25
20
|
// Section 0 = initial children, sections 1..N = {next} branches
|
|
26
21
|
// Each section has its own delay
|
|
27
22
|
const sections = useMemo(() => {
|
|
@@ -64,3 +59,6 @@ export function Timed({
|
|
|
64
59
|
);
|
|
65
60
|
return <>{content}</>;
|
|
66
61
|
}
|
|
62
|
+
|
|
63
|
+
registerMacro('timed', Timed);
|
|
64
|
+
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
|
|
5
|
+
import { registerMacro } from '../../registry';
|
|
6
|
+
import type { MacroProps } from '../../registry';
|
|
6
7
|
|
|
7
|
-
|
|
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
|
-
|
|
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);
|
|
@@ -27,8 +27,7 @@ export function VarDisplay({ name, scope, className, id }: VarDisplayProps) {
|
|
|
27
27
|
|
|
28
28
|
let value: unknown;
|
|
29
29
|
if (scope === 'local') {
|
|
30
|
-
|
|
31
|
-
value = key in localsValues ? localsValues[key] : undefined;
|
|
30
|
+
value = root in localsValues ? localsValues[root] : undefined;
|
|
32
31
|
} else {
|
|
33
32
|
value = storeValue;
|
|
34
33
|
}
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { useLayoutEffect } from 'preact/hooks';
|
|
2
2
|
import { registerWidget } from '../../widgets/widget-registry';
|
|
3
|
-
import
|
|
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 }:
|
|
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/execute-mutation.ts
CHANGED
|
@@ -3,25 +3,8 @@ import { useStoryStore } from '../store';
|
|
|
3
3
|
import { LocalsValuesContext } from '../markup/render';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*/
|
|
9
|
-
export function stripLocalsPrefix(
|
|
10
|
-
values: Record<string, unknown>,
|
|
11
|
-
): Record<string, unknown> {
|
|
12
|
-
const locals: Record<string, unknown> = {};
|
|
13
|
-
for (const [key, val] of Object.entries(values)) {
|
|
14
|
-
if (key.startsWith('@')) {
|
|
15
|
-
locals[key.slice(1)] = val;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
return locals;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Return store variables, temporary, and @-prefixed locals from context.
|
|
23
|
-
* Locals use `@` prefix keys internally; the returned locals dict has
|
|
24
|
-
* the prefix stripped so it can be passed directly to evaluate/execute.
|
|
6
|
+
* Return store variables, temporary, and locals from context.
|
|
7
|
+
* All three dicts use unprefixed keys suitable for evaluate/execute.
|
|
25
8
|
*/
|
|
26
9
|
export function useMergedLocals(): readonly [
|
|
27
10
|
Record<string, unknown>,
|
|
@@ -33,6 +16,6 @@ export function useMergedLocals(): readonly [
|
|
|
33
16
|
const localsValues = useContext(LocalsValuesContext);
|
|
34
17
|
|
|
35
18
|
return useMemo(() => {
|
|
36
|
-
return [variables, temporary,
|
|
19
|
+
return [variables, temporary, localsValues] as const;
|
|
37
20
|
}, [variables, temporary, localsValues]);
|
|
38
21
|
}
|
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';
|