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,84 @@
|
|
|
1
|
+
import { createQNCEEngine, DEMO_STORY } from '../src';
|
|
2
|
+
import { Checkpoint } from '../src/engine/types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Demonstrates the state persistence and checkpoint features of the QNCE Engine.
|
|
6
|
+
* This example covers:
|
|
7
|
+
* 1. Saving the complete narrative state.
|
|
8
|
+
* 2. Loading that state into a new engine instance.
|
|
9
|
+
* 3. Creating lightweight checkpoints for undo/redo functionality.
|
|
10
|
+
* 4. Restoring the state from a checkpoint.
|
|
11
|
+
*/
|
|
12
|
+
async function runPersistenceDemo() {
|
|
13
|
+
console.log('🚀 Starting QNCE Engine Persistence Demo 🚀\n');
|
|
14
|
+
|
|
15
|
+
// 1. Initialize the engine and advance the story
|
|
16
|
+
const engine = createQNCEEngine(DEMO_STORY);
|
|
17
|
+
console.log('Initial Node:', engine.getCurrentNode().text);
|
|
18
|
+
|
|
19
|
+
let choices = engine.getAvailableChoices();
|
|
20
|
+
engine.selectChoice(choices[1]); // Choose "Examine the shimmering portal."
|
|
21
|
+
console.log('\nChoice Made. Current Node:', engine.getCurrentNode().text);
|
|
22
|
+
|
|
23
|
+
choices = engine.getAvailableChoices();
|
|
24
|
+
engine.selectChoice(choices[0]); // Choose "Step through the portal."
|
|
25
|
+
console.log('Choice Made. Current Node:', engine.getCurrentNode().text);
|
|
26
|
+
console.log('Current Flags:', engine.getFlags());
|
|
27
|
+
|
|
28
|
+
// 2. Create a checkpoint before making another choice
|
|
29
|
+
console.log('\n💾 Creating a checkpoint...');
|
|
30
|
+
const checkpoint = await engine.createCheckpoint('Before the final choice');
|
|
31
|
+
console.log(`Checkpoint '${checkpoint.name}' created with ID: ${checkpoint.id}`);
|
|
32
|
+
|
|
33
|
+
// 3. Save the complete engine state to a string
|
|
34
|
+
console.log('\n💾 Saving engine state to JSON...');
|
|
35
|
+
const savedState = await engine.saveState({ prettyPrint: true });
|
|
36
|
+
const savedStateJSON = JSON.stringify(savedState);
|
|
37
|
+
console.log('State saved successfully! Size:', savedStateJSON.length, 'bytes');
|
|
38
|
+
// In a real application, you would store this JSON string in localStorage,
|
|
39
|
+
// a file, or a remote database.
|
|
40
|
+
|
|
41
|
+
// 4. Continue the story
|
|
42
|
+
choices = engine.getAvailableChoices();
|
|
43
|
+
engine.selectChoice(choices[0]);
|
|
44
|
+
console.log('\nMade one more choice. Current Node:', engine.getCurrentNode().text);
|
|
45
|
+
console.log('Final Flags:', engine.getFlags());
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
// 5. Restore from the checkpoint to "undo" the last choice
|
|
49
|
+
console.log(`\n🔄 Restoring from checkpoint '${checkpoint.name}'...`);
|
|
50
|
+
await engine.restoreFromCheckpoint(checkpoint.id);
|
|
51
|
+
console.log('State restored from checkpoint!');
|
|
52
|
+
console.log('Current Node after restore:', engine.getCurrentNode().text);
|
|
53
|
+
console.log('Flags after restore:', engine.getFlags());
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
// 6. Create a new engine instance and load the saved state
|
|
57
|
+
console.log('\n🔄 Creating a new engine and loading the saved state...');
|
|
58
|
+
const newEngine = createQNCEEngine(DEMO_STORY);
|
|
59
|
+
|
|
60
|
+
// The state can be loaded from a JSON string or a parsed object
|
|
61
|
+
await newEngine.loadState(savedState);
|
|
62
|
+
console.log('State loaded into new engine instance!');
|
|
63
|
+
|
|
64
|
+
// 7. Verify that the state was restored correctly
|
|
65
|
+
console.log('\n🔍 Verifying restored state...');
|
|
66
|
+
console.log('Restored Node:', newEngine.getCurrentNode().text);
|
|
67
|
+
console.log('Restored Flags:', newEngine.getFlags());
|
|
68
|
+
console.log(
|
|
69
|
+
'History length:',
|
|
70
|
+
newEngine.getHistory().length,
|
|
71
|
+
'| Expected:',
|
|
72
|
+
2
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const areFlagsEqual = JSON.stringify(engine.getFlags()) === JSON.stringify(newEngine.getFlags());
|
|
76
|
+
console.log('Verification successful:', areFlagsEqual);
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
console.log('\n✅ Persistence Demo Completed Successfully! ✅');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
runPersistenceDemo().catch(error => {
|
|
83
|
+
console.error('An error occurred during the persistence demo:', error);
|
|
84
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// QNCE Branching Demo - JavaScript Quick Start
|
|
2
|
+
// Demonstrates v1.2.0 Advanced Branching API integration
|
|
3
|
+
|
|
4
|
+
const { createQNCEEngine } = require('../dist/engine/core');
|
|
5
|
+
|
|
6
|
+
console.log('🚀 QNCE Engine v1.2.0 - Advanced Branching Demo');
|
|
7
|
+
console.log('=================================================\n');
|
|
8
|
+
|
|
9
|
+
// Create a simple story for demonstration
|
|
10
|
+
const simpleStory = {
|
|
11
|
+
nodes: [
|
|
12
|
+
{
|
|
13
|
+
id: 'start',
|
|
14
|
+
text: 'You discover a mysterious quantum portal. What do you do?',
|
|
15
|
+
choices: [
|
|
16
|
+
{
|
|
17
|
+
text: 'Step through the portal',
|
|
18
|
+
nextNodeId: 'portal-world',
|
|
19
|
+
flagEffects: { courage: 5, portal_used: true }
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
text: 'Study the portal carefully',
|
|
23
|
+
nextNodeId: 'study-portal',
|
|
24
|
+
flagEffects: { wisdom: 5, careful: true }
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 'portal-world',
|
|
30
|
+
text: 'You emerge in a strange quantum realm where multiple realities overlap.',
|
|
31
|
+
choices: []
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: 'study-portal',
|
|
35
|
+
text: 'Your careful study reveals ancient quantum equations etched around the portal\'s edge.',
|
|
36
|
+
choices: []
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
initialNodeId: 'start'
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Create the QNCE engine
|
|
43
|
+
console.log('✅ Creating QNCE Engine...');
|
|
44
|
+
const engine = createQNCEEngine(simpleStory);
|
|
45
|
+
|
|
46
|
+
// Display current state
|
|
47
|
+
console.log('📖 Current Story Node:');
|
|
48
|
+
console.log(' ' + engine.getCurrentNode().text);
|
|
49
|
+
|
|
50
|
+
console.log('\n🎯 Available Choices:');
|
|
51
|
+
const choices = engine.getAvailableChoices();
|
|
52
|
+
choices.forEach((choice, i) => {
|
|
53
|
+
console.log(` ${i + 1}. ${choice.text}`);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Make a choice automatically for demo
|
|
57
|
+
if (choices.length > 0) {
|
|
58
|
+
console.log('\n🚀 Making choice: "' + choices[0].text + '"');
|
|
59
|
+
engine.selectChoice(choices[0]);
|
|
60
|
+
|
|
61
|
+
console.log('\n📖 New Story Node:');
|
|
62
|
+
console.log(' ' + engine.getCurrentNode().text);
|
|
63
|
+
|
|
64
|
+
console.log('\n🏴 Updated Flags:');
|
|
65
|
+
const flags = engine.getFlags();
|
|
66
|
+
Object.entries(flags).forEach(([key, value]) => {
|
|
67
|
+
console.log(` ${key}: ${value}`);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
console.log('\n📚 Choice History:');
|
|
71
|
+
const history = engine.getHistory();
|
|
72
|
+
history.forEach((nodeId, i) => {
|
|
73
|
+
console.log(` ${i + 1}. ${nodeId}`);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.log('\n🎉 Demo Complete!');
|
|
78
|
+
console.log('=================');
|
|
79
|
+
console.log('✅ QNCE Engine v1.2.0 basic functionality working');
|
|
80
|
+
console.log('📚 For advanced branching features, see TypeScript examples');
|
|
81
|
+
console.log('🔗 Advanced features: AI generation, dynamic branches, analytics');
|
|
82
|
+
console.log('\n🚀 Ready for your interactive narratives!');
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { createRoot } from 'react-dom/client';
|
|
3
|
+
import {
|
|
4
|
+
createQNCEEngine,
|
|
5
|
+
DEMO_STORY,
|
|
6
|
+
useQNCE,
|
|
7
|
+
UndoRedoControls,
|
|
8
|
+
AutosaveIndicator,
|
|
9
|
+
useKeyboardShortcuts
|
|
10
|
+
} from '../src/index';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* QNCE Engine UI Components Demo
|
|
14
|
+
* Sprint 3.6: Demonstrates autosave indicators, undo/redo controls, and keyboard shortcuts
|
|
15
|
+
*
|
|
16
|
+
* Features showcased:
|
|
17
|
+
* - UndoRedoControls component with different layouts and themes
|
|
18
|
+
* - AutosaveIndicator with various configurations
|
|
19
|
+
* - Keyboard shortcuts integration
|
|
20
|
+
* - Real-time state updates and visual feedback
|
|
21
|
+
* - Accessibility features
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const QNCEDemo: React.FC = () => {
|
|
25
|
+
const [engine] = useState(() => createQNCEEngine(DEMO_STORY));
|
|
26
|
+
const { currentNode, availableChoices, flags, selectChoice, resetNarrative } = useQNCE(engine);
|
|
27
|
+
|
|
28
|
+
// Enable keyboard shortcuts
|
|
29
|
+
useKeyboardShortcuts(engine, {
|
|
30
|
+
enabled: true,
|
|
31
|
+
bindings: {
|
|
32
|
+
undo: ['ctrl+z', 'cmd+z'],
|
|
33
|
+
redo: ['ctrl+y', 'cmd+y', 'ctrl+shift+z'],
|
|
34
|
+
save: ['ctrl+s', 'cmd+s'],
|
|
35
|
+
reset: ['ctrl+r'] // Enable reset with confirmation
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const [showAdvanced, setShowAdvanced] = useState(false);
|
|
40
|
+
|
|
41
|
+
const appStyle: React.CSSProperties = {
|
|
42
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
|
|
43
|
+
maxWidth: '800px',
|
|
44
|
+
margin: '0 auto',
|
|
45
|
+
padding: '2rem',
|
|
46
|
+
lineHeight: 1.6,
|
|
47
|
+
color: '#333'
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const headerStyle: React.CSSProperties = {
|
|
51
|
+
textAlign: 'center',
|
|
52
|
+
marginBottom: '2rem',
|
|
53
|
+
padding: '1rem',
|
|
54
|
+
backgroundColor: '#f8f9fa',
|
|
55
|
+
borderRadius: '8px',
|
|
56
|
+
border: '1px solid #e9ecef'
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const sectionStyle: React.CSSProperties = {
|
|
60
|
+
marginBottom: '2rem',
|
|
61
|
+
padding: '1rem',
|
|
62
|
+
backgroundColor: '#ffffff',
|
|
63
|
+
borderRadius: '8px',
|
|
64
|
+
border: '1px solid #e9ecef',
|
|
65
|
+
boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const narrativeStyle: React.CSSProperties = {
|
|
69
|
+
fontSize: '1.1rem',
|
|
70
|
+
lineHeight: 1.8,
|
|
71
|
+
marginBottom: '1rem',
|
|
72
|
+
padding: '1rem',
|
|
73
|
+
backgroundColor: '#f8f9fa',
|
|
74
|
+
borderLeft: '4px solid #007bff',
|
|
75
|
+
borderRadius: '0 4px 4px 0'
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const choiceButtonStyle: React.CSSProperties = {
|
|
79
|
+
display: 'block',
|
|
80
|
+
width: '100%',
|
|
81
|
+
padding: '0.75rem 1rem',
|
|
82
|
+
margin: '0.5rem 0',
|
|
83
|
+
fontSize: '1rem',
|
|
84
|
+
backgroundColor: '#007bff',
|
|
85
|
+
color: 'white',
|
|
86
|
+
border: 'none',
|
|
87
|
+
borderRadius: '4px',
|
|
88
|
+
cursor: 'pointer',
|
|
89
|
+
transition: 'background-color 0.2s'
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const controlsContainerStyle: React.CSSProperties = {
|
|
93
|
+
display: 'flex',
|
|
94
|
+
gap: '1rem',
|
|
95
|
+
alignItems: 'center',
|
|
96
|
+
flexWrap: 'wrap',
|
|
97
|
+
padding: '1rem',
|
|
98
|
+
backgroundColor: '#f8f9fa',
|
|
99
|
+
borderRadius: '8px',
|
|
100
|
+
marginBottom: '1rem'
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const flagsStyle: React.CSSProperties = {
|
|
104
|
+
fontSize: '0.9rem',
|
|
105
|
+
color: '#6c757d',
|
|
106
|
+
backgroundColor: '#e9ecef',
|
|
107
|
+
padding: '0.5rem',
|
|
108
|
+
borderRadius: '4px',
|
|
109
|
+
fontFamily: 'monospace'
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<div style={appStyle}>
|
|
114
|
+
{/* Header */}
|
|
115
|
+
<header style={headerStyle}>
|
|
116
|
+
<h1>🎮 QNCE Engine UI Components Demo</h1>
|
|
117
|
+
<p>Sprint 3.6: Interactive showcase of autosave indicators, undo/redo controls, and keyboard shortcuts</p>
|
|
118
|
+
<p style={{ fontSize: '0.9rem', color: '#6c757d' }}>
|
|
119
|
+
<strong>Keyboard shortcuts:</strong> Ctrl/Cmd+Z (Undo), Ctrl/Cmd+Y (Redo), Ctrl/Cmd+S (Save)
|
|
120
|
+
</p>
|
|
121
|
+
</header>
|
|
122
|
+
|
|
123
|
+
{/* Controls Section */}
|
|
124
|
+
<section style={sectionStyle}>
|
|
125
|
+
<h2>🎛️ Controls & Status</h2>
|
|
126
|
+
<div style={controlsContainerStyle}>
|
|
127
|
+
{/* Autosave Indicator */}
|
|
128
|
+
<AutosaveIndicator
|
|
129
|
+
engine={engine}
|
|
130
|
+
variant="detailed"
|
|
131
|
+
showTimestamp={true}
|
|
132
|
+
autoHideDelay={3000}
|
|
133
|
+
/>
|
|
134
|
+
|
|
135
|
+
{/* Undo/Redo Controls - Horizontal Layout */}
|
|
136
|
+
<UndoRedoControls
|
|
137
|
+
engine={engine}
|
|
138
|
+
size="md"
|
|
139
|
+
layout="horizontal"
|
|
140
|
+
showLabels={true}
|
|
141
|
+
onUndo={() => console.log('Undo performed')}
|
|
142
|
+
onRedo={() => console.log('Redo performed')}
|
|
143
|
+
/>
|
|
144
|
+
|
|
145
|
+
{/* Reset Button */}
|
|
146
|
+
<button
|
|
147
|
+
onClick={resetNarrative}
|
|
148
|
+
style={{
|
|
149
|
+
padding: '0.5rem 1rem',
|
|
150
|
+
backgroundColor: '#dc3545',
|
|
151
|
+
color: 'white',
|
|
152
|
+
border: 'none',
|
|
153
|
+
borderRadius: '4px',
|
|
154
|
+
cursor: 'pointer'
|
|
155
|
+
}}
|
|
156
|
+
>
|
|
157
|
+
Reset
|
|
158
|
+
</button>
|
|
159
|
+
|
|
160
|
+
{/* Toggle Advanced View */}
|
|
161
|
+
<button
|
|
162
|
+
onClick={() => setShowAdvanced(!showAdvanced)}
|
|
163
|
+
style={{
|
|
164
|
+
padding: '0.5rem 1rem',
|
|
165
|
+
backgroundColor: '#6c757d',
|
|
166
|
+
color: 'white',
|
|
167
|
+
border: 'none',
|
|
168
|
+
borderRadius: '4px',
|
|
169
|
+
cursor: 'pointer'
|
|
170
|
+
}}
|
|
171
|
+
>
|
|
172
|
+
{showAdvanced ? 'Hide' : 'Show'} Advanced
|
|
173
|
+
</button>
|
|
174
|
+
</div>
|
|
175
|
+
</section>
|
|
176
|
+
|
|
177
|
+
{/* Advanced Controls (when enabled) */}
|
|
178
|
+
{showAdvanced && (
|
|
179
|
+
<section style={sectionStyle}>
|
|
180
|
+
<h3>🔧 Advanced Controls</h3>
|
|
181
|
+
<div style={{ display: 'flex', gap: '1rem', alignItems: 'center', flexWrap: 'wrap' }}>
|
|
182
|
+
{/* Vertical Layout Undo/Redo */}
|
|
183
|
+
<div>
|
|
184
|
+
<h4 style={{ margin: '0 0 0.5rem 0', fontSize: '0.9rem' }}>Vertical Layout</h4>
|
|
185
|
+
<UndoRedoControls
|
|
186
|
+
engine={engine}
|
|
187
|
+
size="sm"
|
|
188
|
+
layout="vertical"
|
|
189
|
+
showLabels={false}
|
|
190
|
+
/>
|
|
191
|
+
</div>
|
|
192
|
+
|
|
193
|
+
{/* Large Size Controls */}
|
|
194
|
+
<div>
|
|
195
|
+
<h4 style={{ margin: '0 0 0.5rem 0', fontSize: '0.9rem' }}>Large Size</h4>
|
|
196
|
+
<UndoRedoControls
|
|
197
|
+
engine={engine}
|
|
198
|
+
size="lg"
|
|
199
|
+
layout="horizontal"
|
|
200
|
+
showLabels={true}
|
|
201
|
+
theme={{
|
|
202
|
+
colors: {
|
|
203
|
+
primary: '#28a745',
|
|
204
|
+
background: '#ffffff'
|
|
205
|
+
}
|
|
206
|
+
}}
|
|
207
|
+
/>
|
|
208
|
+
</div>
|
|
209
|
+
|
|
210
|
+
{/* Icon-only Autosave */}
|
|
211
|
+
<div>
|
|
212
|
+
<h4 style={{ margin: '0 0 0.5rem 0', fontSize: '0.9rem' }}>Icon-only</h4>
|
|
213
|
+
<AutosaveIndicator
|
|
214
|
+
engine={engine}
|
|
215
|
+
variant="icon-only"
|
|
216
|
+
showTimestamp={false}
|
|
217
|
+
/>
|
|
218
|
+
</div>
|
|
219
|
+
|
|
220
|
+
{/* Minimal Autosave */}
|
|
221
|
+
<div>
|
|
222
|
+
<h4 style={{ margin: '0 0 0.5rem 0', fontSize: '0.9rem' }}>Minimal</h4>
|
|
223
|
+
<AutosaveIndicator
|
|
224
|
+
engine={engine}
|
|
225
|
+
variant="minimal"
|
|
226
|
+
showTimestamp={false}
|
|
227
|
+
theme={{
|
|
228
|
+
colors: {
|
|
229
|
+
success: '#17a2b8',
|
|
230
|
+
warning: '#ffc107'
|
|
231
|
+
}
|
|
232
|
+
}}
|
|
233
|
+
/>
|
|
234
|
+
</div>
|
|
235
|
+
</div>
|
|
236
|
+
</section>
|
|
237
|
+
)}
|
|
238
|
+
|
|
239
|
+
{/* Narrative Section */}
|
|
240
|
+
<section style={sectionStyle}>
|
|
241
|
+
<h2>📖 Current Scene</h2>
|
|
242
|
+
<div style={narrativeStyle}>
|
|
243
|
+
{currentNode.text}
|
|
244
|
+
</div>
|
|
245
|
+
|
|
246
|
+
{/* Choices */}
|
|
247
|
+
{availableChoices.length > 0 && (
|
|
248
|
+
<div>
|
|
249
|
+
<h3>Choose your path:</h3>
|
|
250
|
+
{availableChoices.map((choice, index) => (
|
|
251
|
+
<button
|
|
252
|
+
key={index}
|
|
253
|
+
onClick={() => selectChoice(choice)}
|
|
254
|
+
style={choiceButtonStyle}
|
|
255
|
+
onMouseEnter={(e) => {
|
|
256
|
+
e.currentTarget.style.backgroundColor = '#0056b3';
|
|
257
|
+
}}
|
|
258
|
+
onMouseLeave={(e) => {
|
|
259
|
+
e.currentTarget.style.backgroundColor = '#007bff';
|
|
260
|
+
}}
|
|
261
|
+
>
|
|
262
|
+
{choice.text}
|
|
263
|
+
</button>
|
|
264
|
+
))}
|
|
265
|
+
</div>
|
|
266
|
+
)}
|
|
267
|
+
|
|
268
|
+
{/* End of story message */}
|
|
269
|
+
{availableChoices.length === 0 && (
|
|
270
|
+
<div style={{ textAlign: 'center', padding: '2rem' }}>
|
|
271
|
+
<h3>🎊 The End</h3>
|
|
272
|
+
<p>You've reached the end of this narrative path.</p>
|
|
273
|
+
<button
|
|
274
|
+
onClick={resetNarrative}
|
|
275
|
+
style={{
|
|
276
|
+
...choiceButtonStyle,
|
|
277
|
+
backgroundColor: '#28a745',
|
|
278
|
+
width: 'auto',
|
|
279
|
+
display: 'inline-block'
|
|
280
|
+
}}
|
|
281
|
+
>
|
|
282
|
+
Start Over
|
|
283
|
+
</button>
|
|
284
|
+
</div>
|
|
285
|
+
)}
|
|
286
|
+
</section>
|
|
287
|
+
|
|
288
|
+
{/* Story State Info */}
|
|
289
|
+
<section style={sectionStyle}>
|
|
290
|
+
<h3>📊 Story State</h3>
|
|
291
|
+
<div style={flagsStyle}>
|
|
292
|
+
<strong>Current Node:</strong> {currentNode.id}<br/>
|
|
293
|
+
<strong>Flags:</strong> {Object.keys(flags).length > 0 ? JSON.stringify(flags, null, 2) : 'None set'}
|
|
294
|
+
</div>
|
|
295
|
+
</section>
|
|
296
|
+
|
|
297
|
+
{/* Instructions */}
|
|
298
|
+
<section style={sectionStyle}>
|
|
299
|
+
<h3>💡 Usage Instructions</h3>
|
|
300
|
+
<ul style={{ lineHeight: 1.8 }}>
|
|
301
|
+
<li><strong>Make choices</strong> to progress through the story and see autosave indicators</li>
|
|
302
|
+
<li><strong>Use keyboard shortcuts:</strong> Ctrl/Cmd+Z to undo, Ctrl/Cmd+Y to redo, Ctrl/Cmd+S to save</li>
|
|
303
|
+
<li><strong>Watch the autosave indicator</strong> show status changes in real-time</li>
|
|
304
|
+
<li><strong>Try the undo/redo buttons</strong> to navigate through your choice history</li>
|
|
305
|
+
<li><strong>Toggle advanced view</strong> to see different component variations</li>
|
|
306
|
+
<li><strong>All components are accessible</strong> with keyboard navigation and screen readers</li>
|
|
307
|
+
</ul>
|
|
308
|
+
</section>
|
|
309
|
+
</div>
|
|
310
|
+
);
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
// Initialize the demo
|
|
314
|
+
const container = document.getElementById('root');
|
|
315
|
+
if (container) {
|
|
316
|
+
const root = createRoot(container);
|
|
317
|
+
root.render(<QNCEDemo />);
|
|
318
|
+
} else {
|
|
319
|
+
console.error('Root container not found');
|
|
320
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
{
|
|
2
|
+
"initialNodeId": "entrance",
|
|
3
|
+
"nodes": [
|
|
4
|
+
{
|
|
5
|
+
"id": "entrance",
|
|
6
|
+
"text": "You stand before an ancient castle. The heavy oak doors are locked, and storm clouds gather overhead.",
|
|
7
|
+
"choices": [
|
|
8
|
+
{
|
|
9
|
+
"text": "Try to force the door open",
|
|
10
|
+
"nextNodeId": "door_attempt"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"text": "Use the golden key",
|
|
14
|
+
"nextNodeId": "unlock_door",
|
|
15
|
+
"flagRequirements": {
|
|
16
|
+
"hasGoldenKey": true
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"text": "Wait outside in the storm",
|
|
21
|
+
"nextNodeId": "storm_wait",
|
|
22
|
+
"enabled": false
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"text": "Purchase a lockpick from the merchant",
|
|
26
|
+
"nextNodeId": "buy_lockpick",
|
|
27
|
+
"inventoryRequirements": {
|
|
28
|
+
"gold": 25,
|
|
29
|
+
"reputation": 5
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"text": "Visit the tavern (opens at sunset)",
|
|
34
|
+
"nextNodeId": "tavern",
|
|
35
|
+
"timeRequirements": {
|
|
36
|
+
"availableAfter": "2025-01-01T18:00:00.000Z",
|
|
37
|
+
"availableBefore": "2025-01-01T23:59:59.000Z"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"id": "door_attempt",
|
|
44
|
+
"text": "You push against the heavy doors, but they remain firmly shut. Your shoulder aches from the effort.",
|
|
45
|
+
"choices": [
|
|
46
|
+
{
|
|
47
|
+
"text": "Return to the entrance",
|
|
48
|
+
"nextNodeId": "entrance"
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"id": "unlock_door",
|
|
54
|
+
"text": "The golden key turns smoothly in the lock. The doors swing open with a satisfying creak, revealing a grand hallway beyond.",
|
|
55
|
+
"choices": [
|
|
56
|
+
{
|
|
57
|
+
"text": "Enter the castle",
|
|
58
|
+
"nextNodeId": "grand_hall",
|
|
59
|
+
"flagEffects": {
|
|
60
|
+
"enteredCastle": true
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"id": "storm_wait",
|
|
67
|
+
"text": "You decide to wait outside, but this choice should never be reachable due to validation.",
|
|
68
|
+
"choices": []
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"id": "buy_lockpick",
|
|
72
|
+
"text": "The merchant smiles as you purchase a masterfully crafted lockpick. 'This will open any door,' he promises.",
|
|
73
|
+
"choices": [
|
|
74
|
+
{
|
|
75
|
+
"text": "Return to the entrance with lockpick",
|
|
76
|
+
"nextNodeId": "entrance",
|
|
77
|
+
"flagEffects": {
|
|
78
|
+
"hasLockpick": true
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"id": "tavern",
|
|
85
|
+
"text": "The warm tavern glows with candlelight. Travelers share stories and the aroma of roasted meat fills the air.",
|
|
86
|
+
"choices": [
|
|
87
|
+
{
|
|
88
|
+
"text": "Listen to the bard's tale",
|
|
89
|
+
"nextNodeId": "bard_story"
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"text": "Ask about the castle",
|
|
93
|
+
"nextNodeId": "castle_info"
|
|
94
|
+
}
|
|
95
|
+
]
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"id": "grand_hall",
|
|
99
|
+
"text": "You stand in an opulent grand hall with marble columns and tapestries depicting ancient battles.",
|
|
100
|
+
"choices": [
|
|
101
|
+
{
|
|
102
|
+
"text": "Explore the east wing",
|
|
103
|
+
"nextNodeId": "east_wing"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"text": "Climb the grand staircase",
|
|
107
|
+
"nextNodeId": "upper_floor"
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"id": "bard_story",
|
|
113
|
+
"text": "The bard tells of a legendary treasure hidden deep within the castle, guarded by ancient magic.",
|
|
114
|
+
"choices": [
|
|
115
|
+
{
|
|
116
|
+
"text": "Ask about the treasure",
|
|
117
|
+
"nextNodeId": "treasure_info",
|
|
118
|
+
"flagEffects": {
|
|
119
|
+
"knowsAboutTreasure": true
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"id": "castle_info",
|
|
126
|
+
"text": "A grizzled traveler warns you about the castle's dangers but mentions a golden key hidden in the forest.",
|
|
127
|
+
"choices": [
|
|
128
|
+
{
|
|
129
|
+
"text": "Ask where to find the key",
|
|
130
|
+
"nextNodeId": "key_location",
|
|
131
|
+
"flagEffects": {
|
|
132
|
+
"knowsKeyLocation": true
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
]
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"id": "treasure_info",
|
|
139
|
+
"text": "The bard leans closer and whispers the secret of the treasure's location...",
|
|
140
|
+
"choices": []
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"id": "key_location",
|
|
144
|
+
"text": "The traveler draws a rough map on a napkin, marking where the golden key lies buried.",
|
|
145
|
+
"choices": [
|
|
146
|
+
{
|
|
147
|
+
"text": "Search for the key",
|
|
148
|
+
"nextNodeId": "find_key",
|
|
149
|
+
"flagEffects": {
|
|
150
|
+
"hasGoldenKey": true,
|
|
151
|
+
"foundKey": true
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
]
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"id": "find_key",
|
|
158
|
+
"text": "After searching through the undergrowth, your fingers close around a cold, golden key. Success!",
|
|
159
|
+
"choices": [
|
|
160
|
+
{
|
|
161
|
+
"text": "Return to the castle",
|
|
162
|
+
"nextNodeId": "entrance"
|
|
163
|
+
}
|
|
164
|
+
]
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"id": "east_wing",
|
|
168
|
+
"text": "The east wing is filled with ancient artifacts and dusty paintings.",
|
|
169
|
+
"choices": []
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
"id": "upper_floor",
|
|
173
|
+
"text": "The upper floor overlooks the grand hall. Moonlight streams through stained glass windows.",
|
|
174
|
+
"choices": []
|
|
175
|
+
}
|
|
176
|
+
]
|
|
177
|
+
}
|