@magicborn/dialogue-forge 0.1.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/README.md +233 -0
- package/bin/dialogue-forge.js +78 -0
- package/demo/app/layout.tsx +36 -0
- package/demo/app/page.tsx +440 -0
- package/demo/components/ThemeSwitcher.tsx +611 -0
- package/demo/next.config.mjs +7 -0
- package/demo/package.json +29 -0
- package/demo/postcss.config.mjs +7 -0
- package/demo/public/logo.svg +1 -0
- package/demo/styles/globals.css +19 -0
- package/demo/tailwind.config.ts +90 -0
- package/demo/tsconfig.json +42 -0
- package/dist/components/ChoiceEdgeV2.d.ts +3 -0
- package/dist/components/ChoiceEdgeV2.js +103 -0
- package/dist/components/CodeBlock.d.ts +8 -0
- package/dist/components/CodeBlock.js +24 -0
- package/dist/components/ConditionAutocomplete.d.ts +14 -0
- package/dist/components/ConditionAutocomplete.js +284 -0
- package/dist/components/ConditionalNodeV2.d.ts +16 -0
- package/dist/components/ConditionalNodeV2.js +147 -0
- package/dist/components/DialogueEditorV2.d.ts +22 -0
- package/dist/components/DialogueEditorV2.js +1170 -0
- package/dist/components/EdgeIcon.d.ts +8 -0
- package/dist/components/EdgeIcon.js +13 -0
- package/dist/components/ExampleLoader.d.ts +11 -0
- package/dist/components/ExampleLoader.js +52 -0
- package/dist/components/ExampleLoaderButton.d.ts +15 -0
- package/dist/components/ExampleLoaderButton.js +102 -0
- package/dist/components/FlagManager.d.ts +11 -0
- package/dist/components/FlagManager.js +282 -0
- package/dist/components/FlagSelector.d.ts +11 -0
- package/dist/components/FlagSelector.js +235 -0
- package/dist/components/GuidePanel.d.ts +7 -0
- package/dist/components/GuidePanel.js +1176 -0
- package/dist/components/Minimap.d.ts +16 -0
- package/dist/components/Minimap.js +93 -0
- package/dist/components/NPCEdgeV2.d.ts +3 -0
- package/dist/components/NPCEdgeV2.js +104 -0
- package/dist/components/NPCNodeV2.d.ts +26 -0
- package/dist/components/NPCNodeV2.js +86 -0
- package/dist/components/NodeEditor.d.ts +18 -0
- package/dist/components/NodeEditor.js +1025 -0
- package/dist/components/PlayView.d.ts +12 -0
- package/dist/components/PlayView.js +307 -0
- package/dist/components/PlayerNodeV2.d.ts +16 -0
- package/dist/components/PlayerNodeV2.js +139 -0
- package/dist/components/ReactFlowPOC.d.ts +61 -0
- package/dist/components/ReactFlowPOC.js +312 -0
- package/dist/components/ScenePlayer.d.ts +18 -0
- package/dist/components/ScenePlayer.js +196 -0
- package/dist/components/YarnView.d.ts +9 -0
- package/dist/components/YarnView.js +45 -0
- package/dist/components/ZoomControls.d.ts +11 -0
- package/dist/components/ZoomControls.js +34 -0
- package/dist/esm/components/ChoiceEdgeV2.d.ts +3 -0
- package/dist/esm/components/ChoiceEdgeV2.js +67 -0
- package/dist/esm/components/CodeBlock.d.ts +8 -0
- package/dist/esm/components/CodeBlock.js +18 -0
- package/dist/esm/components/ConditionAutocomplete.d.ts +14 -0
- package/dist/esm/components/ConditionAutocomplete.js +248 -0
- package/dist/esm/components/ConditionalNodeV2.d.ts +16 -0
- package/dist/esm/components/ConditionalNodeV2.js +111 -0
- package/dist/esm/components/DialogueEditorV2.d.ts +22 -0
- package/dist/esm/components/DialogueEditorV2.js +1134 -0
- package/dist/esm/components/EdgeIcon.d.ts +8 -0
- package/dist/esm/components/EdgeIcon.js +7 -0
- package/dist/esm/components/ExampleLoader.d.ts +11 -0
- package/dist/esm/components/ExampleLoader.js +46 -0
- package/dist/esm/components/ExampleLoaderButton.d.ts +15 -0
- package/dist/esm/components/ExampleLoaderButton.js +66 -0
- package/dist/esm/components/FlagManager.d.ts +11 -0
- package/dist/esm/components/FlagManager.js +246 -0
- package/dist/esm/components/FlagSelector.d.ts +11 -0
- package/dist/esm/components/FlagSelector.js +199 -0
- package/dist/esm/components/GuidePanel.d.ts +7 -0
- package/dist/esm/components/GuidePanel.js +1140 -0
- package/dist/esm/components/Minimap.d.ts +16 -0
- package/dist/esm/components/Minimap.js +57 -0
- package/dist/esm/components/NPCEdgeV2.d.ts +3 -0
- package/dist/esm/components/NPCEdgeV2.js +68 -0
- package/dist/esm/components/NPCNodeV2.d.ts +26 -0
- package/dist/esm/components/NPCNodeV2.js +80 -0
- package/dist/esm/components/NodeEditor.d.ts +18 -0
- package/dist/esm/components/NodeEditor.js +989 -0
- package/dist/esm/components/PlayView.d.ts +12 -0
- package/dist/esm/components/PlayView.js +271 -0
- package/dist/esm/components/PlayerNodeV2.d.ts +16 -0
- package/dist/esm/components/PlayerNodeV2.js +103 -0
- package/dist/esm/components/ReactFlowPOC.d.ts +61 -0
- package/dist/esm/components/ReactFlowPOC.js +275 -0
- package/dist/esm/components/ScenePlayer.d.ts +18 -0
- package/dist/esm/components/ScenePlayer.js +160 -0
- package/dist/esm/components/YarnView.d.ts +9 -0
- package/dist/esm/components/YarnView.js +39 -0
- package/dist/esm/components/ZoomControls.d.ts +11 -0
- package/dist/esm/components/ZoomControls.js +28 -0
- package/dist/esm/examples/example-loader.d.ts +29 -0
- package/dist/esm/examples/example-loader.js +103 -0
- package/dist/esm/examples/examples-registry.d.ts +38 -0
- package/dist/esm/examples/examples-registry.js +153 -0
- package/dist/esm/examples/index.d.ts +26 -0
- package/dist/esm/examples/index.js +50 -0
- package/dist/esm/examples/legacy-examples.d.ts +9 -0
- package/dist/esm/examples/legacy-examples.js +814 -0
- package/dist/esm/examples/yarn-examples.d.ts +35 -0
- package/dist/esm/examples/yarn-examples.js +181 -0
- package/dist/esm/index.d.ts +21 -0
- package/dist/esm/index.js +26 -0
- package/dist/esm/lib/flag-manager.d.ts +21 -0
- package/dist/esm/lib/flag-manager.js +93 -0
- package/dist/esm/lib/yarn-converter/__tests__/round-trip.test.d.ts +1 -0
- package/dist/esm/lib/yarn-converter/__tests__/round-trip.test.js +169 -0
- package/dist/esm/lib/yarn-converter.d.ts +17 -0
- package/dist/esm/lib/yarn-converter.js +521 -0
- package/dist/esm/lib/yarn-runner/__tests__/condition-evaluator.test.d.ts +1 -0
- package/dist/esm/lib/yarn-runner/__tests__/condition-evaluator.test.js +171 -0
- package/dist/esm/lib/yarn-runner/__tests__/node-processor.test.d.ts +1 -0
- package/dist/esm/lib/yarn-runner/__tests__/node-processor.test.js +237 -0
- package/dist/esm/lib/yarn-runner/__tests__/variable-manager.test.d.ts +1 -0
- package/dist/esm/lib/yarn-runner/__tests__/variable-manager.test.js +106 -0
- package/dist/esm/lib/yarn-runner/condition-evaluator.d.ts +12 -0
- package/dist/esm/lib/yarn-runner/condition-evaluator.js +56 -0
- package/dist/esm/lib/yarn-runner/index.d.ts +12 -0
- package/dist/esm/lib/yarn-runner/index.js +11 -0
- package/dist/esm/lib/yarn-runner/node-processor.d.ts +18 -0
- package/dist/esm/lib/yarn-runner/node-processor.js +129 -0
- package/dist/esm/lib/yarn-runner/variable-manager.d.ts +51 -0
- package/dist/esm/lib/yarn-runner/variable-manager.js +120 -0
- package/dist/esm/lib/yarn-runner/variable-operations.d.ts +16 -0
- package/dist/esm/lib/yarn-runner/variable-operations.js +88 -0
- package/dist/esm/types/conditionals.d.ts +29 -0
- package/dist/esm/types/conditionals.js +1 -0
- package/dist/esm/types/constants.d.ts +59 -0
- package/dist/esm/types/constants.js +55 -0
- package/dist/esm/types/flags.d.ts +49 -0
- package/dist/esm/types/flags.js +49 -0
- package/dist/esm/types/game-state.d.ts +62 -0
- package/dist/esm/types/game-state.js +6 -0
- package/dist/esm/types/index.d.ts +77 -0
- package/dist/esm/types/index.js +1 -0
- package/dist/esm/utils/constants.d.ts +5 -0
- package/dist/esm/utils/constants.js +5 -0
- package/dist/esm/utils/feature-flags.d.ts +11 -0
- package/dist/esm/utils/feature-flags.js +11 -0
- package/dist/esm/utils/game-state-flattener.d.ts +41 -0
- package/dist/esm/utils/game-state-flattener.js +135 -0
- package/dist/esm/utils/layout/collision.d.ts +27 -0
- package/dist/esm/utils/layout/collision.js +74 -0
- package/dist/esm/utils/layout/index.d.ts +82 -0
- package/dist/esm/utils/layout/index.js +98 -0
- package/dist/esm/utils/layout/registry.d.ts +91 -0
- package/dist/esm/utils/layout/registry.js +148 -0
- package/dist/esm/utils/layout/strategies/dagre.d.ts +19 -0
- package/dist/esm/utils/layout/strategies/dagre.js +182 -0
- package/dist/esm/utils/layout/strategies/force.d.ts +21 -0
- package/dist/esm/utils/layout/strategies/force.js +178 -0
- package/dist/esm/utils/layout/strategies/grid.d.ts +17 -0
- package/dist/esm/utils/layout/strategies/grid.js +91 -0
- package/dist/esm/utils/layout/strategies/index.d.ts +8 -0
- package/dist/esm/utils/layout/strategies/index.js +8 -0
- package/dist/esm/utils/layout/types.d.ts +100 -0
- package/dist/esm/utils/layout/types.js +7 -0
- package/dist/esm/utils/layout.d.ts +9 -0
- package/dist/esm/utils/layout.js +17 -0
- package/dist/esm/utils/node-helpers.d.ts +7 -0
- package/dist/esm/utils/node-helpers.js +94 -0
- package/dist/esm/utils/reactflow-converter.d.ts +42 -0
- package/dist/esm/utils/reactflow-converter.js +217 -0
- package/dist/examples/example-loader.d.ts +29 -0
- package/dist/examples/example-loader.js +109 -0
- package/dist/examples/examples-registry.d.ts +38 -0
- package/dist/examples/examples-registry.js +160 -0
- package/dist/examples/index.d.ts +26 -0
- package/dist/examples/index.js +63 -0
- package/dist/examples/legacy-examples.d.ts +9 -0
- package/dist/examples/legacy-examples.js +817 -0
- package/dist/examples/yarn-examples.d.ts +35 -0
- package/dist/examples/yarn-examples.js +189 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +66 -0
- package/dist/lib/flag-manager.d.ts +21 -0
- package/dist/lib/flag-manager.js +99 -0
- package/dist/lib/yarn-converter/__tests__/round-trip.test.d.ts +1 -0
- package/dist/lib/yarn-converter/__tests__/round-trip.test.js +171 -0
- package/dist/lib/yarn-converter.d.ts +17 -0
- package/dist/lib/yarn-converter.js +525 -0
- package/dist/lib/yarn-runner/__tests__/condition-evaluator.test.d.ts +1 -0
- package/dist/lib/yarn-runner/__tests__/condition-evaluator.test.js +173 -0
- package/dist/lib/yarn-runner/__tests__/node-processor.test.d.ts +1 -0
- package/dist/lib/yarn-runner/__tests__/node-processor.test.js +239 -0
- package/dist/lib/yarn-runner/__tests__/variable-manager.test.d.ts +1 -0
- package/dist/lib/yarn-runner/__tests__/variable-manager.test.js +108 -0
- package/dist/lib/yarn-runner/condition-evaluator.d.ts +12 -0
- package/dist/lib/yarn-runner/condition-evaluator.js +60 -0
- package/dist/lib/yarn-runner/index.d.ts +12 -0
- package/dist/lib/yarn-runner/index.js +21 -0
- package/dist/lib/yarn-runner/node-processor.d.ts +18 -0
- package/dist/lib/yarn-runner/node-processor.js +133 -0
- package/dist/lib/yarn-runner/variable-manager.d.ts +51 -0
- package/dist/lib/yarn-runner/variable-manager.js +124 -0
- package/dist/lib/yarn-runner/variable-operations.d.ts +16 -0
- package/dist/lib/yarn-runner/variable-operations.js +92 -0
- package/dist/types/conditionals.d.ts +29 -0
- package/dist/types/conditionals.js +2 -0
- package/dist/types/constants.d.ts +59 -0
- package/dist/types/constants.js +58 -0
- package/dist/types/flags.d.ts +49 -0
- package/dist/types/flags.js +52 -0
- package/dist/types/game-state.d.ts +62 -0
- package/dist/types/game-state.js +7 -0
- package/dist/types/index.d.ts +77 -0
- package/dist/types/index.js +2 -0
- package/dist/utils/constants.d.ts +5 -0
- package/dist/utils/constants.js +8 -0
- package/dist/utils/feature-flags.d.ts +11 -0
- package/dist/utils/feature-flags.js +14 -0
- package/dist/utils/game-state-flattener.d.ts +41 -0
- package/dist/utils/game-state-flattener.js +140 -0
- package/dist/utils/layout/collision.d.ts +27 -0
- package/dist/utils/layout/collision.js +77 -0
- package/dist/utils/layout/index.d.ts +82 -0
- package/dist/utils/layout/index.js +109 -0
- package/dist/utils/layout/registry.d.ts +91 -0
- package/dist/utils/layout/registry.js +151 -0
- package/dist/utils/layout/strategies/dagre.d.ts +19 -0
- package/dist/utils/layout/strategies/dagre.js +189 -0
- package/dist/utils/layout/strategies/force.d.ts +21 -0
- package/dist/utils/layout/strategies/force.js +182 -0
- package/dist/utils/layout/strategies/grid.d.ts +17 -0
- package/dist/utils/layout/strategies/grid.js +95 -0
- package/dist/utils/layout/strategies/index.d.ts +8 -0
- package/dist/utils/layout/strategies/index.js +14 -0
- package/dist/utils/layout/types.d.ts +100 -0
- package/dist/utils/layout/types.js +8 -0
- package/dist/utils/layout.d.ts +9 -0
- package/dist/utils/layout.js +25 -0
- package/dist/utils/node-helpers.d.ts +7 -0
- package/dist/utils/node-helpers.js +101 -0
- package/dist/utils/reactflow-converter.d.ts +42 -0
- package/dist/utils/reactflow-converter.js +223 -0
- package/package.json +70 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { DialogueTree } from '../types';
|
|
3
|
+
import { FlagSchema } from '../types/flags';
|
|
4
|
+
import { GameFlagState } from '../types/game-state';
|
|
5
|
+
interface PlayViewProps {
|
|
6
|
+
dialogue: DialogueTree;
|
|
7
|
+
startNodeId?: string;
|
|
8
|
+
flagSchema?: FlagSchema;
|
|
9
|
+
initialFlags?: GameFlagState;
|
|
10
|
+
}
|
|
11
|
+
export declare function PlayView({ dialogue, startNodeId, flagSchema, initialFlags }: PlayViewProps): React.JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,307 @@
|
|
|
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.PlayView = PlayView;
|
|
37
|
+
const react_1 = __importStar(require("react"));
|
|
38
|
+
const flag_manager_1 = require("../lib/flag-manager");
|
|
39
|
+
const yarn_runner_1 = require("../lib/yarn-runner");
|
|
40
|
+
function PlayView({ dialogue, startNodeId, flagSchema, initialFlags }) {
|
|
41
|
+
const [currentNodeId, setCurrentNodeId] = (0, react_1.useState)(startNodeId || dialogue.startNodeId);
|
|
42
|
+
// Initialize game flags with defaults from schema, then merge with initialFlags
|
|
43
|
+
const initialGameFlags = (0, react_1.useMemo)(() => {
|
|
44
|
+
if (flagSchema) {
|
|
45
|
+
const defaults = (0, flag_manager_1.initializeFlags)(flagSchema);
|
|
46
|
+
return { ...defaults, ...initialFlags };
|
|
47
|
+
}
|
|
48
|
+
return initialFlags || {};
|
|
49
|
+
}, [flagSchema, initialFlags]);
|
|
50
|
+
// Initialize variable manager
|
|
51
|
+
const variableManager = (0, react_1.useMemo)(() => {
|
|
52
|
+
return new yarn_runner_1.VariableManager(initialGameFlags, new Set());
|
|
53
|
+
}, [initialGameFlags]);
|
|
54
|
+
const [gameFlags, setGameFlags] = (0, react_1.useState)(initialGameFlags);
|
|
55
|
+
const [history, setHistory] = (0, react_1.useState)([]);
|
|
56
|
+
const [isTyping, setIsTyping] = (0, react_1.useState)(false);
|
|
57
|
+
const [showDebugPanel, setShowDebugPanel] = (0, react_1.useState)(false);
|
|
58
|
+
const chatEndRef = (0, react_1.useRef)(null);
|
|
59
|
+
// Track which flags were set during this run
|
|
60
|
+
const [flagsSetDuringRun, setFlagsSetDuringRun] = (0, react_1.useState)(new Set());
|
|
61
|
+
// Use ref to track latest gameFlags to avoid stale closures
|
|
62
|
+
const gameFlagsRef = (0, react_1.useRef)(gameFlags);
|
|
63
|
+
(0, react_1.useEffect)(() => {
|
|
64
|
+
gameFlagsRef.current = gameFlags;
|
|
65
|
+
}, [gameFlags]);
|
|
66
|
+
// Process current node
|
|
67
|
+
(0, react_1.useEffect)(() => {
|
|
68
|
+
const node = dialogue.nodes[currentNodeId];
|
|
69
|
+
if (!node)
|
|
70
|
+
return;
|
|
71
|
+
// Update variable manager with current game flags (use ref to get latest)
|
|
72
|
+
Object.entries(gameFlagsRef.current).forEach(([key, value]) => {
|
|
73
|
+
variableManager.set(key, value);
|
|
74
|
+
});
|
|
75
|
+
// If it's a player node, just ensure typing is false and show choices
|
|
76
|
+
if (node.type === 'player') {
|
|
77
|
+
setIsTyping(false);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
// Process the node using the modular runner
|
|
81
|
+
setIsTyping(true);
|
|
82
|
+
const timer = setTimeout(() => {
|
|
83
|
+
// Update flags before processing
|
|
84
|
+
if (node.setFlags) {
|
|
85
|
+
// Update dialogue flags (temporary)
|
|
86
|
+
node.setFlags.forEach(flagId => {
|
|
87
|
+
variableManager.addMemoryFlag(flagId);
|
|
88
|
+
});
|
|
89
|
+
// Update game flags (persistent)
|
|
90
|
+
if (flagSchema) {
|
|
91
|
+
const gameFlagIds = node.setFlags.filter(flagId => {
|
|
92
|
+
const flag = flagSchema.flags.find(f => f.id === flagId);
|
|
93
|
+
return flag && flag.type !== 'dialogue';
|
|
94
|
+
});
|
|
95
|
+
if (gameFlagIds.length > 0) {
|
|
96
|
+
const updated = (0, flag_manager_1.mergeFlagUpdates)(gameFlagsRef.current, gameFlagIds, flagSchema);
|
|
97
|
+
setGameFlags(updated);
|
|
98
|
+
// Update variable manager
|
|
99
|
+
gameFlagIds.forEach(flagId => {
|
|
100
|
+
const flag = flagSchema.flags.find(f => f.id === flagId);
|
|
101
|
+
if (flag) {
|
|
102
|
+
variableManager.set(flagId, flag.defaultValue ?? true);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
setFlagsSetDuringRun(prev => {
|
|
106
|
+
const next = new Set(prev);
|
|
107
|
+
gameFlagIds.forEach(f => next.add(f));
|
|
108
|
+
return next;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Process variable operations in content (e.g., <<set $var += 10>>)
|
|
114
|
+
(0, yarn_runner_1.processVariableOperationsInContent)(node.content, variableManager);
|
|
115
|
+
// Process the node
|
|
116
|
+
const result = (0, yarn_runner_1.processNode)(node, variableManager);
|
|
117
|
+
// Add to history if there's content
|
|
118
|
+
if (result.content) {
|
|
119
|
+
setHistory(prev => [...prev, {
|
|
120
|
+
nodeId: node.id,
|
|
121
|
+
type: 'npc',
|
|
122
|
+
speaker: result.speaker,
|
|
123
|
+
content: result.content
|
|
124
|
+
}]);
|
|
125
|
+
}
|
|
126
|
+
// Update game flags from variable manager after operations
|
|
127
|
+
// Use functional update to avoid dependency issues
|
|
128
|
+
setGameFlags(prev => {
|
|
129
|
+
const updatedVars = variableManager.getAllVariables();
|
|
130
|
+
// Filter out undefined values and only update if there are changes
|
|
131
|
+
const definedVars = {};
|
|
132
|
+
for (const [key, value] of Object.entries(updatedVars)) {
|
|
133
|
+
if (value !== undefined)
|
|
134
|
+
definedVars[key] = value;
|
|
135
|
+
}
|
|
136
|
+
const hasChanges = Object.keys(definedVars).some(key => prev[key] !== definedVars[key]);
|
|
137
|
+
return hasChanges ? { ...prev, ...definedVars } : prev;
|
|
138
|
+
});
|
|
139
|
+
setIsTyping(false);
|
|
140
|
+
// Navigate to next node if valid
|
|
141
|
+
if (result.nextNodeId && (0, yarn_runner_1.isValidNextNode)(result.nextNodeId, dialogue.nodes)) {
|
|
142
|
+
setTimeout(() => setCurrentNodeId(result.nextNodeId), 300);
|
|
143
|
+
}
|
|
144
|
+
}, 500);
|
|
145
|
+
return () => clearTimeout(timer);
|
|
146
|
+
}, [currentNodeId, dialogue.startNodeId, flagSchema]); // Removed gameFlags and variableManager from deps
|
|
147
|
+
(0, react_1.useEffect)(() => {
|
|
148
|
+
chatEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
|
149
|
+
}, [history, isTyping]);
|
|
150
|
+
const currentNode = dialogue.nodes[currentNodeId];
|
|
151
|
+
// Get available choices from processed node result
|
|
152
|
+
const processedResult = currentNode ? (0, yarn_runner_1.processNode)(currentNode, variableManager) : null;
|
|
153
|
+
const availableChoices = processedResult?.choices || [];
|
|
154
|
+
const handleChoice = (choice) => {
|
|
155
|
+
setHistory(prev => [...prev, {
|
|
156
|
+
nodeId: choice.id,
|
|
157
|
+
type: 'player',
|
|
158
|
+
content: choice.text
|
|
159
|
+
}]);
|
|
160
|
+
// Process variable operations in choice text
|
|
161
|
+
(0, yarn_runner_1.processVariableOperationsInContent)(choice.text, variableManager);
|
|
162
|
+
// Update game flags from variable manager after operations
|
|
163
|
+
const updatedVars = variableManager.getAllVariables();
|
|
164
|
+
const definedVars = {};
|
|
165
|
+
for (const [key, value] of Object.entries(updatedVars)) {
|
|
166
|
+
if (value !== undefined)
|
|
167
|
+
definedVars[key] = value;
|
|
168
|
+
}
|
|
169
|
+
setGameFlags(prev => ({ ...prev, ...definedVars }));
|
|
170
|
+
if (choice.setFlags) {
|
|
171
|
+
// Update dialogue flags (temporary)
|
|
172
|
+
choice.setFlags.forEach(flagId => {
|
|
173
|
+
variableManager.addMemoryFlag(flagId);
|
|
174
|
+
});
|
|
175
|
+
// Update game flags (persistent)
|
|
176
|
+
if (flagSchema) {
|
|
177
|
+
const gameFlagIds = choice.setFlags.filter(flagId => {
|
|
178
|
+
const flag = flagSchema.flags.find(f => f.id === flagId);
|
|
179
|
+
return flag && flag.type !== 'dialogue';
|
|
180
|
+
});
|
|
181
|
+
if (gameFlagIds.length > 0) {
|
|
182
|
+
setGameFlags(prev => (0, flag_manager_1.mergeFlagUpdates)(prev, gameFlagIds, flagSchema));
|
|
183
|
+
// Update variable manager
|
|
184
|
+
gameFlagIds.forEach(flagId => {
|
|
185
|
+
const flag = flagSchema.flags.find(f => f.id === flagId);
|
|
186
|
+
if (flag) {
|
|
187
|
+
variableManager.set(flagId, flag.defaultValue ?? true);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
setFlagsSetDuringRun(prev => {
|
|
191
|
+
const next = new Set(prev);
|
|
192
|
+
gameFlagIds.forEach(f => next.add(f));
|
|
193
|
+
return next;
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
// Only move to next node if it exists and is valid
|
|
199
|
+
if (choice.nextNodeId && (0, yarn_runner_1.isValidNextNode)(choice.nextNodeId, dialogue.nodes)) {
|
|
200
|
+
setCurrentNodeId(choice.nextNodeId);
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
// Choice leads nowhere - dialogue complete
|
|
204
|
+
setIsTyping(false);
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
const handleRestart = () => {
|
|
208
|
+
setHistory([]);
|
|
209
|
+
variableManager.reset(initialGameFlags, new Set());
|
|
210
|
+
setGameFlags(initialGameFlags);
|
|
211
|
+
setFlagsSetDuringRun(new Set());
|
|
212
|
+
setCurrentNodeId(startNodeId || dialogue.startNodeId);
|
|
213
|
+
};
|
|
214
|
+
// Get all non-dialogue flags from schema
|
|
215
|
+
const gameFlagsList = (0, react_1.useMemo)(() => {
|
|
216
|
+
if (!flagSchema)
|
|
217
|
+
return [];
|
|
218
|
+
return flagSchema.flags.filter(f => f.type !== 'dialogue');
|
|
219
|
+
}, [flagSchema]);
|
|
220
|
+
const flagTypeColors = {
|
|
221
|
+
dialogue: 'bg-gray-500/20 text-gray-400 border-gray-500/30',
|
|
222
|
+
quest: 'bg-blue-500/20 text-blue-400 border-blue-500/30',
|
|
223
|
+
achievement: 'bg-yellow-500/20 text-yellow-400 border-yellow-500/30',
|
|
224
|
+
item: 'bg-green-500/20 text-green-400 border-green-500/30',
|
|
225
|
+
stat: 'bg-purple-500/20 text-purple-400 border-purple-500/30',
|
|
226
|
+
title: 'bg-pink-500/20 text-pink-400 border-pink-500/30',
|
|
227
|
+
global: 'bg-orange-500/20 text-orange-400 border-orange-500/30',
|
|
228
|
+
};
|
|
229
|
+
return (react_1.default.createElement("main", { className: "flex-1 flex flex-col relative" },
|
|
230
|
+
flagSchema && (react_1.default.createElement("button", { onClick: () => setShowDebugPanel(!showDebugPanel), className: "absolute top-4 right-4 z-10 px-3 py-1.5 bg-[#1a1a2e] hover:bg-[#2a2a3e] border border-[#2a2a3e] hover:border-[#e94560] text-gray-400 hover:text-white text-xs rounded-lg transition-colors flex items-center gap-2", title: "Toggle Flag Debug Panel" },
|
|
231
|
+
react_1.default.createElement("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2" },
|
|
232
|
+
react_1.default.createElement("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
|
|
233
|
+
react_1.default.createElement("path", { d: "M9 9h6M9 15h6M9 12h6" })),
|
|
234
|
+
showDebugPanel ? 'Hide' : 'Debug',
|
|
235
|
+
" Flags")),
|
|
236
|
+
showDebugPanel && flagSchema && (react_1.default.createElement("div", { className: "absolute top-12 right-4 w-80 bg-[#0d0d14] border border-[#1a1a2e] rounded-lg shadow-xl z-20 max-h-[calc(100vh-8rem)] overflow-hidden flex flex-col" },
|
|
237
|
+
react_1.default.createElement("div", { className: "p-3 border-b border-[#1a1a2e] flex items-center justify-between" },
|
|
238
|
+
react_1.default.createElement("h3", { className: "text-sm font-semibold text-white" }, "Flag Debug Panel"),
|
|
239
|
+
react_1.default.createElement("button", { onClick: () => setShowDebugPanel(false), className: "p-1 text-gray-400 hover:text-white" },
|
|
240
|
+
react_1.default.createElement("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2" },
|
|
241
|
+
react_1.default.createElement("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
242
|
+
react_1.default.createElement("line", { x1: "6", y1: "6", x2: "18", y2: "18" })))),
|
|
243
|
+
react_1.default.createElement("div", { className: "flex-1 overflow-y-auto p-3 space-y-4" },
|
|
244
|
+
flagsSetDuringRun.size > 0 && (react_1.default.createElement("div", null,
|
|
245
|
+
react_1.default.createElement("h4", { className: "text-xs text-gray-500 uppercase mb-2" },
|
|
246
|
+
"Flags Set This Run (",
|
|
247
|
+
flagsSetDuringRun.size,
|
|
248
|
+
")"),
|
|
249
|
+
react_1.default.createElement("div", { className: "space-y-1" }, Array.from(flagsSetDuringRun).map(flagId => {
|
|
250
|
+
const flag = flagSchema.flags.find(f => f.id === flagId);
|
|
251
|
+
if (!flag)
|
|
252
|
+
return null;
|
|
253
|
+
const value = gameFlags[flagId];
|
|
254
|
+
return (react_1.default.createElement("div", { key: flagId, className: "bg-[#12121a] border border-[#2a2a3e] rounded px-2 py-1.5 text-xs" },
|
|
255
|
+
react_1.default.createElement("div", { className: "flex items-center gap-2" },
|
|
256
|
+
react_1.default.createElement("span", { className: `px-1.5 py-0.5 rounded text-[10px] border ${flagTypeColors[flag.type]}` }, flag.type),
|
|
257
|
+
react_1.default.createElement("span", { className: "font-mono text-white flex-1 truncate" }, flagId),
|
|
258
|
+
value !== undefined && (react_1.default.createElement("span", { className: "text-gray-400" },
|
|
259
|
+
"= ",
|
|
260
|
+
typeof value === 'boolean' ? (value ? 'true' : 'false') : String(value))))));
|
|
261
|
+
})))),
|
|
262
|
+
react_1.default.createElement("div", null,
|
|
263
|
+
react_1.default.createElement("h4", { className: "text-xs text-gray-500 uppercase mb-2" },
|
|
264
|
+
"All Game Flags (",
|
|
265
|
+
gameFlagsList.length,
|
|
266
|
+
")"),
|
|
267
|
+
react_1.default.createElement("div", { className: "space-y-1 max-h-96 overflow-y-auto" }, gameFlagsList.map(flag => {
|
|
268
|
+
const value = gameFlags[flag.id];
|
|
269
|
+
const wasSet = flagsSetDuringRun.has(flag.id);
|
|
270
|
+
const hasValue = value !== undefined;
|
|
271
|
+
return (react_1.default.createElement("div", { key: flag.id, className: `bg-[#12121a] border rounded px-2 py-1.5 text-xs transition-colors ${wasSet ? 'border-[#e94560]/50 bg-[#e94560]/5' : 'border-[#2a2a3e]'}` },
|
|
272
|
+
react_1.default.createElement("div", { className: "flex items-center gap-2 mb-1" },
|
|
273
|
+
react_1.default.createElement("span", { className: `px-1.5 py-0.5 rounded text-[10px] border ${flagTypeColors[flag.type]}` }, flag.type),
|
|
274
|
+
react_1.default.createElement("span", { className: "font-mono text-white flex-1 truncate text-[10px]" }, flag.id),
|
|
275
|
+
wasSet && (react_1.default.createElement("span", { className: "text-[10px] px-1 py-0.5 bg-[#e94560]/20 text-[#e94560] rounded" }, "NEW"))),
|
|
276
|
+
react_1.default.createElement("div", { className: "text-gray-400 text-[10px] truncate" }, flag.name),
|
|
277
|
+
hasValue ? (react_1.default.createElement("div", { className: "mt-1 text-[10px] text-gray-300" },
|
|
278
|
+
react_1.default.createElement("span", { className: "text-gray-500" }, "Value: "),
|
|
279
|
+
react_1.default.createElement("span", { className: "font-mono" }, typeof value === 'boolean' ? (value ? 'true' : 'false') :
|
|
280
|
+
typeof value === 'number' ? value :
|
|
281
|
+
`"${value}"`))) : (react_1.default.createElement("div", { className: "mt-1 text-[10px] text-gray-600 italic" }, "Not set"))));
|
|
282
|
+
})))))),
|
|
283
|
+
react_1.default.createElement("div", { className: "flex-1 overflow-y-auto p-4" },
|
|
284
|
+
react_1.default.createElement("div", { className: "max-w-2xl mx-auto space-y-4" },
|
|
285
|
+
history.map((entry, idx) => (react_1.default.createElement("div", { key: idx, className: `flex ${entry.type === 'player' ? 'justify-end' : 'justify-start'}` },
|
|
286
|
+
react_1.default.createElement("div", { className: `max-w-[80%] rounded-2xl px-4 py-3 ${entry.type === 'player'
|
|
287
|
+
? 'bg-[#e94560] text-white rounded-br-md'
|
|
288
|
+
: 'bg-[#1a1a2e] text-gray-100 rounded-bl-md'}` },
|
|
289
|
+
entry.type === 'npc' && entry.speaker && (react_1.default.createElement("div", { className: "text-xs text-[#e94560] font-medium mb-1" }, entry.speaker)),
|
|
290
|
+
react_1.default.createElement("div", { className: "whitespace-pre-wrap" }, entry.content))))),
|
|
291
|
+
isTyping && (react_1.default.createElement("div", { className: "flex justify-start" },
|
|
292
|
+
react_1.default.createElement("div", { className: "bg-[#1a1a2e] rounded-2xl rounded-bl-md px-4 py-3" },
|
|
293
|
+
react_1.default.createElement("div", { className: "flex gap-1" },
|
|
294
|
+
react_1.default.createElement("span", { className: "w-2 h-2 bg-[#e94560] rounded-full animate-bounce", style: { animationDelay: '0ms' } }),
|
|
295
|
+
react_1.default.createElement("span", { className: "w-2 h-2 bg-[#e94560] rounded-full animate-bounce", style: { animationDelay: '150ms' } }),
|
|
296
|
+
react_1.default.createElement("span", { className: "w-2 h-2 bg-[#e94560] rounded-full animate-bounce", style: { animationDelay: '300ms' } }))))),
|
|
297
|
+
react_1.default.createElement("div", { ref: chatEndRef }))),
|
|
298
|
+
currentNode?.type === 'player' && !isTyping && availableChoices.length > 0 && (react_1.default.createElement("div", { className: "border-t border-[#1a1a2e] bg-[#0d0d14]/80 backdrop-blur-sm p-4" },
|
|
299
|
+
react_1.default.createElement("div", { className: "max-w-2xl mx-auto space-y-2" }, availableChoices.map((choice) => (react_1.default.createElement("button", { key: choice.id, onClick: () => handleChoice(choice), className: "w-full text-left px-4 py-3 rounded-lg border border-[#2a2a3e] hover:border-[#e94560] bg-[#12121a] hover:bg-[#1a1a2e] text-gray-200 transition-all group flex items-center justify-between" },
|
|
300
|
+
react_1.default.createElement("span", null, choice.text),
|
|
301
|
+
react_1.default.createElement("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", className: "text-gray-600 group-hover:text-[#e94560] transition-colors" },
|
|
302
|
+
react_1.default.createElement("polyline", { points: "9 18 15 12 9 6" })))))))),
|
|
303
|
+
currentNode?.type === 'npc' && !currentNode.nextNodeId && !isTyping && (react_1.default.createElement("div", { className: "border-t border-[#1a1a2e] bg-[#0d0d14]/80 backdrop-blur-sm p-4" },
|
|
304
|
+
react_1.default.createElement("div", { className: "max-w-2xl mx-auto text-center" },
|
|
305
|
+
react_1.default.createElement("p", { className: "text-gray-500 mb-3" }, "End of dialogue"),
|
|
306
|
+
react_1.default.createElement("button", { onClick: handleRestart, className: "px-4 py-2 bg-[#e94560] hover:bg-[#d63850] text-white rounded-lg transition-colors" }, "Play Again"))))));
|
|
307
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { NodeProps } from 'reactflow';
|
|
3
|
+
import { DialogueNode } from '../types';
|
|
4
|
+
import { FlagSchema } from '../types/flags';
|
|
5
|
+
import { LayoutDirection } from '../utils/layout';
|
|
6
|
+
interface PlayerNodeData {
|
|
7
|
+
node: DialogueNode;
|
|
8
|
+
flagSchema?: FlagSchema;
|
|
9
|
+
isDimmed?: boolean;
|
|
10
|
+
isInPath?: boolean;
|
|
11
|
+
layoutDirection?: LayoutDirection;
|
|
12
|
+
isStartNode?: boolean;
|
|
13
|
+
isEndNode?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare function PlayerNodeV2({ data, selected }: NodeProps<PlayerNodeData>): React.JSX.Element;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,139 @@
|
|
|
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.PlayerNodeV2 = PlayerNodeV2;
|
|
37
|
+
const react_1 = __importStar(require("react"));
|
|
38
|
+
const reactflow_1 = require("reactflow");
|
|
39
|
+
const lucide_react_1 = require("lucide-react");
|
|
40
|
+
// Color scheme for choice edges (same as current implementation)
|
|
41
|
+
const CHOICE_COLORS = ['#e94560', '#8b5cf6', '#06b6d4', '#22c55e', '#f59e0b'];
|
|
42
|
+
function PlayerNodeV2({ data, selected }) {
|
|
43
|
+
const { node, flagSchema, isDimmed, isInPath, layoutDirection = 'TB', isStartNode, isEndNode } = data;
|
|
44
|
+
const choices = node.choices || [];
|
|
45
|
+
const updateNodeInternals = (0, reactflow_1.useUpdateNodeInternals)();
|
|
46
|
+
const headerRef = (0, react_1.useRef)(null);
|
|
47
|
+
const choiceRefs = (0, react_1.useRef)([]);
|
|
48
|
+
const [handlePositions, setHandlePositions] = (0, react_1.useState)([]);
|
|
49
|
+
// Handle positions based on layout direction
|
|
50
|
+
const isHorizontal = layoutDirection === 'LR';
|
|
51
|
+
const targetPosition = isHorizontal ? reactflow_1.Position.Left : reactflow_1.Position.Top;
|
|
52
|
+
const sourcePosition = isHorizontal ? reactflow_1.Position.Right : reactflow_1.Position.Bottom;
|
|
53
|
+
// Calculate handle positions based on actual rendered heights
|
|
54
|
+
(0, react_1.useEffect)(() => {
|
|
55
|
+
if (headerRef.current && choices.length > 0) {
|
|
56
|
+
const positions = [];
|
|
57
|
+
const headerHeight = headerRef.current.offsetHeight;
|
|
58
|
+
let cumulativeHeight = headerHeight;
|
|
59
|
+
choices.forEach((_choice, idx) => {
|
|
60
|
+
const choiceEl = choiceRefs.current[idx];
|
|
61
|
+
if (choiceEl) {
|
|
62
|
+
const choiceHeight = choiceEl.offsetHeight;
|
|
63
|
+
const handleY = cumulativeHeight + (choiceHeight / 2);
|
|
64
|
+
positions.push(handleY);
|
|
65
|
+
cumulativeHeight += choiceHeight;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
// Fallback: estimate height
|
|
69
|
+
const estimatedHeight = 32; // py-1.5 (12px) + text (~16px) + flags (~4px) = ~32px
|
|
70
|
+
const handleY = cumulativeHeight + (estimatedHeight / 2);
|
|
71
|
+
positions.push(handleY);
|
|
72
|
+
cumulativeHeight += estimatedHeight;
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
setHandlePositions(positions);
|
|
76
|
+
// Update React Flow internals after positions are calculated
|
|
77
|
+
setTimeout(() => {
|
|
78
|
+
updateNodeInternals(node.id);
|
|
79
|
+
}, 0);
|
|
80
|
+
}
|
|
81
|
+
}, [choices, node.id, updateNodeInternals]);
|
|
82
|
+
// Update node internals when choices change
|
|
83
|
+
(0, react_1.useEffect)(() => {
|
|
84
|
+
updateNodeInternals(node.id);
|
|
85
|
+
}, [choices.length, node.id, updateNodeInternals]);
|
|
86
|
+
// Check if this is an end node (player node with no choices that have nextNodeId)
|
|
87
|
+
const hasNoOutgoingConnections = !choices.some(c => c.nextNodeId);
|
|
88
|
+
// Border color based on state
|
|
89
|
+
const borderClass = selected
|
|
90
|
+
? 'border-df-player-selected shadow-lg shadow-df-glow'
|
|
91
|
+
: isStartNode
|
|
92
|
+
? 'border-df-start shadow-md'
|
|
93
|
+
: isEndNode
|
|
94
|
+
? 'border-df-end shadow-md'
|
|
95
|
+
: 'border-df-player-border';
|
|
96
|
+
return (react_1.default.createElement("div", { className: `rounded-lg border-2 transition-all duration-300 ${borderClass} ${isInPath ? 'border-df-player-selected/70' : ''} bg-df-player-bg min-w-[200px] relative`, style: isDimmed ? { opacity: 0.35, filter: 'saturate(0.3)' } : undefined },
|
|
97
|
+
isStartNode && (react_1.default.createElement("div", { className: "absolute -top-2 -left-2 bg-df-start text-df-text-primary text-[8px] font-bold px-1.5 py-0.5 rounded-full flex items-center gap-0.5 shadow-lg z-10" },
|
|
98
|
+
react_1.default.createElement(lucide_react_1.Play, { size: 8, fill: "currentColor" }),
|
|
99
|
+
" START")),
|
|
100
|
+
isEndNode && (react_1.default.createElement("div", { className: "absolute -top-2 -right-2 bg-df-end text-df-text-primary text-[8px] font-bold px-1.5 py-0.5 rounded-full flex items-center gap-0.5 shadow-lg z-10" },
|
|
101
|
+
react_1.default.createElement(lucide_react_1.Flag, { size: 8 }),
|
|
102
|
+
" END")),
|
|
103
|
+
react_1.default.createElement(reactflow_1.Handle, { type: "target", position: targetPosition, className: "!bg-df-control-bg !border-df-control-border !w-4 !h-4 !rounded-full" }),
|
|
104
|
+
react_1.default.createElement("div", { ref: headerRef, className: "px-3 py-1.5 border-b border-df-control-border bg-df-player-header flex items-center gap-2 rounded-t-lg" },
|
|
105
|
+
react_1.default.createElement(lucide_react_1.GitBranch, { size: 12, className: "text-df-player-selected" }),
|
|
106
|
+
react_1.default.createElement("span", { className: "text-[10px] font-mono text-df-text-secondary truncate flex-1" }, node.id),
|
|
107
|
+
react_1.default.createElement("span", { className: "text-[10px] text-df-text-tertiary" }, "PLAYER")),
|
|
108
|
+
react_1.default.createElement("div", { className: "border-t border-df-control-border" }, choices.map((choice, idx) => {
|
|
109
|
+
// Use calculated position or fallback
|
|
110
|
+
const choiceColor = CHOICE_COLORS[idx % CHOICE_COLORS.length];
|
|
111
|
+
return (react_1.default.createElement("div", { key: choice.id, ref: el => {
|
|
112
|
+
choiceRefs.current[idx] = el;
|
|
113
|
+
}, className: "px-3 py-1.5 text-[10px] text-df-text-secondary flex items-center gap-2 border-b border-df-control-border last:border-0 relative" },
|
|
114
|
+
react_1.default.createElement("div", { className: "flex-1 min-w-0" },
|
|
115
|
+
react_1.default.createElement("span", { className: "truncate block bg-df-base border border-df-control-border rounded px-2 py-1 text-df-text-primary" },
|
|
116
|
+
"\"",
|
|
117
|
+
choice.text || 'Empty choice',
|
|
118
|
+
"\""),
|
|
119
|
+
choice.setFlags && choice.setFlags.length > 0 && (react_1.default.createElement("div", { className: "mt-0.5 flex flex-wrap gap-0.5" }, choice.setFlags.map(flagId => {
|
|
120
|
+
const flag = flagSchema?.flags.find(f => f.id === flagId);
|
|
121
|
+
const flagType = flag?.type || 'dialogue';
|
|
122
|
+
const colorClass = flagType === 'dialogue' ? 'bg-df-flag-dialogue-bg text-df-flag-dialogue border-df-flag-dialogue' :
|
|
123
|
+
flagType === 'quest' ? 'bg-df-flag-quest-bg text-df-flag-quest border-df-flag-quest' :
|
|
124
|
+
flagType === 'achievement' ? 'bg-df-flag-achievement-bg text-df-flag-achievement border-df-flag-achievement' :
|
|
125
|
+
flagType === 'item' ? 'bg-df-flag-item-bg text-df-flag-item border-df-flag-item' :
|
|
126
|
+
flagType === 'stat' ? 'bg-df-flag-stat-bg text-df-flag-stat border-df-flag-stat' :
|
|
127
|
+
flagType === 'title' ? 'bg-df-flag-title-bg text-df-flag-title border-df-flag-title' :
|
|
128
|
+
flagType === 'global' ? 'bg-df-flag-global-bg text-df-flag-global border-df-flag-global' :
|
|
129
|
+
'bg-df-flag-dialogue-bg text-df-flag-dialogue border-df-flag-dialogue';
|
|
130
|
+
return (react_1.default.createElement("span", { key: flagId, className: `text-[7px] px-0.5 py-0 rounded border ${colorClass}`, title: flag?.name || flagId }, flagType === 'dialogue' ? 't' : flagType[0]));
|
|
131
|
+
})))),
|
|
132
|
+
react_1.default.createElement(reactflow_1.Handle, { type: "source", position: reactflow_1.Position.Right, id: `choice-${idx}`, style: {
|
|
133
|
+
top: `50%`,
|
|
134
|
+
transform: `translateY(-50%)`,
|
|
135
|
+
right: '-6px',
|
|
136
|
+
borderColor: choiceColor,
|
|
137
|
+
}, className: "!bg-df-control-bg !border-2 hover:!border-df-player-selected hover:!bg-df-player-selected/20 !w-3 !h-3 !rounded-full" })));
|
|
138
|
+
}))));
|
|
139
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proof of Concept: React Flow Implementation with Custom Choice Edges
|
|
3
|
+
*
|
|
4
|
+
* This demonstrates how we can use React Flow's custom edges feature
|
|
5
|
+
* to implement our choice-based edge system.
|
|
6
|
+
*
|
|
7
|
+
* Key concepts:
|
|
8
|
+
* 1. Dynamic handles on PlayerNode (one handle per choice)
|
|
9
|
+
* 2. Custom ChoiceEdge component that colors based on choice index
|
|
10
|
+
* 3. Edge data stores choiceIndex and choiceId
|
|
11
|
+
*
|
|
12
|
+
* To use this, install: npm install reactflow
|
|
13
|
+
*/
|
|
14
|
+
import React from 'react';
|
|
15
|
+
import { DialogueTree } from '../types';
|
|
16
|
+
/**
|
|
17
|
+
* Custom Edge Component for Player Choice Connections
|
|
18
|
+
*
|
|
19
|
+
* This edge:
|
|
20
|
+
* - Colors based on choice index (from edge data)
|
|
21
|
+
* - Uses bezier path for smooth curves
|
|
22
|
+
* - Matches our current visual style
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* NPC Node Component
|
|
26
|
+
*
|
|
27
|
+
* Features:
|
|
28
|
+
* - Single output handle at bottom
|
|
29
|
+
* - Speaker + content display
|
|
30
|
+
* - Matches current styling
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* Player Node Component
|
|
34
|
+
*
|
|
35
|
+
* Features:
|
|
36
|
+
* - Dynamic handles: one per choice (positioned on right side)
|
|
37
|
+
* - Each handle positioned at choice's Y offset
|
|
38
|
+
* - Matches current styling
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* Convert DialogueTree to React Flow format
|
|
42
|
+
*/
|
|
43
|
+
export declare function convertDialogueTreeToReactFlow(dialogue: DialogueTree): {
|
|
44
|
+
nodes: any[];
|
|
45
|
+
edges: any[];
|
|
46
|
+
nodeTypes: {};
|
|
47
|
+
edgeTypes: {};
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Main React Flow Component (POC)
|
|
51
|
+
*
|
|
52
|
+
* Usage:
|
|
53
|
+
* ```tsx
|
|
54
|
+
* <ReactFlowProvider>
|
|
55
|
+
* <ReactFlowPOC dialogue={dialogueTree} />
|
|
56
|
+
* </ReactFlowProvider>
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export declare function ReactFlowPOC({ dialogue }: {
|
|
60
|
+
dialogue: DialogueTree;
|
|
61
|
+
}): React.JSX.Element;
|