@q1k-oss/btree-workflows 0.0.1 → 0.0.2
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 +13 -13
- package/dist/index.cjs +5011 -0
- package/dist/index.d.cts +3320 -0
- package/dist/index.d.ts +3320 -0
- package/dist/index.js +4879 -0
- package/package.json +33 -3
- package/.claude/settings.local.json +0 -31
- package/CLAUDE.md +0 -181
- package/behaviour-tree-workflows-landing/index.html +0 -16
- package/behaviour-tree-workflows-landing/package-lock.json +0 -2074
- package/behaviour-tree-workflows-landing/package.json +0 -31
- package/behaviour-tree-workflows-landing/public/favicon.svg +0 -17
- package/behaviour-tree-workflows-landing/src/App.css +0 -103
- package/behaviour-tree-workflows-landing/src/App.tsx +0 -176
- package/behaviour-tree-workflows-landing/src/components/BlackboardInspector.css +0 -89
- package/behaviour-tree-workflows-landing/src/components/BlackboardInspector.tsx +0 -64
- package/behaviour-tree-workflows-landing/src/components/ExampleSelector.css +0 -64
- package/behaviour-tree-workflows-landing/src/components/ExampleSelector.tsx +0 -34
- package/behaviour-tree-workflows-landing/src/components/ExecutionLog.css +0 -107
- package/behaviour-tree-workflows-landing/src/components/ExecutionLog.tsx +0 -85
- package/behaviour-tree-workflows-landing/src/components/Header.css +0 -50
- package/behaviour-tree-workflows-landing/src/components/Header.tsx +0 -26
- package/behaviour-tree-workflows-landing/src/components/StatusBadge.css +0 -45
- package/behaviour-tree-workflows-landing/src/components/StatusBadge.tsx +0 -15
- package/behaviour-tree-workflows-landing/src/components/Toolbar.css +0 -74
- package/behaviour-tree-workflows-landing/src/components/Toolbar.tsx +0 -53
- package/behaviour-tree-workflows-landing/src/components/TreeVisualizer.css +0 -67
- package/behaviour-tree-workflows-landing/src/components/TreeVisualizer.tsx +0 -192
- package/behaviour-tree-workflows-landing/src/components/YamlEditor.css +0 -18
- package/behaviour-tree-workflows-landing/src/components/YamlEditor.tsx +0 -96
- package/behaviour-tree-workflows-landing/src/lib/count-nodes.ts +0 -11
- package/behaviour-tree-workflows-landing/src/lib/execution-engine.ts +0 -96
- package/behaviour-tree-workflows-landing/src/lib/tree-layout.ts +0 -136
- package/behaviour-tree-workflows-landing/src/lib/yaml-examples.ts +0 -549
- package/behaviour-tree-workflows-landing/src/main.tsx +0 -9
- package/behaviour-tree-workflows-landing/src/stubs/activepieces.ts +0 -18
- package/behaviour-tree-workflows-landing/src/stubs/fs.ts +0 -24
- package/behaviour-tree-workflows-landing/src/stubs/path.ts +0 -16
- package/behaviour-tree-workflows-landing/src/stubs/temporal-activity.ts +0 -6
- package/behaviour-tree-workflows-landing/src/stubs/temporal-workflow.ts +0 -22
- package/behaviour-tree-workflows-landing/tsconfig.json +0 -25
- package/behaviour-tree-workflows-landing/vite.config.ts +0 -40
- package/demo-google-sheets.ts +0 -181
- package/demo-runtime-variables.ts +0 -174
- package/demo-template.ts +0 -208
- package/docs/ARCHITECTURE_SUMMARY.md +0 -613
- package/docs/NODE_REFERENCE.md +0 -504
- package/docs/README.md +0 -53
- package/docs/custom-nodes-architecture.md +0 -826
- package/docs/observability.md +0 -175
- package/docs/yaml-specification.md +0 -990
- package/examples/temporal/README.md +0 -117
- package/examples/temporal/activities.ts +0 -373
- package/examples/temporal/client.ts +0 -115
- package/examples/temporal/python-worker/activities.py +0 -339
- package/examples/temporal/python-worker/requirements.txt +0 -12
- package/examples/temporal/python-worker/worker.py +0 -106
- package/examples/temporal/worker.ts +0 -66
- package/examples/temporal/workflows.ts +0 -6
- package/examples/temporal/yaml-workflow-loader.ts +0 -105
- package/examples/yaml-test.ts +0 -97
- package/examples/yaml-workflows/01-simple-sequence.yaml +0 -25
- package/examples/yaml-workflows/02-parallel-timeout.yaml +0 -45
- package/examples/yaml-workflows/03-ecommerce-checkout.yaml +0 -94
- package/examples/yaml-workflows/04-ai-agent-workflow.yaml +0 -346
- package/examples/yaml-workflows/05-order-processing.yaml +0 -146
- package/examples/yaml-workflows/06-activity-test.yaml +0 -71
- package/examples/yaml-workflows/07-activity-simple-test.yaml +0 -43
- package/examples/yaml-workflows/08-file-processing.yaml +0 -141
- package/examples/yaml-workflows/09-http-request.yaml +0 -137
- package/examples/yaml-workflows/README.md +0 -211
- package/src/actions/code-execution.schema.ts +0 -27
- package/src/actions/code-execution.ts +0 -218
- package/src/actions/generate-file.test.ts +0 -516
- package/src/actions/generate-file.ts +0 -166
- package/src/actions/http-request.test.ts +0 -784
- package/src/actions/http-request.ts +0 -228
- package/src/actions/index.ts +0 -20
- package/src/actions/parse-file.test.ts +0 -448
- package/src/actions/parse-file.ts +0 -139
- package/src/actions/python-script.test.ts +0 -439
- package/src/actions/python-script.ts +0 -154
- package/src/base-node.test.ts +0 -511
- package/src/base-node.ts +0 -605
- package/src/behavior-tree.test.ts +0 -431
- package/src/behavior-tree.ts +0 -283
- package/src/blackboard.test.ts +0 -222
- package/src/blackboard.ts +0 -192
- package/src/composites/conditional.schema.ts +0 -19
- package/src/composites/conditional.test.ts +0 -309
- package/src/composites/conditional.ts +0 -129
- package/src/composites/for-each.schema.ts +0 -23
- package/src/composites/for-each.test.ts +0 -254
- package/src/composites/for-each.ts +0 -132
- package/src/composites/index.ts +0 -15
- package/src/composites/memory-sequence.schema.ts +0 -19
- package/src/composites/memory-sequence.test.ts +0 -223
- package/src/composites/memory-sequence.ts +0 -98
- package/src/composites/parallel.schema.ts +0 -28
- package/src/composites/parallel.test.ts +0 -502
- package/src/composites/parallel.ts +0 -157
- package/src/composites/reactive-sequence.schema.ts +0 -19
- package/src/composites/reactive-sequence.test.ts +0 -170
- package/src/composites/reactive-sequence.ts +0 -85
- package/src/composites/recovery.schema.ts +0 -19
- package/src/composites/recovery.test.ts +0 -366
- package/src/composites/recovery.ts +0 -90
- package/src/composites/selector.schema.ts +0 -19
- package/src/composites/selector.test.ts +0 -387
- package/src/composites/selector.ts +0 -85
- package/src/composites/sequence.schema.ts +0 -19
- package/src/composites/sequence.test.ts +0 -337
- package/src/composites/sequence.ts +0 -72
- package/src/composites/sub-tree.schema.ts +0 -21
- package/src/composites/sub-tree.test.ts +0 -893
- package/src/composites/sub-tree.ts +0 -177
- package/src/composites/while.schema.ts +0 -24
- package/src/composites/while.test.ts +0 -381
- package/src/composites/while.ts +0 -149
- package/src/data-store/index.ts +0 -10
- package/src/data-store/memory-store.ts +0 -161
- package/src/data-store/types.ts +0 -94
- package/src/debug/breakpoint.test.ts +0 -47
- package/src/debug/breakpoint.ts +0 -30
- package/src/debug/index.ts +0 -17
- package/src/debug/resume-point.test.ts +0 -49
- package/src/debug/resume-point.ts +0 -29
- package/src/decorators/delay.schema.ts +0 -21
- package/src/decorators/delay.test.ts +0 -261
- package/src/decorators/delay.ts +0 -140
- package/src/decorators/force-result.schema.ts +0 -32
- package/src/decorators/force-result.test.ts +0 -133
- package/src/decorators/force-result.ts +0 -63
- package/src/decorators/index.ts +0 -13
- package/src/decorators/invert.schema.ts +0 -19
- package/src/decorators/invert.test.ts +0 -135
- package/src/decorators/invert.ts +0 -42
- package/src/decorators/keep-running.schema.ts +0 -20
- package/src/decorators/keep-running.test.ts +0 -105
- package/src/decorators/keep-running.ts +0 -49
- package/src/decorators/precondition.schema.ts +0 -19
- package/src/decorators/precondition.test.ts +0 -351
- package/src/decorators/precondition.ts +0 -139
- package/src/decorators/repeat.schema.ts +0 -21
- package/src/decorators/repeat.test.ts +0 -187
- package/src/decorators/repeat.ts +0 -94
- package/src/decorators/run-once.schema.ts +0 -19
- package/src/decorators/run-once.test.ts +0 -140
- package/src/decorators/run-once.ts +0 -61
- package/src/decorators/soft-assert.schema.ts +0 -19
- package/src/decorators/soft-assert.test.ts +0 -107
- package/src/decorators/soft-assert.ts +0 -68
- package/src/decorators/timeout.schema.ts +0 -21
- package/src/decorators/timeout.test.ts +0 -274
- package/src/decorators/timeout.ts +0 -159
- package/src/errors.test.ts +0 -63
- package/src/errors.ts +0 -34
- package/src/events.test.ts +0 -347
- package/src/events.ts +0 -183
- package/src/index.ts +0 -80
- package/src/integrations/index.ts +0 -30
- package/src/integrations/integration-action.test.ts +0 -571
- package/src/integrations/integration-action.ts +0 -233
- package/src/integrations/piece-executor.ts +0 -320
- package/src/observability/execution-tracker.ts +0 -320
- package/src/observability/index.ts +0 -23
- package/src/observability/sinks.ts +0 -138
- package/src/observability/types.ts +0 -130
- package/src/registry-utils.ts +0 -147
- package/src/registry.test.ts +0 -466
- package/src/registry.ts +0 -334
- package/src/schemas/base.schema.ts +0 -104
- package/src/schemas/index.ts +0 -223
- package/src/schemas/integration.test.ts +0 -238
- package/src/schemas/tree-definition.schema.ts +0 -170
- package/src/schemas/validation.test.ts +0 -146
- package/src/schemas/validation.ts +0 -122
- package/src/scripting/index.ts +0 -22
- package/src/templates/template-loader.test.ts +0 -281
- package/src/templates/template-loader.ts +0 -152
- package/src/temporal-integration.test.ts +0 -213
- package/src/test-nodes.ts +0 -259
- package/src/types.ts +0 -503
- package/src/utilities/index.ts +0 -17
- package/src/utilities/log-message.test.ts +0 -275
- package/src/utilities/log-message.ts +0 -134
- package/src/utilities/regex-extract.test.ts +0 -138
- package/src/utilities/regex-extract.ts +0 -108
- package/src/utilities/variable-resolver.test.ts +0 -416
- package/src/utilities/variable-resolver.ts +0 -318
- package/src/utils/error-handler.test.ts +0 -117
- package/src/utils/error-handler.ts +0 -48
- package/src/utils/signal-check.test.ts +0 -234
- package/src/utils/signal-check.ts +0 -140
- package/src/yaml/errors.ts +0 -143
- package/src/yaml/index.ts +0 -30
- package/src/yaml/loader.ts +0 -39
- package/src/yaml/parser.ts +0 -286
- package/src/yaml/validation/semantic-validator.ts +0 -196
- package/templates/google-sheets/insert-row.yaml +0 -76
- package/templates/notification-sender.yaml +0 -33
- package/templates/order-validation.yaml +0 -44
- package/tsconfig.json +0 -24
- package/vitest.config.ts +0 -25
- package/workflows/order-processor.yaml +0 -59
- package/workflows/process-order-workflow.yaml +0 -142
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import React, { useRef, useEffect } from 'react';
|
|
2
|
-
import { EditorView, keymap } from '@codemirror/view';
|
|
3
|
-
import { EditorState } from '@codemirror/state';
|
|
4
|
-
import { basicSetup } from 'codemirror';
|
|
5
|
-
import { yaml } from '@codemirror/lang-yaml';
|
|
6
|
-
import { oneDark } from '@codemirror/theme-one-dark';
|
|
7
|
-
import { linter, type Diagnostic } from '@codemirror/lint';
|
|
8
|
-
import { validateYamlString } from '../lib/execution-engine';
|
|
9
|
-
import { indentWithTab } from '@codemirror/commands';
|
|
10
|
-
import './YamlEditor.css';
|
|
11
|
-
|
|
12
|
-
interface YamlEditorProps {
|
|
13
|
-
value: string;
|
|
14
|
-
onChange: (value: string) => void;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export const YamlEditor: React.FC<YamlEditorProps> = ({ value, onChange }) => {
|
|
18
|
-
const containerRef = useRef<HTMLDivElement>(null);
|
|
19
|
-
const viewRef = useRef<EditorView | null>(null);
|
|
20
|
-
const valueRef = useRef(value);
|
|
21
|
-
|
|
22
|
-
// Keep ref current
|
|
23
|
-
valueRef.current = value;
|
|
24
|
-
|
|
25
|
-
useEffect(() => {
|
|
26
|
-
if (!containerRef.current) return;
|
|
27
|
-
|
|
28
|
-
const yamlLinter = linter((view) => {
|
|
29
|
-
const text = view.state.doc.toString();
|
|
30
|
-
if (!text.trim()) return [];
|
|
31
|
-
|
|
32
|
-
const result = validateYamlString(text);
|
|
33
|
-
if (result.valid) return [];
|
|
34
|
-
|
|
35
|
-
const diagnostics: Diagnostic[] = result.errors.map((errMsg) => ({
|
|
36
|
-
from: 0,
|
|
37
|
-
to: Math.min(text.length, 1),
|
|
38
|
-
severity: 'error' as const,
|
|
39
|
-
message: errMsg,
|
|
40
|
-
}));
|
|
41
|
-
return diagnostics;
|
|
42
|
-
}, { delay: 500 });
|
|
43
|
-
|
|
44
|
-
const state = EditorState.create({
|
|
45
|
-
doc: value,
|
|
46
|
-
extensions: [
|
|
47
|
-
basicSetup,
|
|
48
|
-
yaml(),
|
|
49
|
-
oneDark,
|
|
50
|
-
yamlLinter,
|
|
51
|
-
keymap.of([indentWithTab]),
|
|
52
|
-
EditorView.updateListener.of((update) => {
|
|
53
|
-
if (update.docChanged) {
|
|
54
|
-
const newVal = update.state.doc.toString();
|
|
55
|
-
onChange(newVal);
|
|
56
|
-
}
|
|
57
|
-
}),
|
|
58
|
-
EditorView.theme({
|
|
59
|
-
'&': { height: '100%', fontSize: '13px' },
|
|
60
|
-
'.cm-scroller': { fontFamily: "'JetBrains Mono', monospace" },
|
|
61
|
-
'.cm-content': { padding: '8px 0' },
|
|
62
|
-
}),
|
|
63
|
-
],
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
const view = new EditorView({
|
|
67
|
-
state,
|
|
68
|
-
parent: containerRef.current,
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
viewRef.current = view;
|
|
72
|
-
|
|
73
|
-
return () => {
|
|
74
|
-
view.destroy();
|
|
75
|
-
viewRef.current = null;
|
|
76
|
-
};
|
|
77
|
-
// Only run on mount
|
|
78
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
79
|
-
}, []);
|
|
80
|
-
|
|
81
|
-
// Sync external value changes
|
|
82
|
-
useEffect(() => {
|
|
83
|
-
const view = viewRef.current;
|
|
84
|
-
if (!view) return;
|
|
85
|
-
const currentText = view.state.doc.toString();
|
|
86
|
-
if (currentText !== value) {
|
|
87
|
-
view.dispatch({
|
|
88
|
-
changes: { from: 0, to: currentText.length, insert: value },
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
}, [value]);
|
|
92
|
-
|
|
93
|
-
return (
|
|
94
|
-
<div className="yaml-editor" ref={containerRef} />
|
|
95
|
-
);
|
|
96
|
-
};
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { Registry, registerStandardNodes } from '@btree/index';
|
|
2
|
-
import { ScopedBlackboard } from '@btree/blackboard';
|
|
3
|
-
import { NodeEventEmitter } from '@btree/events';
|
|
4
|
-
import { loadTreeFromYaml, validateYaml } from '@btree/yaml/index';
|
|
5
|
-
import type { TreeNode, TemporalContext } from '@btree/types';
|
|
6
|
-
import { NodeStatus } from '@btree/types';
|
|
7
|
-
import type { NodeEvent } from '@btree/events';
|
|
8
|
-
import { countNodes } from './count-nodes';
|
|
9
|
-
|
|
10
|
-
let _registry: Registry | null = null;
|
|
11
|
-
|
|
12
|
-
export function getRegistry(): Registry {
|
|
13
|
-
if (!_registry) {
|
|
14
|
-
_registry = new Registry();
|
|
15
|
-
registerStandardNodes(_registry);
|
|
16
|
-
}
|
|
17
|
-
return _registry;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface ParseResult {
|
|
21
|
-
tree: TreeNode | null;
|
|
22
|
-
error: string | null;
|
|
23
|
-
nodeCount: number;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function parseYamlToTree(yamlString: string): ParseResult {
|
|
27
|
-
const registry = getRegistry();
|
|
28
|
-
try {
|
|
29
|
-
const tree = loadTreeFromYaml(yamlString, registry);
|
|
30
|
-
return { tree, error: null, nodeCount: countNodes(tree) };
|
|
31
|
-
} catch (err) {
|
|
32
|
-
return {
|
|
33
|
-
tree: null,
|
|
34
|
-
error: err instanceof Error ? err.message : String(err),
|
|
35
|
-
nodeCount: 0,
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function validateYamlString(yamlString: string): { valid: boolean; errors: string[] } {
|
|
41
|
-
const registry = getRegistry();
|
|
42
|
-
const result = validateYaml(yamlString, registry, { collectAllErrors: true });
|
|
43
|
-
return {
|
|
44
|
-
valid: result.valid,
|
|
45
|
-
errors: result.errors.map((e) => e.message),
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export interface ExecutionResult {
|
|
50
|
-
status: NodeStatus;
|
|
51
|
-
blackboard: Record<string, unknown>;
|
|
52
|
-
events: NodeEvent<unknown>[];
|
|
53
|
-
error?: string;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export type EventCallback = (event: NodeEvent<unknown>) => void;
|
|
57
|
-
|
|
58
|
-
export async function executeTree(
|
|
59
|
-
tree: TreeNode,
|
|
60
|
-
onEvent?: EventCallback,
|
|
61
|
-
): Promise<ExecutionResult> {
|
|
62
|
-
const blackboard = new ScopedBlackboard();
|
|
63
|
-
const eventEmitter = new NodeEventEmitter();
|
|
64
|
-
const events: NodeEvent<unknown>[] = [];
|
|
65
|
-
|
|
66
|
-
eventEmitter.onAll((event: NodeEvent<unknown>) => {
|
|
67
|
-
events.push(event);
|
|
68
|
-
onEvent?.(event);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
const registry = getRegistry();
|
|
72
|
-
|
|
73
|
-
const context: TemporalContext = {
|
|
74
|
-
blackboard,
|
|
75
|
-
treeRegistry: registry,
|
|
76
|
-
timestamp: Date.now(),
|
|
77
|
-
eventEmitter,
|
|
78
|
-
sessionId: `playground-${Date.now()}`,
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
try {
|
|
82
|
-
const status = await tree.tick(context);
|
|
83
|
-
return {
|
|
84
|
-
status,
|
|
85
|
-
blackboard: blackboard.toJSON(),
|
|
86
|
-
events,
|
|
87
|
-
};
|
|
88
|
-
} catch (err) {
|
|
89
|
-
return {
|
|
90
|
-
status: NodeStatus.FAILURE,
|
|
91
|
-
blackboard: blackboard.toJSON(),
|
|
92
|
-
events,
|
|
93
|
-
error: err instanceof Error ? err.message : String(err),
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
}
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import type { TreeNode } from '@btree/types';
|
|
2
|
-
|
|
3
|
-
export interface LayoutNode {
|
|
4
|
-
id: string;
|
|
5
|
-
name: string;
|
|
6
|
-
type: string;
|
|
7
|
-
category: 'composite' | 'decorator' | 'action' | 'condition';
|
|
8
|
-
x: number;
|
|
9
|
-
y: number;
|
|
10
|
-
width: number;
|
|
11
|
-
height: number;
|
|
12
|
-
children: LayoutNode[];
|
|
13
|
-
status: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface LayoutEdge {
|
|
17
|
-
fromX: number;
|
|
18
|
-
fromY: number;
|
|
19
|
-
toX: number;
|
|
20
|
-
toY: number;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const NODE_WIDTH = 160;
|
|
24
|
-
const NODE_HEIGHT = 44;
|
|
25
|
-
const H_GAP = 16;
|
|
26
|
-
const V_GAP = 56;
|
|
27
|
-
|
|
28
|
-
const COMPOSITE_TYPES = new Set([
|
|
29
|
-
'Sequence', 'Selector', 'Parallel', 'Conditional', 'ForEach',
|
|
30
|
-
'While', 'Recovery', 'ReactiveSequence', 'MemorySequence', 'SubTree',
|
|
31
|
-
]);
|
|
32
|
-
|
|
33
|
-
const DECORATOR_TYPES = new Set([
|
|
34
|
-
'Timeout', 'Delay', 'Repeat', 'Invert', 'ForceSuccess', 'ForceFailure',
|
|
35
|
-
'RunOnce', 'KeepRunningUntilFailure', 'Precondition', 'SoftAssert',
|
|
36
|
-
]);
|
|
37
|
-
|
|
38
|
-
const CONDITION_TYPES = new Set([
|
|
39
|
-
'CheckCondition', 'AlwaysCondition',
|
|
40
|
-
]);
|
|
41
|
-
|
|
42
|
-
function getCategory(type: string): LayoutNode['category'] {
|
|
43
|
-
if (COMPOSITE_TYPES.has(type)) return 'composite';
|
|
44
|
-
if (DECORATOR_TYPES.has(type)) return 'decorator';
|
|
45
|
-
if (CONDITION_TYPES.has(type)) return 'condition';
|
|
46
|
-
return 'action';
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function computeSubtreeWidth(node: TreeNode): number {
|
|
50
|
-
if (!node.children || node.children.length === 0) {
|
|
51
|
-
return NODE_WIDTH;
|
|
52
|
-
}
|
|
53
|
-
const childrenWidth = node.children.reduce(
|
|
54
|
-
(sum, child) => sum + computeSubtreeWidth(child),
|
|
55
|
-
0,
|
|
56
|
-
);
|
|
57
|
-
return Math.max(NODE_WIDTH, childrenWidth + H_GAP * (node.children.length - 1));
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function layoutNode(
|
|
61
|
-
node: TreeNode,
|
|
62
|
-
x: number,
|
|
63
|
-
y: number,
|
|
64
|
-
nodeStates: Map<string, string>,
|
|
65
|
-
): LayoutNode {
|
|
66
|
-
const subtreeWidth = computeSubtreeWidth(node);
|
|
67
|
-
const nodeX = x + subtreeWidth / 2 - NODE_WIDTH / 2;
|
|
68
|
-
|
|
69
|
-
const layoutChildren: LayoutNode[] = [];
|
|
70
|
-
if (node.children && node.children.length > 0) {
|
|
71
|
-
let childX = x;
|
|
72
|
-
const childY = y + NODE_HEIGHT + V_GAP;
|
|
73
|
-
for (const child of node.children) {
|
|
74
|
-
const childWidth = computeSubtreeWidth(child);
|
|
75
|
-
layoutChildren.push(layoutNode(child, childX, childY, nodeStates));
|
|
76
|
-
childX += childWidth + H_GAP;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return {
|
|
81
|
-
id: node.id,
|
|
82
|
-
name: node.name || node.id,
|
|
83
|
-
type: node.type,
|
|
84
|
-
category: getCategory(node.type),
|
|
85
|
-
x: nodeX,
|
|
86
|
-
y,
|
|
87
|
-
width: NODE_WIDTH,
|
|
88
|
-
height: NODE_HEIGHT,
|
|
89
|
-
children: layoutChildren,
|
|
90
|
-
status: nodeStates.get(node.id) || 'idle',
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export function layoutTree(
|
|
95
|
-
root: TreeNode,
|
|
96
|
-
nodeStates: Map<string, string> = new Map(),
|
|
97
|
-
): { root: LayoutNode; width: number; height: number; edges: LayoutEdge[] } {
|
|
98
|
-
const layoutRoot = layoutNode(root, 0, 0, nodeStates);
|
|
99
|
-
|
|
100
|
-
function getMaxY(node: LayoutNode): number {
|
|
101
|
-
let maxY = node.y + node.height;
|
|
102
|
-
for (const child of node.children) {
|
|
103
|
-
maxY = Math.max(maxY, getMaxY(child));
|
|
104
|
-
}
|
|
105
|
-
return maxY;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function getMaxX(node: LayoutNode): number {
|
|
109
|
-
let maxX = node.x + node.width;
|
|
110
|
-
for (const child of node.children) {
|
|
111
|
-
maxX = Math.max(maxX, getMaxX(child));
|
|
112
|
-
}
|
|
113
|
-
return maxX;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const edges: LayoutEdge[] = [];
|
|
117
|
-
function collectEdges(node: LayoutNode) {
|
|
118
|
-
for (const child of node.children) {
|
|
119
|
-
edges.push({
|
|
120
|
-
fromX: node.x + node.width / 2,
|
|
121
|
-
fromY: node.y + node.height,
|
|
122
|
-
toX: child.x + child.width / 2,
|
|
123
|
-
toY: child.y,
|
|
124
|
-
});
|
|
125
|
-
collectEdges(child);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
collectEdges(layoutRoot);
|
|
129
|
-
|
|
130
|
-
return {
|
|
131
|
-
root: layoutRoot,
|
|
132
|
-
width: getMaxX(layoutRoot),
|
|
133
|
-
height: getMaxY(layoutRoot),
|
|
134
|
-
edges,
|
|
135
|
-
};
|
|
136
|
-
}
|