qnce-engine 0.1.0 → 1.2.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.
- package/README.md +959 -5
- package/dist/cli/audit.js +6 -4
- package/dist/cli/audit.js.map +1 -1
- package/dist/cli/init.js +11 -9
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/perf.d.ts +30 -0
- package/dist/cli/perf.d.ts.map +1 -0
- package/dist/cli/perf.js +220 -0
- package/dist/cli/perf.js.map +1 -0
- package/dist/cli/play.d.ts +4 -0
- package/dist/cli/play.d.ts.map +1 -0
- package/dist/cli/play.js +259 -0
- package/dist/cli/play.js.map +1 -0
- package/dist/engine/condition.d.ts +69 -0
- package/dist/engine/condition.d.ts.map +1 -0
- package/dist/engine/condition.js +195 -0
- package/dist/engine/condition.js.map +1 -0
- package/dist/engine/core.d.ts +378 -11
- package/dist/engine/core.d.ts.map +1 -1
- package/dist/engine/core.js +1433 -13
- package/dist/engine/core.js.map +1 -1
- package/dist/engine/demo-story.d.ts.map +1 -1
- package/dist/engine/demo-story.js +103 -14
- package/dist/engine/demo-story.js.map +1 -1
- package/dist/engine/errors.d.ts +76 -0
- package/dist/engine/errors.d.ts.map +1 -0
- package/dist/engine/errors.js +178 -0
- package/dist/engine/errors.js.map +1 -0
- package/dist/engine/types.d.ts +445 -0
- package/dist/engine/types.d.ts.map +1 -0
- package/dist/engine/types.js +9 -0
- package/dist/engine/types.js.map +1 -0
- package/dist/engine/validation.d.ts +110 -0
- package/dist/engine/validation.d.ts.map +1 -0
- package/dist/engine/validation.js +261 -0
- package/dist/engine/validation.js.map +1 -0
- package/dist/examples/examples/autosave-undo-demo.js +248 -0
- package/dist/examples/examples/persistence-demo.js +63 -0
- package/dist/examples/src/engine/condition.js +194 -0
- package/dist/examples/src/engine/core.js +1382 -0
- package/dist/examples/src/engine/demo-story.js +200 -0
- package/dist/examples/src/engine/types.js +8 -0
- package/dist/examples/src/index.js +35 -0
- package/dist/examples/src/integrations/react.js +322 -0
- package/dist/examples/src/narrative/branching/engine-simple.js +348 -0
- package/dist/examples/src/narrative/branching/index.js +55 -0
- package/dist/examples/src/narrative/branching/models.js +5 -0
- package/dist/examples/src/performance/ObjectPool.js +296 -0
- package/dist/examples/src/performance/PerfReporter.js +280 -0
- package/dist/examples/src/performance/ThreadPool.js +347 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -3
- package/dist/index.js.map +1 -1
- package/dist/integrations/react.d.ts +200 -0
- package/dist/integrations/react.d.ts.map +1 -0
- package/dist/integrations/react.js +365 -0
- package/dist/integrations/react.js.map +1 -0
- package/dist/narrative/branching/engine-simple.d.ts +84 -0
- package/dist/narrative/branching/engine-simple.d.ts.map +1 -0
- package/dist/narrative/branching/engine-simple.js +349 -0
- package/dist/narrative/branching/engine-simple.js.map +1 -0
- package/dist/narrative/branching/engine.d.ts +1 -0
- package/dist/narrative/branching/engine.d.ts.map +1 -0
- package/dist/narrative/branching/engine.js +2 -0
- package/dist/narrative/branching/engine.js.map +1 -0
- package/dist/narrative/branching/index.d.ts +12 -0
- package/dist/narrative/branching/index.d.ts.map +1 -0
- package/dist/narrative/branching/index.js +56 -0
- package/dist/narrative/branching/index.js.map +1 -0
- package/dist/narrative/branching/models.d.ts +223 -0
- package/dist/narrative/branching/models.d.ts.map +1 -0
- package/dist/narrative/branching/models.js +6 -0
- package/dist/narrative/branching/models.js.map +1 -0
- package/dist/performance/HotReloadDelta.d.ts +124 -0
- package/dist/performance/HotReloadDelta.d.ts.map +1 -0
- package/dist/performance/HotReloadDelta.js +328 -0
- package/dist/performance/HotReloadDelta.js.map +1 -0
- package/dist/performance/ObjectPool.d.ts +150 -0
- package/dist/performance/ObjectPool.d.ts.map +1 -0
- package/dist/performance/ObjectPool.js +297 -0
- package/dist/performance/ObjectPool.js.map +1 -0
- package/dist/performance/PerfReporter.d.ts +123 -0
- package/dist/performance/PerfReporter.d.ts.map +1 -0
- package/dist/performance/PerfReporter.js +281 -0
- package/dist/performance/PerfReporter.js.map +1 -0
- package/dist/performance/ThreadPool.d.ts +107 -0
- package/dist/performance/ThreadPool.d.ts.map +1 -0
- package/dist/performance/ThreadPool.js +348 -0
- package/dist/performance/ThreadPool.js.map +1 -0
- package/dist/ui/__tests__/AutosaveIndicator.test.d.ts +2 -0
- package/dist/ui/__tests__/AutosaveIndicator.test.d.ts.map +1 -0
- package/dist/ui/__tests__/AutosaveIndicator.test.js +329 -0
- package/dist/ui/__tests__/AutosaveIndicator.test.js.map +1 -0
- package/dist/ui/__tests__/UndoRedoControls.test.d.ts +2 -0
- package/dist/ui/__tests__/UndoRedoControls.test.d.ts.map +1 -0
- package/dist/ui/__tests__/UndoRedoControls.test.js +245 -0
- package/dist/ui/__tests__/UndoRedoControls.test.js.map +1 -0
- package/dist/ui/__tests__/autosave-simple.test.d.ts +2 -0
- package/dist/ui/__tests__/autosave-simple.test.d.ts.map +1 -0
- package/dist/ui/__tests__/autosave-simple.test.js +29 -0
- package/dist/ui/__tests__/autosave-simple.test.js.map +1 -0
- package/dist/ui/__tests__/setup.d.ts +2 -0
- package/dist/ui/__tests__/setup.d.ts.map +1 -0
- package/dist/ui/__tests__/setup.js +40 -0
- package/dist/ui/__tests__/setup.js.map +1 -0
- package/dist/ui/__tests__/smoke-test.d.ts +2 -0
- package/dist/ui/__tests__/smoke-test.d.ts.map +1 -0
- package/dist/ui/__tests__/smoke-test.js +18 -0
- package/dist/ui/__tests__/smoke-test.js.map +1 -0
- package/dist/ui/__tests__/smoke-test.test.d.ts +2 -0
- package/dist/ui/__tests__/smoke-test.test.d.ts.map +1 -0
- package/dist/ui/__tests__/smoke-test.test.js +18 -0
- package/dist/ui/__tests__/smoke-test.test.js.map +1 -0
- package/dist/ui/__tests__/useKeyboardShortcuts.test.d.ts +2 -0
- package/dist/ui/__tests__/useKeyboardShortcuts.test.d.ts.map +1 -0
- package/dist/ui/__tests__/useKeyboardShortcuts.test.js +374 -0
- package/dist/ui/__tests__/useKeyboardShortcuts.test.js.map +1 -0
- package/dist/ui/components/AutosaveIndicator.d.ts +18 -0
- package/dist/ui/components/AutosaveIndicator.d.ts.map +1 -0
- package/dist/ui/components/AutosaveIndicator.js +175 -0
- package/dist/ui/components/AutosaveIndicator.js.map +1 -0
- package/dist/ui/components/UndoRedoControls.d.ts +16 -0
- package/dist/ui/components/UndoRedoControls.d.ts.map +1 -0
- package/dist/ui/components/UndoRedoControls.js +144 -0
- package/dist/ui/components/UndoRedoControls.js.map +1 -0
- package/dist/ui/hooks/useKeyboardShortcuts.d.ts +22 -0
- package/dist/ui/hooks/useKeyboardShortcuts.d.ts.map +1 -0
- package/dist/ui/hooks/useKeyboardShortcuts.js +162 -0
- package/dist/ui/hooks/useKeyboardShortcuts.js.map +1 -0
- package/dist/ui/index.d.ts +9 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +14 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/types.d.ts +141 -0
- package/dist/ui/types.d.ts.map +1 -0
- package/dist/ui/types.js +51 -0
- package/dist/ui/types.js.map +1 -0
- package/docs/PERFORMANCE.md +355 -0
- package/docs/branching/ERD.md +214 -0
- package/docs/branching/PDM.md +443 -0
- package/docs/branching/RELEASE-v1.2.0.md +169 -0
- package/examples/autosave-undo-demo.ts +306 -0
- package/examples/branching-advanced-demo.ts +339 -0
- package/examples/branching-demo-simple.ts +0 -0
- package/examples/branching-demo.ts +0 -0
- package/examples/branching-quickstart.ts +314 -0
- package/examples/persistence-demo.ts +84 -0
- package/examples/quickstart-demo.js +82 -0
- package/examples/tsconfig.json +13 -0
- package/examples/ui-components-demo.tsx +320 -0
- package/examples/validation-demo-story.json +177 -0
- package/examples/validation-demo.js +163 -0
- package/package.json +41 -8
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEMO_STORY = void 0;
|
|
4
|
+
// Demo narrative data for QNCE Engine
|
|
5
|
+
exports.DEMO_STORY = {
|
|
6
|
+
initialNodeId: 'start',
|
|
7
|
+
nodes: [
|
|
8
|
+
{
|
|
9
|
+
id: 'start',
|
|
10
|
+
text: 'You stand at a crossroads in an ancient land. Mysterious energy flows through the air. Do you go left or right?',
|
|
11
|
+
choices: [
|
|
12
|
+
{ text: 'Left', nextNodeId: 'river', flagEffects: { wentLeft: true, curiosity: 1 } },
|
|
13
|
+
{ text: 'Right', nextNodeId: 'mountain', flagEffects: { wentLeft: false, courage: 1 } },
|
|
14
|
+
],
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: 'river',
|
|
18
|
+
text: 'You encounter a rushing river. The water sparkles with an otherworldly light. Do you swim or build a raft?',
|
|
19
|
+
choices: [
|
|
20
|
+
{ text: 'Swim', nextNodeId: 'swim', flagEffects: { gotWet: true, courage: 2 } },
|
|
21
|
+
{ text: 'Build a raft', nextNodeId: 'raft', flagEffects: { gotWet: false, patience: 1 } },
|
|
22
|
+
{
|
|
23
|
+
text: 'Use magic to cross (requires curiosity)',
|
|
24
|
+
nextNodeId: 'magical_crossing',
|
|
25
|
+
flagEffects: { usedMagic: true, magicPower: 1 },
|
|
26
|
+
condition: 'flags.curiosity >= 1'
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: 'mountain',
|
|
32
|
+
text: 'You find a towering mountain shrouded in mist. Ancient runes glow faintly on its slopes. Do you climb or go around?',
|
|
33
|
+
choices: [
|
|
34
|
+
{ text: 'Climb', nextNodeId: 'climb', flagEffects: { climbed: true, courage: 2 } },
|
|
35
|
+
{ text: 'Go around', nextNodeId: 'village', flagEffects: { climbed: false, patience: 1 } },
|
|
36
|
+
{
|
|
37
|
+
text: 'Study the runes (requires courage)',
|
|
38
|
+
nextNodeId: 'rune_study',
|
|
39
|
+
flagEffects: { studiedRunes: true, knowledge: 2 },
|
|
40
|
+
condition: 'flags.courage >= 1'
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
// --- Expanded Narrative Branches ---
|
|
45
|
+
{
|
|
46
|
+
id: 'magical_crossing',
|
|
47
|
+
text: 'You focus your energy and step onto the water\'s surface. It holds! You walk across safely, your magical abilities awakened.',
|
|
48
|
+
choices: [
|
|
49
|
+
{ text: 'Continue to the mystical grove', nextNodeId: 'mystical_grove', flagEffects: { discoveredGrove: true } },
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: 'rune_study',
|
|
54
|
+
text: 'The runes reveal ancient secrets of power. You feel wisdom flowing through you.',
|
|
55
|
+
choices: [
|
|
56
|
+
{ text: 'Ascend the mountain with new knowledge', nextNodeId: 'peak_ascension', flagEffects: { enlightened: true } },
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: 'swim',
|
|
61
|
+
text: 'You swim across and reach the other side, but you are soaked and cold. Ahead, you see a cave and a campfire.',
|
|
62
|
+
choices: [
|
|
63
|
+
{ text: 'Warm up by the campfire', nextNodeId: 'campfire', flagEffects: { warmedUp: true } },
|
|
64
|
+
{ text: 'Explore the cave', nextNodeId: 'cave', flagEffects: { exploredCave: true, curiosity: 1 } },
|
|
65
|
+
{
|
|
66
|
+
text: 'Use magic to dry yourself (requires magic power)',
|
|
67
|
+
nextNodeId: 'magical_warmth',
|
|
68
|
+
flagEffects: { magicallyDried: true },
|
|
69
|
+
condition: 'flags.magicPower >= 1'
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
id: 'raft',
|
|
75
|
+
text: 'You build a raft and float safely. On the far bank, a merchant greets you.',
|
|
76
|
+
choices: [
|
|
77
|
+
{ text: 'Trade with the merchant', nextNodeId: 'merchant', flagEffects: { metMerchant: true } },
|
|
78
|
+
{ text: 'Ignore and continue', nextNodeId: 'forest', flagEffects: { metMerchant: false } },
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
id: 'climb',
|
|
83
|
+
text: 'You climb and see a vast land. The wind is strong. Do you signal for help or descend?',
|
|
84
|
+
choices: [
|
|
85
|
+
{ text: 'Signal for help', nextNodeId: 'signal', flagEffects: { signaled: true } },
|
|
86
|
+
{ text: 'Descend', nextNodeId: 'forest', flagEffects: { signaled: false } },
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: 'village',
|
|
91
|
+
text: 'You go around and find a village. The villagers are wary. Do you introduce yourself or sneak past?',
|
|
92
|
+
choices: [
|
|
93
|
+
{ text: 'Introduce yourself', nextNodeId: 'welcome', flagEffects: { welcomed: true } },
|
|
94
|
+
{ text: 'Sneak past', nextNodeId: 'forest', flagEffects: { welcomed: false } },
|
|
95
|
+
],
|
|
96
|
+
},
|
|
97
|
+
// --- New Unique Outcomes & Entanglement ---
|
|
98
|
+
{
|
|
99
|
+
id: 'campfire',
|
|
100
|
+
text: 'You warm up by the fire. A traveler offers you advice. (Success!)',
|
|
101
|
+
choices: [],
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
id: 'cave',
|
|
105
|
+
text: 'Inside the cave, you find ancient markings. Suddenly, you slip and must return to the river bank. (Loop)',
|
|
106
|
+
choices: [
|
|
107
|
+
{ text: 'Return to river', nextNodeId: 'river', flagEffects: { slipped: true } },
|
|
108
|
+
],
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
id: 'merchant',
|
|
112
|
+
text: 'The merchant gives you a mysterious key. (Reward!)',
|
|
113
|
+
choices: [],
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
id: 'forest',
|
|
117
|
+
text: 'You enter a dense forest. The trees whisper ancient secrets. Multiple paths reveal themselves based on your journey.',
|
|
118
|
+
choices: [
|
|
119
|
+
{
|
|
120
|
+
text: 'Use the merchant\'s key (if you have it)',
|
|
121
|
+
nextNodeId: 'shortcut',
|
|
122
|
+
flagEffects: { usedKey: true },
|
|
123
|
+
condition: 'flags.metMerchant'
|
|
124
|
+
},
|
|
125
|
+
{ text: 'Push through the forest', nextNodeId: 'lost', flagEffects: { usedKey: false } },
|
|
126
|
+
{
|
|
127
|
+
text: 'Seek guidance from the spirits (requires wisdom)',
|
|
128
|
+
nextNodeId: 'spirit_guidance',
|
|
129
|
+
flagEffects: { talkedToSpirits: true },
|
|
130
|
+
condition: 'flags.knowledge >= 2 || flags.studiedRunes'
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
text: 'Use enhanced senses (requires multiple experiences)',
|
|
134
|
+
nextNodeId: 'enhanced_path',
|
|
135
|
+
flagEffects: { usedSenses: true },
|
|
136
|
+
condition: 'flags.curiosity >= 1 && flags.courage >= 1'
|
|
137
|
+
}
|
|
138
|
+
],
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
id: 'signal',
|
|
142
|
+
text: 'Your signal is seen! A rescue party arrives. (Success!)',
|
|
143
|
+
choices: [],
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
id: 'welcome',
|
|
147
|
+
text: 'The villagers welcome you and offer food. (Success!)',
|
|
148
|
+
choices: [],
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
id: 'shortcut',
|
|
152
|
+
text: 'You use the key to unlock a hidden path and escape the forest. (Success!)',
|
|
153
|
+
choices: [],
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
id: 'lost',
|
|
157
|
+
text: 'You get lost in the forest. The paths seem to shift and change around you.',
|
|
158
|
+
choices: [
|
|
159
|
+
{ text: 'Try to find your way back', nextNodeId: 'forest', flagEffects: { triedAgain: true } },
|
|
160
|
+
{
|
|
161
|
+
text: 'Meditate and center yourself (requires patience)',
|
|
162
|
+
nextNodeId: 'inner_peace',
|
|
163
|
+
flagEffects: { foundPeace: true },
|
|
164
|
+
condition: 'flags.patience >= 1'
|
|
165
|
+
}
|
|
166
|
+
],
|
|
167
|
+
},
|
|
168
|
+
// --- New Conditional Ending Nodes ---
|
|
169
|
+
{
|
|
170
|
+
id: 'mystical_grove',
|
|
171
|
+
text: 'You discover a hidden grove where time seems to stand still. This is a sacred place of great power. (Magical Success!)',
|
|
172
|
+
choices: [],
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
id: 'peak_ascension',
|
|
176
|
+
text: 'With ancient knowledge guiding you, you reach the mountain\'s peak and unlock the secrets of the realm. (Enlightened Success!)',
|
|
177
|
+
choices: [],
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
id: 'magical_warmth',
|
|
181
|
+
text: 'Your magic flows through you, instantly drying your clothes and warming your body. You feel your power growing stronger. (Magical Mastery!)',
|
|
182
|
+
choices: [],
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
id: 'spirit_guidance',
|
|
186
|
+
text: 'The forest spirits, impressed by your wisdom, guide you to a hidden sanctuary. (Spiritual Success!)',
|
|
187
|
+
choices: [],
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
id: 'enhanced_path',
|
|
191
|
+
text: 'Your varied experiences have heightened your senses. You navigate the forest with ease and discover a treasure trove. (Master Explorer Success!)',
|
|
192
|
+
choices: [],
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
id: 'inner_peace',
|
|
196
|
+
text: 'Through meditation, you find inner peace and the forest reveals its true beauty. You emerge transformed. (Peaceful Resolution!)',
|
|
197
|
+
choices: [],
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// QNCE Engine Types - Sprint 3.3: State Persistence & Checkpoints
|
|
3
|
+
// Comprehensive type definitions for serialization and checkpoint features
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.SUPPORTED_VERSIONS = exports.PERSISTENCE_VERSION = void 0;
|
|
6
|
+
// Version constants
|
|
7
|
+
exports.PERSISTENCE_VERSION = '1.0.0';
|
|
8
|
+
exports.SUPPORTED_VERSIONS = ['1.0.0'];
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.conditionEvaluator = exports.ConditionEvaluationError = exports.ConditionEvaluator = exports.loadStoryData = exports.createQNCEEngine = exports.QNCEEngine = exports.DEMO_STORY = void 0;
|
|
18
|
+
// QNCE Engine - Main Export
|
|
19
|
+
__exportStar(require("./engine/core.js"), exports);
|
|
20
|
+
var demo_story_js_1 = require("./engine/demo-story.js");
|
|
21
|
+
Object.defineProperty(exports, "DEMO_STORY", { enumerable: true, get: function () { return demo_story_js_1.DEMO_STORY; } });
|
|
22
|
+
// Sprint 3.4: Conditional choice display exports
|
|
23
|
+
__exportStar(require("./engine/condition.js"), exports);
|
|
24
|
+
// Sprint 3.5: React integration for autosave/undo functionality
|
|
25
|
+
__exportStar(require("./integrations/react.js"), exports);
|
|
26
|
+
// Re-export for convenience
|
|
27
|
+
var core_js_1 = require("./engine/core.js");
|
|
28
|
+
Object.defineProperty(exports, "QNCEEngine", { enumerable: true, get: function () { return core_js_1.QNCEEngine; } });
|
|
29
|
+
Object.defineProperty(exports, "createQNCEEngine", { enumerable: true, get: function () { return core_js_1.createQNCEEngine; } });
|
|
30
|
+
Object.defineProperty(exports, "loadStoryData", { enumerable: true, get: function () { return core_js_1.loadStoryData; } });
|
|
31
|
+
// Sprint 3.4: Conditional choice types
|
|
32
|
+
var condition_js_1 = require("./engine/condition.js");
|
|
33
|
+
Object.defineProperty(exports, "ConditionEvaluator", { enumerable: true, get: function () { return condition_js_1.ConditionEvaluator; } });
|
|
34
|
+
Object.defineProperty(exports, "ConditionEvaluationError", { enumerable: true, get: function () { return condition_js_1.ConditionEvaluationError; } });
|
|
35
|
+
Object.defineProperty(exports, "conditionEvaluator", { enumerable: true, get: function () { return condition_js_1.conditionEvaluator; } });
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* QNCE Engine React Integration
|
|
4
|
+
*
|
|
5
|
+
* React hooks and utilities for integrating QNCE Engine with React applications.
|
|
6
|
+
* Provides convenient hooks for managing narrative state, undo/redo operations,
|
|
7
|
+
* and autosave functionality.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.useQNCE = useQNCE;
|
|
11
|
+
exports.useUndoRedo = useUndoRedo;
|
|
12
|
+
exports.useAutosave = useAutosave;
|
|
13
|
+
const react_1 = require("react");
|
|
14
|
+
/**
|
|
15
|
+
* React hook for QNCE Engine integration
|
|
16
|
+
*
|
|
17
|
+
* Provides a complete interface for managing QNCE Engine state in React applications,
|
|
18
|
+
* including undo/redo functionality, autosave, and automatic re-renders.
|
|
19
|
+
*
|
|
20
|
+
* @param engine - The QNCE Engine instance
|
|
21
|
+
* @param config - Configuration options for the hook
|
|
22
|
+
* @returns Hook return object with state and actions
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```tsx
|
|
26
|
+
* function NarrativeComponent() {
|
|
27
|
+
* const engine = useMemo(() => createQNCEEngine(DEMO_STORY), []);
|
|
28
|
+
* const {
|
|
29
|
+
* currentNode,
|
|
30
|
+
* availableChoices,
|
|
31
|
+
* selectChoice,
|
|
32
|
+
* undo,
|
|
33
|
+
* redo,
|
|
34
|
+
* canUndo,
|
|
35
|
+
* canRedo
|
|
36
|
+
* } = useQNCE(engine, {
|
|
37
|
+
* enableUndoRedo: true,
|
|
38
|
+
* enableAutosave: true,
|
|
39
|
+
* maxUndoEntries: 50
|
|
40
|
+
* });
|
|
41
|
+
*
|
|
42
|
+
* return (
|
|
43
|
+
* <div>
|
|
44
|
+
* <p>{currentNode?.text}</p>
|
|
45
|
+
*
|
|
46
|
+
* <div>
|
|
47
|
+
* {availableChoices.map(choice => (
|
|
48
|
+
* <button key={choice.text} onClick={() => selectChoice(choice)}>
|
|
49
|
+
* {choice.text}
|
|
50
|
+
* </button>
|
|
51
|
+
* ))}
|
|
52
|
+
* </div>
|
|
53
|
+
*
|
|
54
|
+
* <div>
|
|
55
|
+
* <button onClick={undo} disabled={!canUndo}>
|
|
56
|
+
* Undo
|
|
57
|
+
* </button>
|
|
58
|
+
* <button onClick={redo} disabled={!canRedo}>
|
|
59
|
+
* Redo
|
|
60
|
+
* </button>
|
|
61
|
+
* </div>
|
|
62
|
+
* </div>
|
|
63
|
+
* );
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
function useQNCE(engine, config = {}) {
|
|
68
|
+
const { autoUpdate = true, enableUndoRedo = true, maxUndoEntries = 50, maxRedoEntries = 25, enableAutosave = true, autosaveThrottleMs = 100 } = config;
|
|
69
|
+
// Force re-render counter
|
|
70
|
+
const [, setUpdateCounter] = (0, react_1.useState)(0);
|
|
71
|
+
// Initialize engine configuration
|
|
72
|
+
(0, react_1.useEffect)(() => {
|
|
73
|
+
if (enableUndoRedo) {
|
|
74
|
+
engine.configureUndoRedo({
|
|
75
|
+
enabled: true,
|
|
76
|
+
maxUndoEntries,
|
|
77
|
+
maxRedoEntries
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
if (enableAutosave) {
|
|
81
|
+
engine.configureAutosave({
|
|
82
|
+
enabled: true,
|
|
83
|
+
throttleMs: autosaveThrottleMs,
|
|
84
|
+
triggers: ['choice', 'flag-change', 'state-load']
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}, [engine, enableUndoRedo, maxUndoEntries, maxRedoEntries, enableAutosave, autosaveThrottleMs]);
|
|
88
|
+
// Force re-render when autoUpdate is enabled
|
|
89
|
+
const refresh = (0, react_1.useCallback)(() => {
|
|
90
|
+
if (autoUpdate) {
|
|
91
|
+
setUpdateCounter(prev => prev + 1);
|
|
92
|
+
}
|
|
93
|
+
}, [autoUpdate]);
|
|
94
|
+
// Memoized state getters
|
|
95
|
+
const currentNode = (0, react_1.useMemo)(() => {
|
|
96
|
+
try {
|
|
97
|
+
return engine.getCurrentNode();
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
}, [engine, autoUpdate]);
|
|
103
|
+
const availableChoices = (0, react_1.useMemo)(() => {
|
|
104
|
+
try {
|
|
105
|
+
return engine.getAvailableChoices();
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
}, [engine, autoUpdate]);
|
|
111
|
+
const flags = (0, react_1.useMemo)(() => {
|
|
112
|
+
return engine.getState().flags;
|
|
113
|
+
}, [engine, autoUpdate]);
|
|
114
|
+
// Undo/Redo state
|
|
115
|
+
const canUndo = (0, react_1.useMemo)(() => engine.canUndo(), [engine, autoUpdate]);
|
|
116
|
+
const canRedo = (0, react_1.useMemo)(() => engine.canRedo(), [engine, autoUpdate]);
|
|
117
|
+
const undoCount = (0, react_1.useMemo)(() => engine.getUndoCount(), [engine, autoUpdate]);
|
|
118
|
+
const redoCount = (0, react_1.useMemo)(() => engine.getRedoCount(), [engine, autoUpdate]);
|
|
119
|
+
// Actions with automatic refresh
|
|
120
|
+
const selectChoice = (0, react_1.useCallback)(async (choice) => {
|
|
121
|
+
let choiceToSelect;
|
|
122
|
+
if (typeof choice === 'string') {
|
|
123
|
+
// Find choice by text
|
|
124
|
+
const foundChoice = availableChoices.find(c => c.text === choice);
|
|
125
|
+
if (!foundChoice) {
|
|
126
|
+
throw new Error(`Choice not found: ${choice}`);
|
|
127
|
+
}
|
|
128
|
+
choiceToSelect = foundChoice;
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
choiceToSelect = choice;
|
|
132
|
+
}
|
|
133
|
+
engine.selectChoice(choiceToSelect);
|
|
134
|
+
refresh();
|
|
135
|
+
}, [engine, refresh, availableChoices]);
|
|
136
|
+
const setFlag = (0, react_1.useCallback)((key, value) => {
|
|
137
|
+
engine.setFlag(key, value);
|
|
138
|
+
refresh();
|
|
139
|
+
}, [engine, refresh]);
|
|
140
|
+
const resetNarrative = (0, react_1.useCallback)(() => {
|
|
141
|
+
engine.resetNarrative();
|
|
142
|
+
refresh();
|
|
143
|
+
}, [engine, refresh]);
|
|
144
|
+
const undo = (0, react_1.useCallback)(() => {
|
|
145
|
+
const result = engine.undo();
|
|
146
|
+
refresh();
|
|
147
|
+
return result;
|
|
148
|
+
}, [engine, refresh]);
|
|
149
|
+
const redo = (0, react_1.useCallback)(() => {
|
|
150
|
+
const result = engine.redo();
|
|
151
|
+
refresh();
|
|
152
|
+
return result;
|
|
153
|
+
}, [engine, refresh]);
|
|
154
|
+
const clearHistory = (0, react_1.useCallback)(() => {
|
|
155
|
+
engine.clearHistory();
|
|
156
|
+
refresh();
|
|
157
|
+
}, [engine, refresh]);
|
|
158
|
+
const autosave = (0, react_1.useCallback)(async () => {
|
|
159
|
+
await engine.manualAutosave();
|
|
160
|
+
}, [engine]);
|
|
161
|
+
const configureAutosave = (0, react_1.useCallback)((config) => {
|
|
162
|
+
engine.configureAutosave(config);
|
|
163
|
+
}, [engine]);
|
|
164
|
+
const saveState = (0, react_1.useCallback)(async () => {
|
|
165
|
+
return await engine.saveState();
|
|
166
|
+
}, [engine]);
|
|
167
|
+
const loadState = (0, react_1.useCallback)(async (serializedState) => {
|
|
168
|
+
await engine.loadState(serializedState);
|
|
169
|
+
refresh();
|
|
170
|
+
}, [engine, refresh]);
|
|
171
|
+
return {
|
|
172
|
+
// Core state
|
|
173
|
+
engine,
|
|
174
|
+
currentNode,
|
|
175
|
+
availableChoices,
|
|
176
|
+
flags,
|
|
177
|
+
// Actions
|
|
178
|
+
selectChoice,
|
|
179
|
+
setFlag,
|
|
180
|
+
resetNarrative,
|
|
181
|
+
// Undo/Redo
|
|
182
|
+
undo,
|
|
183
|
+
redo,
|
|
184
|
+
canUndo,
|
|
185
|
+
canRedo,
|
|
186
|
+
undoCount,
|
|
187
|
+
redoCount,
|
|
188
|
+
clearHistory,
|
|
189
|
+
// Autosave
|
|
190
|
+
autosave,
|
|
191
|
+
configureAutosave,
|
|
192
|
+
// State management
|
|
193
|
+
saveState,
|
|
194
|
+
loadState,
|
|
195
|
+
// Utility
|
|
196
|
+
refresh
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Hook for managing just undo/redo functionality
|
|
201
|
+
*
|
|
202
|
+
* A lightweight hook focused specifically on undo/redo operations.
|
|
203
|
+
* Useful when you want to add undo/redo to an existing QNCE integration.
|
|
204
|
+
*
|
|
205
|
+
* @param engine - The QNCE Engine instance
|
|
206
|
+
* @param config - Undo/redo configuration
|
|
207
|
+
* @returns Undo/redo state and actions
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* ```tsx
|
|
211
|
+
* function UndoRedoControls({ engine }: { engine: QNCEEngine }) {
|
|
212
|
+
* const { undo, redo, canUndo, canRedo, undoCount, redoCount } = useUndoRedo(engine);
|
|
213
|
+
*
|
|
214
|
+
* return (
|
|
215
|
+
* <div className="undo-redo-controls">
|
|
216
|
+
* <button onClick={undo} disabled={!canUndo}>
|
|
217
|
+
* ← Undo ({undoCount})
|
|
218
|
+
* </button>
|
|
219
|
+
* <button onClick={redo} disabled={!canRedo}>
|
|
220
|
+
* Redo ({redoCount}) →
|
|
221
|
+
* </button>
|
|
222
|
+
* </div>
|
|
223
|
+
* );
|
|
224
|
+
* }
|
|
225
|
+
* ```
|
|
226
|
+
*/
|
|
227
|
+
function useUndoRedo(engine, config = {}) {
|
|
228
|
+
const { maxUndoEntries = 50, maxRedoEntries = 25 } = config;
|
|
229
|
+
const [, setUpdateCounter] = (0, react_1.useState)(0);
|
|
230
|
+
// Configure undo/redo on mount
|
|
231
|
+
(0, react_1.useEffect)(() => {
|
|
232
|
+
engine.configureUndoRedo({
|
|
233
|
+
enabled: true,
|
|
234
|
+
maxUndoEntries,
|
|
235
|
+
maxRedoEntries
|
|
236
|
+
});
|
|
237
|
+
}, [engine, maxUndoEntries, maxRedoEntries]);
|
|
238
|
+
const refresh = (0, react_1.useCallback)(() => {
|
|
239
|
+
setUpdateCounter(prev => prev + 1);
|
|
240
|
+
}, []);
|
|
241
|
+
const undo = (0, react_1.useCallback)(() => {
|
|
242
|
+
const result = engine.undo();
|
|
243
|
+
refresh();
|
|
244
|
+
return result;
|
|
245
|
+
}, [engine, refresh]);
|
|
246
|
+
const redo = (0, react_1.useCallback)(() => {
|
|
247
|
+
const result = engine.redo();
|
|
248
|
+
refresh();
|
|
249
|
+
return result;
|
|
250
|
+
}, [engine, refresh]);
|
|
251
|
+
const clearHistory = (0, react_1.useCallback)(() => {
|
|
252
|
+
engine.clearHistory();
|
|
253
|
+
refresh();
|
|
254
|
+
}, [engine, refresh]);
|
|
255
|
+
const canUndo = (0, react_1.useMemo)(() => engine.canUndo(), [engine]);
|
|
256
|
+
const canRedo = (0, react_1.useMemo)(() => engine.canRedo(), [engine]);
|
|
257
|
+
const undoCount = (0, react_1.useMemo)(() => engine.getUndoCount(), [engine]);
|
|
258
|
+
const redoCount = (0, react_1.useMemo)(() => engine.getRedoCount(), [engine]);
|
|
259
|
+
const historySummary = (0, react_1.useMemo)(() => ({
|
|
260
|
+
undoCount,
|
|
261
|
+
redoCount,
|
|
262
|
+
canUndo,
|
|
263
|
+
canRedo
|
|
264
|
+
}), [undoCount, redoCount, canUndo, canRedo]);
|
|
265
|
+
return {
|
|
266
|
+
undo,
|
|
267
|
+
redo,
|
|
268
|
+
canUndo,
|
|
269
|
+
canRedo,
|
|
270
|
+
undoCount,
|
|
271
|
+
redoCount,
|
|
272
|
+
clearHistory,
|
|
273
|
+
historySummary
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Hook for managing autosave functionality
|
|
278
|
+
*
|
|
279
|
+
* Provides control over the autosave system for fine-grained management.
|
|
280
|
+
*
|
|
281
|
+
* @param engine - The QNCE Engine instance
|
|
282
|
+
* @param config - Autosave configuration
|
|
283
|
+
* @returns Autosave state and actions
|
|
284
|
+
*
|
|
285
|
+
* @example
|
|
286
|
+
* ```tsx
|
|
287
|
+
* function AutosaveIndicator({ engine }: { engine: QNCEEngine }) {
|
|
288
|
+
* const { autosave, configure, isEnabled } = useAutosave(engine, {
|
|
289
|
+
* throttleMs: 200,
|
|
290
|
+
* triggers: ['choice', 'flag-change']
|
|
291
|
+
* });
|
|
292
|
+
*
|
|
293
|
+
* return (
|
|
294
|
+
* <div>
|
|
295
|
+
* <span>Autosave: {isEnabled ? 'ON' : 'OFF'}</span>
|
|
296
|
+
* <button onClick={autosave}>Save Now</button>
|
|
297
|
+
* </div>
|
|
298
|
+
* );
|
|
299
|
+
* }
|
|
300
|
+
* ```
|
|
301
|
+
*/
|
|
302
|
+
function useAutosave(engine, config = {}) {
|
|
303
|
+
const { throttleMs = 100, triggers = ['choice', 'flag-change', 'state-load'], enabled = true } = config;
|
|
304
|
+
(0, react_1.useEffect)(() => {
|
|
305
|
+
engine.configureAutosave({
|
|
306
|
+
enabled,
|
|
307
|
+
throttleMs,
|
|
308
|
+
triggers: triggers
|
|
309
|
+
});
|
|
310
|
+
}, [engine, enabled, throttleMs, triggers]);
|
|
311
|
+
const autosave = (0, react_1.useCallback)(async () => {
|
|
312
|
+
await engine.manualAutosave();
|
|
313
|
+
}, [engine]);
|
|
314
|
+
const configure = (0, react_1.useCallback)((config) => {
|
|
315
|
+
engine.configureAutosave(config);
|
|
316
|
+
}, [engine]);
|
|
317
|
+
return {
|
|
318
|
+
autosave,
|
|
319
|
+
configure,
|
|
320
|
+
isEnabled: enabled
|
|
321
|
+
};
|
|
322
|
+
}
|