squiffy-runtime 6.0.0-alpha.15 → 6.0.0-alpha.18
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/animation.d.ts +11 -0
- package/dist/events.d.ts +8 -3
- package/dist/import.d.ts +4 -0
- package/dist/linkHandler.d.ts +8 -0
- package/dist/pluginManager.d.ts +23 -0
- package/dist/squiffy.runtime.d.ts +2 -2
- package/dist/squiffy.runtime.global.js +126 -0
- package/dist/squiffy.runtime.global.js.map +1 -0
- package/dist/squiffy.runtime.js +8785 -487
- package/dist/squiffy.runtime.js.map +1 -0
- package/dist/state.d.ts +19 -0
- package/dist/textProcessor.d.ts +8 -13
- package/dist/types.d.ts +5 -2
- package/dist/types.plugins.d.ts +27 -0
- package/dist/updater.d.ts +2 -0
- package/dist/utils.d.ts +1 -2
- package/package.json +12 -5
- package/src/__snapshots__/squiffy.runtime.test.ts.snap +53 -19
- package/src/animation.ts +68 -0
- package/src/events.ts +9 -10
- package/src/import.ts +5 -0
- package/src/linkHandler.ts +18 -0
- package/src/pluginManager.ts +74 -0
- package/src/plugins/animate.ts +97 -0
- package/src/plugins/index.ts +13 -0
- package/src/plugins/live.ts +83 -0
- package/src/plugins/random.ts +22 -0
- package/src/plugins/replaceLabel.ts +22 -0
- package/src/plugins/rotateSequence.ts +61 -0
- package/src/squiffy.runtime.test.ts +306 -134
- package/src/squiffy.runtime.ts +460 -332
- package/src/state.ts +106 -0
- package/src/textProcessor.ts +61 -164
- package/src/types.plugins.ts +41 -0
- package/src/types.ts +5 -2
- package/src/updater.ts +77 -0
- package/src/utils.ts +15 -12
- package/vite.config.ts +36 -0
- package/vitest.config.ts +9 -0
- package/vitest.setup.ts +16 -0
- package/dist/events.js +0 -35
- package/dist/squiffy.runtime.test.js +0 -394
- package/dist/textProcessor.js +0 -166
- package/dist/types.js +0 -1
- package/dist/utils.js +0 -14
package/dist/state.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Emitter, SquiffyEventMap } from './events.js';
|
|
2
|
+
export declare class State {
|
|
3
|
+
persist: boolean;
|
|
4
|
+
storyId: string;
|
|
5
|
+
onSet: (attribute: string, value: any) => void;
|
|
6
|
+
onSetInternal?: (attribute: string, oldValue: any, newValue: any) => void;
|
|
7
|
+
store: Record<string, any>;
|
|
8
|
+
emitter: Emitter<SquiffyEventMap>;
|
|
9
|
+
constructor(persist: boolean, storyId: string, onSet: (attribute: string, value: any) => void, emitter: Emitter<SquiffyEventMap>, onSetInternal?: (attribute: string, oldValue: any, newValue: any) => void);
|
|
10
|
+
private usePersistentStorage;
|
|
11
|
+
set(attribute: string, value: any): void;
|
|
12
|
+
setInternal(attribute: string, value: any, raiseEvents: boolean): void;
|
|
13
|
+
get(attribute: string): any;
|
|
14
|
+
getStore(): Record<string, any>;
|
|
15
|
+
load(): void;
|
|
16
|
+
reset(): void;
|
|
17
|
+
setSeen(sectionName: string): void;
|
|
18
|
+
getSeen(sectionName: string): boolean;
|
|
19
|
+
}
|
package/dist/textProcessor.d.ts
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { default as Handlebars } from 'handlebars';
|
|
2
|
+
import { Section, Story } from './types.js';
|
|
3
|
+
import { State } from './state.js';
|
|
2
4
|
export declare class TextProcessor {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
story: any;
|
|
5
|
+
story: Story;
|
|
6
|
+
state: State;
|
|
6
7
|
getCurrentSection: () => Section;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
process(text: string, data: any): string;
|
|
11
|
-
processTextCommand(text: string, data: any): any;
|
|
12
|
-
processTextCommand_If(section: string, data: any): string;
|
|
13
|
-
processTextCommand_Else(section: string, data: any): string;
|
|
14
|
-
processTextCommand_Label(section: string, data: any): string;
|
|
15
|
-
processTextCommand_Rotate(type: string, section: string): string;
|
|
8
|
+
handlebars: typeof Handlebars;
|
|
9
|
+
constructor(story: Story, state: State, currentSection: () => Section);
|
|
10
|
+
process(text: string, inline: boolean): string;
|
|
16
11
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SquiffyEventHandler, SquiffyEventMap } from
|
|
1
|
+
import { SquiffyEventHandler, SquiffyEventMap } from './events.js';
|
|
2
2
|
export interface SquiffyInitOptions {
|
|
3
3
|
element: HTMLElement;
|
|
4
4
|
story: Story;
|
|
@@ -12,11 +12,13 @@ export interface SquiffySettings {
|
|
|
12
12
|
onSet: (attribute: string, value: any) => void;
|
|
13
13
|
}
|
|
14
14
|
export interface SquiffyApi {
|
|
15
|
+
begin: () => Promise<void>;
|
|
15
16
|
restart: () => void;
|
|
16
17
|
get: (attribute: string) => any;
|
|
17
18
|
set: (attribute: string, value: any) => void;
|
|
18
|
-
clickLink: (link: HTMLElement) => boolean
|
|
19
|
+
clickLink: (link: HTMLElement) => Promise<boolean>;
|
|
19
20
|
update: (story: Story) => void;
|
|
21
|
+
goBack: () => void;
|
|
20
22
|
on<E extends keyof SquiffyEventMap>(event: E, handler: SquiffyEventHandler<E>): () => void;
|
|
21
23
|
off<E extends keyof SquiffyEventMap>(event: E, handler: SquiffyEventHandler<E>): void;
|
|
22
24
|
once<E extends keyof SquiffyEventMap>(event: E, handler: SquiffyEventHandler<E>): () => void;
|
|
@@ -35,6 +37,7 @@ export interface Story {
|
|
|
35
37
|
js: ((squiffy: SquiffyJsFunctionApi, get: (attribute: string) => any, set: (attribute: string, value: any) => void) => void)[];
|
|
36
38
|
start: string;
|
|
37
39
|
id?: string | null;
|
|
40
|
+
uiJsIndex?: number;
|
|
38
41
|
sections: Record<string, Section>;
|
|
39
42
|
}
|
|
40
43
|
export interface Section {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { HelperDelegate } from 'handlebars';
|
|
2
|
+
import { SquiffyEventHandler, SquiffyEventMap } from './events.js';
|
|
3
|
+
import { Animation } from './animation.js';
|
|
4
|
+
export interface SquiffyPlugin {
|
|
5
|
+
name: string;
|
|
6
|
+
init(host: PluginHost): void | Promise<void>;
|
|
7
|
+
onWrite?(el: HTMLElement): void;
|
|
8
|
+
onLoad?(): void;
|
|
9
|
+
}
|
|
10
|
+
export interface HandleLinkResult {
|
|
11
|
+
disableLink?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface PluginHost {
|
|
14
|
+
outputElement: HTMLElement;
|
|
15
|
+
registerHelper(name: string, helper: HelperDelegate): void;
|
|
16
|
+
registerLinkHandler(type: string, handler: (el: HTMLElement) => HandleLinkResult): void;
|
|
17
|
+
get(attribute: string): any;
|
|
18
|
+
set(attribute: string, value: any): void;
|
|
19
|
+
getSectionText(name: string): string | null;
|
|
20
|
+
getPassageText(name: string): string | null;
|
|
21
|
+
processText: (text: string, inline: boolean) => string;
|
|
22
|
+
addTransition: (fn: () => Promise<void>) => void;
|
|
23
|
+
animation: Animation;
|
|
24
|
+
on<E extends keyof SquiffyEventMap>(event: E, handler: SquiffyEventHandler<E>): () => void;
|
|
25
|
+
off<E extends keyof SquiffyEventMap>(event: E, handler: SquiffyEventHandler<E>): void;
|
|
26
|
+
once<E extends keyof SquiffyEventMap>(event: E, handler: SquiffyEventHandler<E>): () => void;
|
|
27
|
+
}
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
export declare function
|
|
2
|
-
export declare function rotate(options: string, current: string | null): string[];
|
|
1
|
+
export declare function fadeReplace(element: HTMLElement, text: string): Promise<void>;
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "squiffy-runtime",
|
|
3
|
-
"version": "6.0.0-alpha.
|
|
3
|
+
"version": "6.0.0-alpha.18",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/squiffy.runtime.js",
|
|
6
6
|
"types": "dist/squiffy.runtime.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"dev": "vitest",
|
|
9
9
|
"test": "vitest --run",
|
|
10
|
-
"build": "
|
|
10
|
+
"build": "vite build",
|
|
11
11
|
"prepublishOnly": "npm run build && npm run test"
|
|
12
12
|
},
|
|
13
13
|
"author": "Alex Warren",
|
|
@@ -20,11 +20,18 @@
|
|
|
20
20
|
"description": "",
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@types/jsdom": "^21.1.7",
|
|
23
|
+
"css.escape": "^1.5.1",
|
|
23
24
|
"global-jsdom": "^25.0.0",
|
|
24
25
|
"jsdom": "^25.0.0",
|
|
25
|
-
"squiffy-compiler": "^6.0.0-alpha.
|
|
26
|
+
"squiffy-compiler": "^6.0.0-alpha.18",
|
|
26
27
|
"typescript": "^5.6.2",
|
|
27
|
-
"
|
|
28
|
+
"vite": "^7.1.3",
|
|
29
|
+
"vite-plugin-dts": "^4.5.4"
|
|
28
30
|
},
|
|
29
|
-
"
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"animejs": "^4.1.3",
|
|
33
|
+
"handlebars": "^4.7.8",
|
|
34
|
+
"marked": "^16.2.0"
|
|
35
|
+
},
|
|
36
|
+
"gitHead": "21a4f7eab5194e97afd2c3a993770d2148c3d023"
|
|
30
37
|
}
|
|
@@ -2,103 +2,137 @@
|
|
|
2
2
|
|
|
3
3
|
exports[`"Hello world" script should run 1`] = `
|
|
4
4
|
"
|
|
5
|
-
<div class="squiffy-output-section"
|
|
5
|
+
<div><div class="squiffy-output-section" data-section="_default" data-undo="{"_section":null,"_seen_sections":null,"_turncount":null}"><div class="squiffy-output-block"><div data-source="[[_default]]"><p>Hello world</p></div></div></div></div>"
|
|
6
6
|
`;
|
|
7
7
|
|
|
8
8
|
exports[`Click a passage link 1`] = `
|
|
9
9
|
"
|
|
10
|
-
<div class="squiffy-output-section"
|
|
10
|
+
<div><div class="squiffy-output-section" data-section="Introduction" data-undo="{"_section":null,"_seen_sections":null,"_turncount":null}"><div class="squiffy-output-block"><div data-source="[[Introduction]]"><h1>Squiffy Test</h1>
|
|
11
11
|
<p>This is <a href="http://textadventures.co.uk">a website link</a>.</p>
|
|
12
12
|
<p>This should be <a class="squiffy-link link-passage" data-passage="passage" role="link" tabindex="0">a link to a passage</a>. Here's <a class="squiffy-link link-passage" data-passage="passage2" role="link" tabindex="0">another one</a>.</p>
|
|
13
13
|
<p>You don't need to specify a name - for example, this <a class="squiffy-link link-passage" data-passage="link" role="link" tabindex="0">link</a> and this <a class="squiffy-link link-section" data-section="section" role="link" tabindex="0">section</a>.</p>
|
|
14
14
|
<p>And this goes to the <a class="squiffy-link link-section" data-section="section2" role="link" tabindex="0">next section</a>.</p>
|
|
15
15
|
<p>This line has links to <a class="squiffy-link link-section" data-section="section 3" role="link" tabindex="0">section 3</a> and <a class="squiffy-link link-section" data-section="section four" role="link" tabindex="0">section 4</a>.</p>
|
|
16
16
|
<p>This line has links to <a class="squiffy-link link-passage" data-passage="passage 3" role="link" tabindex="0">passage 3</a> and <a class="squiffy-link link-passage" data-passage="passage four" role="link" tabindex="0">passage 4</a>.</p>
|
|
17
|
-
<p>Oh look - <a class="squiffy-link link-passage" data-passage="it's a passage with an apostrophe" role="link" tabindex="0">it's a passage with an apostrophe</a>.</p></div></div></div>"
|
|
17
|
+
<p>Oh look - <a class="squiffy-link link-passage" data-passage="it's a passage with an apostrophe" role="link" tabindex="0">it's a passage with an apostrophe</a>.</p></div></div></div></div>"
|
|
18
18
|
`;
|
|
19
19
|
|
|
20
20
|
exports[`Click a passage link 2`] = `
|
|
21
21
|
"
|
|
22
|
-
<div class="squiffy-output-section"
|
|
22
|
+
<div><div class="squiffy-output-section" data-section="Introduction" data-undo="{"_section":null,"_seen_sections":null,"_turncount":null}"><div class="squiffy-output-block"><div data-source="[[Introduction]]"><h1>Squiffy Test</h1>
|
|
23
23
|
<p>This is <a href="http://textadventures.co.uk">a website link</a>.</p>
|
|
24
24
|
<p>This should be <a class="squiffy-link link-passage disabled" data-passage="passage" role="link" tabindex="-1">a link to a passage</a>. Here's <a class="squiffy-link link-passage" data-passage="passage2" role="link" tabindex="0">another one</a>.</p>
|
|
25
25
|
<p>You don't need to specify a name - for example, this <a class="squiffy-link link-passage" data-passage="link" role="link" tabindex="0">link</a> and this <a class="squiffy-link link-section" data-section="section" role="link" tabindex="0">section</a>.</p>
|
|
26
26
|
<p>And this goes to the <a class="squiffy-link link-section" data-section="section2" role="link" tabindex="0">next section</a>.</p>
|
|
27
27
|
<p>This line has links to <a class="squiffy-link link-section" data-section="section 3" role="link" tabindex="0">section 3</a> and <a class="squiffy-link link-section" data-section="section four" role="link" tabindex="0">section 4</a>.</p>
|
|
28
28
|
<p>This line has links to <a class="squiffy-link link-passage" data-passage="passage 3" role="link" tabindex="0">passage 3</a> and <a class="squiffy-link link-passage" data-passage="passage four" role="link" tabindex="0">passage 4</a>.</p>
|
|
29
|
-
<p>Oh look - <a class="squiffy-link link-passage" data-passage="it's a passage with an apostrophe" role="link" tabindex="0">it's a passage with an apostrophe</a>.</p></div></div><div class="squiffy-output-block"><div data-source="[[Introduction]][passage]"><p>Here's some text for the passage.</p></div></div></div>"
|
|
29
|
+
<p>Oh look - <a class="squiffy-link link-passage" data-passage="it's a passage with an apostrophe" role="link" tabindex="0">it's a passage with an apostrophe</a>.</p></div></div><div class="squiffy-output-passage" data-passage="passage" data-undo="{"_turncount":0,"_seen_sections":["Introduction"]}"><div class="squiffy-output-block"><div data-source="[[Introduction]][passage]"><p>Here's some text for the passage.</p></div></div></div></div></div>"
|
|
30
|
+
`;
|
|
31
|
+
|
|
32
|
+
exports[`Click a passage link and go back 1`] = `
|
|
33
|
+
"
|
|
34
|
+
<div><div class="squiffy-output-section" data-section="Introduction" data-undo="{"_section":null,"_seen_sections":null,"_turncount":null}"><div class="squiffy-output-block"><div data-source="[[Introduction]]"><h1>Squiffy Test</h1>
|
|
35
|
+
<p>This is <a href="http://textadventures.co.uk">a website link</a>.</p>
|
|
36
|
+
<p>This should be <a class="squiffy-link link-passage" data-passage="passage" role="link">a link to a passage</a>. Here's <a class="squiffy-link link-passage" data-passage="passage2" role="link" tabindex="0">another one</a>.</p>
|
|
37
|
+
<p>You don't need to specify a name - for example, this <a class="squiffy-link link-passage" data-passage="link" role="link" tabindex="0">link</a> and this <a class="squiffy-link link-section" data-section="section" role="link" tabindex="0">section</a>.</p>
|
|
38
|
+
<p>And this goes to the <a class="squiffy-link link-section" data-section="section2" role="link" tabindex="0">next section</a>.</p>
|
|
39
|
+
<p>This line has links to <a class="squiffy-link link-section" data-section="section 3" role="link" tabindex="0">section 3</a> and <a class="squiffy-link link-section" data-section="section four" role="link" tabindex="0">section 4</a>.</p>
|
|
40
|
+
<p>This line has links to <a class="squiffy-link link-passage" data-passage="passage 3" role="link" tabindex="0">passage 3</a> and <a class="squiffy-link link-passage" data-passage="passage four" role="link" tabindex="0">passage 4</a>.</p>
|
|
41
|
+
<p>Oh look - <a class="squiffy-link link-passage" data-passage="it's a passage with an apostrophe" role="link" tabindex="0">it's a passage with an apostrophe</a>.</p></div></div></div></div>"
|
|
30
42
|
`;
|
|
31
43
|
|
|
32
44
|
exports[`Click a section link 1`] = `
|
|
33
45
|
"
|
|
34
|
-
<div class="squiffy-output-section"
|
|
46
|
+
<div><div class="squiffy-output-section" data-section="Introduction" data-undo="{"_section":null,"_seen_sections":null,"_turncount":null}"><div class="squiffy-output-block"><div data-source="[[Introduction]]"><h1>Squiffy Test</h1>
|
|
35
47
|
<p>This is <a href="http://textadventures.co.uk">a website link</a>.</p>
|
|
36
48
|
<p>This should be <a class="squiffy-link link-passage" data-passage="passage" role="link" tabindex="0">a link to a passage</a>. Here's <a class="squiffy-link link-passage" data-passage="passage2" role="link" tabindex="0">another one</a>.</p>
|
|
37
49
|
<p>You don't need to specify a name - for example, this <a class="squiffy-link link-passage" data-passage="link" role="link" tabindex="0">link</a> and this <a class="squiffy-link link-section" data-section="section" role="link" tabindex="0">section</a>.</p>
|
|
38
50
|
<p>And this goes to the <a class="squiffy-link link-section" data-section="section2" role="link" tabindex="0">next section</a>.</p>
|
|
39
51
|
<p>This line has links to <a class="squiffy-link link-section" data-section="section 3" role="link" tabindex="0">section 3</a> and <a class="squiffy-link link-section" data-section="section four" role="link" tabindex="0">section 4</a>.</p>
|
|
40
52
|
<p>This line has links to <a class="squiffy-link link-passage" data-passage="passage 3" role="link" tabindex="0">passage 3</a> and <a class="squiffy-link link-passage" data-passage="passage four" role="link" tabindex="0">passage 4</a>.</p>
|
|
41
|
-
<p>Oh look - <a class="squiffy-link link-passage" data-passage="it's a passage with an apostrophe" role="link" tabindex="0">it's a passage with an apostrophe</a>.</p></div></div></div>"
|
|
53
|
+
<p>Oh look - <a class="squiffy-link link-passage" data-passage="it's a passage with an apostrophe" role="link" tabindex="0">it's a passage with an apostrophe</a>.</p></div></div></div></div>"
|
|
42
54
|
`;
|
|
43
55
|
|
|
44
56
|
exports[`Click a section link 2`] = `
|
|
45
57
|
"
|
|
46
|
-
<div class="squiffy-output-section"
|
|
58
|
+
<div><div class="squiffy-output-section" data-section="Introduction" data-undo="{"_section":null,"_seen_sections":null,"_turncount":null}"><div class="squiffy-output-block"><div data-source="[[Introduction]]"><h1>Squiffy Test</h1>
|
|
47
59
|
<p>This is <a href="http://textadventures.co.uk">a website link</a>.</p>
|
|
48
60
|
<p>This should be <a class="squiffy-link link-passage" data-passage="passage" role="link" tabindex="0">a link to a passage</a>. Here's <a class="squiffy-link link-passage" data-passage="passage2" role="link" tabindex="0">another one</a>.</p>
|
|
49
61
|
<p>You don't need to specify a name - for example, this <a class="squiffy-link link-passage" data-passage="link" role="link" tabindex="0">link</a> and this <a class="squiffy-link link-section" data-section="section" role="link" tabindex="0">section</a>.</p>
|
|
50
62
|
<p>And this goes to the <a class="squiffy-link link-section" data-section="section2" role="link" tabindex="0">next section</a>.</p>
|
|
51
63
|
<p>This line has links to <a class="squiffy-link link-section" data-section="section 3" role="link" tabindex="0">section 3</a> and <a class="squiffy-link link-section" data-section="section four" role="link" tabindex="0">section 4</a>.</p>
|
|
52
64
|
<p>This line has links to <a class="squiffy-link link-passage" data-passage="passage 3" role="link" tabindex="0">passage 3</a> and <a class="squiffy-link link-passage" data-passage="passage four" role="link" tabindex="0">passage 4</a>.</p>
|
|
53
|
-
<p>Oh look - <a class="squiffy-link link-passage" data-passage="it's a passage with an apostrophe" role="link" tabindex="0">it's a passage with an apostrophe</a>.</p></div></div></div><div class="squiffy-output-section"
|
|
65
|
+
<p>Oh look - <a class="squiffy-link link-passage" data-passage="it's a passage with an apostrophe" role="link" tabindex="0">it's a passage with an apostrophe</a>.</p></div></div></div><div class="squiffy-output-section" data-section="section 3" data-undo="{"_section":"Introduction","_seen_sections":["Introduction"],"_turncount":0}"><div class="squiffy-output-block"><div data-source="[[section 3]]"><p>Another section is here, with passages <a class="squiffy-link link-passage" data-passage="a" role="link" tabindex="0">a</a> and <a class="squiffy-link link-passage" data-passage="b" role="link" tabindex="0">b</a>.</p></div></div></div></div>"
|
|
66
|
+
`;
|
|
67
|
+
|
|
68
|
+
exports[`Click a section link and go back 1`] = `
|
|
69
|
+
"
|
|
70
|
+
<div><div class="squiffy-output-section" data-section="Introduction" data-undo="{"_section":null,"_seen_sections":null,"_turncount":null}"><div class="squiffy-output-block"><div data-source="[[Introduction]]"><h1>Squiffy Test</h1>
|
|
71
|
+
<p>This is <a href="http://textadventures.co.uk">a website link</a>.</p>
|
|
72
|
+
<p>This should be <a class="squiffy-link link-passage" data-passage="passage" role="link" tabindex="0">a link to a passage</a>. Here's <a class="squiffy-link link-passage" data-passage="passage2" role="link" tabindex="0">another one</a>.</p>
|
|
73
|
+
<p>You don't need to specify a name - for example, this <a class="squiffy-link link-passage" data-passage="link" role="link" tabindex="0">link</a> and this <a class="squiffy-link link-section" data-section="section" role="link" tabindex="0">section</a>.</p>
|
|
74
|
+
<p>And this goes to the <a class="squiffy-link link-section" data-section="section2" role="link" tabindex="0">next section</a>.</p>
|
|
75
|
+
<p>This line has links to <a class="squiffy-link link-section" data-section="section 3" role="link" tabindex="0">section 3</a> and <a class="squiffy-link link-section" data-section="section four" role="link" tabindex="0">section 4</a>.</p>
|
|
76
|
+
<p>This line has links to <a class="squiffy-link link-passage" data-passage="passage 3" role="link" tabindex="0">passage 3</a> and <a class="squiffy-link link-passage" data-passage="passage four" role="link" tabindex="0">passage 4</a>.</p>
|
|
77
|
+
<p>Oh look - <a class="squiffy-link link-passage" data-passage="it's a passage with an apostrophe" role="link" tabindex="0">it's a passage with an apostrophe</a>.</p></div></div></div></div>"
|
|
54
78
|
`;
|
|
55
79
|
|
|
56
80
|
exports[`Delete passage 1`] = `
|
|
57
81
|
"
|
|
58
|
-
<div class="squiffy-output-section"
|
|
82
|
+
<div><div class="squiffy-output-section" data-section="_default" data-undo="{"_section":null,"_seen_sections":null,"_turncount":null}"><div class="squiffy-output-block"><div data-source="[[_default]]"><p>Click this: <a class="squiffy-link link-passage disabled" data-passage="a" role="link" tabindex="-1">a</a></p></div></div><div class="squiffy-output-passage" data-passage="a" data-undo="{"_turncount":0,"_seen_sections":["_default"]}"><div class="squiffy-output-block"><div data-source="[[_default]][a]"><p>New passage</p></div></div></div></div></div>"
|
|
59
83
|
`;
|
|
60
84
|
|
|
61
85
|
exports[`Delete passage 2`] = `
|
|
62
86
|
"
|
|
63
|
-
<div class="squiffy-output-section"
|
|
87
|
+
<div><div class="squiffy-output-section" data-section="_default" data-undo="{"_section":null,"_seen_sections":null,"_turncount":null}"><div class="squiffy-output-block"><div data-source="[[_default]]"><p>Click this: <a class="squiffy-link link-passage disabled" data-passage="a" role="link" tabindex="-1">a</a></p></div></div></div></div>"
|
|
64
88
|
`;
|
|
65
89
|
|
|
66
90
|
exports[`Delete section 1`] = `
|
|
67
91
|
"
|
|
68
|
-
<div class="squiffy-output-section"
|
|
92
|
+
<div><div class="squiffy-output-section" data-section="_default" data-undo="{"_section":null,"_seen_sections":null,"_turncount":null}"><div class="squiffy-output-block"><div data-source="[[_default]]"><p>Click this: <a class="squiffy-link link-section" data-section="a" role="link" tabindex="0">a</a></p></div></div></div><div class="squiffy-output-section" data-section="a" data-undo="{"_section":"_default","_seen_sections":["_default"],"_turncount":0}"><div class="squiffy-output-block"><div data-source="[[a]]"><p>New section</p></div></div></div></div>"
|
|
69
93
|
`;
|
|
70
94
|
|
|
71
95
|
exports[`Delete section 2`] = `
|
|
72
96
|
"
|
|
73
|
-
<div class="squiffy-output-section"
|
|
97
|
+
<div><div class="squiffy-output-section" data-section="_default" data-undo="{"_section":null,"_seen_sections":null,"_turncount":null}"><div class="squiffy-output-block"><div data-source="[[_default]]"><p>Click this: <a class="squiffy-link link-section" data-section="a" role="link" tabindex="0">a</a></p></div></div></div></div>"
|
|
98
|
+
`;
|
|
99
|
+
|
|
100
|
+
exports[`Going back handling @clear and attribute changes 1`] = `
|
|
101
|
+
"
|
|
102
|
+
<div><div class="squiffy-clear-stack" style="display: none;"><div><div class="squiffy-output-section" data-section="_default" data-undo="{"_section":null,"_seen_sections":null,"_turncount":null}"><div class="squiffy-output-block"><div data-source="[[_default]]"><p>Choose: <a class="squiffy-link link-passage" data-passage="a" role="link">a</a>, <a class="squiffy-link link-passage disabled" data-passage="b" role="link" tabindex="-1">b</a></p></div></div><div class="squiffy-output-passage" data-passage="b" data-undo="{"_turncount":0,"_seen_sections":["_default"],"test":null}"><div class="squiffy-output-block"><div data-source="[[_default]][b]"><p>You chose b. Now <a class="squiffy-link link-section" data-section="continue" role="link" tabindex="0">continue</a></p></div></div></div></div><div class="squiffy-output-section" data-section="continue" data-undo="{"_section":"_default","_seen_sections":["_default","b"],"test":456,"_turncount":1}"><div class="squiffy-output-block"><div data-source="[[continue]]"><p>Now choose: <a class="squiffy-link link-passage disabled" data-passage="c" role="link" tabindex="-1">c</a>, <a class="squiffy-link link-passage" data-passage="d" role="link" tabindex="0">d</a></p></div></div></div></div></div><div class="squiffy-output-section" data-section="continue" data-clear="true"><div class="squiffy-output-passage" data-passage="c" data-undo="{"_turncount":0,"_seen_sections":["_default","b","continue"],"test":789}"><div class="squiffy-output-block"><div data-source="[[continue]][c]"><p>You chose c. Now <a class="squiffy-link link-section" data-section="finish" role="link" tabindex="0">finish</a></p></div></div></div></div></div>"
|
|
103
|
+
`;
|
|
104
|
+
|
|
105
|
+
exports[`Going back handling @clear and attribute changes 2`] = `
|
|
106
|
+
"
|
|
107
|
+
<div><div class="squiffy-clear-stack" style="display: none;"></div><div class="squiffy-output-section" data-section="_default" data-undo="{"_section":null,"_seen_sections":null,"_turncount":null}"><div class="squiffy-output-block"><div data-source="[[_default]]"><p>Choose: <a class="squiffy-link link-passage" data-passage="a" role="link">a</a>, <a class="squiffy-link link-passage disabled" data-passage="b" role="link" tabindex="-1">b</a></p></div></div><div class="squiffy-output-passage" data-passage="b" data-undo="{"_turncount":0,"_seen_sections":["_default"],"test":null}"><div class="squiffy-output-block"><div data-source="[[_default]][b]"><p>You chose b. Now <a class="squiffy-link link-section" data-section="continue" role="link" tabindex="0">continue</a></p></div></div></div></div><div class="squiffy-output-section" data-section="continue" data-undo="{"_section":"_default","_seen_sections":["_default","b"],"test":456,"_turncount":1}"><div class="squiffy-output-block"><div data-source="[[continue]]"><p>Now choose: <a class="squiffy-link link-passage" data-passage="c" role="link">c</a>, <a class="squiffy-link link-passage" data-passage="d" role="link" tabindex="0">d</a></p></div></div></div></div>"
|
|
74
108
|
`;
|
|
75
109
|
|
|
76
110
|
exports[`Update default section output 1`] = `
|
|
77
111
|
"
|
|
78
|
-
<div class="squiffy-output-section"
|
|
112
|
+
<div><div class="squiffy-output-section" data-section="_default" data-undo="{"_section":null,"_seen_sections":null,"_turncount":null}"><div class="squiffy-output-block"><div data-source="[[_default]]"><p>Hello world</p></div></div></div></div>"
|
|
79
113
|
`;
|
|
80
114
|
|
|
81
115
|
exports[`Update default section output 2`] = `
|
|
82
116
|
"
|
|
83
|
-
<div class="squiffy-output-section"
|
|
117
|
+
<div><div class="squiffy-output-section" data-section="_default" data-undo="{"_section":null,"_seen_sections":null,"_turncount":null}"><div class="squiffy-output-block"><div data-source="[[_default]]"><p>Updated content</p></div></div></div></div>"
|
|
84
118
|
`;
|
|
85
119
|
|
|
86
120
|
exports[`Update passage output - passage name "a" 1`] = `
|
|
87
121
|
"
|
|
88
|
-
<div class="squiffy-output-section"
|
|
122
|
+
<div><div class="squiffy-output-section" data-section="_default" data-undo="{"_section":null,"_seen_sections":null,"_turncount":null}"><div class="squiffy-output-block"><div data-source="[[_default]]"><p>Click this: <a class="squiffy-link link-passage disabled" data-passage="a" role="link" tabindex="-1">a</a></p></div></div><div class="squiffy-output-passage" data-passage="a" data-undo="{"_turncount":0,"_seen_sections":["_default"]}"><div class="squiffy-output-block"><div data-source="[[_default]][a]"><p>Passage a content</p></div></div></div></div></div>"
|
|
89
123
|
`;
|
|
90
124
|
|
|
91
125
|
exports[`Update passage output - passage name "a" 2`] = `
|
|
92
126
|
"
|
|
93
|
-
<div class="squiffy-output-section"
|
|
127
|
+
<div><div class="squiffy-output-section" data-section="_default" data-undo="{"_section":null,"_seen_sections":null,"_turncount":null}"><div class="squiffy-output-block"><div data-source="[[_default]]"><p>Click this: <a class="squiffy-link link-passage disabled" data-passage="a" role="link" tabindex="-1">a</a></p></div></div><div class="squiffy-output-passage" data-passage="a" data-undo="{"_turncount":0,"_seen_sections":["_default"]}"><div class="squiffy-output-block"><div data-source="[[_default]][a]"><p>Updated passage content</p></div></div></div></div></div>"
|
|
94
128
|
`;
|
|
95
129
|
|
|
96
130
|
exports[`Update passage output - passage name "a'1" 1`] = `
|
|
97
131
|
"
|
|
98
|
-
<div class="squiffy-output-section"
|
|
132
|
+
<div><div class="squiffy-output-section" data-section="_default" data-undo="{"_section":null,"_seen_sections":null,"_turncount":null}"><div class="squiffy-output-block"><div data-source="[[_default]]"><p>Click this: <a class="squiffy-link link-passage disabled" data-passage="a'1" role="link" tabindex="-1">a'1</a></p></div></div><div class="squiffy-output-passage" data-passage="a'1" data-undo="{"_turncount":0,"_seen_sections":["_default"]}"><div class="squiffy-output-block"><div data-source="[[_default]][a'1]"><p>Passage a content</p></div></div></div></div></div>"
|
|
99
133
|
`;
|
|
100
134
|
|
|
101
135
|
exports[`Update passage output - passage name "a'1" 2`] = `
|
|
102
136
|
"
|
|
103
|
-
<div class="squiffy-output-section"
|
|
137
|
+
<div><div class="squiffy-output-section" data-section="_default" data-undo="{"_section":null,"_seen_sections":null,"_turncount":null}"><div class="squiffy-output-block"><div data-source="[[_default]]"><p>Click this: <a class="squiffy-link link-passage disabled" data-passage="a'1" role="link" tabindex="-1">a'1</a></p></div></div><div class="squiffy-output-passage" data-passage="a'1" data-undo="{"_turncount":0,"_seen_sections":["_default"]}"><div class="squiffy-output-block"><div data-source="[[_default]][a'1]"><p>Updated passage content</p></div></div></div></div></div>"
|
|
104
138
|
`;
|
package/src/animation.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { animate, stagger, text } from "animejs";
|
|
2
|
+
|
|
3
|
+
export class Animation {
|
|
4
|
+
animations: {[name: string]: (el: HTMLElement, params: Record<string, any>, onComplete: () => void, loop: boolean) => void} = {};
|
|
5
|
+
linkAnimations = new Map<HTMLElement, () => void>();
|
|
6
|
+
|
|
7
|
+
registerAnimation(name: string, handler: (el: HTMLElement, params: Record<string, any>, onComplete: () => void, loop: boolean) => void) {
|
|
8
|
+
this.animations[name] = handler;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
runAnimation(name: string, el: HTMLElement, params: Record<string, any>, onComplete: () => void, loop: boolean) {
|
|
12
|
+
const animation = this.animations[name];
|
|
13
|
+
if (animation) {
|
|
14
|
+
animation(el, params, onComplete, loop);
|
|
15
|
+
} else {
|
|
16
|
+
console.warn(`No animation registered with name: ${name}`);
|
|
17
|
+
onComplete();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
addLinkAnimation(link: HTMLElement, fn: () => void) {
|
|
22
|
+
this.linkAnimations.set(link, fn);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
runLinkAnimation(link: HTMLElement) {
|
|
26
|
+
const fn = this.linkAnimations.get(link);
|
|
27
|
+
if (fn) {
|
|
28
|
+
fn();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
constructor() {
|
|
33
|
+
this.registerAnimation("typewriter", function(el, params, onComplete, loop) {
|
|
34
|
+
const { chars } = text.split(el, { words: false, chars: true });
|
|
35
|
+
|
|
36
|
+
const fadeDuration = params.fadeDuration || 100; // ms fade-in per character
|
|
37
|
+
const interval = params.interval || 100; // ms between each character appearing
|
|
38
|
+
|
|
39
|
+
animate(chars, {
|
|
40
|
+
opacity: [0, 1],
|
|
41
|
+
easing: "linear",
|
|
42
|
+
duration: fadeDuration,
|
|
43
|
+
delay: stagger(interval, { start: params.start || 0 }),
|
|
44
|
+
loop: loop,
|
|
45
|
+
onComplete: onComplete
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
this.registerAnimation("toast", function(el, params, onComplete, loop) {
|
|
50
|
+
const { words } = text.split(el, { words: true, chars: false });
|
|
51
|
+
|
|
52
|
+
const fadeDuration = params.fadeDuration || 100; // ms fade-in per word
|
|
53
|
+
const interval = params.interval || 200; // ms between each word appearing
|
|
54
|
+
|
|
55
|
+
animate(words, {
|
|
56
|
+
opacity: [0, 1],
|
|
57
|
+
y: [
|
|
58
|
+
{ to: ["100%", "0%"] },
|
|
59
|
+
],
|
|
60
|
+
easing: "linear",
|
|
61
|
+
duration: fadeDuration,
|
|
62
|
+
delay: stagger(interval, { start: params.start || 0 }),
|
|
63
|
+
loop: loop,
|
|
64
|
+
onComplete: onComplete
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
package/src/events.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unsafe-function-type */
|
|
2
2
|
|
|
3
3
|
export type SquiffyEventMap = {
|
|
4
|
-
linkClick: { linkType:
|
|
4
|
+
linkClick: { linkType: string }; // a story link was clicked
|
|
5
|
+
set: { attribute: string, value: any }; // an attribute was set
|
|
6
|
+
canGoBackChanged: { canGoBack: boolean }; // whether the "back" button should be enabled
|
|
5
7
|
};
|
|
6
8
|
|
|
7
9
|
export type SquiffyEventHandler<E extends keyof SquiffyEventMap> =
|
|
@@ -29,14 +31,11 @@ export class Emitter<Events extends Record<string, any>> {
|
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
emit<E extends keyof Events>(event: E, payload: Events[E]) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
console.error(`[Squiffy] handler for "${String(event)}" failed`, err);
|
|
38
|
-
}
|
|
39
|
-
});
|
|
34
|
+
this.listeners.get(event)?.forEach(h => {
|
|
35
|
+
try { (h as any)(payload); } catch (err) {
|
|
36
|
+
// Swallow so a bad handler doesn't break the game; optionally log.
|
|
37
|
+
console.error(`[Squiffy] handler for "${String(event)}" failed`, err);
|
|
38
|
+
}
|
|
40
39
|
});
|
|
41
40
|
}
|
|
42
41
|
}
|
package/src/import.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { HandleLinkResult } from "./types.plugins.js";
|
|
2
|
+
|
|
3
|
+
export class LinkHandler {
|
|
4
|
+
linkHandlers: {[type: string]: (el: HTMLElement) => HandleLinkResult} = {};
|
|
5
|
+
|
|
6
|
+
registerLinkHandler(type: string, handler: (el: HTMLElement) => HandleLinkResult) {
|
|
7
|
+
this.linkHandlers[type] = handler;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
handleLink(link: HTMLElement): [found: boolean, type: string, result: HandleLinkResult] {
|
|
11
|
+
const type = link.getAttribute("data-handler") || "";
|
|
12
|
+
const handler = this.linkHandlers[type];
|
|
13
|
+
if (handler) {
|
|
14
|
+
return [true, type, handler(link)];
|
|
15
|
+
}
|
|
16
|
+
return [false, type, null];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { TextProcessor } from "./textProcessor.js";
|
|
2
|
+
import { SquiffyPlugin } from "./types.plugins.js";
|
|
3
|
+
import { State } from "./state.js";
|
|
4
|
+
import { LinkHandler } from "./linkHandler.js";
|
|
5
|
+
import { Emitter, SquiffyEventMap } from "./events.js";
|
|
6
|
+
import { Animation } from "./animation.js";
|
|
7
|
+
|
|
8
|
+
export class PluginManager {
|
|
9
|
+
outputElement: HTMLElement;
|
|
10
|
+
textProcessor: TextProcessor;
|
|
11
|
+
state: State;
|
|
12
|
+
linkHandler: LinkHandler;
|
|
13
|
+
getSectionText: (name: string) => string | null;
|
|
14
|
+
getPassageText: (name: string) => string | null;
|
|
15
|
+
processText: (text: string, inline: boolean) => string;
|
|
16
|
+
addTransition: (fn: () => Promise<void>) => void;
|
|
17
|
+
emitter: Emitter<SquiffyEventMap>;
|
|
18
|
+
plugins: SquiffyPlugin[] = [];
|
|
19
|
+
animation: Animation;
|
|
20
|
+
|
|
21
|
+
constructor(outputElement: HTMLElement,
|
|
22
|
+
textProcessor: TextProcessor,
|
|
23
|
+
state: State,
|
|
24
|
+
linkHandler: LinkHandler,
|
|
25
|
+
getSectionText: (name: string) => string | null,
|
|
26
|
+
getPassageText: (name: string) => string | null,
|
|
27
|
+
processText: (text: string, inline: boolean) => string,
|
|
28
|
+
addTransition: (fn: () => Promise<void>) => void,
|
|
29
|
+
animation: Animation,
|
|
30
|
+
emitter: Emitter<SquiffyEventMap>) {
|
|
31
|
+
this.outputElement = outputElement;
|
|
32
|
+
this.textProcessor = textProcessor;
|
|
33
|
+
this.state = state;
|
|
34
|
+
this.linkHandler = linkHandler;
|
|
35
|
+
this.getSectionText = getSectionText;
|
|
36
|
+
this.getPassageText = getPassageText;
|
|
37
|
+
this.processText = processText;
|
|
38
|
+
this.addTransition = addTransition;
|
|
39
|
+
this.animation = animation;
|
|
40
|
+
this.emitter = emitter;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
add(plugin: SquiffyPlugin) {
|
|
44
|
+
const instance = plugin.init({
|
|
45
|
+
outputElement: this.outputElement,
|
|
46
|
+
registerHelper: (name, helper) => {
|
|
47
|
+
this.textProcessor.handlebars.registerHelper(name, helper);
|
|
48
|
+
},
|
|
49
|
+
registerLinkHandler: (type, handler) => {
|
|
50
|
+
this.linkHandler.registerLinkHandler(type, handler);
|
|
51
|
+
},
|
|
52
|
+
get: (attribute) => this.state.get(attribute),
|
|
53
|
+
set: (attribute, value) => this.state.set(attribute, value),
|
|
54
|
+
getSectionText: this.getSectionText,
|
|
55
|
+
getPassageText: this.getPassageText,
|
|
56
|
+
processText: this.processText,
|
|
57
|
+
addTransition: this.addTransition,
|
|
58
|
+
animation: this.animation,
|
|
59
|
+
on: (e, h) => this.emitter.on(e, h),
|
|
60
|
+
off: (e, h) => this.emitter.off(e, h),
|
|
61
|
+
once: (e, h) => this.emitter.once(e, h),
|
|
62
|
+
});
|
|
63
|
+
this.plugins.push(plugin);
|
|
64
|
+
return instance;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
onWrite(el: HTMLElement) {
|
|
68
|
+
this.plugins.forEach(p => p.onWrite?.(el));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
onLoad() {
|
|
72
|
+
this.plugins.forEach(p => p.onLoad?.());
|
|
73
|
+
}
|
|
74
|
+
}
|