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.
Files changed (48) hide show
  1. package/LICENSE +22 -0
  2. package/dist/animation.d.ts +21 -0
  3. package/dist/events.d.ts +20 -0
  4. package/dist/import.d.ts +4 -0
  5. package/dist/inputValidation.d.ts +20 -0
  6. package/dist/linkHandler.d.ts +8 -0
  7. package/dist/pluginManager.d.ts +23 -0
  8. package/dist/squiffy.runtime.css +143 -0
  9. package/dist/squiffy.runtime.d.ts +3 -51
  10. package/dist/squiffy.runtime.global.js +132 -0
  11. package/dist/squiffy.runtime.global.js.map +1 -0
  12. package/dist/squiffy.runtime.js +8941 -628
  13. package/dist/squiffy.runtime.js.map +1 -0
  14. package/dist/squiffy.runtime.test.d.ts +1 -0
  15. package/dist/state.d.ts +19 -0
  16. package/dist/textProcessor.d.ts +11 -0
  17. package/dist/types.d.ts +58 -0
  18. package/dist/types.plugins.d.ts +27 -0
  19. package/dist/updater.d.ts +2 -0
  20. package/dist/utils.d.ts +1 -0
  21. package/package.json +27 -5
  22. package/src/__snapshots__/squiffy.runtime.test.ts.snap +63 -19
  23. package/src/animation.ts +142 -0
  24. package/src/events.ts +41 -0
  25. package/src/import.ts +5 -0
  26. package/src/inputValidation.ts +83 -0
  27. package/src/linkHandler.ts +18 -0
  28. package/src/pluginManager.ts +74 -0
  29. package/src/plugins/animate.ts +115 -0
  30. package/src/plugins/index.ts +13 -0
  31. package/src/plugins/live.ts +82 -0
  32. package/src/plugins/plugins.test.ts +309 -0
  33. package/src/plugins/random.ts +22 -0
  34. package/src/plugins/replaceLabel.ts +27 -0
  35. package/src/plugins/rotateSequence.ts +72 -0
  36. package/src/squiffy.runtime.css +143 -0
  37. package/src/squiffy.runtime.test.ts +2029 -106
  38. package/src/squiffy.runtime.ts +537 -576
  39. package/src/state.ts +124 -0
  40. package/src/textProcessor.ts +205 -0
  41. package/src/types.plugins.ts +41 -0
  42. package/src/types.ts +82 -0
  43. package/src/updater.ts +107 -0
  44. package/src/utils.ts +17 -0
  45. package/tsconfig.json +4 -1
  46. package/vite.config.ts +52 -0
  47. package/vitest.config.ts +9 -0
  48. 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 {};
@@ -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
+ }
@@ -0,0 +1,4 @@
1
+ import * as animejs from "animejs";
2
+ export declare const imports: {
3
+ animejs: typeof animejs;
4
+ };
@@ -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
- export interface SquiffyInitOptions {
2
- element: HTMLElement;
3
- story: Story;
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>;