@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.
Files changed (241) hide show
  1. package/README.md +233 -0
  2. package/bin/dialogue-forge.js +78 -0
  3. package/demo/app/layout.tsx +36 -0
  4. package/demo/app/page.tsx +440 -0
  5. package/demo/components/ThemeSwitcher.tsx +611 -0
  6. package/demo/next.config.mjs +7 -0
  7. package/demo/package.json +29 -0
  8. package/demo/postcss.config.mjs +7 -0
  9. package/demo/public/logo.svg +1 -0
  10. package/demo/styles/globals.css +19 -0
  11. package/demo/tailwind.config.ts +90 -0
  12. package/demo/tsconfig.json +42 -0
  13. package/dist/components/ChoiceEdgeV2.d.ts +3 -0
  14. package/dist/components/ChoiceEdgeV2.js +103 -0
  15. package/dist/components/CodeBlock.d.ts +8 -0
  16. package/dist/components/CodeBlock.js +24 -0
  17. package/dist/components/ConditionAutocomplete.d.ts +14 -0
  18. package/dist/components/ConditionAutocomplete.js +284 -0
  19. package/dist/components/ConditionalNodeV2.d.ts +16 -0
  20. package/dist/components/ConditionalNodeV2.js +147 -0
  21. package/dist/components/DialogueEditorV2.d.ts +22 -0
  22. package/dist/components/DialogueEditorV2.js +1170 -0
  23. package/dist/components/EdgeIcon.d.ts +8 -0
  24. package/dist/components/EdgeIcon.js +13 -0
  25. package/dist/components/ExampleLoader.d.ts +11 -0
  26. package/dist/components/ExampleLoader.js +52 -0
  27. package/dist/components/ExampleLoaderButton.d.ts +15 -0
  28. package/dist/components/ExampleLoaderButton.js +102 -0
  29. package/dist/components/FlagManager.d.ts +11 -0
  30. package/dist/components/FlagManager.js +282 -0
  31. package/dist/components/FlagSelector.d.ts +11 -0
  32. package/dist/components/FlagSelector.js +235 -0
  33. package/dist/components/GuidePanel.d.ts +7 -0
  34. package/dist/components/GuidePanel.js +1176 -0
  35. package/dist/components/Minimap.d.ts +16 -0
  36. package/dist/components/Minimap.js +93 -0
  37. package/dist/components/NPCEdgeV2.d.ts +3 -0
  38. package/dist/components/NPCEdgeV2.js +104 -0
  39. package/dist/components/NPCNodeV2.d.ts +26 -0
  40. package/dist/components/NPCNodeV2.js +86 -0
  41. package/dist/components/NodeEditor.d.ts +18 -0
  42. package/dist/components/NodeEditor.js +1025 -0
  43. package/dist/components/PlayView.d.ts +12 -0
  44. package/dist/components/PlayView.js +307 -0
  45. package/dist/components/PlayerNodeV2.d.ts +16 -0
  46. package/dist/components/PlayerNodeV2.js +139 -0
  47. package/dist/components/ReactFlowPOC.d.ts +61 -0
  48. package/dist/components/ReactFlowPOC.js +312 -0
  49. package/dist/components/ScenePlayer.d.ts +18 -0
  50. package/dist/components/ScenePlayer.js +196 -0
  51. package/dist/components/YarnView.d.ts +9 -0
  52. package/dist/components/YarnView.js +45 -0
  53. package/dist/components/ZoomControls.d.ts +11 -0
  54. package/dist/components/ZoomControls.js +34 -0
  55. package/dist/esm/components/ChoiceEdgeV2.d.ts +3 -0
  56. package/dist/esm/components/ChoiceEdgeV2.js +67 -0
  57. package/dist/esm/components/CodeBlock.d.ts +8 -0
  58. package/dist/esm/components/CodeBlock.js +18 -0
  59. package/dist/esm/components/ConditionAutocomplete.d.ts +14 -0
  60. package/dist/esm/components/ConditionAutocomplete.js +248 -0
  61. package/dist/esm/components/ConditionalNodeV2.d.ts +16 -0
  62. package/dist/esm/components/ConditionalNodeV2.js +111 -0
  63. package/dist/esm/components/DialogueEditorV2.d.ts +22 -0
  64. package/dist/esm/components/DialogueEditorV2.js +1134 -0
  65. package/dist/esm/components/EdgeIcon.d.ts +8 -0
  66. package/dist/esm/components/EdgeIcon.js +7 -0
  67. package/dist/esm/components/ExampleLoader.d.ts +11 -0
  68. package/dist/esm/components/ExampleLoader.js +46 -0
  69. package/dist/esm/components/ExampleLoaderButton.d.ts +15 -0
  70. package/dist/esm/components/ExampleLoaderButton.js +66 -0
  71. package/dist/esm/components/FlagManager.d.ts +11 -0
  72. package/dist/esm/components/FlagManager.js +246 -0
  73. package/dist/esm/components/FlagSelector.d.ts +11 -0
  74. package/dist/esm/components/FlagSelector.js +199 -0
  75. package/dist/esm/components/GuidePanel.d.ts +7 -0
  76. package/dist/esm/components/GuidePanel.js +1140 -0
  77. package/dist/esm/components/Minimap.d.ts +16 -0
  78. package/dist/esm/components/Minimap.js +57 -0
  79. package/dist/esm/components/NPCEdgeV2.d.ts +3 -0
  80. package/dist/esm/components/NPCEdgeV2.js +68 -0
  81. package/dist/esm/components/NPCNodeV2.d.ts +26 -0
  82. package/dist/esm/components/NPCNodeV2.js +80 -0
  83. package/dist/esm/components/NodeEditor.d.ts +18 -0
  84. package/dist/esm/components/NodeEditor.js +989 -0
  85. package/dist/esm/components/PlayView.d.ts +12 -0
  86. package/dist/esm/components/PlayView.js +271 -0
  87. package/dist/esm/components/PlayerNodeV2.d.ts +16 -0
  88. package/dist/esm/components/PlayerNodeV2.js +103 -0
  89. package/dist/esm/components/ReactFlowPOC.d.ts +61 -0
  90. package/dist/esm/components/ReactFlowPOC.js +275 -0
  91. package/dist/esm/components/ScenePlayer.d.ts +18 -0
  92. package/dist/esm/components/ScenePlayer.js +160 -0
  93. package/dist/esm/components/YarnView.d.ts +9 -0
  94. package/dist/esm/components/YarnView.js +39 -0
  95. package/dist/esm/components/ZoomControls.d.ts +11 -0
  96. package/dist/esm/components/ZoomControls.js +28 -0
  97. package/dist/esm/examples/example-loader.d.ts +29 -0
  98. package/dist/esm/examples/example-loader.js +103 -0
  99. package/dist/esm/examples/examples-registry.d.ts +38 -0
  100. package/dist/esm/examples/examples-registry.js +153 -0
  101. package/dist/esm/examples/index.d.ts +26 -0
  102. package/dist/esm/examples/index.js +50 -0
  103. package/dist/esm/examples/legacy-examples.d.ts +9 -0
  104. package/dist/esm/examples/legacy-examples.js +814 -0
  105. package/dist/esm/examples/yarn-examples.d.ts +35 -0
  106. package/dist/esm/examples/yarn-examples.js +181 -0
  107. package/dist/esm/index.d.ts +21 -0
  108. package/dist/esm/index.js +26 -0
  109. package/dist/esm/lib/flag-manager.d.ts +21 -0
  110. package/dist/esm/lib/flag-manager.js +93 -0
  111. package/dist/esm/lib/yarn-converter/__tests__/round-trip.test.d.ts +1 -0
  112. package/dist/esm/lib/yarn-converter/__tests__/round-trip.test.js +169 -0
  113. package/dist/esm/lib/yarn-converter.d.ts +17 -0
  114. package/dist/esm/lib/yarn-converter.js +521 -0
  115. package/dist/esm/lib/yarn-runner/__tests__/condition-evaluator.test.d.ts +1 -0
  116. package/dist/esm/lib/yarn-runner/__tests__/condition-evaluator.test.js +171 -0
  117. package/dist/esm/lib/yarn-runner/__tests__/node-processor.test.d.ts +1 -0
  118. package/dist/esm/lib/yarn-runner/__tests__/node-processor.test.js +237 -0
  119. package/dist/esm/lib/yarn-runner/__tests__/variable-manager.test.d.ts +1 -0
  120. package/dist/esm/lib/yarn-runner/__tests__/variable-manager.test.js +106 -0
  121. package/dist/esm/lib/yarn-runner/condition-evaluator.d.ts +12 -0
  122. package/dist/esm/lib/yarn-runner/condition-evaluator.js +56 -0
  123. package/dist/esm/lib/yarn-runner/index.d.ts +12 -0
  124. package/dist/esm/lib/yarn-runner/index.js +11 -0
  125. package/dist/esm/lib/yarn-runner/node-processor.d.ts +18 -0
  126. package/dist/esm/lib/yarn-runner/node-processor.js +129 -0
  127. package/dist/esm/lib/yarn-runner/variable-manager.d.ts +51 -0
  128. package/dist/esm/lib/yarn-runner/variable-manager.js +120 -0
  129. package/dist/esm/lib/yarn-runner/variable-operations.d.ts +16 -0
  130. package/dist/esm/lib/yarn-runner/variable-operations.js +88 -0
  131. package/dist/esm/types/conditionals.d.ts +29 -0
  132. package/dist/esm/types/conditionals.js +1 -0
  133. package/dist/esm/types/constants.d.ts +59 -0
  134. package/dist/esm/types/constants.js +55 -0
  135. package/dist/esm/types/flags.d.ts +49 -0
  136. package/dist/esm/types/flags.js +49 -0
  137. package/dist/esm/types/game-state.d.ts +62 -0
  138. package/dist/esm/types/game-state.js +6 -0
  139. package/dist/esm/types/index.d.ts +77 -0
  140. package/dist/esm/types/index.js +1 -0
  141. package/dist/esm/utils/constants.d.ts +5 -0
  142. package/dist/esm/utils/constants.js +5 -0
  143. package/dist/esm/utils/feature-flags.d.ts +11 -0
  144. package/dist/esm/utils/feature-flags.js +11 -0
  145. package/dist/esm/utils/game-state-flattener.d.ts +41 -0
  146. package/dist/esm/utils/game-state-flattener.js +135 -0
  147. package/dist/esm/utils/layout/collision.d.ts +27 -0
  148. package/dist/esm/utils/layout/collision.js +74 -0
  149. package/dist/esm/utils/layout/index.d.ts +82 -0
  150. package/dist/esm/utils/layout/index.js +98 -0
  151. package/dist/esm/utils/layout/registry.d.ts +91 -0
  152. package/dist/esm/utils/layout/registry.js +148 -0
  153. package/dist/esm/utils/layout/strategies/dagre.d.ts +19 -0
  154. package/dist/esm/utils/layout/strategies/dagre.js +182 -0
  155. package/dist/esm/utils/layout/strategies/force.d.ts +21 -0
  156. package/dist/esm/utils/layout/strategies/force.js +178 -0
  157. package/dist/esm/utils/layout/strategies/grid.d.ts +17 -0
  158. package/dist/esm/utils/layout/strategies/grid.js +91 -0
  159. package/dist/esm/utils/layout/strategies/index.d.ts +8 -0
  160. package/dist/esm/utils/layout/strategies/index.js +8 -0
  161. package/dist/esm/utils/layout/types.d.ts +100 -0
  162. package/dist/esm/utils/layout/types.js +7 -0
  163. package/dist/esm/utils/layout.d.ts +9 -0
  164. package/dist/esm/utils/layout.js +17 -0
  165. package/dist/esm/utils/node-helpers.d.ts +7 -0
  166. package/dist/esm/utils/node-helpers.js +94 -0
  167. package/dist/esm/utils/reactflow-converter.d.ts +42 -0
  168. package/dist/esm/utils/reactflow-converter.js +217 -0
  169. package/dist/examples/example-loader.d.ts +29 -0
  170. package/dist/examples/example-loader.js +109 -0
  171. package/dist/examples/examples-registry.d.ts +38 -0
  172. package/dist/examples/examples-registry.js +160 -0
  173. package/dist/examples/index.d.ts +26 -0
  174. package/dist/examples/index.js +63 -0
  175. package/dist/examples/legacy-examples.d.ts +9 -0
  176. package/dist/examples/legacy-examples.js +817 -0
  177. package/dist/examples/yarn-examples.d.ts +35 -0
  178. package/dist/examples/yarn-examples.js +189 -0
  179. package/dist/index.d.ts +21 -0
  180. package/dist/index.js +66 -0
  181. package/dist/lib/flag-manager.d.ts +21 -0
  182. package/dist/lib/flag-manager.js +99 -0
  183. package/dist/lib/yarn-converter/__tests__/round-trip.test.d.ts +1 -0
  184. package/dist/lib/yarn-converter/__tests__/round-trip.test.js +171 -0
  185. package/dist/lib/yarn-converter.d.ts +17 -0
  186. package/dist/lib/yarn-converter.js +525 -0
  187. package/dist/lib/yarn-runner/__tests__/condition-evaluator.test.d.ts +1 -0
  188. package/dist/lib/yarn-runner/__tests__/condition-evaluator.test.js +173 -0
  189. package/dist/lib/yarn-runner/__tests__/node-processor.test.d.ts +1 -0
  190. package/dist/lib/yarn-runner/__tests__/node-processor.test.js +239 -0
  191. package/dist/lib/yarn-runner/__tests__/variable-manager.test.d.ts +1 -0
  192. package/dist/lib/yarn-runner/__tests__/variable-manager.test.js +108 -0
  193. package/dist/lib/yarn-runner/condition-evaluator.d.ts +12 -0
  194. package/dist/lib/yarn-runner/condition-evaluator.js +60 -0
  195. package/dist/lib/yarn-runner/index.d.ts +12 -0
  196. package/dist/lib/yarn-runner/index.js +21 -0
  197. package/dist/lib/yarn-runner/node-processor.d.ts +18 -0
  198. package/dist/lib/yarn-runner/node-processor.js +133 -0
  199. package/dist/lib/yarn-runner/variable-manager.d.ts +51 -0
  200. package/dist/lib/yarn-runner/variable-manager.js +124 -0
  201. package/dist/lib/yarn-runner/variable-operations.d.ts +16 -0
  202. package/dist/lib/yarn-runner/variable-operations.js +92 -0
  203. package/dist/types/conditionals.d.ts +29 -0
  204. package/dist/types/conditionals.js +2 -0
  205. package/dist/types/constants.d.ts +59 -0
  206. package/dist/types/constants.js +58 -0
  207. package/dist/types/flags.d.ts +49 -0
  208. package/dist/types/flags.js +52 -0
  209. package/dist/types/game-state.d.ts +62 -0
  210. package/dist/types/game-state.js +7 -0
  211. package/dist/types/index.d.ts +77 -0
  212. package/dist/types/index.js +2 -0
  213. package/dist/utils/constants.d.ts +5 -0
  214. package/dist/utils/constants.js +8 -0
  215. package/dist/utils/feature-flags.d.ts +11 -0
  216. package/dist/utils/feature-flags.js +14 -0
  217. package/dist/utils/game-state-flattener.d.ts +41 -0
  218. package/dist/utils/game-state-flattener.js +140 -0
  219. package/dist/utils/layout/collision.d.ts +27 -0
  220. package/dist/utils/layout/collision.js +77 -0
  221. package/dist/utils/layout/index.d.ts +82 -0
  222. package/dist/utils/layout/index.js +109 -0
  223. package/dist/utils/layout/registry.d.ts +91 -0
  224. package/dist/utils/layout/registry.js +151 -0
  225. package/dist/utils/layout/strategies/dagre.d.ts +19 -0
  226. package/dist/utils/layout/strategies/dagre.js +189 -0
  227. package/dist/utils/layout/strategies/force.d.ts +21 -0
  228. package/dist/utils/layout/strategies/force.js +182 -0
  229. package/dist/utils/layout/strategies/grid.d.ts +17 -0
  230. package/dist/utils/layout/strategies/grid.js +95 -0
  231. package/dist/utils/layout/strategies/index.d.ts +8 -0
  232. package/dist/utils/layout/strategies/index.js +14 -0
  233. package/dist/utils/layout/types.d.ts +100 -0
  234. package/dist/utils/layout/types.js +8 -0
  235. package/dist/utils/layout.d.ts +9 -0
  236. package/dist/utils/layout.js +25 -0
  237. package/dist/utils/node-helpers.d.ts +7 -0
  238. package/dist/utils/node-helpers.js +101 -0
  239. package/dist/utils/reactflow-converter.d.ts +42 -0
  240. package/dist/utils/reactflow-converter.js +223 -0
  241. package/package.json +70 -0
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Game State Integration Types
3
+ *
4
+ * These types define how Dialogue Forge integrates with your game's state system.
5
+ */
6
+ import type { DialogueTree } from './index';
7
+ import type { FlagSchema } from './flags';
8
+ /**
9
+ * Flag value types - must be Yarn Spinner-compatible
10
+ */
11
+ export type FlagValue = boolean | number | string;
12
+ /**
13
+ * Current game state - flags and their values (flat, Yarn-compatible)
14
+ */
15
+ export interface FlagState {
16
+ [flagId: string]: FlagValue;
17
+ }
18
+ /**
19
+ * Legacy alias for backward compatibility
20
+ */
21
+ export type GameFlagState = FlagState;
22
+ /**
23
+ * Base game state structure that users can extend
24
+ * Must have a 'flags' property, but can have any other structure
25
+ */
26
+ export interface BaseGameState {
27
+ flags?: FlagState;
28
+ }
29
+ /**
30
+ * Convenience type for extending game state
31
+ */
32
+ export type GameState<T extends Record<string, any> = {}> = BaseGameState & T;
33
+ /**
34
+ * Updated flags after dialogue completes
35
+ */
36
+ export interface DialogueResult {
37
+ updatedFlags: FlagState;
38
+ dialogueTree: DialogueTree;
39
+ completedNodeIds: string[];
40
+ }
41
+ /**
42
+ * Props for running a dialogue (simulation/play mode)
43
+ */
44
+ export interface DialogueRunProps {
45
+ dialogue: DialogueTree;
46
+ gameState: Record<string, any>;
47
+ startNodeId?: string;
48
+ onComplete?: (result: DialogueResult) => void;
49
+ onFlagUpdate?: (flags: FlagState) => void;
50
+ }
51
+ /**
52
+ * Props for editing a dialogue
53
+ */
54
+ export interface DialogueEditProps {
55
+ dialogue: DialogueTree | null;
56
+ flagSchema?: FlagSchema;
57
+ onChange: (dialogue: DialogueTree) => void;
58
+ onExportYarn?: (yarn: string) => void;
59
+ onExportJSON?: (json: string) => void;
60
+ className?: string;
61
+ showTitleEditor?: boolean;
62
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Game State Integration Types
3
+ *
4
+ * These types define how Dialogue Forge integrates with your game's state system.
5
+ */
6
+ export {};
@@ -0,0 +1,77 @@
1
+ import { ConditionOperator, NodeType } from './constants';
2
+ export interface Choice {
3
+ id: string;
4
+ text: string;
5
+ nextNodeId?: string;
6
+ conditions?: Condition[];
7
+ setFlags?: string[];
8
+ }
9
+ export interface Condition {
10
+ flag: string;
11
+ operator: ConditionOperator;
12
+ value?: boolean | number | string;
13
+ }
14
+ /**
15
+ * Conditional content block for if/elseif/else statements
16
+ */
17
+ export interface ConditionalBlock {
18
+ id: string;
19
+ type: 'if' | 'elseif' | 'else';
20
+ condition?: Condition[];
21
+ content: string;
22
+ speaker?: string;
23
+ nextNodeId?: string;
24
+ }
25
+ export interface DialogueNode {
26
+ id: string;
27
+ type: NodeType;
28
+ speaker?: string;
29
+ content: string;
30
+ choices?: Choice[];
31
+ nextNodeId?: string;
32
+ setFlags?: string[];
33
+ conditionalBlocks?: ConditionalBlock[];
34
+ x: number;
35
+ y: number;
36
+ }
37
+ export interface DialogueTree {
38
+ id: string;
39
+ title: string;
40
+ startNodeId: string;
41
+ nodes: Record<string, DialogueNode>;
42
+ }
43
+ import { FlagSchema } from './flags';
44
+ export interface DialogueEditorProps {
45
+ dialogue: DialogueTree | null;
46
+ onChange: (dialogue: DialogueTree) => void;
47
+ onExportYarn?: (yarn: string) => void;
48
+ onExportJSON?: (json: string) => void;
49
+ flagSchema?: FlagSchema;
50
+ className?: string;
51
+ showTitleEditor?: boolean;
52
+ onNodeAdd?: (node: DialogueNode) => void;
53
+ onNodeDelete?: (nodeId: string) => void;
54
+ onNodeUpdate?: (nodeId: string, updates: Partial<DialogueNode>) => void;
55
+ onConnect?: (sourceId: string, targetId: string, sourceHandle?: string) => void;
56
+ onDisconnect?: (edgeId: string, sourceId: string, targetId: string) => void;
57
+ onNodeSelect?: (nodeId: string | null) => void;
58
+ onNodeDoubleClick?: (nodeId: string) => void;
59
+ }
60
+ export interface ContextMenu {
61
+ x: number;
62
+ y: number;
63
+ graphX: number;
64
+ graphY: number;
65
+ }
66
+ export interface EdgeDropMenu extends ContextMenu {
67
+ fromNodeId: string;
68
+ fromChoiceIdx?: number;
69
+ }
70
+ export interface DraggingEdge {
71
+ fromNodeId: string;
72
+ fromChoiceIdx?: number;
73
+ startX: number;
74
+ startY: number;
75
+ endX: number;
76
+ endY: number;
77
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ export declare const NODE_WIDTH = 200;
2
+ export declare const NODE_HEIGHT = 100;
3
+ export declare const DEFAULT_GRAPH_SCALE = 0.85;
4
+ export declare const MIN_SCALE = 0.3;
5
+ export declare const MAX_SCALE = 2;
@@ -0,0 +1,5 @@
1
+ export const NODE_WIDTH = 200;
2
+ export const NODE_HEIGHT = 100;
3
+ export const DEFAULT_GRAPH_SCALE = 0.85;
4
+ export const MIN_SCALE = 0.3;
5
+ export const MAX_SCALE = 2;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Feature Flags
3
+ *
4
+ * Control optional features that are only for development/debugging.
5
+ * When users install the package, these can be disabled.
6
+ */
7
+ /**
8
+ * Enable debug tools (ExampleLoader, etc.)
9
+ * Set to false in production builds or when distributing the package.
10
+ */
11
+ export declare const ENABLE_DEBUG_TOOLS = true;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Feature Flags
3
+ *
4
+ * Control optional features that are only for development/debugging.
5
+ * When users install the package, these can be disabled.
6
+ */
7
+ /**
8
+ * Enable debug tools (ExampleLoader, etc.)
9
+ * Set to false in production builds or when distributing the package.
10
+ */
11
+ export const ENABLE_DEBUG_TOOLS = true;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Game State Flattening Utility
3
+ *
4
+ * Flattens nested game state structures into Yarn Spinner-compatible flat variables.
5
+ * Supports: flags, player, characters (as object), and any other nested structures.
6
+ *
7
+ * Rules:
8
+ * - Only includes truthy values (skips 0, false, null, undefined, empty strings)
9
+ * - Flattens nested objects using underscore separator
10
+ * - Characters are objects, not arrays (uses object keys as part of path)
11
+ * - All values must be boolean | number | string (Yarn-compatible)
12
+ */
13
+ import type { FlagState } from '../types/game-state';
14
+ export interface FlattenConfig {
15
+ /** Skip null/undefined values (default: true) */
16
+ excludeNull?: boolean;
17
+ /** Separator for nested keys (default: '_') */
18
+ separator?: string;
19
+ /** Maximum depth to flatten (default: 5, prevents infinite recursion) */
20
+ maxDepth?: number;
21
+ }
22
+ export interface FlattenedState {
23
+ flags: FlagState;
24
+ metadata: {
25
+ sourcePaths: Record<string, string>;
26
+ };
27
+ }
28
+ /**
29
+ * Flattens a game state object into Yarn Spinner-compatible flat variables
30
+ */
31
+ export declare function flattenGameState(gameState: any, config?: FlattenConfig): FlattenedState;
32
+ /**
33
+ * Validates that gameState has a valid structure
34
+ * Throws descriptive errors if validation fails
35
+ */
36
+ export declare function validateGameState(gameState: any): asserts gameState is Record<string, any>;
37
+ /**
38
+ * Extracts flags from gameState, flattening if necessary
39
+ * This is the main entry point for ScenePlayer
40
+ */
41
+ export declare function extractFlagsFromGameState(gameState: any, config?: FlattenConfig): FlagState;
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Game State Flattening Utility
3
+ *
4
+ * Flattens nested game state structures into Yarn Spinner-compatible flat variables.
5
+ * Supports: flags, player, characters (as object), and any other nested structures.
6
+ *
7
+ * Rules:
8
+ * - Only includes truthy values (skips 0, false, null, undefined, empty strings)
9
+ * - Flattens nested objects using underscore separator
10
+ * - Characters are objects, not arrays (uses object keys as part of path)
11
+ * - All values must be boolean | number | string (Yarn-compatible)
12
+ */
13
+ /**
14
+ * Validates that a value is Yarn Spinner-compatible
15
+ */
16
+ function isYarnCompatible(value) {
17
+ return typeof value === 'boolean' ||
18
+ typeof value === 'number' ||
19
+ typeof value === 'string';
20
+ }
21
+ /**
22
+ * Checks if a value is "truthy" for our purposes
23
+ * - Numbers: only include if !== 0
24
+ * - Booleans: only include if true
25
+ * - Strings: only include if non-empty
26
+ * - null/undefined: excluded if excludeNull is true
27
+ */
28
+ function isTruthyValue(value, excludeNull) {
29
+ if (value == null) {
30
+ return !excludeNull; // Include null only if excludeNull is false
31
+ }
32
+ if (typeof value === 'number') {
33
+ return value !== 0; // Exclude zero
34
+ }
35
+ if (typeof value === 'boolean') {
36
+ return value === true; // Only include true
37
+ }
38
+ if (typeof value === 'string') {
39
+ return value.length > 0; // Exclude empty strings
40
+ }
41
+ return true; // Objects/arrays will be flattened further
42
+ }
43
+ /**
44
+ * Flattens a game state object into Yarn Spinner-compatible flat variables
45
+ */
46
+ export function flattenGameState(gameState, config = {}) {
47
+ if (!gameState || typeof gameState !== 'object') {
48
+ throw new Error('GameState must be an object');
49
+ }
50
+ const { excludeNull = true, separator = '_', maxDepth = 5, } = config;
51
+ const flags = {};
52
+ const sourcePaths = {};
53
+ /**
54
+ * Recursively flatten a value
55
+ */
56
+ function flattenValue(value, path, depth = 0) {
57
+ // Prevent infinite recursion
58
+ if (depth > maxDepth) {
59
+ console.warn(`Max depth (${maxDepth}) reached at path: ${path}`);
60
+ return;
61
+ }
62
+ // Handle primitives (Yarn-compatible types)
63
+ if (isYarnCompatible(value)) {
64
+ // Only include truthy values
65
+ if (isTruthyValue(value, excludeNull)) {
66
+ const key = path.replace(/\./g, separator);
67
+ flags[key] = value;
68
+ sourcePaths[key] = path;
69
+ }
70
+ return;
71
+ }
72
+ // Handle null/undefined
73
+ if (value == null) {
74
+ if (!excludeNull) {
75
+ // Could store as string representation, but Yarn doesn't support null
76
+ // Skip for now - Yarn only supports boolean | number | string
77
+ }
78
+ return;
79
+ }
80
+ // Handle arrays - flatten all items
81
+ if (Array.isArray(value)) {
82
+ value.forEach((item, index) => {
83
+ const arrayPath = `${path}[${index}]`;
84
+ flattenValue(item, arrayPath, depth + 1);
85
+ });
86
+ return;
87
+ }
88
+ // Handle objects - flatten all properties
89
+ if (typeof value === 'object') {
90
+ Object.entries(value).forEach(([key, val]) => {
91
+ // Skip if value is not truthy (for nested objects, we still traverse)
92
+ // but we'll check truthiness when we hit primitives
93
+ const newPath = path ? `${path}.${key}` : key;
94
+ flattenValue(val, newPath, depth + 1);
95
+ });
96
+ return;
97
+ }
98
+ // Unknown type - skip
99
+ console.warn(`Skipping unsupported type at path: ${path} (type: ${typeof value})`);
100
+ }
101
+ // Flatten all top-level keys
102
+ Object.entries(gameState).forEach(([key, value]) => {
103
+ flattenValue(value, key, 0);
104
+ });
105
+ return {
106
+ flags,
107
+ metadata: {
108
+ sourcePaths,
109
+ },
110
+ };
111
+ }
112
+ /**
113
+ * Validates that gameState has a valid structure
114
+ * Throws descriptive errors if validation fails
115
+ */
116
+ export function validateGameState(gameState) {
117
+ if (gameState == null) {
118
+ throw new Error('GameState cannot be null or undefined');
119
+ }
120
+ if (typeof gameState !== 'object') {
121
+ throw new Error(`GameState must be an object, got: ${typeof gameState}`);
122
+ }
123
+ if (Array.isArray(gameState)) {
124
+ throw new Error('GameState cannot be an array. Use an object with keys like { flags: {...}, player: {...} }');
125
+ }
126
+ }
127
+ /**
128
+ * Extracts flags from gameState, flattening if necessary
129
+ * This is the main entry point for ScenePlayer
130
+ */
131
+ export function extractFlagsFromGameState(gameState, config) {
132
+ validateGameState(gameState);
133
+ const flattened = flattenGameState(gameState, config);
134
+ return flattened.flags;
135
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Node Collision Resolution
3
+ *
4
+ * Utility for resolving overlapping nodes in freeform layout.
5
+ * Iteratively pushes overlapping nodes apart until stable.
6
+ *
7
+ * @see https://reactflow.dev/examples/layout/node-collisions
8
+ */
9
+ import { DialogueTree } from '../../types';
10
+ interface CollisionOptions {
11
+ /** Maximum iterations before giving up */
12
+ maxIterations?: number;
13
+ /** Overlap ratio threshold to trigger resolution (0-1) */
14
+ overlapThreshold?: number;
15
+ /** Extra margin to add when pushing nodes apart */
16
+ margin?: number;
17
+ }
18
+ /**
19
+ * Resolve node collisions for freeform layout.
20
+ * Iteratively pushes overlapping nodes apart until no collisions remain.
21
+ *
22
+ * @param dialogue - The dialogue tree with potentially overlapping nodes
23
+ * @param options - Configuration options
24
+ * @returns Updated dialogue tree with resolved positions
25
+ */
26
+ export declare function resolveNodeCollisions(dialogue: DialogueTree, options?: CollisionOptions): DialogueTree;
27
+ export {};
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Node Collision Resolution
3
+ *
4
+ * Utility for resolving overlapping nodes in freeform layout.
5
+ * Iteratively pushes overlapping nodes apart until stable.
6
+ *
7
+ * @see https://reactflow.dev/examples/layout/node-collisions
8
+ */
9
+ // ============================================================================
10
+ // Constants
11
+ // ============================================================================
12
+ const NODE_WIDTH = 220;
13
+ const NODE_HEIGHT = 120;
14
+ // ============================================================================
15
+ // Implementation
16
+ // ============================================================================
17
+ /**
18
+ * Resolve node collisions for freeform layout.
19
+ * Iteratively pushes overlapping nodes apart until no collisions remain.
20
+ *
21
+ * @param dialogue - The dialogue tree with potentially overlapping nodes
22
+ * @param options - Configuration options
23
+ * @returns Updated dialogue tree with resolved positions
24
+ */
25
+ export function resolveNodeCollisions(dialogue, options = {}) {
26
+ const { maxIterations = 50, overlapThreshold = 0.3, margin = 20 } = options;
27
+ // Create mutable position array
28
+ const nodePositions = Object.values(dialogue.nodes).map(node => ({
29
+ id: node.id,
30
+ x: node.x,
31
+ y: node.y,
32
+ width: NODE_WIDTH,
33
+ height: NODE_HEIGHT,
34
+ }));
35
+ // Iteratively resolve collisions
36
+ for (let iter = 0; iter < maxIterations; iter++) {
37
+ let hasCollision = false;
38
+ for (let i = 0; i < nodePositions.length; i++) {
39
+ for (let j = i + 1; j < nodePositions.length; j++) {
40
+ const a = nodePositions[i];
41
+ const b = nodePositions[j];
42
+ // Calculate overlap
43
+ const overlapX = Math.max(0, Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x));
44
+ const overlapY = Math.max(0, Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y));
45
+ if (overlapX > 0 && overlapY > 0) {
46
+ const overlapRatio = (overlapX * overlapY) / Math.min(a.width * a.height, b.width * b.height);
47
+ if (overlapRatio > overlapThreshold) {
48
+ hasCollision = true;
49
+ // Calculate push direction (center to center)
50
+ const dx = (b.x + b.width / 2) - (a.x + a.width / 2);
51
+ const dy = (b.y + b.height / 2) - (a.y + a.height / 2);
52
+ const dist = Math.sqrt(dx * dx + dy * dy) || 1;
53
+ // Push apart
54
+ const pushX = (dx / dist) * (overlapX / 2 + margin);
55
+ const pushY = (dy / dist) * (overlapY / 2 + margin);
56
+ a.x -= pushX / 2;
57
+ a.y -= pushY / 2;
58
+ b.x += pushX / 2;
59
+ b.y += pushY / 2;
60
+ }
61
+ }
62
+ }
63
+ }
64
+ // Exit early if no collisions found
65
+ if (!hasCollision)
66
+ break;
67
+ }
68
+ // Build updated dialogue with new positions
69
+ const updatedNodes = {};
70
+ for (const pos of nodePositions) {
71
+ updatedNodes[pos.id] = { ...dialogue.nodes[pos.id], x: pos.x, y: pos.y };
72
+ }
73
+ return { ...dialogue, nodes: updatedNodes };
74
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Layout Module
3
+ *
4
+ * Provides automatic graph layout algorithms for dialogue trees.
5
+ * Uses the Strategy pattern to allow swappable layout algorithms.
6
+ *
7
+ * ## Quick Start
8
+ *
9
+ * ```typescript
10
+ * import { layoutRegistry, applyLayout } from './layout';
11
+ *
12
+ * // Apply default layout (dagre)
13
+ * const result = applyLayout(dialogue);
14
+ *
15
+ * // Apply specific layout
16
+ * const result = applyLayout(dialogue, 'force');
17
+ *
18
+ * // With options
19
+ * const result = applyLayout(dialogue, 'dagre', { direction: 'LR' });
20
+ *
21
+ * // List available layouts
22
+ * console.log(layoutRegistry.list());
23
+ * ```
24
+ *
25
+ * ## Adding Custom Layouts
26
+ *
27
+ * See LAYOUT_STRATEGIES.md for documentation on creating custom layouts.
28
+ *
29
+ * @module layout
30
+ */
31
+ import { DialogueTree } from '../../types';
32
+ import { LayoutOptions, LayoutResult, LayoutDirection } from './types';
33
+ /**
34
+ * Apply a layout algorithm to a dialogue tree
35
+ *
36
+ * @param dialogue - The dialogue tree to layout
37
+ * @param strategyId - Optional strategy ID (defaults to 'dagre')
38
+ * @param options - Optional layout configuration
39
+ * @returns Layout result with updated dialogue and metadata
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * // Default dagre layout
44
+ * const result = applyLayout(dialogue);
45
+ *
46
+ * // Horizontal dagre layout
47
+ * const result = applyLayout(dialogue, 'dagre', { direction: 'LR' });
48
+ *
49
+ * // Force-directed layout
50
+ * const result = applyLayout(dialogue, 'force');
51
+ *
52
+ * // Grid layout
53
+ * const result = applyLayout(dialogue, 'grid');
54
+ * ```
55
+ */
56
+ export declare function applyLayout(dialogue: DialogueTree, strategyId?: string, options?: LayoutOptions): LayoutResult;
57
+ /**
58
+ * Get the list of available layout strategies
59
+ */
60
+ export declare function listLayouts(): Array<{
61
+ id: string;
62
+ name: string;
63
+ description: string;
64
+ isDefault: boolean;
65
+ }>;
66
+ /**
67
+ * Apply dagre layout (backward compatible function)
68
+ * @deprecated Use applyLayout(dialogue, 'dagre', options) instead
69
+ */
70
+ export declare function applyDagreLayout(dialogue: DialogueTree, direction?: LayoutDirection): DialogueTree;
71
+ /**
72
+ * Apply hierarchical layout (backward compatible alias)
73
+ * @deprecated Use applyLayout(dialogue, 'dagre', options) instead
74
+ */
75
+ export declare const applyHierarchicalLayout: typeof applyDagreLayout;
76
+ /**
77
+ * Resolve node collisions (kept for backward compatibility)
78
+ */
79
+ export { resolveNodeCollisions } from './collision';
80
+ export type { LayoutStrategy, LayoutOptions, LayoutResult, LayoutDirection } from './types';
81
+ export { layoutRegistry } from './registry';
82
+ export { DagreLayoutStrategy, ForceLayoutStrategy, GridLayoutStrategy } from './strategies';
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Layout Module
3
+ *
4
+ * Provides automatic graph layout algorithms for dialogue trees.
5
+ * Uses the Strategy pattern to allow swappable layout algorithms.
6
+ *
7
+ * ## Quick Start
8
+ *
9
+ * ```typescript
10
+ * import { layoutRegistry, applyLayout } from './layout';
11
+ *
12
+ * // Apply default layout (dagre)
13
+ * const result = applyLayout(dialogue);
14
+ *
15
+ * // Apply specific layout
16
+ * const result = applyLayout(dialogue, 'force');
17
+ *
18
+ * // With options
19
+ * const result = applyLayout(dialogue, 'dagre', { direction: 'LR' });
20
+ *
21
+ * // List available layouts
22
+ * console.log(layoutRegistry.list());
23
+ * ```
24
+ *
25
+ * ## Adding Custom Layouts
26
+ *
27
+ * See LAYOUT_STRATEGIES.md for documentation on creating custom layouts.
28
+ *
29
+ * @module layout
30
+ */
31
+ import { layoutRegistry } from './registry';
32
+ import { DagreLayoutStrategy, ForceLayoutStrategy, GridLayoutStrategy } from './strategies';
33
+ // ============================================================================
34
+ // Register Built-in Strategies
35
+ // ============================================================================
36
+ // Register all built-in strategies
37
+ layoutRegistry.register(new DagreLayoutStrategy(), true); // Default
38
+ layoutRegistry.register(new ForceLayoutStrategy());
39
+ layoutRegistry.register(new GridLayoutStrategy());
40
+ // ============================================================================
41
+ // Convenience Functions
42
+ // ============================================================================
43
+ /**
44
+ * Apply a layout algorithm to a dialogue tree
45
+ *
46
+ * @param dialogue - The dialogue tree to layout
47
+ * @param strategyId - Optional strategy ID (defaults to 'dagre')
48
+ * @param options - Optional layout configuration
49
+ * @returns Layout result with updated dialogue and metadata
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * // Default dagre layout
54
+ * const result = applyLayout(dialogue);
55
+ *
56
+ * // Horizontal dagre layout
57
+ * const result = applyLayout(dialogue, 'dagre', { direction: 'LR' });
58
+ *
59
+ * // Force-directed layout
60
+ * const result = applyLayout(dialogue, 'force');
61
+ *
62
+ * // Grid layout
63
+ * const result = applyLayout(dialogue, 'grid');
64
+ * ```
65
+ */
66
+ export function applyLayout(dialogue, strategyId, options) {
67
+ return layoutRegistry.apply(strategyId, dialogue, options);
68
+ }
69
+ /**
70
+ * Get the list of available layout strategies
71
+ */
72
+ export function listLayouts() {
73
+ return layoutRegistry.list();
74
+ }
75
+ // ============================================================================
76
+ // Backward Compatibility
77
+ // ============================================================================
78
+ /**
79
+ * Apply dagre layout (backward compatible function)
80
+ * @deprecated Use applyLayout(dialogue, 'dagre', options) instead
81
+ */
82
+ export function applyDagreLayout(dialogue, direction = 'TB') {
83
+ const result = applyLayout(dialogue, 'dagre', { direction });
84
+ return result.dialogue;
85
+ }
86
+ /**
87
+ * Apply hierarchical layout (backward compatible alias)
88
+ * @deprecated Use applyLayout(dialogue, 'dagre', options) instead
89
+ */
90
+ export const applyHierarchicalLayout = applyDagreLayout;
91
+ /**
92
+ * Resolve node collisions (kept for backward compatibility)
93
+ */
94
+ export { resolveNodeCollisions } from './collision';
95
+ // Registry
96
+ export { layoutRegistry } from './registry';
97
+ // Strategies (for direct use or extension)
98
+ export { DagreLayoutStrategy, ForceLayoutStrategy, GridLayoutStrategy } from './strategies';