@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,440 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState, useCallback } from 'react';
|
|
4
|
+
import { DialogueEditorV2 } from '@magicborn/dialogue-forge/src/components/DialogueEditorV2';
|
|
5
|
+
import { FlagManager } from '@magicborn/dialogue-forge/src/components/FlagManager';
|
|
6
|
+
import { GuidePanel } from '@magicborn/dialogue-forge/src/components/GuidePanel';
|
|
7
|
+
import { FlagSchema, exampleFlagSchema } from '@magicborn/dialogue-forge/src/types/flags';
|
|
8
|
+
import { DialogueTree } from '@magicborn/dialogue-forge/src/types';
|
|
9
|
+
import { exportToYarn } from '@magicborn/dialogue-forge/src/lib/yarn-converter';
|
|
10
|
+
import {
|
|
11
|
+
listExamples,
|
|
12
|
+
getExampleDialogue,
|
|
13
|
+
listDemoFlagSchemas,
|
|
14
|
+
getDemoFlagSchema
|
|
15
|
+
} from '@magicborn/dialogue-forge/src/examples';
|
|
16
|
+
import { Play, Layout, FileText } from 'lucide-react';
|
|
17
|
+
import { ThemeSwitcher } from '../components/ThemeSwitcher';
|
|
18
|
+
|
|
19
|
+
type ViewMode = 'graph' | 'yarn' | 'play';
|
|
20
|
+
|
|
21
|
+
// Tell Next.js this page is static (no dynamic params/searchParams)
|
|
22
|
+
export const dynamic = 'force-static';
|
|
23
|
+
|
|
24
|
+
// Demo examples - these are specific to the demo app
|
|
25
|
+
const demoDialogues: Record<string, DialogueTree> = {
|
|
26
|
+
'mysterious-stranger': {
|
|
27
|
+
id: 'mysterious-stranger',
|
|
28
|
+
title: 'Demo: The Mysterious Stranger',
|
|
29
|
+
startNodeId: 'start',
|
|
30
|
+
nodes: {
|
|
31
|
+
'start': {
|
|
32
|
+
id: 'start',
|
|
33
|
+
type: 'npc',
|
|
34
|
+
speaker: 'Stranger',
|
|
35
|
+
x: 300,
|
|
36
|
+
y: 100,
|
|
37
|
+
content: "You find yourself at a crossroads. A cloaked figure emerges from the shadows.",
|
|
38
|
+
nextNodeId: 'greeting',
|
|
39
|
+
},
|
|
40
|
+
'greeting': {
|
|
41
|
+
id: 'greeting',
|
|
42
|
+
type: 'npc',
|
|
43
|
+
speaker: 'Stranger',
|
|
44
|
+
x: 300,
|
|
45
|
+
y: 200,
|
|
46
|
+
content: "\"Traveler... I've been waiting for you. What brings you to these lands?\"",
|
|
47
|
+
nextNodeId: 'first_choice',
|
|
48
|
+
},
|
|
49
|
+
'first_choice': {
|
|
50
|
+
id: 'first_choice',
|
|
51
|
+
type: 'player',
|
|
52
|
+
content: '',
|
|
53
|
+
x: 300,
|
|
54
|
+
y: 300,
|
|
55
|
+
choices: [
|
|
56
|
+
{
|
|
57
|
+
id: 'choice_treasure',
|
|
58
|
+
text: "I seek the legendary treasure.",
|
|
59
|
+
nextNodeId: 'treasure_response',
|
|
60
|
+
conditions: [
|
|
61
|
+
{ flag: 'reputation', operator: 'greater_equal', value: 0 },
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
id: 'choice_knowledge',
|
|
66
|
+
text: "I'm searching for ancient knowledge.",
|
|
67
|
+
nextNodeId: 'knowledge_response',
|
|
68
|
+
conditions: [
|
|
69
|
+
{ flag: 'reputation', operator: 'greater_equal', value: 0 },
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: 'choice_high_rep',
|
|
74
|
+
text: "I am a hero of this land!",
|
|
75
|
+
nextNodeId: 'high_rep_response',
|
|
76
|
+
conditions: [
|
|
77
|
+
{ flag: 'reputation', operator: 'greater_than', value: 50 },
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
'treasure_response': {
|
|
83
|
+
id: 'treasure_response',
|
|
84
|
+
type: 'npc',
|
|
85
|
+
speaker: 'Stranger',
|
|
86
|
+
x: 200,
|
|
87
|
+
y: 450,
|
|
88
|
+
content: "\"Many have sought the same. Take this map—it shows the entrance to the catacombs.\"",
|
|
89
|
+
nextNodeId: undefined,
|
|
90
|
+
},
|
|
91
|
+
'knowledge_response': {
|
|
92
|
+
id: 'knowledge_response',
|
|
93
|
+
type: 'npc',
|
|
94
|
+
speaker: 'Stranger',
|
|
95
|
+
x: 400,
|
|
96
|
+
y: 450,
|
|
97
|
+
content: "\"A seeker of truth... Take this tome. It contains the riddles you must solve.\"",
|
|
98
|
+
nextNodeId: undefined,
|
|
99
|
+
},
|
|
100
|
+
'high_rep_response': {
|
|
101
|
+
id: 'high_rep_response',
|
|
102
|
+
type: 'npc',
|
|
103
|
+
speaker: 'Stranger',
|
|
104
|
+
x: 500,
|
|
105
|
+
y: 450,
|
|
106
|
+
content: "\"Ah, a hero! Your reputation precedes you. I have something special for you...\"",
|
|
107
|
+
nextNodeId: undefined,
|
|
108
|
+
setFlags: ['reputation'],
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
'tavern-quest': {
|
|
113
|
+
id: 'tavern-quest',
|
|
114
|
+
title: 'Demo: Tavern Quest',
|
|
115
|
+
startNodeId: 'enter_tavern',
|
|
116
|
+
nodes: {
|
|
117
|
+
'enter_tavern': {
|
|
118
|
+
id: 'enter_tavern',
|
|
119
|
+
type: 'npc',
|
|
120
|
+
speaker: 'Narrator',
|
|
121
|
+
x: 300,
|
|
122
|
+
y: 50,
|
|
123
|
+
content: "You push open the heavy wooden door and enter the Rusty Dragon tavern.",
|
|
124
|
+
nextNodeId: 'bartender_greet',
|
|
125
|
+
},
|
|
126
|
+
'bartender_greet': {
|
|
127
|
+
id: 'bartender_greet',
|
|
128
|
+
type: 'npc',
|
|
129
|
+
speaker: 'Bartender',
|
|
130
|
+
x: 300,
|
|
131
|
+
y: 150,
|
|
132
|
+
content: "\"Welcome, stranger! What can I get ya? We've got ale, mead, or if you're looking for work, I might have something.\"",
|
|
133
|
+
nextNodeId: 'tavern_choice',
|
|
134
|
+
},
|
|
135
|
+
'tavern_choice': {
|
|
136
|
+
id: 'tavern_choice',
|
|
137
|
+
type: 'player',
|
|
138
|
+
content: '',
|
|
139
|
+
x: 300,
|
|
140
|
+
y: 280,
|
|
141
|
+
choices: [
|
|
142
|
+
{ id: 'order_ale', text: "I'll have an ale.", nextNodeId: 'drink_ale' },
|
|
143
|
+
{ id: 'ask_work', text: "What kind of work?", nextNodeId: 'work_info' },
|
|
144
|
+
{ id: 'look_around', text: "I'll just look around.", nextNodeId: 'observe_tavern' },
|
|
145
|
+
{
|
|
146
|
+
id: 'vip_entrance',
|
|
147
|
+
text: "I'm a VIP member.",
|
|
148
|
+
nextNodeId: 'vip_response',
|
|
149
|
+
conditions: [
|
|
150
|
+
{ flag: 'reputation', operator: 'greater_than', value: 75 },
|
|
151
|
+
],
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
},
|
|
155
|
+
'drink_ale': {
|
|
156
|
+
id: 'drink_ale',
|
|
157
|
+
type: 'npc',
|
|
158
|
+
speaker: 'Bartender',
|
|
159
|
+
x: 100,
|
|
160
|
+
y: 420,
|
|
161
|
+
content: "\"Coming right up!\" He slides a frothy mug across the bar.",
|
|
162
|
+
nextNodeId: undefined,
|
|
163
|
+
},
|
|
164
|
+
'work_info': {
|
|
165
|
+
id: 'work_info',
|
|
166
|
+
type: 'npc',
|
|
167
|
+
speaker: 'Bartender',
|
|
168
|
+
x: 300,
|
|
169
|
+
y: 420,
|
|
170
|
+
content: "\"Rats in the cellar. Big ones. I'll pay 10 gold if you clear 'em out.\"",
|
|
171
|
+
nextNodeId: 'accept_quest',
|
|
172
|
+
},
|
|
173
|
+
'accept_quest': {
|
|
174
|
+
id: 'accept_quest',
|
|
175
|
+
type: 'player',
|
|
176
|
+
content: '',
|
|
177
|
+
x: 300,
|
|
178
|
+
y: 550,
|
|
179
|
+
choices: [
|
|
180
|
+
{ id: 'accept', text: "I'll do it.", nextNodeId: 'quest_accepted' },
|
|
181
|
+
{ id: 'decline', text: "Not interested." },
|
|
182
|
+
],
|
|
183
|
+
},
|
|
184
|
+
'quest_accepted': {
|
|
185
|
+
id: 'quest_accepted',
|
|
186
|
+
type: 'npc',
|
|
187
|
+
speaker: 'Bartender',
|
|
188
|
+
x: 300,
|
|
189
|
+
y: 680,
|
|
190
|
+
content: "\"Great! The cellar door is in the back. Good luck!\"",
|
|
191
|
+
nextNodeId: undefined,
|
|
192
|
+
},
|
|
193
|
+
'observe_tavern': {
|
|
194
|
+
id: 'observe_tavern',
|
|
195
|
+
type: 'npc',
|
|
196
|
+
speaker: 'Narrator',
|
|
197
|
+
x: 500,
|
|
198
|
+
y: 420,
|
|
199
|
+
content: "You notice a hooded figure in the corner, watching you intently...",
|
|
200
|
+
nextNodeId: undefined,
|
|
201
|
+
},
|
|
202
|
+
'vip_response': {
|
|
203
|
+
id: 'vip_response',
|
|
204
|
+
type: 'npc',
|
|
205
|
+
speaker: 'Bartender',
|
|
206
|
+
x: 600,
|
|
207
|
+
y: 420,
|
|
208
|
+
content: "\"Of course! Right this way to the VIP lounge. Your reputation grants you access.\"",
|
|
209
|
+
nextNodeId: undefined,
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
// Enhanced flag schema with reputation for demo examples
|
|
216
|
+
const demoFlagSchema: FlagSchema = {
|
|
217
|
+
...exampleFlagSchema,
|
|
218
|
+
flags: [
|
|
219
|
+
...exampleFlagSchema.flags,
|
|
220
|
+
{
|
|
221
|
+
id: 'reputation',
|
|
222
|
+
name: 'Reputation',
|
|
223
|
+
type: 'stat',
|
|
224
|
+
description: 'Player reputation score',
|
|
225
|
+
defaultValue: 0,
|
|
226
|
+
},
|
|
227
|
+
],
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
export default function DialogueForgeDemo() {
|
|
231
|
+
const [dialogueTree, setDialogueTree] = useState<DialogueTree>(demoDialogues['mysterious-stranger']);
|
|
232
|
+
const [flagSchema, setFlagSchema] = useState<FlagSchema>(demoFlagSchema);
|
|
233
|
+
const [viewMode, setViewMode] = useState<ViewMode>('graph');
|
|
234
|
+
|
|
235
|
+
// Panel states
|
|
236
|
+
const [showFlagManager, setShowFlagManager] = useState(false);
|
|
237
|
+
const [showGuide, setShowGuide] = useState(false);
|
|
238
|
+
const [showExamplePicker, setShowExamplePicker] = useState(false);
|
|
239
|
+
|
|
240
|
+
const handleExportYarn = useCallback(() => {
|
|
241
|
+
const yarn = exportToYarn(dialogueTree);
|
|
242
|
+
const blob = new Blob([yarn], { type: 'text/plain' });
|
|
243
|
+
const url = URL.createObjectURL(blob);
|
|
244
|
+
const a = document.createElement('a');
|
|
245
|
+
a.href = url;
|
|
246
|
+
a.download = `${dialogueTree.title.replace(/\s+/g, '_')}.yarn`;
|
|
247
|
+
a.click();
|
|
248
|
+
URL.revokeObjectURL(url);
|
|
249
|
+
}, [dialogueTree]);
|
|
250
|
+
|
|
251
|
+
const handleLoadExample = useCallback((exampleId: string) => {
|
|
252
|
+
// Try demo examples first
|
|
253
|
+
if (demoDialogues[exampleId]) {
|
|
254
|
+
setDialogueTree(demoDialogues[exampleId]);
|
|
255
|
+
setShowExamplePicker(false);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
// Try package examples
|
|
259
|
+
const example = getExampleDialogue(exampleId);
|
|
260
|
+
if (example) {
|
|
261
|
+
setDialogueTree(example);
|
|
262
|
+
}
|
|
263
|
+
setShowExamplePicker(false);
|
|
264
|
+
}, []);
|
|
265
|
+
|
|
266
|
+
const handleLoadFlagSchema = useCallback((schemaId: string) => {
|
|
267
|
+
const schema = getDemoFlagSchema(schemaId);
|
|
268
|
+
if (schema) {
|
|
269
|
+
setFlagSchema(schema);
|
|
270
|
+
}
|
|
271
|
+
}, []);
|
|
272
|
+
|
|
273
|
+
// Get all available examples
|
|
274
|
+
const allExamples = [
|
|
275
|
+
...Object.keys(demoDialogues),
|
|
276
|
+
...listExamples()
|
|
277
|
+
].filter((v, i, a) => a.indexOf(v) === i); // unique
|
|
278
|
+
|
|
279
|
+
const allFlagSchemas = listDemoFlagSchemas();
|
|
280
|
+
|
|
281
|
+
return (
|
|
282
|
+
<div className="w-full h-screen flex flex-col">
|
|
283
|
+
{/* Header */}
|
|
284
|
+
<div className="max-w-7xl mx-auto px-4 py-4 flex-shrink-0 w-full">
|
|
285
|
+
<div className="flex items-center justify-between">
|
|
286
|
+
<div>
|
|
287
|
+
<h1 className="text-2xl font-bold text-white mb-1">Dialogue Forge Editor</h1>
|
|
288
|
+
<p className="text-zinc-400 text-sm">
|
|
289
|
+
Create interactive dialogues with a visual node-based editor. Export to Yarn Spinner format.
|
|
290
|
+
</p>
|
|
291
|
+
</div>
|
|
292
|
+
<div className="flex items-center gap-2">
|
|
293
|
+
<ThemeSwitcher />
|
|
294
|
+
<div className="w-px h-6 bg-zinc-700" />
|
|
295
|
+
<button
|
|
296
|
+
onClick={() => setShowExamplePicker(true)}
|
|
297
|
+
className="px-3 py-1.5 bg-indigo-600 hover:bg-indigo-700 text-white text-sm rounded-lg transition-colors"
|
|
298
|
+
>
|
|
299
|
+
Load Example
|
|
300
|
+
</button>
|
|
301
|
+
<div className="w-px h-6 bg-zinc-700" />
|
|
302
|
+
{/* View Mode Toggle */}
|
|
303
|
+
<div className="flex items-center gap-1 bg-[#12121a] border border-[#2a2a3e] rounded-lg p-1">
|
|
304
|
+
<button
|
|
305
|
+
onClick={() => setViewMode('graph')}
|
|
306
|
+
className={`px-3 py-1.5 text-sm rounded transition-colors flex items-center gap-1.5 ${
|
|
307
|
+
viewMode === 'graph'
|
|
308
|
+
? 'bg-indigo-600 text-white'
|
|
309
|
+
: 'text-gray-400 hover:text-white'
|
|
310
|
+
}`}
|
|
311
|
+
title="Graph Editor"
|
|
312
|
+
>
|
|
313
|
+
<Layout size={14} />
|
|
314
|
+
<span className="hidden sm:inline">Editor</span>
|
|
315
|
+
</button>
|
|
316
|
+
<button
|
|
317
|
+
onClick={() => setViewMode('play')}
|
|
318
|
+
className={`px-3 py-1.5 text-sm rounded transition-colors flex items-center gap-1.5 ${
|
|
319
|
+
viewMode === 'play'
|
|
320
|
+
? 'bg-green-600 text-white'
|
|
321
|
+
: 'text-gray-400 hover:text-white'
|
|
322
|
+
}`}
|
|
323
|
+
title="Play Dialogue"
|
|
324
|
+
>
|
|
325
|
+
<Play size={14} />
|
|
326
|
+
<span className="hidden sm:inline">Play</span>
|
|
327
|
+
</button>
|
|
328
|
+
</div>
|
|
329
|
+
<button
|
|
330
|
+
onClick={() => setShowExamplePicker(true)}
|
|
331
|
+
className="px-3 py-1.5 bg-indigo-600 hover:bg-indigo-700 text-white text-sm rounded-lg transition-colors"
|
|
332
|
+
>
|
|
333
|
+
Load Example
|
|
334
|
+
</button>
|
|
335
|
+
</div>
|
|
336
|
+
</div>
|
|
337
|
+
</div>
|
|
338
|
+
|
|
339
|
+
{/* Editor/Player */}
|
|
340
|
+
<div className="flex-1 w-full min-h-0">
|
|
341
|
+
<DialogueEditorV2
|
|
342
|
+
dialogue={dialogueTree}
|
|
343
|
+
onChange={setDialogueTree}
|
|
344
|
+
onExportYarn={handleExportYarn}
|
|
345
|
+
flagSchema={flagSchema}
|
|
346
|
+
viewMode={viewMode}
|
|
347
|
+
onViewModeChange={setViewMode}
|
|
348
|
+
className="w-full h-full"
|
|
349
|
+
onOpenFlagManager={() => setShowFlagManager(true)}
|
|
350
|
+
onOpenGuide={() => setShowGuide(true)}
|
|
351
|
+
/>
|
|
352
|
+
</div>
|
|
353
|
+
|
|
354
|
+
{/* Flag Manager Modal */}
|
|
355
|
+
{showFlagManager && (
|
|
356
|
+
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
|
|
357
|
+
<div className="bg-[#0d0d14] border border-[#2a2a3e] rounded-lg shadow-xl max-w-4xl w-full max-h-[80vh] overflow-hidden">
|
|
358
|
+
<FlagManager
|
|
359
|
+
flagSchema={flagSchema}
|
|
360
|
+
dialogue={dialogueTree}
|
|
361
|
+
onUpdate={setFlagSchema}
|
|
362
|
+
onClose={() => setShowFlagManager(false)}
|
|
363
|
+
/>
|
|
364
|
+
</div>
|
|
365
|
+
</div>
|
|
366
|
+
)}
|
|
367
|
+
|
|
368
|
+
{/* Guide Panel */}
|
|
369
|
+
<GuidePanel
|
|
370
|
+
isOpen={showGuide}
|
|
371
|
+
onClose={() => setShowGuide(false)}
|
|
372
|
+
/>
|
|
373
|
+
|
|
374
|
+
{/* Example Picker Modal */}
|
|
375
|
+
{showExamplePicker && (
|
|
376
|
+
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
|
|
377
|
+
<div className="bg-[#0d0d14] border border-[#2a2a3e] rounded-lg shadow-xl max-w-lg w-full p-6">
|
|
378
|
+
<div className="flex items-center justify-between mb-4">
|
|
379
|
+
<h2 className="text-lg font-semibold text-white">Load Example Dialogue</h2>
|
|
380
|
+
<button
|
|
381
|
+
onClick={() => setShowExamplePicker(false)}
|
|
382
|
+
className="text-gray-400 hover:text-white"
|
|
383
|
+
>
|
|
384
|
+
✕
|
|
385
|
+
</button>
|
|
386
|
+
</div>
|
|
387
|
+
|
|
388
|
+
<div className="space-y-2 max-h-64 overflow-y-auto">
|
|
389
|
+
<h3 className="text-sm font-medium text-gray-400 mb-2">Demo Examples</h3>
|
|
390
|
+
{Object.entries(demoDialogues).map(([id, dialogue]) => (
|
|
391
|
+
<button
|
|
392
|
+
key={id}
|
|
393
|
+
onClick={() => handleLoadExample(id)}
|
|
394
|
+
className="w-full text-left px-4 py-3 bg-[#12121a] hover:bg-[#1a1a2e] border border-[#2a2a3e] rounded-lg transition-colors"
|
|
395
|
+
>
|
|
396
|
+
<div className="font-medium text-white">{dialogue.title}</div>
|
|
397
|
+
<div className="text-xs text-gray-400">
|
|
398
|
+
{Object.keys(dialogue.nodes).length} nodes
|
|
399
|
+
</div>
|
|
400
|
+
</button>
|
|
401
|
+
))}
|
|
402
|
+
|
|
403
|
+
{listExamples().length > 0 && (
|
|
404
|
+
<>
|
|
405
|
+
<h3 className="text-sm font-medium text-gray-400 mt-4 mb-2">Package Examples</h3>
|
|
406
|
+
{listExamples().map((id) => (
|
|
407
|
+
<button
|
|
408
|
+
key={id}
|
|
409
|
+
onClick={() => handleLoadExample(id)}
|
|
410
|
+
className="w-full text-left px-4 py-3 bg-[#12121a] hover:bg-[#1a1a2e] border border-[#2a2a3e] rounded-lg transition-colors"
|
|
411
|
+
>
|
|
412
|
+
<div className="font-medium text-white">{id}</div>
|
|
413
|
+
</button>
|
|
414
|
+
))}
|
|
415
|
+
</>
|
|
416
|
+
)}
|
|
417
|
+
</div>
|
|
418
|
+
|
|
419
|
+
{allFlagSchemas.length > 0 && (
|
|
420
|
+
<div className="mt-4 pt-4 border-t border-[#2a2a3e]">
|
|
421
|
+
<h3 className="text-sm font-medium text-gray-400 mb-2">Flag Schemas</h3>
|
|
422
|
+
<div className="flex flex-wrap gap-2">
|
|
423
|
+
{allFlagSchemas.map((id) => (
|
|
424
|
+
<button
|
|
425
|
+
key={id}
|
|
426
|
+
onClick={() => handleLoadFlagSchema(id)}
|
|
427
|
+
className="px-3 py-1 text-xs bg-purple-500/20 text-purple-400 border border-purple-500/30 rounded hover:bg-purple-500/30 transition-colors"
|
|
428
|
+
>
|
|
429
|
+
{id}
|
|
430
|
+
</button>
|
|
431
|
+
))}
|
|
432
|
+
</div>
|
|
433
|
+
</div>
|
|
434
|
+
)}
|
|
435
|
+
</div>
|
|
436
|
+
</div>
|
|
437
|
+
)}
|
|
438
|
+
</div>
|
|
439
|
+
);
|
|
440
|
+
}
|