@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,91 @@
1
+ /**
2
+ * Layout Strategy Registry
3
+ *
4
+ * Central registry for all available layout algorithms.
5
+ * Implements the Registry pattern to manage strategy instances.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * // Register a custom strategy
10
+ * layoutRegistry.register(new MyCustomLayout());
11
+ *
12
+ * // Use a strategy
13
+ * const result = layoutRegistry.apply('dagre', dialogue, { direction: 'LR' });
14
+ *
15
+ * // List available strategies
16
+ * const strategies = layoutRegistry.list();
17
+ * ```
18
+ */
19
+ import { DialogueTree } from '../../types';
20
+ import { LayoutStrategy, LayoutOptions, LayoutResult } from './types';
21
+ declare class LayoutStrategyRegistry {
22
+ private strategies;
23
+ private defaultStrategyId;
24
+ /**
25
+ * Register a layout strategy
26
+ * @param strategy - The strategy to register
27
+ * @param isDefault - Whether this should be the default strategy
28
+ */
29
+ register(strategy: LayoutStrategy, isDefault?: boolean): void;
30
+ /**
31
+ * Unregister a layout strategy
32
+ * @param id - The strategy ID to remove
33
+ */
34
+ unregister(id: string): boolean;
35
+ /**
36
+ * Get a strategy by ID
37
+ * @param id - The strategy ID
38
+ */
39
+ get(id: string): LayoutStrategy | undefined;
40
+ /**
41
+ * Get the default strategy
42
+ */
43
+ getDefault(): LayoutStrategy | undefined;
44
+ /**
45
+ * Set the default strategy
46
+ * @param id - The strategy ID to set as default
47
+ */
48
+ setDefault(id: string): boolean;
49
+ /**
50
+ * List all registered strategies
51
+ */
52
+ list(): Array<{
53
+ id: string;
54
+ name: string;
55
+ description: string;
56
+ isDefault: boolean;
57
+ }>;
58
+ /**
59
+ * Apply a layout strategy to a dialogue tree
60
+ * @param id - The strategy ID (or undefined to use default)
61
+ * @param dialogue - The dialogue tree to layout
62
+ * @param options - Layout options
63
+ */
64
+ apply(id: string | undefined, dialogue: DialogueTree, options?: LayoutOptions): LayoutResult;
65
+ /**
66
+ * Check if a strategy is registered
67
+ * @param id - The strategy ID
68
+ */
69
+ has(id: string): boolean;
70
+ /**
71
+ * Get the number of registered strategies
72
+ */
73
+ get size(): number;
74
+ /**
75
+ * Clear all registered strategies
76
+ */
77
+ clear(): void;
78
+ }
79
+ /**
80
+ * Global layout strategy registry
81
+ *
82
+ * Use this to register custom strategies or apply layouts:
83
+ * ```typescript
84
+ * import { layoutRegistry } from './layout';
85
+ *
86
+ * // Apply layout
87
+ * const result = layoutRegistry.apply('dagre', dialogue);
88
+ * ```
89
+ */
90
+ export declare const layoutRegistry: LayoutStrategyRegistry;
91
+ export {};
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+ /**
3
+ * Layout Strategy Registry
4
+ *
5
+ * Central registry for all available layout algorithms.
6
+ * Implements the Registry pattern to manage strategy instances.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * // Register a custom strategy
11
+ * layoutRegistry.register(new MyCustomLayout());
12
+ *
13
+ * // Use a strategy
14
+ * const result = layoutRegistry.apply('dagre', dialogue, { direction: 'LR' });
15
+ *
16
+ * // List available strategies
17
+ * const strategies = layoutRegistry.list();
18
+ * ```
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.layoutRegistry = void 0;
22
+ // ============================================================================
23
+ // Registry Implementation
24
+ // ============================================================================
25
+ class LayoutStrategyRegistry {
26
+ constructor() {
27
+ this.strategies = new Map();
28
+ this.defaultStrategyId = null;
29
+ }
30
+ /**
31
+ * Register a layout strategy
32
+ * @param strategy - The strategy to register
33
+ * @param isDefault - Whether this should be the default strategy
34
+ */
35
+ register(strategy, isDefault = false) {
36
+ if (this.strategies.has(strategy.id)) {
37
+ console.warn(`Layout strategy "${strategy.id}" is already registered. Overwriting.`);
38
+ }
39
+ this.strategies.set(strategy.id, { strategy, isDefault });
40
+ if (isDefault || this.defaultStrategyId === null) {
41
+ this.defaultStrategyId = strategy.id;
42
+ }
43
+ }
44
+ /**
45
+ * Unregister a layout strategy
46
+ * @param id - The strategy ID to remove
47
+ */
48
+ unregister(id) {
49
+ const removed = this.strategies.delete(id);
50
+ if (removed && this.defaultStrategyId === id) {
51
+ // Set a new default if we removed the default
52
+ const first = this.strategies.keys().next().value;
53
+ this.defaultStrategyId = first || null;
54
+ }
55
+ return removed;
56
+ }
57
+ /**
58
+ * Get a strategy by ID
59
+ * @param id - The strategy ID
60
+ */
61
+ get(id) {
62
+ return this.strategies.get(id)?.strategy;
63
+ }
64
+ /**
65
+ * Get the default strategy
66
+ */
67
+ getDefault() {
68
+ if (!this.defaultStrategyId)
69
+ return undefined;
70
+ return this.get(this.defaultStrategyId);
71
+ }
72
+ /**
73
+ * Set the default strategy
74
+ * @param id - The strategy ID to set as default
75
+ */
76
+ setDefault(id) {
77
+ if (!this.strategies.has(id)) {
78
+ console.warn(`Cannot set default: strategy "${id}" not found`);
79
+ return false;
80
+ }
81
+ this.defaultStrategyId = id;
82
+ return true;
83
+ }
84
+ /**
85
+ * List all registered strategies
86
+ */
87
+ list() {
88
+ return Array.from(this.strategies.entries()).map(([id, entry]) => ({
89
+ id,
90
+ name: entry.strategy.name,
91
+ description: entry.strategy.description,
92
+ isDefault: id === this.defaultStrategyId,
93
+ }));
94
+ }
95
+ /**
96
+ * Apply a layout strategy to a dialogue tree
97
+ * @param id - The strategy ID (or undefined to use default)
98
+ * @param dialogue - The dialogue tree to layout
99
+ * @param options - Layout options
100
+ */
101
+ apply(id, dialogue, options) {
102
+ const strategyId = id || this.defaultStrategyId;
103
+ if (!strategyId) {
104
+ throw new Error('No layout strategy available. Register a strategy first.');
105
+ }
106
+ const strategy = this.get(strategyId);
107
+ if (!strategy) {
108
+ throw new Error(`Layout strategy "${strategyId}" not found`);
109
+ }
110
+ // Check if strategy supports this dialogue
111
+ if (strategy.supports && !strategy.supports(dialogue)) {
112
+ console.warn(`Strategy "${strategyId}" may not support this dialogue structure`);
113
+ }
114
+ return strategy.apply(dialogue, options);
115
+ }
116
+ /**
117
+ * Check if a strategy is registered
118
+ * @param id - The strategy ID
119
+ */
120
+ has(id) {
121
+ return this.strategies.has(id);
122
+ }
123
+ /**
124
+ * Get the number of registered strategies
125
+ */
126
+ get size() {
127
+ return this.strategies.size;
128
+ }
129
+ /**
130
+ * Clear all registered strategies
131
+ */
132
+ clear() {
133
+ this.strategies.clear();
134
+ this.defaultStrategyId = null;
135
+ }
136
+ }
137
+ // ============================================================================
138
+ // Singleton Instance
139
+ // ============================================================================
140
+ /**
141
+ * Global layout strategy registry
142
+ *
143
+ * Use this to register custom strategies or apply layouts:
144
+ * ```typescript
145
+ * import { layoutRegistry } from './layout';
146
+ *
147
+ * // Apply layout
148
+ * const result = layoutRegistry.apply('dagre', dialogue);
149
+ * ```
150
+ */
151
+ exports.layoutRegistry = new LayoutStrategyRegistry();
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Dagre Layout Strategy
3
+ *
4
+ * Hierarchical layout using the dagre library.
5
+ * Best for dialogue trees with clear start-to-end flow.
6
+ *
7
+ * @see https://github.com/dagrejs/dagre
8
+ * @see https://reactflow.dev/examples/layout/dagre
9
+ */
10
+ import { DialogueTree } from '../../../types';
11
+ import { LayoutStrategy, LayoutOptions, LayoutResult } from '../types';
12
+ export declare class DagreLayoutStrategy implements LayoutStrategy {
13
+ readonly id = "dagre";
14
+ readonly name = "Dagre (Hierarchical)";
15
+ readonly description = "Hierarchical layout that flows from start to end. Best for linear dialogue with branches.";
16
+ readonly defaultOptions: Partial<LayoutOptions>;
17
+ apply(dialogue: DialogueTree, options?: LayoutOptions): LayoutResult;
18
+ supports(dialogue: DialogueTree): boolean;
19
+ }
@@ -0,0 +1,189 @@
1
+ "use strict";
2
+ /**
3
+ * Dagre Layout Strategy
4
+ *
5
+ * Hierarchical layout using the dagre library.
6
+ * Best for dialogue trees with clear start-to-end flow.
7
+ *
8
+ * @see https://github.com/dagrejs/dagre
9
+ * @see https://reactflow.dev/examples/layout/dagre
10
+ */
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.DagreLayoutStrategy = void 0;
16
+ const dagre_1 = __importDefault(require("@dagrejs/dagre"));
17
+ // ============================================================================
18
+ // Constants
19
+ // ============================================================================
20
+ const NODE_WIDTH = 220;
21
+ const NODE_HEIGHT = 120;
22
+ const EXTRA_HEIGHT_PER_ITEM = 30;
23
+ // ============================================================================
24
+ // Helper Functions
25
+ // ============================================================================
26
+ /**
27
+ * Calculate depth of each node from start using BFS
28
+ */
29
+ function calculateNodeDepths(dialogue) {
30
+ const depths = new Map();
31
+ if (!dialogue.startNodeId)
32
+ return depths;
33
+ const queue = [
34
+ { id: dialogue.startNodeId, depth: 0 }
35
+ ];
36
+ const visited = new Set();
37
+ while (queue.length > 0) {
38
+ const { id, depth } = queue.shift();
39
+ if (visited.has(id))
40
+ continue;
41
+ visited.add(id);
42
+ depths.set(id, depth);
43
+ const node = dialogue.nodes[id];
44
+ if (!node)
45
+ continue;
46
+ // Queue connected nodes
47
+ const nextIds = getOutgoingNodeIds(node);
48
+ for (const nextId of nextIds) {
49
+ if (dialogue.nodes[nextId] && !visited.has(nextId)) {
50
+ queue.push({ id: nextId, depth: depth + 1 });
51
+ }
52
+ }
53
+ }
54
+ return depths;
55
+ }
56
+ /**
57
+ * Get all outgoing node IDs from a node
58
+ */
59
+ function getOutgoingNodeIds(node) {
60
+ const ids = [];
61
+ if (node.nextNodeId)
62
+ ids.push(node.nextNodeId);
63
+ node.choices?.forEach(c => c.nextNodeId && ids.push(c.nextNodeId));
64
+ node.conditionalBlocks?.forEach(b => b.nextNodeId && ids.push(b.nextNodeId));
65
+ return ids;
66
+ }
67
+ /**
68
+ * Estimate node height based on content
69
+ */
70
+ function estimateNodeHeight(node) {
71
+ const itemCount = Math.max(node.choices?.length || 0, node.conditionalBlocks?.length || 0);
72
+ return NODE_HEIGHT + itemCount * EXTRA_HEIGHT_PER_ITEM;
73
+ }
74
+ // ============================================================================
75
+ // Strategy Implementation
76
+ // ============================================================================
77
+ class DagreLayoutStrategy {
78
+ constructor() {
79
+ this.id = 'dagre';
80
+ this.name = 'Dagre (Hierarchical)';
81
+ this.description = 'Hierarchical layout that flows from start to end. Best for linear dialogue with branches.';
82
+ this.defaultOptions = {
83
+ direction: 'TB',
84
+ nodeSpacingX: 80,
85
+ nodeSpacingY: 120,
86
+ margin: 50,
87
+ };
88
+ }
89
+ apply(dialogue, options) {
90
+ const startTime = performance.now();
91
+ const opts = { ...this.defaultOptions, ...options };
92
+ const direction = opts.direction || 'TB';
93
+ const isHorizontal = direction === 'LR';
94
+ // Create dagre graph
95
+ const g = new dagre_1.default.graphlib.Graph();
96
+ g.setDefaultEdgeLabel(() => ({}));
97
+ // Configure layout
98
+ g.setGraph({
99
+ rankdir: direction,
100
+ nodesep: isHorizontal ? opts.nodeSpacingX : opts.nodeSpacingY,
101
+ ranksep: isHorizontal ? opts.nodeSpacingY : opts.nodeSpacingX,
102
+ marginx: opts.margin,
103
+ marginy: opts.margin,
104
+ ranker: 'network-simplex',
105
+ align: 'UL',
106
+ acyclicer: 'greedy',
107
+ edgesep: 15,
108
+ });
109
+ // Add nodes ordered by depth
110
+ const nodeDepths = calculateNodeDepths(dialogue);
111
+ const sortedNodeIds = Object.keys(dialogue.nodes).sort((a, b) => {
112
+ return (nodeDepths.get(a) ?? Infinity) - (nodeDepths.get(b) ?? Infinity);
113
+ });
114
+ for (const nodeId of sortedNodeIds) {
115
+ const node = dialogue.nodes[nodeId];
116
+ g.setNode(nodeId, {
117
+ width: NODE_WIDTH,
118
+ height: estimateNodeHeight(node),
119
+ });
120
+ }
121
+ // Add edges with weights
122
+ for (const node of Object.values(dialogue.nodes)) {
123
+ if (node.nextNodeId && dialogue.nodes[node.nextNodeId]) {
124
+ g.setEdge(node.id, node.nextNodeId, { weight: 3, minlen: 1 });
125
+ }
126
+ for (const choice of node.choices || []) {
127
+ if (choice.nextNodeId && dialogue.nodes[choice.nextNodeId]) {
128
+ g.setEdge(node.id, choice.nextNodeId, { weight: 2, minlen: 1 });
129
+ }
130
+ }
131
+ for (const block of node.conditionalBlocks || []) {
132
+ if (block.nextNodeId && dialogue.nodes[block.nextNodeId]) {
133
+ g.setEdge(node.id, block.nextNodeId, { weight: 2, minlen: 1 });
134
+ }
135
+ }
136
+ }
137
+ // Run layout
138
+ dagre_1.default.layout(g);
139
+ // Extract positions and calculate bounds
140
+ let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
141
+ const updatedNodes = {};
142
+ for (const nodeId of Object.keys(dialogue.nodes)) {
143
+ const node = dialogue.nodes[nodeId];
144
+ const dagreNode = g.node(nodeId);
145
+ if (dagreNode) {
146
+ const x = dagreNode.x - NODE_WIDTH / 2;
147
+ const y = dagreNode.y - NODE_HEIGHT / 2;
148
+ updatedNodes[nodeId] = { ...node, x, y };
149
+ minX = Math.min(minX, x);
150
+ maxX = Math.max(maxX, x + NODE_WIDTH);
151
+ minY = Math.min(minY, y);
152
+ maxY = Math.max(maxY, y + NODE_HEIGHT);
153
+ }
154
+ else {
155
+ // Orphan node
156
+ updatedNodes[nodeId] = { ...node };
157
+ }
158
+ }
159
+ // Ensure start node is at edge
160
+ const startNodeId = dialogue.startNodeId;
161
+ if (startNodeId && updatedNodes[startNodeId]) {
162
+ const threshold = 50;
163
+ if (isHorizontal && updatedNodes[startNodeId].x - minX > threshold) {
164
+ updatedNodes[startNodeId].x = minX;
165
+ }
166
+ else if (!isHorizontal && updatedNodes[startNodeId].y - minY > threshold) {
167
+ updatedNodes[startNodeId].y = minY;
168
+ }
169
+ }
170
+ const computeTimeMs = performance.now() - startTime;
171
+ return {
172
+ dialogue: { ...dialogue, nodes: updatedNodes },
173
+ metadata: {
174
+ computeTimeMs,
175
+ nodeCount: Object.keys(dialogue.nodes).length,
176
+ bounds: {
177
+ minX, minY, maxX, maxY,
178
+ width: maxX - minX,
179
+ height: maxY - minY,
180
+ },
181
+ },
182
+ };
183
+ }
184
+ supports(dialogue) {
185
+ // Dagre works best with connected graphs that have a clear start
186
+ return !!dialogue.startNodeId && Object.keys(dialogue.nodes).length > 0;
187
+ }
188
+ }
189
+ exports.DagreLayoutStrategy = DagreLayoutStrategy;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Force-Directed Layout Strategy
3
+ *
4
+ * Physics-based layout that spreads nodes evenly.
5
+ * Good for exploring graph structure without hierarchy.
6
+ *
7
+ * Uses a simple force simulation:
8
+ * - Nodes repel each other (like charged particles)
9
+ * - Connected nodes attract (like springs)
10
+ */
11
+ import { DialogueTree } from '../../../types';
12
+ import { LayoutStrategy, LayoutOptions, LayoutResult } from '../types';
13
+ export declare class ForceLayoutStrategy implements LayoutStrategy {
14
+ readonly id = "force";
15
+ readonly name = "Force-Directed";
16
+ readonly description = "Physics-based layout that spreads nodes evenly. Good for exploring complex graphs.";
17
+ readonly defaultOptions: Partial<LayoutOptions>;
18
+ apply(dialogue: DialogueTree, options?: LayoutOptions): LayoutResult;
19
+ private emptyResult;
20
+ supports(): boolean;
21
+ }
@@ -0,0 +1,182 @@
1
+ "use strict";
2
+ /**
3
+ * Force-Directed Layout Strategy
4
+ *
5
+ * Physics-based layout that spreads nodes evenly.
6
+ * Good for exploring graph structure without hierarchy.
7
+ *
8
+ * Uses a simple force simulation:
9
+ * - Nodes repel each other (like charged particles)
10
+ * - Connected nodes attract (like springs)
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.ForceLayoutStrategy = void 0;
14
+ // ============================================================================
15
+ // Constants
16
+ // ============================================================================
17
+ const NODE_WIDTH = 220;
18
+ const NODE_HEIGHT = 120;
19
+ // Force simulation parameters
20
+ const REPULSION_STRENGTH = 5000; // How strongly nodes push each other
21
+ const ATTRACTION_STRENGTH = 0.1; // How strongly edges pull nodes together
22
+ const DAMPING = 0.9; // Velocity reduction per iteration
23
+ const MAX_ITERATIONS = 100; // Maximum simulation steps
24
+ const MIN_MOVEMENT = 0.5; // Stop when movement is below this
25
+ // ============================================================================
26
+ // Helper Functions
27
+ // ============================================================================
28
+ function getConnectedNodeIds(node) {
29
+ const ids = [];
30
+ if (node.nextNodeId)
31
+ ids.push(node.nextNodeId);
32
+ node.choices?.forEach(c => c.nextNodeId && ids.push(c.nextNodeId));
33
+ node.conditionalBlocks?.forEach(b => b.nextNodeId && ids.push(b.nextNodeId));
34
+ return ids;
35
+ }
36
+ // ============================================================================
37
+ // Strategy Implementation
38
+ // ============================================================================
39
+ class ForceLayoutStrategy {
40
+ constructor() {
41
+ this.id = 'force';
42
+ this.name = 'Force-Directed';
43
+ this.description = 'Physics-based layout that spreads nodes evenly. Good for exploring complex graphs.';
44
+ this.defaultOptions = {
45
+ nodeSpacingX: 300,
46
+ nodeSpacingY: 200,
47
+ margin: 50,
48
+ };
49
+ }
50
+ apply(dialogue, options) {
51
+ const startTime = performance.now();
52
+ const opts = { ...this.defaultOptions, ...options };
53
+ const nodeIds = Object.keys(dialogue.nodes);
54
+ if (nodeIds.length === 0) {
55
+ return this.emptyResult(dialogue, startTime);
56
+ }
57
+ // Initialize node positions in a circle
58
+ const states = new Map();
59
+ const centerX = 500;
60
+ const centerY = 500;
61
+ const radius = Math.max(200, nodeIds.length * 30);
62
+ nodeIds.forEach((id, i) => {
63
+ const angle = (2 * Math.PI * i) / nodeIds.length;
64
+ states.set(id, {
65
+ id,
66
+ x: centerX + radius * Math.cos(angle),
67
+ y: centerY + radius * Math.sin(angle),
68
+ vx: 0,
69
+ vy: 0,
70
+ });
71
+ });
72
+ // Build edge list for attraction
73
+ const edges = [];
74
+ for (const node of Object.values(dialogue.nodes)) {
75
+ for (const targetId of getConnectedNodeIds(node)) {
76
+ if (dialogue.nodes[targetId]) {
77
+ edges.push({ source: node.id, target: targetId });
78
+ }
79
+ }
80
+ }
81
+ // Run force simulation
82
+ for (let iter = 0; iter < MAX_ITERATIONS; iter++) {
83
+ let maxMovement = 0;
84
+ // Calculate repulsion forces between all node pairs
85
+ for (let i = 0; i < nodeIds.length; i++) {
86
+ for (let j = i + 1; j < nodeIds.length; j++) {
87
+ const a = states.get(nodeIds[i]);
88
+ const b = states.get(nodeIds[j]);
89
+ const dx = b.x - a.x;
90
+ const dy = b.y - a.y;
91
+ const dist = Math.sqrt(dx * dx + dy * dy) || 1;
92
+ // Repulsion force (inverse square law)
93
+ const force = REPULSION_STRENGTH / (dist * dist);
94
+ const fx = (dx / dist) * force;
95
+ const fy = (dy / dist) * force;
96
+ a.vx -= fx;
97
+ a.vy -= fy;
98
+ b.vx += fx;
99
+ b.vy += fy;
100
+ }
101
+ }
102
+ // Calculate attraction forces along edges
103
+ for (const edge of edges) {
104
+ const source = states.get(edge.source);
105
+ const target = states.get(edge.target);
106
+ if (!source || !target)
107
+ continue;
108
+ const dx = target.x - source.x;
109
+ const dy = target.y - source.y;
110
+ const dist = Math.sqrt(dx * dx + dy * dy) || 1;
111
+ // Attraction force (Hooke's law)
112
+ const force = dist * ATTRACTION_STRENGTH;
113
+ const fx = (dx / dist) * force;
114
+ const fy = (dy / dist) * force;
115
+ source.vx += fx;
116
+ source.vy += fy;
117
+ target.vx -= fx;
118
+ target.vy -= fy;
119
+ }
120
+ // Apply velocities with damping
121
+ for (const state of states.values()) {
122
+ state.x += state.vx;
123
+ state.y += state.vy;
124
+ state.vx *= DAMPING;
125
+ state.vy *= DAMPING;
126
+ maxMovement = Math.max(maxMovement, Math.abs(state.vx) + Math.abs(state.vy));
127
+ }
128
+ // Early exit if stable
129
+ if (maxMovement < MIN_MOVEMENT)
130
+ break;
131
+ }
132
+ // Calculate bounds and apply positions
133
+ let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
134
+ const updatedNodes = {};
135
+ for (const [id, state] of states) {
136
+ const node = dialogue.nodes[id];
137
+ updatedNodes[id] = { ...node, x: state.x, y: state.y };
138
+ minX = Math.min(minX, state.x);
139
+ maxX = Math.max(maxX, state.x + NODE_WIDTH);
140
+ minY = Math.min(minY, state.y);
141
+ maxY = Math.max(maxY, state.y + NODE_HEIGHT);
142
+ }
143
+ // Normalize to start from margin
144
+ const margin = opts.margin || 50;
145
+ const offsetX = margin - minX;
146
+ const offsetY = margin - minY;
147
+ for (const id of nodeIds) {
148
+ updatedNodes[id].x += offsetX;
149
+ updatedNodes[id].y += offsetY;
150
+ }
151
+ const computeTimeMs = performance.now() - startTime;
152
+ return {
153
+ dialogue: { ...dialogue, nodes: updatedNodes },
154
+ metadata: {
155
+ computeTimeMs,
156
+ nodeCount: nodeIds.length,
157
+ bounds: {
158
+ minX: margin,
159
+ minY: margin,
160
+ maxX: maxX + offsetX,
161
+ maxY: maxY + offsetY,
162
+ width: maxX - minX,
163
+ height: maxY - minY,
164
+ },
165
+ },
166
+ };
167
+ }
168
+ emptyResult(dialogue, startTime) {
169
+ return {
170
+ dialogue,
171
+ metadata: {
172
+ computeTimeMs: performance.now() - startTime,
173
+ nodeCount: 0,
174
+ bounds: { minX: 0, minY: 0, maxX: 0, maxY: 0, width: 0, height: 0 },
175
+ },
176
+ };
177
+ }
178
+ supports() {
179
+ return true; // Works with any graph
180
+ }
181
+ }
182
+ exports.ForceLayoutStrategy = ForceLayoutStrategy;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Grid Layout Strategy
3
+ *
4
+ * Simple grid-based layout that arranges nodes in rows and columns.
5
+ * Useful for getting a quick overview of all nodes.
6
+ */
7
+ import { DialogueTree } from '../../../types';
8
+ import { LayoutStrategy, LayoutOptions, LayoutResult } from '../types';
9
+ export declare class GridLayoutStrategy implements LayoutStrategy {
10
+ readonly id = "grid";
11
+ readonly name = "Grid";
12
+ readonly description = "Arranges nodes in a simple grid pattern. Good for viewing all nodes at once.";
13
+ readonly defaultOptions: Partial<LayoutOptions>;
14
+ apply(dialogue: DialogueTree, options?: LayoutOptions): LayoutResult;
15
+ private emptyResult;
16
+ supports(): boolean;
17
+ }