@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,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
|
+
}
|