squiffy-runtime 6.0.0-alpha.9 → 6.0.0-beta.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/LICENSE +22 -0
- package/dist/animation.d.ts +21 -0
- package/dist/events.d.ts +20 -0
- package/dist/import.d.ts +4 -0
- package/dist/inputValidation.d.ts +20 -0
- package/dist/linkHandler.d.ts +8 -0
- package/dist/pluginManager.d.ts +23 -0
- package/dist/squiffy.runtime.css +143 -0
- package/dist/squiffy.runtime.d.ts +3 -51
- package/dist/squiffy.runtime.global.js +132 -0
- package/dist/squiffy.runtime.global.js.map +1 -0
- package/dist/squiffy.runtime.js +8941 -628
- package/dist/squiffy.runtime.js.map +1 -0
- package/dist/squiffy.runtime.test.d.ts +1 -0
- package/dist/state.d.ts +19 -0
- package/dist/textProcessor.d.ts +11 -0
- package/dist/types.d.ts +58 -0
- package/dist/types.plugins.d.ts +27 -0
- package/dist/updater.d.ts +2 -0
- package/dist/utils.d.ts +1 -0
- package/package.json +27 -5
- package/src/__snapshots__/squiffy.runtime.test.ts.snap +63 -19
- package/src/animation.ts +142 -0
- package/src/events.ts +41 -0
- package/src/import.ts +5 -0
- package/src/inputValidation.ts +83 -0
- package/src/linkHandler.ts +18 -0
- package/src/pluginManager.ts +74 -0
- package/src/plugins/animate.ts +115 -0
- package/src/plugins/index.ts +13 -0
- package/src/plugins/live.ts +82 -0
- package/src/plugins/plugins.test.ts +309 -0
- package/src/plugins/random.ts +22 -0
- package/src/plugins/replaceLabel.ts +27 -0
- package/src/plugins/rotateSequence.ts +72 -0
- package/src/squiffy.runtime.css +143 -0
- package/src/squiffy.runtime.test.ts +2029 -106
- package/src/squiffy.runtime.ts +537 -576
- package/src/state.ts +124 -0
- package/src/textProcessor.ts +205 -0
- package/src/types.plugins.ts +41 -0
- package/src/types.ts +82 -0
- package/src/updater.ts +107 -0
- package/src/utils.ts +17 -0
- package/tsconfig.json +4 -1
- package/vite.config.ts +52 -0
- package/vitest.config.ts +9 -0
- package/vitest.setup.ts +16 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2014-2026 Alex Warren, textadventures.co.uk and contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type AnimationHandler = (el: HTMLElement, params: Record<string, any>, onComplete: () => void, loop: boolean) => void;
|
|
2
|
+
export interface AnimationOptions {
|
|
3
|
+
initiallyHidden?: boolean;
|
|
4
|
+
}
|
|
5
|
+
interface RegisteredAnimation {
|
|
6
|
+
handler: AnimationHandler;
|
|
7
|
+
options: AnimationOptions;
|
|
8
|
+
}
|
|
9
|
+
export declare class Animation {
|
|
10
|
+
animations: {
|
|
11
|
+
[name: string]: RegisteredAnimation;
|
|
12
|
+
};
|
|
13
|
+
linkAnimations: Map<HTMLElement, () => void>;
|
|
14
|
+
registerAnimation(name: string, handler: AnimationHandler, options?: AnimationOptions): void;
|
|
15
|
+
isInitiallyHidden(name: string): boolean;
|
|
16
|
+
runAnimation(name: string, el: HTMLElement, params: Record<string, any>, onComplete: () => void, loop: boolean): void;
|
|
17
|
+
addLinkAnimation(link: HTMLElement, fn: () => void): void;
|
|
18
|
+
runLinkAnimation(link: HTMLElement): void;
|
|
19
|
+
constructor();
|
|
20
|
+
}
|
|
21
|
+
export {};
|
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type SquiffyEventMap = {
|
|
2
|
+
linkClick: {
|
|
3
|
+
linkType: string;
|
|
4
|
+
};
|
|
5
|
+
set: {
|
|
6
|
+
attribute: string;
|
|
7
|
+
value: any;
|
|
8
|
+
};
|
|
9
|
+
canGoBackChanged: {
|
|
10
|
+
canGoBack: boolean;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
export type SquiffyEventHandler<E extends keyof SquiffyEventMap> = (payload: SquiffyEventMap[E]) => void;
|
|
14
|
+
export declare class Emitter<Events extends Record<string, any>> {
|
|
15
|
+
private listeners;
|
|
16
|
+
on<E extends keyof Events>(event: E, handler: (p: Events[E]) => void): () => void;
|
|
17
|
+
off<E extends keyof Events>(event: E, handler: (p: Events[E]) => void): void;
|
|
18
|
+
once<E extends keyof Events>(event: E, handler: (p: Events[E]) => void): () => void;
|
|
19
|
+
emit<E extends keyof Events>(event: E, payload: Events[E]): void;
|
|
20
|
+
}
|
package/dist/import.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input validation for Squiffy stories.
|
|
3
|
+
*
|
|
4
|
+
* Handles HTML5 form validation constraints (required, minlength, pattern, etc.)
|
|
5
|
+
* and disables section/passage links until all inputs in the current section are valid.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Check if all inputs in the given element are valid according to HTML5 validation constraints.
|
|
9
|
+
*/
|
|
10
|
+
export declare function areInputsValid(sectionElement: HTMLElement | null): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Update the enabled/disabled state of all section and passage links
|
|
13
|
+
* based on whether all inputs are currently valid.
|
|
14
|
+
*/
|
|
15
|
+
export declare function updateLinkStates(sectionElement: HTMLElement | null): void;
|
|
16
|
+
/**
|
|
17
|
+
* Set up input validation listeners on all inputs in the given section element.
|
|
18
|
+
* This should be called whenever new content is added to the section.
|
|
19
|
+
*/
|
|
20
|
+
export declare function setupInputValidation(sectionElement: HTMLElement | null): void;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { HandleLinkResult } from './types.plugins.js';
|
|
2
|
+
export declare class LinkHandler {
|
|
3
|
+
linkHandlers: {
|
|
4
|
+
[type: string]: (el: HTMLElement) => HandleLinkResult;
|
|
5
|
+
};
|
|
6
|
+
registerLinkHandler(type: string, handler: (el: HTMLElement) => HandleLinkResult): void;
|
|
7
|
+
handleLink(link: HTMLElement): [found: boolean, type: string, result: HandleLinkResult];
|
|
8
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
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
|
+
export declare class PluginManager {
|
|
8
|
+
outputElement: HTMLElement;
|
|
9
|
+
textProcessor: TextProcessor;
|
|
10
|
+
state: State;
|
|
11
|
+
linkHandler: LinkHandler;
|
|
12
|
+
getSectionText: (name: string) => string | null;
|
|
13
|
+
getPassageText: (name: string) => string | null;
|
|
14
|
+
processText: (text: string, inline: boolean) => string;
|
|
15
|
+
addTransition: (fn: () => Promise<void>) => void;
|
|
16
|
+
emitter: Emitter<SquiffyEventMap>;
|
|
17
|
+
plugins: SquiffyPlugin[];
|
|
18
|
+
animation: Animation;
|
|
19
|
+
constructor(outputElement: HTMLElement, textProcessor: TextProcessor, state: State, linkHandler: LinkHandler, getSectionText: (name: string) => string | null, getPassageText: (name: string) => string | null, processText: (text: string, inline: boolean) => string, addTransition: (fn: () => Promise<void>) => void, animation: Animation, emitter: Emitter<SquiffyEventMap>);
|
|
20
|
+
add(plugin: SquiffyPlugin): void | Promise<void>;
|
|
21
|
+
onWrite(el: HTMLElement): void;
|
|
22
|
+
onLoad(): void;
|
|
23
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/* Core Squiffy Runtime Styles */
|
|
2
|
+
|
|
3
|
+
/* Link styles */
|
|
4
|
+
a.squiffy-link
|
|
5
|
+
{
|
|
6
|
+
text-decoration: underline;
|
|
7
|
+
text-decoration-color: Blue;
|
|
8
|
+
color: Blue;
|
|
9
|
+
cursor: pointer;
|
|
10
|
+
transition: color 0.4s ease-in-out, text-decoration-color 0.4s ease-in-out;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/* Rotate/sequence links - visually distinct as they update in-place rather than advancing the story */
|
|
14
|
+
a.squiffy-link[data-handler="rotate"],
|
|
15
|
+
a.squiffy-link[data-handler="sequence"] {
|
|
16
|
+
text-decoration-style: dotted;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* Disabled link states */
|
|
20
|
+
a.squiffy-link.disabled,
|
|
21
|
+
div.squiffy-output-section:not(:last-child) a.squiffy-link,
|
|
22
|
+
div.squiffy-output-section.links-disabled a.squiffy-link
|
|
23
|
+
{
|
|
24
|
+
text-decoration: inherit;
|
|
25
|
+
text-decoration-color: transparent;
|
|
26
|
+
color: inherit !important;
|
|
27
|
+
cursor: inherit;
|
|
28
|
+
pointer-events: none;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* Links disabled due to input validation */
|
|
32
|
+
a.squiffy-link.validation-disabled
|
|
33
|
+
{
|
|
34
|
+
text-decoration: inherit;
|
|
35
|
+
text-decoration-color: transparent;
|
|
36
|
+
color: #999 !important;
|
|
37
|
+
cursor: not-allowed;
|
|
38
|
+
pointer-events: none;
|
|
39
|
+
opacity: 0.6;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Invalid input field states */
|
|
43
|
+
input.squiffy-invalid,
|
|
44
|
+
textarea.squiffy-invalid,
|
|
45
|
+
select.squiffy-invalid
|
|
46
|
+
{
|
|
47
|
+
border-color: #dc3545 !important;
|
|
48
|
+
box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* Valid input field states (optional, provides positive feedback) */
|
|
52
|
+
input:not([disabled]):valid,
|
|
53
|
+
textarea:not([disabled]):valid,
|
|
54
|
+
select:not([disabled]):valid
|
|
55
|
+
{
|
|
56
|
+
border-color: #28a745;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* Focus state for invalid inputs */
|
|
60
|
+
input.squiffy-invalid:focus,
|
|
61
|
+
textarea.squiffy-invalid:focus,
|
|
62
|
+
select.squiffy-invalid:focus
|
|
63
|
+
{
|
|
64
|
+
outline: none;
|
|
65
|
+
border-color: #dc3545 !important;
|
|
66
|
+
box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* Header button styles */
|
|
70
|
+
button.squiffy-header-button
|
|
71
|
+
{
|
|
72
|
+
text-decoration: underline;
|
|
73
|
+
color: Blue;
|
|
74
|
+
cursor: pointer;
|
|
75
|
+
background: none;
|
|
76
|
+
border: none;
|
|
77
|
+
font-family: inherit;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* Output block spacing */
|
|
81
|
+
div.squiffy-output-block:not(:last-child),
|
|
82
|
+
div.squiffy-output-passage:not(:last-child),
|
|
83
|
+
div.squiffy-output-section:not(:last-child)
|
|
84
|
+
{
|
|
85
|
+
margin-bottom: 1em;
|
|
86
|
+
border-bottom: 1px solid #ccc;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* Fade animations */
|
|
90
|
+
.fade-out {
|
|
91
|
+
opacity: 0;
|
|
92
|
+
transition: opacity 200ms;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.fade-in {
|
|
96
|
+
opacity: 1;
|
|
97
|
+
transition: opacity 200ms;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* Continue animation - click to proceed */
|
|
101
|
+
.squiffy-continue {
|
|
102
|
+
text-decoration: underline;
|
|
103
|
+
text-decoration-color: Blue;
|
|
104
|
+
color: Blue;
|
|
105
|
+
cursor: pointer;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/* Style: pulse - fades in and out */
|
|
109
|
+
.squiffy-continue-pulse {
|
|
110
|
+
animation: squiffy-pulse 1.5s ease-in-out infinite;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
@keyframes squiffy-pulse {
|
|
114
|
+
0%, 100% { opacity: 1; }
|
|
115
|
+
50% { opacity: 0.4; }
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* Style: glow - pulsing glow effect */
|
|
119
|
+
.squiffy-continue-glow {
|
|
120
|
+
animation: squiffy-glow 1.5s ease-in-out infinite;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
@keyframes squiffy-glow {
|
|
124
|
+
0%, 100% { text-shadow: 0 0 4px rgba(0, 0, 255, 0.5); }
|
|
125
|
+
50% { text-shadow: 0 0 12px rgba(0, 0, 255, 0.8), 0 0 20px rgba(0, 0, 255, 0.4); }
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* Style: bounce - subtle vertical bounce */
|
|
129
|
+
.squiffy-continue-bounce {
|
|
130
|
+
display: inline-block;
|
|
131
|
+
animation: squiffy-bounce 0.6s ease-in-out infinite;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
@keyframes squiffy-bounce {
|
|
135
|
+
0%, 100% { transform: translateY(0); }
|
|
136
|
+
50% { transform: translateY(-4px); }
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* Style: wave - character spans need special handling for underline */
|
|
140
|
+
.squiffy-continue-wave {
|
|
141
|
+
text-decoration: none;
|
|
142
|
+
border-bottom: 1px solid Blue;
|
|
143
|
+
}
|
|
@@ -1,51 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
scroll?: string;
|
|
5
|
-
persist?: boolean;
|
|
6
|
-
onSet?: (attribute: string, value: any) => void;
|
|
7
|
-
}
|
|
8
|
-
export interface SquiffySettings {
|
|
9
|
-
scroll: string;
|
|
10
|
-
persist: boolean;
|
|
11
|
-
onSet: (attribute: string, value: any) => void;
|
|
12
|
-
}
|
|
13
|
-
export interface SquiffyApi {
|
|
14
|
-
restart: () => void;
|
|
15
|
-
get: (attribute: string) => any;
|
|
16
|
-
set: (attribute: string, value: any) => void;
|
|
17
|
-
clickLink: (link: HTMLElement) => boolean;
|
|
18
|
-
update: (story: Story) => void;
|
|
19
|
-
}
|
|
20
|
-
interface SquiffyJsFunctionApi {
|
|
21
|
-
get: (attribute: string) => any;
|
|
22
|
-
set: (attribute: string, value: any) => void;
|
|
23
|
-
ui: {
|
|
24
|
-
transition: (f: any) => void;
|
|
25
|
-
};
|
|
26
|
-
story: {
|
|
27
|
-
go: (section: string) => void;
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
export interface Story {
|
|
31
|
-
js: ((squiffy: SquiffyJsFunctionApi, get: (attribute: string) => any, set: (attribute: string, value: any) => void) => void)[];
|
|
32
|
-
start: string;
|
|
33
|
-
id?: string | null;
|
|
34
|
-
sections: Record<string, Section>;
|
|
35
|
-
}
|
|
36
|
-
export interface Section {
|
|
37
|
-
text?: string;
|
|
38
|
-
clear?: boolean;
|
|
39
|
-
attributes?: string[];
|
|
40
|
-
jsIndex?: number;
|
|
41
|
-
passages?: Record<string, Passage>;
|
|
42
|
-
passageCount?: number;
|
|
43
|
-
}
|
|
44
|
-
export interface Passage {
|
|
45
|
-
text?: string;
|
|
46
|
-
clear?: boolean;
|
|
47
|
-
attributes?: string[];
|
|
48
|
-
jsIndex?: number;
|
|
49
|
-
}
|
|
50
|
-
export declare const init: (options: SquiffyInitOptions) => SquiffyApi;
|
|
51
|
-
export {};
|
|
1
|
+
import { SquiffyApi, SquiffyInitOptions } from './types.js';
|
|
2
|
+
export type { SquiffyApi } from './types.js';
|
|
3
|
+
export declare const init: (options: SquiffyInitOptions) => Promise<SquiffyApi>;
|