@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,1140 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { CodeBlock } from './CodeBlock';
|
|
3
|
+
export function GuidePanel({ isOpen, onClose }) {
|
|
4
|
+
const [activeSection, setActiveSection] = useState('overview');
|
|
5
|
+
if (!isOpen)
|
|
6
|
+
return null;
|
|
7
|
+
const sections = {
|
|
8
|
+
overview: {
|
|
9
|
+
title: 'Getting Started',
|
|
10
|
+
content: (React.createElement("div", { className: "space-y-4 text-sm" },
|
|
11
|
+
React.createElement("p", null, "Dialogue Forge is a visual node-based editor for creating branching dialogue systems. Export to Yarn Spinner format for use in game engines."),
|
|
12
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "Node Types"),
|
|
13
|
+
React.createElement("div", { className: "space-y-3" },
|
|
14
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-[#2a2a3e]" },
|
|
15
|
+
React.createElement("strong", { className: "text-[#e94560]" }, "NPC Node"),
|
|
16
|
+
React.createElement("p", { className: "text-gray-400 text-xs mt-1" }, "Character dialogue. Has speaker name, dialogue text, and one output port (bottom circle) that connects to the next node.")),
|
|
17
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-[#2a2a3e]" },
|
|
18
|
+
React.createElement("strong", { className: "text-purple-400" }, "Player Node"),
|
|
19
|
+
React.createElement("p", { className: "text-gray-400 text-xs mt-1" }, "Choice point with multiple options. Each choice has its own output port (right side circles) that can branch to different nodes."))),
|
|
20
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "Basic Workflow"),
|
|
21
|
+
React.createElement("ol", { className: "list-decimal list-inside space-y-2 text-sm ml-2" },
|
|
22
|
+
React.createElement("li", null,
|
|
23
|
+
React.createElement("strong", null, "Create nodes"),
|
|
24
|
+
" - Right-click empty space or drag from output port"),
|
|
25
|
+
React.createElement("li", null,
|
|
26
|
+
React.createElement("strong", null, "Edit content"),
|
|
27
|
+
" - Click a node to edit in the side panel"),
|
|
28
|
+
React.createElement("li", null,
|
|
29
|
+
React.createElement("strong", null, "Connect nodes"),
|
|
30
|
+
" - Drag from output ports to connect"),
|
|
31
|
+
React.createElement("li", null,
|
|
32
|
+
React.createElement("strong", null, "Set flags"),
|
|
33
|
+
" - Add flags to track game state"),
|
|
34
|
+
React.createElement("li", null,
|
|
35
|
+
React.createElement("strong", null, "Export"),
|
|
36
|
+
" - Download Yarn file for your game")),
|
|
37
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "Creating Nodes"),
|
|
38
|
+
React.createElement("ul", { className: "list-disc list-inside space-y-1 text-sm ml-2" },
|
|
39
|
+
React.createElement("li", null,
|
|
40
|
+
React.createElement("strong", null, "Right-click"),
|
|
41
|
+
" anywhere on the graph \u2192 Select \"NPC Node\" or \"Player Node\""),
|
|
42
|
+
React.createElement("li", null,
|
|
43
|
+
React.createElement("strong", null, "Drag from output port"),
|
|
44
|
+
" \u2192 Release in empty space \u2192 Select node type from menu"),
|
|
45
|
+
React.createElement("li", null,
|
|
46
|
+
React.createElement("strong", null, "Right-click a node"),
|
|
47
|
+
" \u2192 \"Duplicate\" to copy it")),
|
|
48
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "Editing Nodes"),
|
|
49
|
+
React.createElement("ul", { className: "list-disc list-inside space-y-1 text-sm ml-2" },
|
|
50
|
+
React.createElement("li", null,
|
|
51
|
+
React.createElement("strong", null, "Click a node"),
|
|
52
|
+
" to select it and open the editor panel"),
|
|
53
|
+
React.createElement("li", null, "Edit speaker, content, next node, and flags in the side panel"),
|
|
54
|
+
React.createElement("li", null,
|
|
55
|
+
React.createElement("strong", null, "Right-click a node"),
|
|
56
|
+
" for quick actions: Edit, Add Choice, Duplicate, Delete, Play from Here")),
|
|
57
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "Connecting Nodes"),
|
|
58
|
+
React.createElement("ul", { className: "list-disc list-inside space-y-1 text-sm ml-2" },
|
|
59
|
+
React.createElement("li", null,
|
|
60
|
+
React.createElement("strong", null, "Drag from output port"),
|
|
61
|
+
" (circle at bottom of NPC or right of choice)"),
|
|
62
|
+
React.createElement("li", null,
|
|
63
|
+
React.createElement("strong", null, "Release on target node"),
|
|
64
|
+
" to connect directly"),
|
|
65
|
+
React.createElement("li", null,
|
|
66
|
+
React.createElement("strong", null, "Release in empty space"),
|
|
67
|
+
" to create a new connected node"),
|
|
68
|
+
React.createElement("li", null, "Each choice in a Player node can connect to different NPC nodes for branching"))))
|
|
69
|
+
},
|
|
70
|
+
flags: {
|
|
71
|
+
title: 'Game State & Flags',
|
|
72
|
+
content: (React.createElement("div", { className: "space-y-4 text-sm" },
|
|
73
|
+
React.createElement("div", { className: "bg-[#1a2a3e] border-l-4 border-[#e94560] p-4 rounded" },
|
|
74
|
+
React.createElement("h3", { className: "text-white font-semibold mb-2" }, "Game State & Yarn Variables"),
|
|
75
|
+
React.createElement("p", { className: "text-gray-300 text-xs mb-2" },
|
|
76
|
+
React.createElement("strong", null, "Dialogue Forge automatically flattens your game state into Yarn Spinner-compatible variables.")),
|
|
77
|
+
React.createElement("p", { className: "text-gray-400 text-xs mb-2" }, "Pass any JSON game state structure to ScenePlayer. Nested objects (player, characters, flags) are automatically flattened into flat variables that Yarn Spinner can use."),
|
|
78
|
+
React.createElement("p", { className: "text-gray-400 text-xs" },
|
|
79
|
+
"In Yarn Spinner, these become ",
|
|
80
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "$variable"),
|
|
81
|
+
" commands like ",
|
|
82
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "<<set $player_hp = 100>>"),
|
|
83
|
+
".")),
|
|
84
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "Game State Structure"),
|
|
85
|
+
React.createElement("p", { className: "text-gray-400 mb-3" }, "Your game state can have any structure. Dialogue Forge supports nested objects and automatically flattens them:"),
|
|
86
|
+
React.createElement(CodeBlock, { code: `// Example game state with nested structures
|
|
87
|
+
interface GameState {
|
|
88
|
+
// Direct flags (optional, but recommended)
|
|
89
|
+
flags?: {
|
|
90
|
+
quest_dragon_slayer: 'not_started' | 'started' | 'complete';
|
|
91
|
+
item_ancient_key: boolean;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// Player properties (flattened to $player_*)
|
|
95
|
+
player?: {
|
|
96
|
+
hp: number;
|
|
97
|
+
maxHp: number;
|
|
98
|
+
name: string;
|
|
99
|
+
// Nested objects are flattened too
|
|
100
|
+
affinity: {
|
|
101
|
+
A: number; // Becomes $player_affinity_A
|
|
102
|
+
B: number; // Becomes $player_affinity_B
|
|
103
|
+
};
|
|
104
|
+
elementAffinity: {
|
|
105
|
+
fire: number; // Becomes $player_elementAffinity_fire
|
|
106
|
+
water: number; // Becomes $player_elementAffinity_water
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Characters as object (not array)
|
|
111
|
+
characters?: {
|
|
112
|
+
alice: { hp: 50, name: 'Alice' }; // Becomes $characters_alice_hp, $characters_alice_name
|
|
113
|
+
bob: { hp: 30, name: 'Bob' }; // Becomes $characters_bob_hp, $characters_bob_name
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// Any other game data
|
|
117
|
+
inventory?: string[];
|
|
118
|
+
location?: string;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const gameState: GameState = {
|
|
122
|
+
flags: {
|
|
123
|
+
quest_dragon_slayer: 'not_started',
|
|
124
|
+
item_ancient_key: false,
|
|
125
|
+
},
|
|
126
|
+
player: {
|
|
127
|
+
hp: 75,
|
|
128
|
+
maxHp: 100,
|
|
129
|
+
name: 'Hero',
|
|
130
|
+
affinity: { A: 0.8, B: 0.5, C: 0.2 },
|
|
131
|
+
elementAffinity: { fire: 0.9, water: 0.3 },
|
|
132
|
+
},
|
|
133
|
+
characters: {
|
|
134
|
+
alice: { hp: 50, name: 'Alice' },
|
|
135
|
+
bob: { hp: 30, name: 'Bob' },
|
|
136
|
+
},
|
|
137
|
+
};`, language: "typescript" }),
|
|
138
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "Automatic Flattening"),
|
|
139
|
+
React.createElement("p", { className: "text-gray-400 mb-3" }, "When you pass gameState to ScenePlayer, it's automatically flattened into Yarn-compatible variables:"),
|
|
140
|
+
React.createElement("div", { className: "bg-[#12121a] p-4 rounded border border-[#2a2a3e]" },
|
|
141
|
+
React.createElement("p", { className: "text-gray-300 text-xs mb-2 font-semibold" }, "Flattening Rules:"),
|
|
142
|
+
React.createElement("ul", { className: "list-disc list-inside space-y-1 text-xs text-gray-400 ml-2" },
|
|
143
|
+
React.createElement("li", null,
|
|
144
|
+
React.createElement("strong", null, "Nested objects:"),
|
|
145
|
+
" ",
|
|
146
|
+
React.createElement("code", null, "player.hp"),
|
|
147
|
+
" \u2192 ",
|
|
148
|
+
React.createElement("code", null, "$player_hp")),
|
|
149
|
+
React.createElement("li", null,
|
|
150
|
+
React.createElement("strong", null, "Deep nesting:"),
|
|
151
|
+
" ",
|
|
152
|
+
React.createElement("code", null, "player.affinity.A"),
|
|
153
|
+
" \u2192 ",
|
|
154
|
+
React.createElement("code", null, "$player_affinity_A")),
|
|
155
|
+
React.createElement("li", null,
|
|
156
|
+
React.createElement("strong", null, "Object keys:"),
|
|
157
|
+
" ",
|
|
158
|
+
React.createElement("code", null, "characters.alice.hp"),
|
|
159
|
+
" \u2192 ",
|
|
160
|
+
React.createElement("code", null, "$characters_alice_hp")),
|
|
161
|
+
React.createElement("li", null,
|
|
162
|
+
React.createElement("strong", null, "Arrays:"),
|
|
163
|
+
" ",
|
|
164
|
+
React.createElement("code", null, "inventory[0]"),
|
|
165
|
+
" \u2192 ",
|
|
166
|
+
React.createElement("code", null, "$inventory_0")),
|
|
167
|
+
React.createElement("li", null,
|
|
168
|
+
React.createElement("strong", null, "Only truthy values:"),
|
|
169
|
+
" Skips 0, false, null, undefined, empty strings"),
|
|
170
|
+
React.createElement("li", null,
|
|
171
|
+
React.createElement("strong", null, "Yarn-compatible types:"),
|
|
172
|
+
" Only boolean, number, string (validated)"))),
|
|
173
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "Using in Yarn Conditions"),
|
|
174
|
+
React.createElement("p", { className: "text-gray-400 mb-3" }, "Flattened variables can be used in dialogue conditions:"),
|
|
175
|
+
React.createElement(CodeBlock, { code: `// In your dialogue nodes, use flattened variable names:
|
|
176
|
+
|
|
177
|
+
// Check player HP
|
|
178
|
+
<<if $player_hp >= 50>>
|
|
179
|
+
Merchant: "You look healthy!"
|
|
180
|
+
<<else>>
|
|
181
|
+
Merchant: "You should rest..."
|
|
182
|
+
<<endif>>
|
|
183
|
+
|
|
184
|
+
// Check rune affinity
|
|
185
|
+
<<if $player_affinity_A > 0.7>>
|
|
186
|
+
Wizard: "You have strong affinity with rune A!"
|
|
187
|
+
<<endif>>
|
|
188
|
+
|
|
189
|
+
// Check character status
|
|
190
|
+
<<if $characters_alice_hp < 20>>
|
|
191
|
+
Healer: "Alice needs healing!"
|
|
192
|
+
<<endif>>
|
|
193
|
+
|
|
194
|
+
// Check element affinity
|
|
195
|
+
<<if $player_elementAffinity_fire > 0.8>>
|
|
196
|
+
Fire Mage: "You're a natural with fire magic!"
|
|
197
|
+
<<endif>>`, language: "yarn" }),
|
|
198
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "Flag Schema (For Editor)"),
|
|
199
|
+
React.createElement("p", { className: "text-gray-400 mb-3" },
|
|
200
|
+
"Flag Schema is used in the ",
|
|
201
|
+
React.createElement("strong", null, "editor"),
|
|
202
|
+
" for autocomplete and validation. It's separate from game state:"),
|
|
203
|
+
React.createElement(CodeBlock, { code: `import { FlagSchema } from '@magicborn/dialogue-forge';
|
|
204
|
+
|
|
205
|
+
// Flag Schema helps the editor understand what flags exist
|
|
206
|
+
const flagSchema: FlagSchema = {
|
|
207
|
+
flags: [
|
|
208
|
+
{
|
|
209
|
+
id: 'quest_dragon_slayer',
|
|
210
|
+
name: 'Dragon Slayer Quest',
|
|
211
|
+
type: 'quest',
|
|
212
|
+
valueType: 'string',
|
|
213
|
+
defaultValue: 'not_started'
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
id: 'player_hp', // Matches flattened game state
|
|
217
|
+
name: 'Player HP',
|
|
218
|
+
type: 'stat',
|
|
219
|
+
valueType: 'number',
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
id: 'player_affinity_A', // Matches flattened game state
|
|
223
|
+
name: 'Rune A Affinity',
|
|
224
|
+
type: 'stat',
|
|
225
|
+
valueType: 'number',
|
|
226
|
+
},
|
|
227
|
+
]
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
// Use in editor for autocomplete
|
|
231
|
+
<DialogueEditorV2
|
|
232
|
+
dialogue={dialogue}
|
|
233
|
+
flagSchema={flagSchema} // Helps with autocomplete
|
|
234
|
+
onChange={...}
|
|
235
|
+
/>
|
|
236
|
+
|
|
237
|
+
// Game state is used in player
|
|
238
|
+
<ScenePlayer
|
|
239
|
+
dialogue={dialogue}
|
|
240
|
+
gameState={gameState} // Full game state (automatically flattened)
|
|
241
|
+
onComplete={...}
|
|
242
|
+
/>`, language: "typescript" }),
|
|
243
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "Flag Types"),
|
|
244
|
+
React.createElement("div", { className: "space-y-2 text-sm" },
|
|
245
|
+
React.createElement("div", { className: "bg-[#12121a] p-2 rounded border border-[#2a2a3e]" },
|
|
246
|
+
React.createElement("strong", { className: "text-blue-400" }, "quest"),
|
|
247
|
+
" - Quest state (",
|
|
248
|
+
React.createElement("code", { className: "text-xs" }, "'not_started'"),
|
|
249
|
+
", ",
|
|
250
|
+
React.createElement("code", { className: "text-xs" }, "'started'"),
|
|
251
|
+
", ",
|
|
252
|
+
React.createElement("code", { className: "text-xs" }, "'complete'"),
|
|
253
|
+
")"),
|
|
254
|
+
React.createElement("div", { className: "bg-[#12121a] p-2 rounded border border-[#2a2a3e]" },
|
|
255
|
+
React.createElement("strong", { className: "text-yellow-400" }, "achievement"),
|
|
256
|
+
" - Unlocked achievements (true/false)"),
|
|
257
|
+
React.createElement("div", { className: "bg-[#12121a] p-2 rounded border border-[#2a2a3e]" },
|
|
258
|
+
React.createElement("strong", { className: "text-green-400" }, "item"),
|
|
259
|
+
" - Inventory items (true/false or quantity)"),
|
|
260
|
+
React.createElement("div", { className: "bg-[#12121a] p-2 rounded border border-[#2a2a3e]" },
|
|
261
|
+
React.createElement("strong", { className: "text-purple-400" }, "stat"),
|
|
262
|
+
" - Player statistics (numbers: hp, gold, affinity, etc.)"),
|
|
263
|
+
React.createElement("div", { className: "bg-[#12121a] p-2 rounded border border-[#2a2a3e]" },
|
|
264
|
+
React.createElement("strong", { className: "text-pink-400" }, "title"),
|
|
265
|
+
" - Earned player titles (true/false)"),
|
|
266
|
+
React.createElement("div", { className: "bg-[#12121a] p-2 rounded border border-[#2a2a3e]" },
|
|
267
|
+
React.createElement("strong", { className: "text-gray-400" }, "dialogue"),
|
|
268
|
+
" - Temporary, dialogue-scoped flags")),
|
|
269
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "Best Practices"),
|
|
270
|
+
React.createElement("div", { className: "space-y-2 text-sm" },
|
|
271
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-[#2a2a3e]" },
|
|
272
|
+
React.createElement("strong", { className: "text-white text-xs" }, "1. Use descriptive names"),
|
|
273
|
+
React.createElement("p", { className: "text-gray-400 text-xs mt-1" },
|
|
274
|
+
"Flattened names should be clear: ",
|
|
275
|
+
React.createElement("code", null, "$player_hp"),
|
|
276
|
+
" not ",
|
|
277
|
+
React.createElement("code", null, "$p_h"))),
|
|
278
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-[#2a2a3e]" },
|
|
279
|
+
React.createElement("strong", { className: "text-white text-xs" }, "2. Keep structures consistent"),
|
|
280
|
+
React.createElement("p", { className: "text-gray-400 text-xs mt-1" }, "Use the same game state structure throughout your app for predictable flattening")),
|
|
281
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-[#2a2a3e]" },
|
|
282
|
+
React.createElement("strong", { className: "text-white text-xs" }, "3. Characters as objects, not arrays"),
|
|
283
|
+
React.createElement("p", { className: "text-gray-400 text-xs mt-1" },
|
|
284
|
+
"Use ",
|
|
285
|
+
React.createElement("code", null,
|
|
286
|
+
"characters: ",
|
|
287
|
+
'{',
|
|
288
|
+
" alice: ",
|
|
289
|
+
'{...}',
|
|
290
|
+
" ",
|
|
291
|
+
'}'),
|
|
292
|
+
" not ",
|
|
293
|
+
React.createElement("code", null, "characters: [alice, ...]"),
|
|
294
|
+
" for better naming")),
|
|
295
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-[#2a2a3e]" },
|
|
296
|
+
React.createElement("strong", { className: "text-white text-xs" }, "4. Only truthy values are included"),
|
|
297
|
+
React.createElement("p", { className: "text-gray-400 text-xs mt-1" }, "Zero, false, null, undefined, and empty strings are automatically excluded from flattening")))))
|
|
298
|
+
},
|
|
299
|
+
integration: {
|
|
300
|
+
title: 'Integration Guide',
|
|
301
|
+
content: (React.createElement("div", { className: "space-y-4 text-sm" },
|
|
302
|
+
React.createElement("p", null, "How to integrate Dialogue Forge with your game."),
|
|
303
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "1. Install Package"),
|
|
304
|
+
React.createElement("div", { className: "bg-[#12121a] p-4 rounded border border-[#2a2a3e]" },
|
|
305
|
+
React.createElement("pre", { className: "text-xs font-mono text-gray-300" }, "npm install @magicborn/dialogue-forge")),
|
|
306
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "2. Define Flag Schema"),
|
|
307
|
+
React.createElement(CodeBlock, { code: `import { FlagSchema } from '@magicborn/dialogue-forge';
|
|
308
|
+
|
|
309
|
+
const flagSchema: FlagSchema = {
|
|
310
|
+
flags: [
|
|
311
|
+
{ id: 'quest_dragon_slayer', type: 'quest', valueType: 'string' },
|
|
312
|
+
{ id: 'item_ancient_key', type: 'item', valueType: 'boolean' },
|
|
313
|
+
{ id: 'stat_gold', type: 'stat', valueType: 'number', defaultValue: 0 },
|
|
314
|
+
]
|
|
315
|
+
};`, language: "typescript" }),
|
|
316
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "3. Load Dialogue from Yarn"),
|
|
317
|
+
React.createElement(CodeBlock, { code: `import { importFromYarn } from '@magicborn/dialogue-forge';
|
|
318
|
+
|
|
319
|
+
// Load Yarn file
|
|
320
|
+
const yarnContent = await loadFile('merchant.yarn');
|
|
321
|
+
const dialogue = importFromYarn(yarnContent, 'Merchant Dialogue');`, language: "typescript" }),
|
|
322
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "4. Define Game State"),
|
|
323
|
+
React.createElement("p", { className: "text-gray-400 text-sm mb-2" }, "Game state can be any JSON structure. Flags represent the dialogue-relevant portion."),
|
|
324
|
+
React.createElement(CodeBlock, { code: `// Define your game state structure
|
|
325
|
+
interface GameState {
|
|
326
|
+
flags: {
|
|
327
|
+
quest_dragon_slayer: 'complete' | 'started' | 'not_started';
|
|
328
|
+
item_ancient_key: boolean;
|
|
329
|
+
stat_gold: number;
|
|
330
|
+
stat_reputation: number;
|
|
331
|
+
};
|
|
332
|
+
player: {
|
|
333
|
+
name: string;
|
|
334
|
+
level: number;
|
|
335
|
+
location: string;
|
|
336
|
+
};
|
|
337
|
+
// ... any other game data
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Initialize game state
|
|
341
|
+
const [gameState, setGameState] = useState<GameState>({
|
|
342
|
+
flags: {
|
|
343
|
+
quest_dragon_slayer: 'not_started',
|
|
344
|
+
item_ancient_key: false,
|
|
345
|
+
stat_gold: 1000,
|
|
346
|
+
stat_reputation: 50,
|
|
347
|
+
},
|
|
348
|
+
player: {
|
|
349
|
+
name: 'Hero',
|
|
350
|
+
level: 5,
|
|
351
|
+
location: 'town',
|
|
352
|
+
},
|
|
353
|
+
});`, language: "typescript" }),
|
|
354
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "5. Edit Dialogue"),
|
|
355
|
+
React.createElement(CodeBlock, { code: `import { DialogueEditorV2, exportToYarn } from '@magicborn/dialogue-forge';
|
|
356
|
+
|
|
357
|
+
<DialogueEditorV2
|
|
358
|
+
dialogue={dialogue}
|
|
359
|
+
onChange={(updated) => {
|
|
360
|
+
// Save edited dialogue
|
|
361
|
+
const yarn = exportToYarn(updated);
|
|
362
|
+
saveYarnFile(yarn);
|
|
363
|
+
}}
|
|
364
|
+
flagSchema={flagSchema}
|
|
365
|
+
// Event hooks
|
|
366
|
+
onNodeAdd={(node) => console.log('Node added:', node.id)}
|
|
367
|
+
onNodeDelete={(nodeId) => console.log('Node deleted:', nodeId)}
|
|
368
|
+
onConnect={(source, target) => console.log('Connected:', source, '->', target)}
|
|
369
|
+
/>`, language: "typescript" }),
|
|
370
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "6. Define Game State"),
|
|
371
|
+
React.createElement("p", { className: "text-gray-400 text-sm mb-2" }, "Game state can be any JSON object. Flags should represent the dialogue-relevant portion of your game state."),
|
|
372
|
+
React.createElement(CodeBlock, { code: `// Example game state structure
|
|
373
|
+
interface GameState {
|
|
374
|
+
flags: {
|
|
375
|
+
quest_dragon_slayer: 'complete' | 'started' | 'not_started';
|
|
376
|
+
item_ancient_key: boolean;
|
|
377
|
+
stat_gold: number;
|
|
378
|
+
stat_reputation: number;
|
|
379
|
+
};
|
|
380
|
+
player: {
|
|
381
|
+
name: string;
|
|
382
|
+
level: number;
|
|
383
|
+
location: string;
|
|
384
|
+
};
|
|
385
|
+
inventory: string[];
|
|
386
|
+
// ... any other game data
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Initialize game state
|
|
390
|
+
const gameState: GameState = {
|
|
391
|
+
flags: {
|
|
392
|
+
quest_dragon_slayer: 'not_started',
|
|
393
|
+
item_ancient_key: false,
|
|
394
|
+
stat_gold: 1000,
|
|
395
|
+
stat_reputation: 50,
|
|
396
|
+
},
|
|
397
|
+
player: {
|
|
398
|
+
name: 'Hero',
|
|
399
|
+
level: 5,
|
|
400
|
+
location: 'town',
|
|
401
|
+
},
|
|
402
|
+
inventory: ['sword', 'potion'],
|
|
403
|
+
};`, language: "typescript" }),
|
|
404
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "7. Run Dialogue (Scene Player)"),
|
|
405
|
+
React.createElement(CodeBlock, { code: `import { ScenePlayer } from '@magicborn/dialogue-forge';
|
|
406
|
+
|
|
407
|
+
<ScenePlayer
|
|
408
|
+
dialogue={dialogue}
|
|
409
|
+
gameState={gameState} // Pass full game state
|
|
410
|
+
onComplete={(result) => {
|
|
411
|
+
// Update game state with new flags
|
|
412
|
+
setGameState(prev => ({
|
|
413
|
+
...prev,
|
|
414
|
+
flags: {
|
|
415
|
+
...prev.flags,
|
|
416
|
+
...result.updatedFlags
|
|
417
|
+
}
|
|
418
|
+
}));
|
|
419
|
+
|
|
420
|
+
// Now certain dialogues are locked/unlocked
|
|
421
|
+
// based on the updated flags
|
|
422
|
+
}}
|
|
423
|
+
onFlagUpdate={(flags) => {
|
|
424
|
+
// Real-time updates as dialogue progresses
|
|
425
|
+
setGameState(prev => ({
|
|
426
|
+
...prev,
|
|
427
|
+
flags: { ...prev.flags, ...flags }
|
|
428
|
+
}));
|
|
429
|
+
}}
|
|
430
|
+
// Event hooks
|
|
431
|
+
onNodeEnter={(nodeId, node) => {
|
|
432
|
+
console.log('Entered node:', nodeId, node);
|
|
433
|
+
// Trigger animations, sound effects, etc.
|
|
434
|
+
}}
|
|
435
|
+
onNodeExit={(nodeId, node) => {
|
|
436
|
+
console.log('Exited node:', nodeId, node);
|
|
437
|
+
}}
|
|
438
|
+
onChoiceSelect={(nodeId, choice) => {
|
|
439
|
+
console.log('Selected choice:', choice.text);
|
|
440
|
+
// Track player decisions
|
|
441
|
+
}}
|
|
442
|
+
onDialogueStart={() => {
|
|
443
|
+
console.log('Dialogue started');
|
|
444
|
+
}}
|
|
445
|
+
onDialogueEnd={() => {
|
|
446
|
+
console.log('Dialogue ended');
|
|
447
|
+
}}
|
|
448
|
+
/>`, language: "typescript" }),
|
|
449
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "Complete Example"),
|
|
450
|
+
React.createElement(CodeBlock, { code: `import {
|
|
451
|
+
DialogueEditorV2,
|
|
452
|
+
ScenePlayer,
|
|
453
|
+
importFromYarn,
|
|
454
|
+
exportToYarn,
|
|
455
|
+
FlagSchema
|
|
456
|
+
} from '@magicborn/dialogue-forge';
|
|
457
|
+
|
|
458
|
+
// 1. Define flag schema
|
|
459
|
+
const flagSchema: FlagSchema = {
|
|
460
|
+
flags: [
|
|
461
|
+
{ id: 'quest_complete', type: 'quest', valueType: 'boolean' },
|
|
462
|
+
{ id: 'item_key', type: 'item', valueType: 'boolean' },
|
|
463
|
+
{ id: 'stat_gold', type: 'stat', valueType: 'number', defaultValue: 0 },
|
|
464
|
+
]
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
// 2. Define game state
|
|
468
|
+
interface GameState {
|
|
469
|
+
flags: {
|
|
470
|
+
quest_complete: boolean;
|
|
471
|
+
item_key: boolean;
|
|
472
|
+
stat_gold: number;
|
|
473
|
+
};
|
|
474
|
+
player: {
|
|
475
|
+
name: string;
|
|
476
|
+
level: number;
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const [gameState, setGameState] = useState<GameState>({
|
|
481
|
+
flags: {
|
|
482
|
+
quest_complete: false,
|
|
483
|
+
item_key: false,
|
|
484
|
+
stat_gold: 1000,
|
|
485
|
+
},
|
|
486
|
+
player: {
|
|
487
|
+
name: 'Hero',
|
|
488
|
+
level: 5,
|
|
489
|
+
},
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
// 3. Load dialogue
|
|
493
|
+
const dialogue = importFromYarn(yarnFile, 'Merchant');
|
|
494
|
+
|
|
495
|
+
// 4. Edit dialogue with event hooks
|
|
496
|
+
<DialogueEditorV2
|
|
497
|
+
dialogue={dialogue}
|
|
498
|
+
onChange={(updated) => {
|
|
499
|
+
const yarn = exportToYarn(updated);
|
|
500
|
+
saveFile(yarn);
|
|
501
|
+
}}
|
|
502
|
+
flagSchema={flagSchema}
|
|
503
|
+
// Event hooks
|
|
504
|
+
onNodeAdd={(node) => {
|
|
505
|
+
console.log('Node added:', node.id);
|
|
506
|
+
// Track node creation
|
|
507
|
+
}}
|
|
508
|
+
onNodeDelete={(nodeId) => {
|
|
509
|
+
console.log('Node deleted:', nodeId);
|
|
510
|
+
}}
|
|
511
|
+
onNodeUpdate={(nodeId, updates) => {
|
|
512
|
+
console.log('Node updated:', nodeId, updates);
|
|
513
|
+
}}
|
|
514
|
+
onConnect={(sourceId, targetId, sourceHandle) => {
|
|
515
|
+
console.log('Connected:', sourceId, '->', targetId);
|
|
516
|
+
}}
|
|
517
|
+
onDisconnect={(edgeId, sourceId, targetId) => {
|
|
518
|
+
console.log('Disconnected:', sourceId, '->', targetId);
|
|
519
|
+
}}
|
|
520
|
+
onNodeSelect={(nodeId) => {
|
|
521
|
+
console.log('Node selected:', nodeId);
|
|
522
|
+
}}
|
|
523
|
+
/>
|
|
524
|
+
|
|
525
|
+
// 5. Run dialogue with event hooks
|
|
526
|
+
<ScenePlayer
|
|
527
|
+
dialogue={dialogue}
|
|
528
|
+
gameState={gameState}
|
|
529
|
+
onComplete={(result) => {
|
|
530
|
+
// Update game state with new flags
|
|
531
|
+
setGameState(prev => ({
|
|
532
|
+
...prev,
|
|
533
|
+
flags: { ...prev.flags, ...result.updatedFlags }
|
|
534
|
+
}));
|
|
535
|
+
}}
|
|
536
|
+
onNodeEnter={(nodeId, node) => {
|
|
537
|
+
console.log('Entered node:', nodeId);
|
|
538
|
+
// Play animations, sound effects
|
|
539
|
+
}}
|
|
540
|
+
onChoiceSelect={(nodeId, choice) => {
|
|
541
|
+
console.log('Selected:', choice.text);
|
|
542
|
+
// Track player decisions
|
|
543
|
+
}}
|
|
544
|
+
/>`, language: "typescript" }),
|
|
545
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "How Flags Work with Unreal"),
|
|
546
|
+
React.createElement("div", { className: "space-y-3 text-sm" },
|
|
547
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-[#2a2a3e]" },
|
|
548
|
+
React.createElement("strong", { className: "text-white" }, "1. Export to Yarn"),
|
|
549
|
+
React.createElement("p", { className: "text-gray-400 text-xs mt-1" },
|
|
550
|
+
"When you export, flags become Yarn commands: ",
|
|
551
|
+
React.createElement("code", { className: "text-xs" }, "<<set $quest_dragon_slayer = \"started\">>"))),
|
|
552
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-[#2a2a3e]" },
|
|
553
|
+
React.createElement("strong", { className: "text-white" }, "2. Import to Unreal"),
|
|
554
|
+
React.createElement("p", { className: "text-gray-400 text-xs mt-1" },
|
|
555
|
+
"Yarn Spinner loads the .yarn file and recognizes ",
|
|
556
|
+
React.createElement("code", { className: "text-xs" }, "$variable"),
|
|
557
|
+
" references. Variables are stored in Yarn Spinner's ",
|
|
558
|
+
React.createElement("strong", null, "Variable Storage"),
|
|
559
|
+
" component.")),
|
|
560
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-[#2a2a3e]" },
|
|
561
|
+
React.createElement("strong", { className: "text-white" }, "3. Game Sets Variables"),
|
|
562
|
+
React.createElement("p", { className: "text-gray-400 text-xs mt-1" },
|
|
563
|
+
"Your Unreal code sets initial state: ",
|
|
564
|
+
React.createElement("code", { className: "text-xs" }, "VariableStorage->SetValue(\"quest_dragon_slayer\", \"not_started\")"))),
|
|
565
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-[#2a2a3e]" },
|
|
566
|
+
React.createElement("strong", { className: "text-white" }, "4. Dialogue Reacts"),
|
|
567
|
+
React.createElement("p", { className: "text-gray-400 text-xs mt-1" },
|
|
568
|
+
"Yarn checks variables: ",
|
|
569
|
+
React.createElement("code", { className: "text-xs" }, "<<if $quest_dragon_slayer == \"started\">>"),
|
|
570
|
+
" reads from Variable Storage.")),
|
|
571
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-[#2a2a3e]" },
|
|
572
|
+
React.createElement("strong", { className: "text-white" }, "5. Dialogue Sets Variables"),
|
|
573
|
+
React.createElement("p", { className: "text-gray-400 text-xs mt-1" },
|
|
574
|
+
"When dialogue runs ",
|
|
575
|
+
React.createElement("code", { className: "text-xs" }, "<<set $var>>"),
|
|
576
|
+
", it updates Variable Storage. Your game can read these back."))),
|
|
577
|
+
React.createElement("div", { className: "bg-[#1a2a3e] border-l-4 border-blue-500 p-4 rounded mt-4" },
|
|
578
|
+
React.createElement("p", { className: "text-gray-300 text-xs" },
|
|
579
|
+
React.createElement("strong", null, "Remember:"),
|
|
580
|
+
" Variables live in Yarn Spinner's Variable Storage at runtime, not in the .yarn file. The file only contains commands that manipulate the storage."))))
|
|
581
|
+
},
|
|
582
|
+
yarn: {
|
|
583
|
+
title: 'Yarn Spinner',
|
|
584
|
+
content: (React.createElement("div", { className: "space-y-4 text-sm" },
|
|
585
|
+
React.createElement("p", null, "Dialogue Forge exports to Yarn Spinner format for use in game engines like Unreal Engine."),
|
|
586
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "Basic Syntax"),
|
|
587
|
+
React.createElement("div", { className: "bg-[#12121a] p-4 rounded border border-[#2a2a3e]" },
|
|
588
|
+
React.createElement("pre", { className: "text-xs font-mono text-gray-300 whitespace-pre-wrap" }, `title: node_id
|
|
589
|
+
---
|
|
590
|
+
Speaker: Dialogue text here
|
|
591
|
+
<<set $flag_name = true>>
|
|
592
|
+
<<jump next_node_id>>
|
|
593
|
+
===`)),
|
|
594
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "Exporting"),
|
|
595
|
+
React.createElement("ol", { className: "list-decimal list-inside space-y-2 text-sm ml-2" },
|
|
596
|
+
React.createElement("li", null,
|
|
597
|
+
"Click the ",
|
|
598
|
+
React.createElement("strong", null, "Yarn"),
|
|
599
|
+
" tab (code icon) to see generated script"),
|
|
600
|
+
React.createElement("li", null,
|
|
601
|
+
"Click ",
|
|
602
|
+
React.createElement("strong", null, "\"Download .yarn\""),
|
|
603
|
+
" to save the file"),
|
|
604
|
+
React.createElement("li", null,
|
|
605
|
+
"Import the ",
|
|
606
|
+
React.createElement("code", { className: "bg-[#12121a] px-1 rounded" }, ".yarn"),
|
|
607
|
+
" file into your game engine")),
|
|
608
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "Importing"),
|
|
609
|
+
React.createElement("ol", { className: "list-decimal list-inside space-y-2 text-sm ml-2" },
|
|
610
|
+
React.createElement("li", null,
|
|
611
|
+
"Click the ",
|
|
612
|
+
React.createElement("strong", null, "Import"),
|
|
613
|
+
" button (upload icon)"),
|
|
614
|
+
React.createElement("li", null,
|
|
615
|
+
"Select a ",
|
|
616
|
+
React.createElement("code", { className: "bg-[#12121a] px-1 rounded" }, ".yarn"),
|
|
617
|
+
" file"),
|
|
618
|
+
React.createElement("li", null, "Nodes are automatically created from the Yarn structure"),
|
|
619
|
+
React.createElement("li", null, "Edit visually, then export again")),
|
|
620
|
+
React.createElement("p", { className: "text-gray-400 text-xs mt-3" },
|
|
621
|
+
React.createElement("strong", null, "Note:"),
|
|
622
|
+
" Flags are managed separately. Import/export flag schemas using the Flag Manager."),
|
|
623
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "\u2705 Yarn Features Supported"),
|
|
624
|
+
React.createElement("div", { className: "space-y-3" },
|
|
625
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-[#2a2a3e]" },
|
|
626
|
+
React.createElement("strong", { className: "text-green-400 text-xs" }, "Core Features"),
|
|
627
|
+
React.createElement("ul", { className: "list-disc list-inside space-y-1 text-xs mt-2 ml-2 text-gray-400" },
|
|
628
|
+
React.createElement("li", null,
|
|
629
|
+
"Dialogue text with speakers (",
|
|
630
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "Speaker: Text"),
|
|
631
|
+
")"),
|
|
632
|
+
React.createElement("li", null,
|
|
633
|
+
"Player choices (",
|
|
634
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "-> Choice text"),
|
|
635
|
+
")"),
|
|
636
|
+
React.createElement("li", null,
|
|
637
|
+
"Node structure (",
|
|
638
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "title:"),
|
|
639
|
+
", ",
|
|
640
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "---"),
|
|
641
|
+
", ",
|
|
642
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "==="),
|
|
643
|
+
")"))),
|
|
644
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-[#2a2a3e]" },
|
|
645
|
+
React.createElement("strong", { className: "text-green-400 text-xs" }, "Commands"),
|
|
646
|
+
React.createElement("ul", { className: "list-disc list-inside space-y-1 text-xs mt-2 ml-2 text-gray-400" },
|
|
647
|
+
React.createElement("li", null,
|
|
648
|
+
"Flag setting (",
|
|
649
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "<<set $flag = true>>"),
|
|
650
|
+
")"),
|
|
651
|
+
React.createElement("li", null,
|
|
652
|
+
"Jumps (",
|
|
653
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "<<jump node_id>>"),
|
|
654
|
+
")"))),
|
|
655
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-green-500/30" },
|
|
656
|
+
React.createElement("strong", { className: "text-green-400 text-xs" }, "\u2705 Conditional Blocks (Full Support)"),
|
|
657
|
+
React.createElement("ul", { className: "list-disc list-inside space-y-1 text-xs mt-2 ml-2 text-gray-400" },
|
|
658
|
+
React.createElement("li", null,
|
|
659
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "<<if condition>>"),
|
|
660
|
+
" - Conditional dialogue blocks"),
|
|
661
|
+
React.createElement("li", null,
|
|
662
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "<<elseif condition>>"),
|
|
663
|
+
" - Alternative conditions"),
|
|
664
|
+
React.createElement("li", null,
|
|
665
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "<<else>>"),
|
|
666
|
+
" - Default fallback"),
|
|
667
|
+
React.createElement("li", null,
|
|
668
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "<<endif>>"),
|
|
669
|
+
" - End conditional block")),
|
|
670
|
+
React.createElement("p", { className: "text-gray-500 text-xs mt-2" }, "Supports nested conditionals in NPC nodes with multiple blocks")),
|
|
671
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-green-500/30" },
|
|
672
|
+
React.createElement("strong", { className: "text-green-400 text-xs" }, "\u2705 Conditional Choices"),
|
|
673
|
+
React.createElement("ul", { className: "list-disc list-inside space-y-1 text-xs mt-2 ml-2 text-gray-400" },
|
|
674
|
+
React.createElement("li", null,
|
|
675
|
+
"Choices can have conditions that wrap them in ",
|
|
676
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "<<if>>"),
|
|
677
|
+
" blocks"),
|
|
678
|
+
React.createElement("li", null, "Choices only appear when conditions are met"),
|
|
679
|
+
React.createElement("li", null, "Supports multiple conditions with AND logic"))),
|
|
680
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-green-500/30" },
|
|
681
|
+
React.createElement("strong", { className: "text-green-400 text-xs" }, "\u2705 Condition Operators"),
|
|
682
|
+
React.createElement("ul", { className: "list-disc list-inside space-y-1 text-xs mt-2 ml-2 text-gray-400" },
|
|
683
|
+
React.createElement("li", null,
|
|
684
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "is_set"),
|
|
685
|
+
" - Check if flag exists"),
|
|
686
|
+
React.createElement("li", null,
|
|
687
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "is_not_set"),
|
|
688
|
+
" - Check if flag doesn't exist"),
|
|
689
|
+
React.createElement("li", null,
|
|
690
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "=="),
|
|
691
|
+
" - Equals"),
|
|
692
|
+
React.createElement("li", null,
|
|
693
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "!="),
|
|
694
|
+
" - Not equals"),
|
|
695
|
+
React.createElement("li", null,
|
|
696
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, ">"),
|
|
697
|
+
" - Greater than"),
|
|
698
|
+
React.createElement("li", null,
|
|
699
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "<"),
|
|
700
|
+
" - Less than"),
|
|
701
|
+
React.createElement("li", null,
|
|
702
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, ">="),
|
|
703
|
+
" - Greater or equal"),
|
|
704
|
+
React.createElement("li", null,
|
|
705
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "<="),
|
|
706
|
+
" - Less or equal"))),
|
|
707
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-yellow-500/30" },
|
|
708
|
+
React.createElement("strong", { className: "text-yellow-400 text-xs" }, "\u26A0\uFE0F Partially Supported"),
|
|
709
|
+
React.createElement("ul", { className: "list-disc list-inside space-y-1 text-xs mt-2 ml-2 text-gray-400" },
|
|
710
|
+
React.createElement("li", null,
|
|
711
|
+
"Basic variable setting (",
|
|
712
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "<<set $flag = true>>"),
|
|
713
|
+
") - Boolean only"),
|
|
714
|
+
React.createElement("li", null, "String/number variables - Not yet supported"),
|
|
715
|
+
React.createElement("li", null,
|
|
716
|
+
"Variable operations (",
|
|
717
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "+="),
|
|
718
|
+
", ",
|
|
719
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "-="),
|
|
720
|
+
", etc.) - Not yet supported"),
|
|
721
|
+
React.createElement("li", null,
|
|
722
|
+
"Variable references in text (",
|
|
723
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" },
|
|
724
|
+
"\"Hello ",
|
|
725
|
+
'{$name}',
|
|
726
|
+
"\""),
|
|
727
|
+
") - Not yet supported"))),
|
|
728
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-orange-500/30" },
|
|
729
|
+
React.createElement("strong", { className: "text-orange-400 text-xs" }, "\u274C Not Yet Supported"),
|
|
730
|
+
React.createElement("ul", { className: "list-disc list-inside space-y-1 text-xs mt-2 ml-2 text-gray-500" },
|
|
731
|
+
React.createElement("li", null,
|
|
732
|
+
React.createElement("strong", null, "Commands:"),
|
|
733
|
+
" ",
|
|
734
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "<<wait 2>>"),
|
|
735
|
+
", ",
|
|
736
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "<<stop>>"),
|
|
737
|
+
", ",
|
|
738
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "<<command param>>")),
|
|
739
|
+
React.createElement("li", null,
|
|
740
|
+
React.createElement("strong", null, "Detour:"),
|
|
741
|
+
" ",
|
|
742
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "<<detour node_id>>"),
|
|
743
|
+
" (temporary jump with return)"),
|
|
744
|
+
React.createElement("li", null,
|
|
745
|
+
React.createElement("strong", null, "Once:"),
|
|
746
|
+
" ",
|
|
747
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "<<once>>"),
|
|
748
|
+
" (options appear only once)"),
|
|
749
|
+
React.createElement("li", null,
|
|
750
|
+
React.createElement("strong", null, "Shortcuts:"),
|
|
751
|
+
" ",
|
|
752
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "[[text|node]]"),
|
|
753
|
+
" (inline navigation)"),
|
|
754
|
+
React.createElement("li", null,
|
|
755
|
+
React.createElement("strong", null, "Tags:"),
|
|
756
|
+
" ",
|
|
757
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "#tag"),
|
|
758
|
+
" (node metadata)"),
|
|
759
|
+
React.createElement("li", null,
|
|
760
|
+
React.createElement("strong", null, "Node Headers:"),
|
|
761
|
+
" ",
|
|
762
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "color:"),
|
|
763
|
+
", ",
|
|
764
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "group:"),
|
|
765
|
+
", ",
|
|
766
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "style: note")),
|
|
767
|
+
React.createElement("li", null,
|
|
768
|
+
React.createElement("strong", null, "Functions:"),
|
|
769
|
+
" ",
|
|
770
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "visited(\"node_id\")"),
|
|
771
|
+
", ",
|
|
772
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "random(min, max)"),
|
|
773
|
+
", ",
|
|
774
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "dice(sides)")),
|
|
775
|
+
React.createElement("li", null,
|
|
776
|
+
React.createElement("strong", null, "Line Groups:"),
|
|
777
|
+
" Random/sequential line selection"),
|
|
778
|
+
React.createElement("li", null,
|
|
779
|
+
React.createElement("strong", null, "Smart Variables:"),
|
|
780
|
+
" Auto-incrementing, dependencies"),
|
|
781
|
+
React.createElement("li", null,
|
|
782
|
+
React.createElement("strong", null, "Enums:"),
|
|
783
|
+
" Enum type support"))),
|
|
784
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-blue-500/30 mt-3" },
|
|
785
|
+
React.createElement("strong", { className: "text-blue-400 text-xs" }, "\uD83D\uDCCB Yarn Spinner Feature Roadmap"),
|
|
786
|
+
React.createElement("p", { className: "text-xs text-gray-400 mt-2" }, "We're actively working on full Yarn Spinner compatibility. Next priorities:"),
|
|
787
|
+
React.createElement("ol", { className: "list-decimal list-inside space-y-1 text-xs mt-2 ml-2 text-gray-400" },
|
|
788
|
+
React.createElement("li", null,
|
|
789
|
+
React.createElement("strong", null, "Full Variable System"),
|
|
790
|
+
" - String, number, boolean variables with operations"),
|
|
791
|
+
React.createElement("li", null,
|
|
792
|
+
React.createElement("strong", null, "Advanced Set Operations"),
|
|
793
|
+
" - ",
|
|
794
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "+="),
|
|
795
|
+
", ",
|
|
796
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "-="),
|
|
797
|
+
", ",
|
|
798
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "*="),
|
|
799
|
+
", ",
|
|
800
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "/=")),
|
|
801
|
+
React.createElement("li", null,
|
|
802
|
+
React.createElement("strong", null, "Rebuild PlayView"),
|
|
803
|
+
" - Proper Yarn Spinner execution engine"),
|
|
804
|
+
React.createElement("li", null,
|
|
805
|
+
React.createElement("strong", null, "Commands & Shortcuts"),
|
|
806
|
+
" - wait, stop, detour, once, [[text|node]]"),
|
|
807
|
+
React.createElement("li", null,
|
|
808
|
+
React.createElement("strong", null, "Functions & Tags"),
|
|
809
|
+
" - visited(), random(), #tags, node headers")),
|
|
810
|
+
React.createElement("p", { className: "text-xs text-gray-500 mt-2 italic" }, "See the Roadmap section for detailed implementation plans."))),
|
|
811
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "Example: Conditional Dialogue with If/Elseif/Else"),
|
|
812
|
+
React.createElement("div", { className: "bg-[#12121a] p-4 rounded border border-[#2a2a3e]" },
|
|
813
|
+
React.createElement("pre", { className: "text-xs font-mono text-gray-300 whitespace-pre-wrap" }, `title: merchant_greeting
|
|
814
|
+
---
|
|
815
|
+
<<if $quest_dragon_slayer == "complete">>
|
|
816
|
+
Merchant: "Thank you for slaying the dragon! Here's your reward."
|
|
817
|
+
<<set $stat_gold += 500>>
|
|
818
|
+
<<set $achievement_dragon_slayer = true>>
|
|
819
|
+
<<elseif $quest_dragon_slayer == "started">>
|
|
820
|
+
Merchant: "How goes the quest? I heard the dragon is still alive."
|
|
821
|
+
<<elseif $stat_reputation >= 50>>
|
|
822
|
+
Merchant: "Welcome, honored traveler. I've heard good things about you."
|
|
823
|
+
<<else>>
|
|
824
|
+
Merchant: "Welcome, traveler. What can I do for you?"
|
|
825
|
+
<<endif>>
|
|
826
|
+
<<jump merchant_shop>>
|
|
827
|
+
===`),
|
|
828
|
+
React.createElement("p", { className: "text-xs text-gray-400 mt-2" }, "This example shows multiple conditions: first checks if quest is complete, then if started, then if reputation is high enough, otherwise shows default greeting.")),
|
|
829
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "Example: Conditional Choices"),
|
|
830
|
+
React.createElement("div", { className: "bg-[#12121a] p-4 rounded border border-[#2a2a3e]" },
|
|
831
|
+
React.createElement("pre", { className: "text-xs font-mono text-gray-300 whitespace-pre-wrap" }, `title: guard_checkpoint
|
|
832
|
+
---
|
|
833
|
+
Guard: You're not allowed in!
|
|
834
|
+
-> Sure I am! The boss knows me! <<if $reputation > 10>>
|
|
835
|
+
<<jump allowed_in>>
|
|
836
|
+
-> Please?
|
|
837
|
+
<<jump begging>>
|
|
838
|
+
-> I'll come back later.
|
|
839
|
+
<<jump leave>>
|
|
840
|
+
===`),
|
|
841
|
+
React.createElement("p", { className: "text-xs text-gray-400 mt-2" },
|
|
842
|
+
"The first choice only appears if ",
|
|
843
|
+
React.createElement("code", { className: "text-xs" }, "$reputation > 10"),
|
|
844
|
+
". Other choices always show.")),
|
|
845
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "Example: Conditional Choices with Multiple Conditions"),
|
|
846
|
+
React.createElement("div", { className: "bg-[#12121a] p-4 rounded border border-[#2a2a3e]" },
|
|
847
|
+
React.createElement("pre", { className: "text-xs font-mono text-gray-300 whitespace-pre-wrap" }, `title: merchant_shop
|
|
848
|
+
---
|
|
849
|
+
Merchant: What would you like to buy?
|
|
850
|
+
-> "Buy the sword" <<if $stat_gold >= 100 and $reputation >= 20>>
|
|
851
|
+
<<set $item_sword = true>>
|
|
852
|
+
<<set $stat_gold -= 100>>
|
|
853
|
+
<<jump purchase_complete>>
|
|
854
|
+
-> "Buy the potion" <<if $stat_gold >= 50>>
|
|
855
|
+
<<set $item_potion = true>>
|
|
856
|
+
<<set $stat_gold -= 50>>
|
|
857
|
+
<<jump purchase_complete>>
|
|
858
|
+
-> "I'll come back later"
|
|
859
|
+
<<jump leave>>
|
|
860
|
+
===`),
|
|
861
|
+
React.createElement("p", { className: "text-xs text-gray-400 mt-2" },
|
|
862
|
+
"Use ",
|
|
863
|
+
React.createElement("code", { className: "text-xs" }, "and"),
|
|
864
|
+
" to combine multiple conditions. Choices only appear when all conditions are met.")),
|
|
865
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "Code Example"),
|
|
866
|
+
React.createElement("div", { className: "bg-[#12121a] p-4 rounded border border-[#2a2a3e]" },
|
|
867
|
+
React.createElement("pre", { className: "text-xs font-mono text-gray-300 whitespace-pre-wrap overflow-x-auto" }, `import { importFromYarn, exportToYarn } from '@magicborn/dialogue-forge';
|
|
868
|
+
|
|
869
|
+
// Import existing Yarn file
|
|
870
|
+
const yarnContent = await fetch('dialogue.yarn').then(r => r.text());
|
|
871
|
+
const dialogue = importFromYarn(yarnContent, 'My Dialogue');
|
|
872
|
+
|
|
873
|
+
// Edit it...
|
|
874
|
+
|
|
875
|
+
// Export back to Yarn
|
|
876
|
+
const newYarn = exportToYarn(dialogue);
|
|
877
|
+
await saveFile('dialogue.yarn', newYarn);`))))
|
|
878
|
+
},
|
|
879
|
+
shortcuts: {
|
|
880
|
+
title: 'Keyboard Shortcuts',
|
|
881
|
+
content: (React.createElement("div", { className: "space-y-4 text-sm" },
|
|
882
|
+
React.createElement("ul", { className: "space-y-2" },
|
|
883
|
+
React.createElement("li", { className: "flex items-start gap-2" },
|
|
884
|
+
React.createElement("kbd", { className: "bg-[#12121a] px-2 py-1 rounded text-xs font-mono border border-[#2a2a3e]" }, "Ctrl+Z"),
|
|
885
|
+
React.createElement("span", { className: "text-gray-400" }, "Undo last action (Cmd+Z on Mac)")),
|
|
886
|
+
React.createElement("li", { className: "flex items-start gap-2" },
|
|
887
|
+
React.createElement("kbd", { className: "bg-[#12121a] px-2 py-1 rounded text-xs font-mono border border-[#2a2a3e]" }, "Ctrl+Y"),
|
|
888
|
+
React.createElement("span", { className: "text-gray-400" }, "Redo last action (Cmd+Y on Mac)")),
|
|
889
|
+
React.createElement("li", { className: "flex items-start gap-2" },
|
|
890
|
+
React.createElement("kbd", { className: "bg-[#12121a] px-2 py-1 rounded text-xs font-mono border border-[#2a2a3e]" }, "Delete"),
|
|
891
|
+
React.createElement("span", { className: "text-gray-400" }, "Delete selected node(s) or edge(s)")),
|
|
892
|
+
React.createElement("li", { className: "flex items-start gap-2" },
|
|
893
|
+
React.createElement("kbd", { className: "bg-[#12121a] px-2 py-1 rounded text-xs font-mono border border-[#2a2a3e]" }, "Escape"),
|
|
894
|
+
React.createElement("span", { className: "text-gray-400" }, "Close menus, deselect node")),
|
|
895
|
+
React.createElement("li", { className: "flex items-start gap-2" },
|
|
896
|
+
React.createElement("kbd", { className: "bg-[#12121a] px-2 py-1 rounded text-xs font-mono border border-[#2a2a3e]" }, "Right-click"),
|
|
897
|
+
React.createElement("span", { className: "text-gray-400" }, "Context menu (on graph or node)")),
|
|
898
|
+
React.createElement("li", { className: "flex items-start gap-2" },
|
|
899
|
+
React.createElement("kbd", { className: "bg-[#12121a] px-2 py-1 rounded text-xs font-mono border border-[#2a2a3e]" }, "Scroll"),
|
|
900
|
+
React.createElement("span", { className: "text-gray-400" }, "Zoom in/out on graph")),
|
|
901
|
+
React.createElement("li", { className: "flex items-start gap-2" },
|
|
902
|
+
React.createElement("kbd", { className: "bg-[#12121a] px-2 py-1 rounded text-xs font-mono border border-[#2a2a3e]" }, "Drag"),
|
|
903
|
+
React.createElement("span", { className: "text-gray-400" }, "Pan the graph view (middle mouse or space + drag)")),
|
|
904
|
+
React.createElement("li", { className: "flex items-start gap-2" },
|
|
905
|
+
React.createElement("kbd", { className: "bg-[#12121a] px-2 py-1 rounded text-xs font-mono border border-[#2a2a3e]" }, "Drag node"),
|
|
906
|
+
React.createElement("span", { className: "text-gray-400" }, "Move node position")),
|
|
907
|
+
React.createElement("li", { className: "flex items-start gap-2" },
|
|
908
|
+
React.createElement("kbd", { className: "bg-[#12121a] px-2 py-1 rounded text-xs font-mono border border-[#2a2a3e]" }, "Drag port"),
|
|
909
|
+
React.createElement("span", { className: "text-gray-400" }, "Create connection to another node")),
|
|
910
|
+
React.createElement("li", { className: "flex items-start gap-2" },
|
|
911
|
+
React.createElement("kbd", { className: "bg-[#12121a] px-2 py-1 rounded text-xs font-mono border border-[#2a2a3e]" }, "Click + Drag"),
|
|
912
|
+
React.createElement("span", { className: "text-gray-400" }, "Select multiple nodes (selection box)")))))
|
|
913
|
+
},
|
|
914
|
+
roadmap: {
|
|
915
|
+
title: 'Roadmap & Issues',
|
|
916
|
+
content: (React.createElement("div", { className: "space-y-4 text-sm" },
|
|
917
|
+
React.createElement("div", { className: "bg-[#1a2a3e] border-l-4 border-yellow-500 p-4 rounded mb-4" },
|
|
918
|
+
React.createElement("p", { className: "text-gray-300 text-xs mb-2" }, "This section tracks what we're working on and known issues. Check back for updates!"),
|
|
919
|
+
React.createElement("p", { className: "text-gray-400 text-xs" },
|
|
920
|
+
React.createElement("strong", null, "Note:"),
|
|
921
|
+
" For best experience, consider using the editor and simulator as separate pages in your application. The current embedded view may have scroll limitations.")),
|
|
922
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "\u2705 Recently Completed"),
|
|
923
|
+
React.createElement("div", { className: "space-y-2" },
|
|
924
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-green-500/30" },
|
|
925
|
+
React.createElement("div", { className: "flex items-start gap-2" },
|
|
926
|
+
React.createElement("span", { className: "text-green-500 text-xs" }, "\u2705"),
|
|
927
|
+
React.createElement("div", null,
|
|
928
|
+
React.createElement("strong", { className: "text-white text-xs" }, "React Flow Migration"),
|
|
929
|
+
React.createElement("p", { className: "text-gray-400 text-xs mt-1" }, "Complete rewrite using React Flow with custom nodes, edges, and improved UX")))),
|
|
930
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-green-500/30" },
|
|
931
|
+
React.createElement("div", { className: "flex items-start gap-2" },
|
|
932
|
+
React.createElement("span", { className: "text-green-500 text-xs" }, "\u2705"),
|
|
933
|
+
React.createElement("div", null,
|
|
934
|
+
React.createElement("strong", { className: "text-white text-xs" }, "Undo/Redo System"),
|
|
935
|
+
React.createElement("p", { className: "text-gray-400 text-xs mt-1" }, "Action history with Ctrl+Z / Cmd+Z and Ctrl+Y / Cmd+Y (React Flow built-in)")))),
|
|
936
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-green-500/30" },
|
|
937
|
+
React.createElement("div", { className: "flex items-start gap-2" },
|
|
938
|
+
React.createElement("span", { className: "text-green-500 text-xs" }, "\u2705"),
|
|
939
|
+
React.createElement("div", null,
|
|
940
|
+
React.createElement("strong", { className: "text-white text-xs" }, "Multi-Select & Delete"),
|
|
941
|
+
React.createElement("p", { className: "text-gray-400 text-xs mt-1" }, "Selection box to select multiple nodes, bulk delete with Delete key (known issue: square selection doesn't always capture all nodes - deprioritized)")))),
|
|
942
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-green-500/30" },
|
|
943
|
+
React.createElement("div", { className: "flex items-start gap-2" },
|
|
944
|
+
React.createElement("span", { className: "text-green-500 text-xs" }, "\u2705"),
|
|
945
|
+
React.createElement("div", null,
|
|
946
|
+
React.createElement("strong", { className: "text-white text-xs" }, "Minimap"),
|
|
947
|
+
React.createElement("p", { className: "text-gray-400 text-xs mt-1" }, "Graph overview with navigation (React Flow built-in)")))),
|
|
948
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-green-500/30" },
|
|
949
|
+
React.createElement("div", { className: "flex items-start gap-2" },
|
|
950
|
+
React.createElement("span", { className: "text-green-500 text-xs" }, "\u2705"),
|
|
951
|
+
React.createElement("div", null,
|
|
952
|
+
React.createElement("strong", { className: "text-white text-xs" }, "Edge Hover & Deletion"),
|
|
953
|
+
React.createElement("p", { className: "text-gray-400 text-xs mt-1" }, "Edges highlight on hover and can be deleted by selecting and pressing Delete"))))),
|
|
954
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "In Progress"),
|
|
955
|
+
React.createElement("div", { className: "space-y-2" },
|
|
956
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-yellow-500/30" },
|
|
957
|
+
React.createElement("div", { className: "flex items-start gap-2" },
|
|
958
|
+
React.createElement("span", { className: "text-yellow-500 text-xs" }, "\uD83D\uDD04"),
|
|
959
|
+
React.createElement("div", null,
|
|
960
|
+
React.createElement("strong", { className: "text-white text-xs" }, "Enhanced Yarn Spinner Support"),
|
|
961
|
+
React.createElement("p", { className: "text-gray-400 text-xs mt-1" }, "Adding more Yarn Spinner features and improving graph node functionality"))))),
|
|
962
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "Planned (High Priority)"),
|
|
963
|
+
React.createElement("div", { className: "space-y-2" },
|
|
964
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-[#2a2a3e]" },
|
|
965
|
+
React.createElement("div", { className: "flex items-start gap-2" },
|
|
966
|
+
React.createElement("span", { className: "text-blue-500 text-xs" }, "\uD83D\uDCCB"),
|
|
967
|
+
React.createElement("div", null,
|
|
968
|
+
React.createElement("strong", { className: "text-white text-xs" }, "Copy/Paste"),
|
|
969
|
+
React.createElement("p", { className: "text-gray-400 text-xs mt-1" }, "Copy selected nodes and paste with offset, duplicate nodes with connections")))),
|
|
970
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-[#2a2a3e]" },
|
|
971
|
+
React.createElement("div", { className: "flex items-start gap-2" },
|
|
972
|
+
React.createElement("span", { className: "text-blue-500 text-xs" }, "\uD83D\uDCCB"),
|
|
973
|
+
React.createElement("div", null,
|
|
974
|
+
React.createElement("strong", { className: "text-white text-xs" }, "Variables System"),
|
|
975
|
+
React.createElement("p", { className: "text-gray-400 text-xs mt-1" }, "Full Yarn variable support with UI for variable management")))),
|
|
976
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-[#2a2a3e]" },
|
|
977
|
+
React.createElement("div", { className: "flex items-start gap-2" },
|
|
978
|
+
React.createElement("span", { className: "text-blue-500 text-xs" }, "\uD83D\uDCCB"),
|
|
979
|
+
React.createElement("div", null,
|
|
980
|
+
React.createElement("strong", { className: "text-white text-xs" }, "Node Search/Filter"),
|
|
981
|
+
React.createElement("p", { className: "text-gray-400 text-xs mt-1" }, "Search nodes by ID, content, or flags used"))))),
|
|
982
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "Planned (Medium Priority)"),
|
|
983
|
+
React.createElement("div", { className: "space-y-2" },
|
|
984
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-[#2a2a3e]" },
|
|
985
|
+
React.createElement("div", { className: "flex items-start gap-2" },
|
|
986
|
+
React.createElement("span", { className: "text-blue-500 text-xs" }, "\uD83D\uDCCB"),
|
|
987
|
+
React.createElement("div", null,
|
|
988
|
+
React.createElement("strong", { className: "text-white text-xs" }, "Advanced Set Operations"),
|
|
989
|
+
React.createElement("p", { className: "text-gray-400 text-xs mt-1" }, "Increment, decrement, multiply, divide for variables")))),
|
|
990
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-[#2a2a3e]" },
|
|
991
|
+
React.createElement("div", { className: "flex items-start gap-2" },
|
|
992
|
+
React.createElement("span", { className: "text-blue-500 text-xs" }, "\uD83D\uDCCB"),
|
|
993
|
+
React.createElement("div", null,
|
|
994
|
+
React.createElement("strong", { className: "text-white text-xs" }, "Commands Support"),
|
|
995
|
+
React.createElement("p", { className: "text-gray-400 text-xs mt-1" }, "Yarn Spinner command nodes with parameters")))),
|
|
996
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-[#2a2a3e]" },
|
|
997
|
+
React.createElement("div", { className: "flex items-start gap-2" },
|
|
998
|
+
React.createElement("span", { className: "text-blue-500 text-xs" }, "\uD83D\uDCCB"),
|
|
999
|
+
React.createElement("div", null,
|
|
1000
|
+
React.createElement("strong", { className: "text-white text-xs" }, "Node Alignment Tools"),
|
|
1001
|
+
React.createElement("p", { className: "text-gray-400 text-xs mt-1" }, "Align, distribute, and snap to grid"))))),
|
|
1002
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "Known Issues"),
|
|
1003
|
+
React.createElement("div", { className: "space-y-2" },
|
|
1004
|
+
React.createElement("div", { className: "bg-[#12121a] p-3 rounded border border-orange-500/30" },
|
|
1005
|
+
React.createElement("div", { className: "flex items-start gap-2" },
|
|
1006
|
+
React.createElement("span", { className: "text-orange-500 text-xs" }, "\u26A0\uFE0F"),
|
|
1007
|
+
React.createElement("div", null,
|
|
1008
|
+
React.createElement("strong", { className: "text-white text-xs" }, "Square Selection"),
|
|
1009
|
+
React.createElement("p", { className: "text-gray-400 text-xs mt-1" }, "Selection box doesn't always capture all nodes within the selection area (deprioritized)")))))))
|
|
1010
|
+
},
|
|
1011
|
+
theming: {
|
|
1012
|
+
title: 'Theming',
|
|
1013
|
+
content: (React.createElement("div", { className: "space-y-4 text-sm" },
|
|
1014
|
+
React.createElement("p", { className: "text-gray-300" },
|
|
1015
|
+
"Dialogue Forge uses Tailwind CSS theme variables that you can override in your application. All theme variables are prefixed with ",
|
|
1016
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "df-"),
|
|
1017
|
+
" to avoid conflicts."),
|
|
1018
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "How to Override Theme"),
|
|
1019
|
+
React.createElement("p", { className: "text-gray-300 mb-3" }, "Copy the theme variables below into your CSS file and override the values you want to change:"),
|
|
1020
|
+
React.createElement(CodeBlock, { code: `@theme {
|
|
1021
|
+
/* Base Colors */
|
|
1022
|
+
--color-df-base: oklch(0.15 0.02 250);
|
|
1023
|
+
--color-df-surface: oklch(0.18 0.02 260);
|
|
1024
|
+
--color-df-elevated: oklch(0.22 0.02 270);
|
|
1025
|
+
|
|
1026
|
+
/* NPC Node Colors */
|
|
1027
|
+
--color-df-npc-bg: oklch(0.25 0.04 45);
|
|
1028
|
+
--color-df-npc-border: oklch(0.40 0.08 35);
|
|
1029
|
+
--color-df-npc-header: oklch(0.30 0.10 25);
|
|
1030
|
+
--color-df-npc-selected: oklch(0.45 0.12 15);
|
|
1031
|
+
|
|
1032
|
+
/* Player Node Colors */
|
|
1033
|
+
--color-df-player-bg: oklch(0.22 0.08 300);
|
|
1034
|
+
--color-df-player-border: oklch(0.45 0.15 310);
|
|
1035
|
+
--color-df-player-header: oklch(0.28 0.12 290);
|
|
1036
|
+
--color-df-player-selected: oklch(0.55 0.20 280);
|
|
1037
|
+
|
|
1038
|
+
/* Conditional Node */
|
|
1039
|
+
--color-df-conditional-bg: oklch(0.24 0.06 150);
|
|
1040
|
+
--color-df-conditional-border: oklch(0.42 0.12 140);
|
|
1041
|
+
--color-df-conditional-header: oklch(0.30 0.10 145);
|
|
1042
|
+
|
|
1043
|
+
/* Start/End */
|
|
1044
|
+
--color-df-start: oklch(0.55 0.15 140);
|
|
1045
|
+
--color-df-start-bg: oklch(0.25 0.08 140);
|
|
1046
|
+
--color-df-end: oklch(0.50 0.15 45);
|
|
1047
|
+
--color-df-end-bg: oklch(0.25 0.08 45);
|
|
1048
|
+
|
|
1049
|
+
/* Edges */
|
|
1050
|
+
--color-df-edge-default: oklch(0.40 0.03 250);
|
|
1051
|
+
--color-df-edge-default-hover: oklch(0.50 0.05 250);
|
|
1052
|
+
--color-df-edge-choice-1: oklch(0.50 0.18 15);
|
|
1053
|
+
--color-df-edge-choice-2: oklch(0.55 0.20 280);
|
|
1054
|
+
--color-df-edge-choice-3: oklch(0.52 0.18 200);
|
|
1055
|
+
--color-df-edge-choice-4: oklch(0.58 0.16 120);
|
|
1056
|
+
--color-df-edge-choice-5: oklch(0.50 0.15 45);
|
|
1057
|
+
--color-df-edge-loop: oklch(0.55 0.15 60);
|
|
1058
|
+
--color-df-edge-dimmed: oklch(0.25 0.02 250);
|
|
1059
|
+
|
|
1060
|
+
/* Status Colors */
|
|
1061
|
+
--color-df-error: oklch(0.55 0.22 25);
|
|
1062
|
+
--color-df-warning: oklch(0.65 0.18 70);
|
|
1063
|
+
--color-df-success: oklch(0.60 0.18 150);
|
|
1064
|
+
--color-df-info: oklch(0.55 0.15 220);
|
|
1065
|
+
|
|
1066
|
+
/* Text Colors */
|
|
1067
|
+
--color-df-text-primary: oklch(0.85 0.02 250);
|
|
1068
|
+
--color-df-text-secondary: oklch(0.65 0.02 250);
|
|
1069
|
+
--color-df-text-tertiary: oklch(0.45 0.02 250);
|
|
1070
|
+
|
|
1071
|
+
/* UI Control Colors */
|
|
1072
|
+
--color-df-control-bg: oklch(0.18 0.02 260);
|
|
1073
|
+
--color-df-control-border: oklch(0.30 0.03 250);
|
|
1074
|
+
--color-df-control-hover: oklch(0.25 0.03 250);
|
|
1075
|
+
|
|
1076
|
+
/* Flag Colors */
|
|
1077
|
+
--color-df-flag-dialogue: oklch(0.45 0.03 250);
|
|
1078
|
+
--color-df-flag-dialogue-bg: oklch(0.20 0.02 250);
|
|
1079
|
+
--color-df-flag-quest: oklch(0.50 0.15 220);
|
|
1080
|
+
--color-df-flag-quest-bg: oklch(0.22 0.08 220);
|
|
1081
|
+
--color-df-flag-achievement: oklch(0.60 0.18 70);
|
|
1082
|
+
--color-df-flag-achievement-bg: oklch(0.25 0.10 70);
|
|
1083
|
+
--color-df-flag-item: oklch(0.55 0.15 150);
|
|
1084
|
+
--color-df-flag-item-bg: oklch(0.25 0.08 150);
|
|
1085
|
+
--color-df-flag-stat: oklch(0.55 0.18 280);
|
|
1086
|
+
--color-df-flag-stat-bg: oklch(0.25 0.10 280);
|
|
1087
|
+
--color-df-flag-title: oklch(0.55 0.18 330);
|
|
1088
|
+
--color-df-flag-title-bg: oklch(0.25 0.10 330);
|
|
1089
|
+
--color-df-flag-global: oklch(0.50 0.15 45);
|
|
1090
|
+
--color-df-flag-global-bg: oklch(0.25 0.08 45);
|
|
1091
|
+
|
|
1092
|
+
/* Canvas/Background */
|
|
1093
|
+
--color-df-canvas-bg: oklch(0.12 0.01 250);
|
|
1094
|
+
--color-df-canvas-grid: oklch(0.20 0.02 250);
|
|
1095
|
+
|
|
1096
|
+
/* Sidebar/Editor */
|
|
1097
|
+
--color-df-sidebar-bg: oklch(0.18 0.02 260);
|
|
1098
|
+
--color-df-sidebar-border: oklch(0.35 0.05 250);
|
|
1099
|
+
--color-df-editor-bg: oklch(0.15 0.02 240);
|
|
1100
|
+
--color-df-editor-border: oklch(0.30 0.03 250);
|
|
1101
|
+
}`, language: "css" }),
|
|
1102
|
+
React.createElement("h3", { className: "text-lg font-semibold mt-6 mb-2 text-white" }, "Example: Custom Blue Theme"),
|
|
1103
|
+
React.createElement(CodeBlock, { code: `@theme {
|
|
1104
|
+
/* Override NPC colors to blue */
|
|
1105
|
+
--color-df-npc-bg: oklch(0.25 0.08 220);
|
|
1106
|
+
--color-df-npc-border: oklch(0.50 0.15 220);
|
|
1107
|
+
--color-df-npc-selected: oklch(0.60 0.20 220);
|
|
1108
|
+
|
|
1109
|
+
/* Override player colors to purple */
|
|
1110
|
+
--color-df-player-bg: oklch(0.25 0.10 300);
|
|
1111
|
+
--color-df-player-border: oklch(0.55 0.20 300);
|
|
1112
|
+
--color-df-player-selected: oklch(0.65 0.25 300);
|
|
1113
|
+
}`, language: "css" }),
|
|
1114
|
+
React.createElement("div", { className: "bg-[#1a2a3e] border-l-4 border-blue-500 p-4 rounded mt-4" },
|
|
1115
|
+
React.createElement("p", { className: "text-gray-300 text-xs" },
|
|
1116
|
+
React.createElement("strong", null, "Note:"),
|
|
1117
|
+
" All Dialogue Forge components use these theme classes (e.g., ",
|
|
1118
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "bg-df-npc-bg"),
|
|
1119
|
+
", ",
|
|
1120
|
+
React.createElement("code", { className: "bg-[#0d0d14] px-1 rounded" }, "text-df-text-primary"),
|
|
1121
|
+
"). By overriding the CSS variables, you change the colors throughout the entire editor without modifying any component code."))))
|
|
1122
|
+
}
|
|
1123
|
+
};
|
|
1124
|
+
return (React.createElement("div", { className: "fixed inset-0 bg-black/70 z-50 flex items-center justify-center p-4", onClick: onClose },
|
|
1125
|
+
React.createElement("div", { className: "bg-[#0d0d14] border border-[#1a1a2e] rounded-xl max-w-5xl w-full max-h-[90vh] overflow-hidden flex", onClick: (e) => e.stopPropagation() },
|
|
1126
|
+
React.createElement("div", { className: "w-64 border-r border-[#1a1a2e] bg-[#12121a] flex flex-col" },
|
|
1127
|
+
React.createElement("div", { className: "p-4 border-b border-[#1a1a2e]" },
|
|
1128
|
+
React.createElement("h2", { className: "text-lg font-semibold text-white" }, "Guide")),
|
|
1129
|
+
React.createElement("nav", { className: "flex-1 overflow-y-auto p-2" }, Object.entries(sections).map(([key, section]) => (React.createElement("button", { key: key, onClick: () => setActiveSection(key), className: `w-full text-left px-3 py-2 rounded text-sm transition-colors mb-1 ${activeSection === key
|
|
1130
|
+
? 'bg-[#e94560] text-white'
|
|
1131
|
+
: 'text-gray-400 hover:text-white hover:bg-[#1a1a2e]'}` }, section.title))))),
|
|
1132
|
+
React.createElement("div", { className: "flex-1 overflow-y-auto p-6" },
|
|
1133
|
+
React.createElement("div", { className: "max-w-none" },
|
|
1134
|
+
React.createElement("h1", { className: "text-2xl font-bold text-white mb-4" }, sections[activeSection].title),
|
|
1135
|
+
React.createElement("div", { className: "text-gray-300" }, sections[activeSection].content))),
|
|
1136
|
+
React.createElement("button", { onClick: onClose, className: "absolute top-4 right-4 p-2 text-gray-400 hover:text-white" },
|
|
1137
|
+
React.createElement("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2" },
|
|
1138
|
+
React.createElement("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
1139
|
+
React.createElement("line", { x1: "6", y1: "6", x2: "18", y2: "18" }))))));
|
|
1140
|
+
}
|