@q1k-oss/btree-workflows 0.0.1

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 (203) hide show
  1. package/.claude/settings.local.json +31 -0
  2. package/CLAUDE.md +181 -0
  3. package/LICENSE +21 -0
  4. package/README.md +920 -0
  5. package/behaviour-tree-workflows-landing/index.html +16 -0
  6. package/behaviour-tree-workflows-landing/package-lock.json +2074 -0
  7. package/behaviour-tree-workflows-landing/package.json +31 -0
  8. package/behaviour-tree-workflows-landing/public/favicon.svg +17 -0
  9. package/behaviour-tree-workflows-landing/src/App.css +103 -0
  10. package/behaviour-tree-workflows-landing/src/App.tsx +176 -0
  11. package/behaviour-tree-workflows-landing/src/components/BlackboardInspector.css +89 -0
  12. package/behaviour-tree-workflows-landing/src/components/BlackboardInspector.tsx +64 -0
  13. package/behaviour-tree-workflows-landing/src/components/ExampleSelector.css +64 -0
  14. package/behaviour-tree-workflows-landing/src/components/ExampleSelector.tsx +34 -0
  15. package/behaviour-tree-workflows-landing/src/components/ExecutionLog.css +107 -0
  16. package/behaviour-tree-workflows-landing/src/components/ExecutionLog.tsx +85 -0
  17. package/behaviour-tree-workflows-landing/src/components/Header.css +50 -0
  18. package/behaviour-tree-workflows-landing/src/components/Header.tsx +26 -0
  19. package/behaviour-tree-workflows-landing/src/components/StatusBadge.css +45 -0
  20. package/behaviour-tree-workflows-landing/src/components/StatusBadge.tsx +15 -0
  21. package/behaviour-tree-workflows-landing/src/components/Toolbar.css +74 -0
  22. package/behaviour-tree-workflows-landing/src/components/Toolbar.tsx +53 -0
  23. package/behaviour-tree-workflows-landing/src/components/TreeVisualizer.css +67 -0
  24. package/behaviour-tree-workflows-landing/src/components/TreeVisualizer.tsx +192 -0
  25. package/behaviour-tree-workflows-landing/src/components/YamlEditor.css +18 -0
  26. package/behaviour-tree-workflows-landing/src/components/YamlEditor.tsx +96 -0
  27. package/behaviour-tree-workflows-landing/src/lib/count-nodes.ts +11 -0
  28. package/behaviour-tree-workflows-landing/src/lib/execution-engine.ts +96 -0
  29. package/behaviour-tree-workflows-landing/src/lib/tree-layout.ts +136 -0
  30. package/behaviour-tree-workflows-landing/src/lib/yaml-examples.ts +549 -0
  31. package/behaviour-tree-workflows-landing/src/main.tsx +9 -0
  32. package/behaviour-tree-workflows-landing/src/stubs/activepieces.ts +18 -0
  33. package/behaviour-tree-workflows-landing/src/stubs/fs.ts +24 -0
  34. package/behaviour-tree-workflows-landing/src/stubs/path.ts +16 -0
  35. package/behaviour-tree-workflows-landing/src/stubs/temporal-activity.ts +6 -0
  36. package/behaviour-tree-workflows-landing/src/stubs/temporal-workflow.ts +22 -0
  37. package/behaviour-tree-workflows-landing/tsconfig.json +25 -0
  38. package/behaviour-tree-workflows-landing/vite.config.ts +40 -0
  39. package/demo-google-sheets.ts +181 -0
  40. package/demo-runtime-variables.ts +174 -0
  41. package/demo-template.ts +208 -0
  42. package/docs/ARCHITECTURE_SUMMARY.md +613 -0
  43. package/docs/NODE_REFERENCE.md +504 -0
  44. package/docs/README.md +53 -0
  45. package/docs/custom-nodes-architecture.md +826 -0
  46. package/docs/observability.md +175 -0
  47. package/docs/yaml-specification.md +990 -0
  48. package/examples/temporal/README.md +117 -0
  49. package/examples/temporal/activities.ts +373 -0
  50. package/examples/temporal/client.ts +115 -0
  51. package/examples/temporal/python-worker/activities.py +339 -0
  52. package/examples/temporal/python-worker/requirements.txt +12 -0
  53. package/examples/temporal/python-worker/worker.py +106 -0
  54. package/examples/temporal/worker.ts +66 -0
  55. package/examples/temporal/workflows.ts +6 -0
  56. package/examples/temporal/yaml-workflow-loader.ts +105 -0
  57. package/examples/yaml-test.ts +97 -0
  58. package/examples/yaml-workflows/01-simple-sequence.yaml +25 -0
  59. package/examples/yaml-workflows/02-parallel-timeout.yaml +45 -0
  60. package/examples/yaml-workflows/03-ecommerce-checkout.yaml +94 -0
  61. package/examples/yaml-workflows/04-ai-agent-workflow.yaml +346 -0
  62. package/examples/yaml-workflows/05-order-processing.yaml +146 -0
  63. package/examples/yaml-workflows/06-activity-test.yaml +71 -0
  64. package/examples/yaml-workflows/07-activity-simple-test.yaml +43 -0
  65. package/examples/yaml-workflows/08-file-processing.yaml +141 -0
  66. package/examples/yaml-workflows/09-http-request.yaml +137 -0
  67. package/examples/yaml-workflows/README.md +211 -0
  68. package/package.json +38 -0
  69. package/src/actions/code-execution.schema.ts +27 -0
  70. package/src/actions/code-execution.ts +218 -0
  71. package/src/actions/generate-file.test.ts +516 -0
  72. package/src/actions/generate-file.ts +166 -0
  73. package/src/actions/http-request.test.ts +784 -0
  74. package/src/actions/http-request.ts +228 -0
  75. package/src/actions/index.ts +20 -0
  76. package/src/actions/parse-file.test.ts +448 -0
  77. package/src/actions/parse-file.ts +139 -0
  78. package/src/actions/python-script.test.ts +439 -0
  79. package/src/actions/python-script.ts +154 -0
  80. package/src/base-node.test.ts +511 -0
  81. package/src/base-node.ts +605 -0
  82. package/src/behavior-tree.test.ts +431 -0
  83. package/src/behavior-tree.ts +283 -0
  84. package/src/blackboard.test.ts +222 -0
  85. package/src/blackboard.ts +192 -0
  86. package/src/composites/conditional.schema.ts +19 -0
  87. package/src/composites/conditional.test.ts +309 -0
  88. package/src/composites/conditional.ts +129 -0
  89. package/src/composites/for-each.schema.ts +23 -0
  90. package/src/composites/for-each.test.ts +254 -0
  91. package/src/composites/for-each.ts +132 -0
  92. package/src/composites/index.ts +15 -0
  93. package/src/composites/memory-sequence.schema.ts +19 -0
  94. package/src/composites/memory-sequence.test.ts +223 -0
  95. package/src/composites/memory-sequence.ts +98 -0
  96. package/src/composites/parallel.schema.ts +28 -0
  97. package/src/composites/parallel.test.ts +502 -0
  98. package/src/composites/parallel.ts +157 -0
  99. package/src/composites/reactive-sequence.schema.ts +19 -0
  100. package/src/composites/reactive-sequence.test.ts +170 -0
  101. package/src/composites/reactive-sequence.ts +85 -0
  102. package/src/composites/recovery.schema.ts +19 -0
  103. package/src/composites/recovery.test.ts +366 -0
  104. package/src/composites/recovery.ts +90 -0
  105. package/src/composites/selector.schema.ts +19 -0
  106. package/src/composites/selector.test.ts +387 -0
  107. package/src/composites/selector.ts +85 -0
  108. package/src/composites/sequence.schema.ts +19 -0
  109. package/src/composites/sequence.test.ts +337 -0
  110. package/src/composites/sequence.ts +72 -0
  111. package/src/composites/sub-tree.schema.ts +21 -0
  112. package/src/composites/sub-tree.test.ts +893 -0
  113. package/src/composites/sub-tree.ts +177 -0
  114. package/src/composites/while.schema.ts +24 -0
  115. package/src/composites/while.test.ts +381 -0
  116. package/src/composites/while.ts +149 -0
  117. package/src/data-store/index.ts +10 -0
  118. package/src/data-store/memory-store.ts +161 -0
  119. package/src/data-store/types.ts +94 -0
  120. package/src/debug/breakpoint.test.ts +47 -0
  121. package/src/debug/breakpoint.ts +30 -0
  122. package/src/debug/index.ts +17 -0
  123. package/src/debug/resume-point.test.ts +49 -0
  124. package/src/debug/resume-point.ts +29 -0
  125. package/src/decorators/delay.schema.ts +21 -0
  126. package/src/decorators/delay.test.ts +261 -0
  127. package/src/decorators/delay.ts +140 -0
  128. package/src/decorators/force-result.schema.ts +32 -0
  129. package/src/decorators/force-result.test.ts +133 -0
  130. package/src/decorators/force-result.ts +63 -0
  131. package/src/decorators/index.ts +13 -0
  132. package/src/decorators/invert.schema.ts +19 -0
  133. package/src/decorators/invert.test.ts +135 -0
  134. package/src/decorators/invert.ts +42 -0
  135. package/src/decorators/keep-running.schema.ts +20 -0
  136. package/src/decorators/keep-running.test.ts +105 -0
  137. package/src/decorators/keep-running.ts +49 -0
  138. package/src/decorators/precondition.schema.ts +19 -0
  139. package/src/decorators/precondition.test.ts +351 -0
  140. package/src/decorators/precondition.ts +139 -0
  141. package/src/decorators/repeat.schema.ts +21 -0
  142. package/src/decorators/repeat.test.ts +187 -0
  143. package/src/decorators/repeat.ts +94 -0
  144. package/src/decorators/run-once.schema.ts +19 -0
  145. package/src/decorators/run-once.test.ts +140 -0
  146. package/src/decorators/run-once.ts +61 -0
  147. package/src/decorators/soft-assert.schema.ts +19 -0
  148. package/src/decorators/soft-assert.test.ts +107 -0
  149. package/src/decorators/soft-assert.ts +68 -0
  150. package/src/decorators/timeout.schema.ts +21 -0
  151. package/src/decorators/timeout.test.ts +274 -0
  152. package/src/decorators/timeout.ts +159 -0
  153. package/src/errors.test.ts +63 -0
  154. package/src/errors.ts +34 -0
  155. package/src/events.test.ts +347 -0
  156. package/src/events.ts +183 -0
  157. package/src/index.ts +80 -0
  158. package/src/integrations/index.ts +30 -0
  159. package/src/integrations/integration-action.test.ts +571 -0
  160. package/src/integrations/integration-action.ts +233 -0
  161. package/src/integrations/piece-executor.ts +320 -0
  162. package/src/observability/execution-tracker.ts +320 -0
  163. package/src/observability/index.ts +23 -0
  164. package/src/observability/sinks.ts +138 -0
  165. package/src/observability/types.ts +130 -0
  166. package/src/registry-utils.ts +147 -0
  167. package/src/registry.test.ts +466 -0
  168. package/src/registry.ts +334 -0
  169. package/src/schemas/base.schema.ts +104 -0
  170. package/src/schemas/index.ts +223 -0
  171. package/src/schemas/integration.test.ts +238 -0
  172. package/src/schemas/tree-definition.schema.ts +170 -0
  173. package/src/schemas/validation.test.ts +146 -0
  174. package/src/schemas/validation.ts +122 -0
  175. package/src/scripting/index.ts +22 -0
  176. package/src/templates/template-loader.test.ts +281 -0
  177. package/src/templates/template-loader.ts +152 -0
  178. package/src/temporal-integration.test.ts +213 -0
  179. package/src/test-nodes.ts +259 -0
  180. package/src/types.ts +503 -0
  181. package/src/utilities/index.ts +17 -0
  182. package/src/utilities/log-message.test.ts +275 -0
  183. package/src/utilities/log-message.ts +134 -0
  184. package/src/utilities/regex-extract.test.ts +138 -0
  185. package/src/utilities/regex-extract.ts +108 -0
  186. package/src/utilities/variable-resolver.test.ts +416 -0
  187. package/src/utilities/variable-resolver.ts +318 -0
  188. package/src/utils/error-handler.test.ts +117 -0
  189. package/src/utils/error-handler.ts +48 -0
  190. package/src/utils/signal-check.test.ts +234 -0
  191. package/src/utils/signal-check.ts +140 -0
  192. package/src/yaml/errors.ts +143 -0
  193. package/src/yaml/index.ts +30 -0
  194. package/src/yaml/loader.ts +39 -0
  195. package/src/yaml/parser.ts +286 -0
  196. package/src/yaml/validation/semantic-validator.ts +196 -0
  197. package/templates/google-sheets/insert-row.yaml +76 -0
  198. package/templates/notification-sender.yaml +33 -0
  199. package/templates/order-validation.yaml +44 -0
  200. package/tsconfig.json +24 -0
  201. package/vitest.config.ts +25 -0
  202. package/workflows/order-processor.yaml +59 -0
  203. package/workflows/process-order-workflow.yaml +142 -0
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "btree-workflows-playground",
3
+ "private": true,
4
+ "version": "0.0.1",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc && vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "@codemirror/lang-yaml": "^6.1.1",
13
+ "@codemirror/lint": "^6.8.2",
14
+ "@codemirror/state": "^6.4.1",
15
+ "@codemirror/theme-one-dark": "^6.1.2",
16
+ "@codemirror/view": "^6.34.1",
17
+ "codemirror": "^6.0.1",
18
+ "js-yaml": "^4.1.1",
19
+ "react": "^18.3.1",
20
+ "react-dom": "^18.3.1",
21
+ "zod": "^4.3.6"
22
+ },
23
+ "devDependencies": {
24
+ "@types/js-yaml": "^4.0.9",
25
+ "@types/react": "^18.3.12",
26
+ "@types/react-dom": "^18.3.1",
27
+ "@vitejs/plugin-react": "^4.3.4",
28
+ "typescript": "^5.6.3",
29
+ "vite": "^6.0.3"
30
+ }
31
+ }
@@ -0,0 +1,17 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="none">
2
+ <circle cx="16" cy="4" r="3.5" fill="#818cf8" stroke="#6366f1" stroke-width="1"/>
3
+ <circle cx="6" cy="16" r="3" fill="#34d399" stroke="#10b981" stroke-width="1"/>
4
+ <circle cx="16" cy="16" r="3" fill="#fbbf24" stroke="#f59e0b" stroke-width="1"/>
5
+ <circle cx="26" cy="16" r="3" fill="#34d399" stroke="#10b981" stroke-width="1"/>
6
+ <circle cx="3" cy="27" r="2.5" fill="#60a5fa" stroke="#3b82f6" stroke-width="1"/>
7
+ <circle cx="10" cy="27" r="2.5" fill="#60a5fa" stroke="#3b82f6" stroke-width="1"/>
8
+ <circle cx="22" cy="27" r="2.5" fill="#60a5fa" stroke="#3b82f6" stroke-width="1"/>
9
+ <circle cx="29" cy="27" r="2.5" fill="#60a5fa" stroke="#3b82f6" stroke-width="1"/>
10
+ <line x1="16" y1="7.5" x2="6" y2="13" stroke="#475569" stroke-width="1.2"/>
11
+ <line x1="16" y1="7.5" x2="16" y2="13" stroke="#475569" stroke-width="1.2"/>
12
+ <line x1="16" y1="7.5" x2="26" y2="13" stroke="#475569" stroke-width="1.2"/>
13
+ <line x1="6" y1="19" x2="3" y2="24.5" stroke="#475569" stroke-width="1.2"/>
14
+ <line x1="6" y1="19" x2="10" y2="24.5" stroke="#475569" stroke-width="1.2"/>
15
+ <line x1="26" y1="19" x2="22" y2="24.5" stroke="#475569" stroke-width="1.2"/>
16
+ <line x1="26" y1="19" x2="29" y2="24.5" stroke="#475569" stroke-width="1.2"/>
17
+ </svg>
@@ -0,0 +1,103 @@
1
+ :root {
2
+ --color-bg: #0f172a;
3
+ --color-surface-1: #1e293b;
4
+ --color-surface-2: #334155;
5
+ --color-border: #334155;
6
+ --color-text: #e2e8f0;
7
+ --color-text-muted: #94a3b8;
8
+ --color-accent: #818cf8;
9
+ }
10
+
11
+ * {
12
+ margin: 0;
13
+ padding: 0;
14
+ box-sizing: border-box;
15
+ }
16
+
17
+ html, body, #root {
18
+ height: 100%;
19
+ width: 100%;
20
+ overflow: hidden;
21
+ }
22
+
23
+ body {
24
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
25
+ background: var(--color-bg);
26
+ color: var(--color-text);
27
+ -webkit-font-smoothing: antialiased;
28
+ }
29
+
30
+ .app {
31
+ display: flex;
32
+ flex-direction: column;
33
+ height: 100vh;
34
+ overflow: hidden;
35
+ }
36
+
37
+ .app__error {
38
+ padding: 6px 20px;
39
+ background: rgba(248, 113, 113, 0.1);
40
+ border-bottom: 1px solid rgba(248, 113, 113, 0.3);
41
+ font-size: 12px;
42
+ color: #f87171;
43
+ font-family: 'JetBrains Mono', monospace;
44
+ }
45
+
46
+ .app__error span {
47
+ font-weight: 600;
48
+ }
49
+
50
+ .app__main {
51
+ flex: 1;
52
+ display: flex;
53
+ min-height: 0;
54
+ border-bottom: 1px solid var(--color-border);
55
+ }
56
+
57
+ .app__editor {
58
+ width: 40%;
59
+ min-width: 300px;
60
+ border-right: 1px solid var(--color-border);
61
+ overflow: hidden;
62
+ }
63
+
64
+ .app__tree {
65
+ flex: 1;
66
+ overflow: hidden;
67
+ }
68
+
69
+ .app__bottom {
70
+ display: flex;
71
+ height: 220px;
72
+ flex-shrink: 0;
73
+ }
74
+
75
+ .app__log {
76
+ width: 50%;
77
+ border-right: 1px solid var(--color-border);
78
+ overflow: hidden;
79
+ }
80
+
81
+ .app__blackboard {
82
+ flex: 1;
83
+ overflow: hidden;
84
+ }
85
+
86
+ /* Scrollbar styling */
87
+ ::-webkit-scrollbar {
88
+ width: 6px;
89
+ height: 6px;
90
+ }
91
+
92
+ ::-webkit-scrollbar-track {
93
+ background: transparent;
94
+ }
95
+
96
+ ::-webkit-scrollbar-thumb {
97
+ background: var(--color-surface-2);
98
+ border-radius: 3px;
99
+ }
100
+
101
+ ::-webkit-scrollbar-thumb:hover {
102
+ background: #475569;
103
+ }
@@ -0,0 +1,176 @@
1
+ import React, { useState, useCallback, useRef, useEffect } from 'react';
2
+ import type { TreeNode } from '@btree/types';
3
+ import type { NodeEvent } from '@btree/events';
4
+ import { NodeStatus } from '@btree/types';
5
+ import { Header } from './components/Header';
6
+ import { ExampleSelector } from './components/ExampleSelector';
7
+ import { Toolbar } from './components/Toolbar';
8
+ import { YamlEditor } from './components/YamlEditor';
9
+ import { TreeVisualizer } from './components/TreeVisualizer';
10
+ import { ExecutionLog } from './components/ExecutionLog';
11
+ import { BlackboardInspector } from './components/BlackboardInspector';
12
+ import { examples } from './lib/yaml-examples';
13
+ import { parseYamlToTree, executeTree } from './lib/execution-engine';
14
+ import './App.css';
15
+
16
+ export default function App() {
17
+ const [selectedExampleId, setSelectedExampleId] = useState(examples[0].id);
18
+ const [yamlValue, setYamlValue] = useState(examples[0].yaml);
19
+ const [tree, setTree] = useState<TreeNode | null>(null);
20
+ const [nodeCount, setNodeCount] = useState(0);
21
+ const [parseError, setParseError] = useState<string | null>(null);
22
+ const [isRunning, setIsRunning] = useState(false);
23
+ const [status, setStatus] = useState<string>('IDLE');
24
+ const [events, setEvents] = useState<NodeEvent<unknown>[]>([]);
25
+ const [nodeStates, setNodeStates] = useState<Map<string, string>>(new Map());
26
+ const [blackboard, setBlackboard] = useState<Record<string, unknown>>({});
27
+ const [executable, setExecutable] = useState(true);
28
+ const debounceRef = useRef<ReturnType<typeof setTimeout>>();
29
+
30
+ // Parse YAML on initial load
31
+ useEffect(() => {
32
+ const result = parseYamlToTree(examples[0].yaml);
33
+ if (result.tree) {
34
+ setTree(result.tree);
35
+ setNodeCount(result.nodeCount);
36
+ }
37
+ setExecutable(examples[0].executable);
38
+ }, []);
39
+
40
+ const handleYamlChange = useCallback((newYaml: string) => {
41
+ setYamlValue(newYaml);
42
+
43
+ // Debounce parsing
44
+ if (debounceRef.current) clearTimeout(debounceRef.current);
45
+ debounceRef.current = setTimeout(() => {
46
+ const result = parseYamlToTree(newYaml);
47
+ if (result.tree) {
48
+ setTree(result.tree);
49
+ setNodeCount(result.nodeCount);
50
+ setParseError(null);
51
+ } else {
52
+ setParseError(result.error);
53
+ }
54
+ }, 300);
55
+ }, []);
56
+
57
+ const handleExampleSelect = useCallback((example: typeof examples[0]) => {
58
+ setSelectedExampleId(example.id);
59
+ setYamlValue(example.yaml);
60
+ setExecutable(example.executable);
61
+
62
+ const result = parseYamlToTree(example.yaml);
63
+ if (result.tree) {
64
+ setTree(result.tree);
65
+ setNodeCount(result.nodeCount);
66
+ setParseError(null);
67
+ } else {
68
+ setParseError(result.error);
69
+ }
70
+
71
+ // Reset execution state
72
+ setStatus('IDLE');
73
+ setEvents([]);
74
+ setNodeStates(new Map());
75
+ setBlackboard({});
76
+ }, []);
77
+
78
+ const handleRun = useCallback(async () => {
79
+ if (!tree || isRunning) return;
80
+
81
+ // Re-parse to get a fresh tree (nodes may retain state from previous runs)
82
+ const result = parseYamlToTree(yamlValue);
83
+ if (!result.tree) return;
84
+
85
+ setIsRunning(true);
86
+ setStatus('RUNNING');
87
+ setEvents([]);
88
+ setNodeStates(new Map());
89
+ setBlackboard({});
90
+
91
+ const execResult = await executeTree(result.tree, (event) => {
92
+ setEvents((prev) => [...prev, event]);
93
+
94
+ // Update node states in real-time
95
+ const data = event.data as Record<string, unknown> | undefined;
96
+ if (event.type === 'tick_start') {
97
+ setNodeStates((prev) => {
98
+ const next = new Map(prev);
99
+ next.set(event.nodeId, 'running');
100
+ return next;
101
+ });
102
+ } else if (event.type === 'tick_end' && data?.status) {
103
+ const s = (data.status as string).toLowerCase();
104
+ setNodeStates((prev) => {
105
+ const next = new Map(prev);
106
+ next.set(event.nodeId, s);
107
+ return next;
108
+ });
109
+ } else if (event.type === 'error') {
110
+ setNodeStates((prev) => {
111
+ const next = new Map(prev);
112
+ next.set(event.nodeId, 'failure');
113
+ return next;
114
+ });
115
+ }
116
+ });
117
+
118
+ setStatus(execResult.status);
119
+ setBlackboard(execResult.blackboard);
120
+ setIsRunning(false);
121
+ }, [tree, isRunning, yamlValue]);
122
+
123
+ const handleReset = useCallback(() => {
124
+ setStatus('IDLE');
125
+ setEvents([]);
126
+ setNodeStates(new Map());
127
+ setBlackboard({});
128
+
129
+ // Re-parse the current YAML
130
+ const result = parseYamlToTree(yamlValue);
131
+ if (result.tree) {
132
+ setTree(result.tree);
133
+ setNodeCount(result.nodeCount);
134
+ }
135
+ }, [yamlValue]);
136
+
137
+ return (
138
+ <div className="app">
139
+ <Header />
140
+ <ExampleSelector
141
+ examples={examples}
142
+ selectedId={selectedExampleId}
143
+ onSelect={handleExampleSelect}
144
+ />
145
+ <Toolbar
146
+ status={status}
147
+ isRunning={isRunning}
148
+ executable={executable}
149
+ nodeCount={nodeCount}
150
+ onRun={handleRun}
151
+ onReset={handleReset}
152
+ />
153
+ {parseError && (
154
+ <div className="app__error">
155
+ <span>Parse Error:</span> {parseError}
156
+ </div>
157
+ )}
158
+ <div className="app__main">
159
+ <div className="app__editor">
160
+ <YamlEditor value={yamlValue} onChange={handleYamlChange} />
161
+ </div>
162
+ <div className="app__tree">
163
+ <TreeVisualizer tree={tree} nodeStates={nodeStates} />
164
+ </div>
165
+ </div>
166
+ <div className="app__bottom">
167
+ <div className="app__log">
168
+ <ExecutionLog events={events} />
169
+ </div>
170
+ <div className="app__blackboard">
171
+ <BlackboardInspector data={blackboard} />
172
+ </div>
173
+ </div>
174
+ </div>
175
+ );
176
+ }
@@ -0,0 +1,89 @@
1
+ .blackboard-inspector {
2
+ display: flex;
3
+ flex-direction: column;
4
+ height: 100%;
5
+ overflow: hidden;
6
+ }
7
+
8
+ .blackboard-inspector__header {
9
+ display: flex;
10
+ align-items: center;
11
+ justify-content: space-between;
12
+ padding: 8px 12px;
13
+ font-size: 12px;
14
+ font-weight: 600;
15
+ color: var(--color-text-muted);
16
+ border-bottom: 1px solid var(--color-border);
17
+ background: var(--color-surface-1);
18
+ flex-shrink: 0;
19
+ }
20
+
21
+ .blackboard-inspector__count {
22
+ font-weight: 400;
23
+ font-family: 'JetBrains Mono', monospace;
24
+ font-size: 11px;
25
+ }
26
+
27
+ .blackboard-inspector__body {
28
+ flex: 1;
29
+ overflow-y: auto;
30
+ }
31
+
32
+ .blackboard-inspector__empty {
33
+ padding: 20px;
34
+ text-align: center;
35
+ color: var(--color-text-muted);
36
+ font-size: 12px;
37
+ }
38
+
39
+ .blackboard-inspector__table {
40
+ width: 100%;
41
+ border-collapse: collapse;
42
+ font-size: 12px;
43
+ }
44
+
45
+ .blackboard-inspector__table th {
46
+ text-align: left;
47
+ padding: 6px 12px;
48
+ font-size: 10px;
49
+ font-weight: 600;
50
+ text-transform: uppercase;
51
+ letter-spacing: 0.05em;
52
+ color: var(--color-text-muted);
53
+ background: var(--color-surface-1);
54
+ border-bottom: 1px solid var(--color-border);
55
+ position: sticky;
56
+ top: 0;
57
+ }
58
+
59
+ .blackboard-inspector__table td {
60
+ padding: 4px 12px;
61
+ border-bottom: 1px solid var(--color-border);
62
+ vertical-align: top;
63
+ }
64
+
65
+ .bb-key {
66
+ font-family: 'JetBrains Mono', monospace;
67
+ font-size: 11px;
68
+ color: #818cf8;
69
+ white-space: nowrap;
70
+ width: 1%;
71
+ }
72
+
73
+ .bb-value {
74
+ font-family: 'JetBrains Mono', monospace;
75
+ font-size: 11px;
76
+ color: var(--color-text);
77
+ }
78
+
79
+ .bb-value pre {
80
+ margin: 0;
81
+ white-space: pre-wrap;
82
+ word-break: break-all;
83
+ }
84
+
85
+ .bb-value--multi pre {
86
+ font-size: 10px;
87
+ max-height: 120px;
88
+ overflow-y: auto;
89
+ }
@@ -0,0 +1,64 @@
1
+ import React from 'react';
2
+ import './BlackboardInspector.css';
3
+
4
+ interface BlackboardInspectorProps {
5
+ data: Record<string, unknown>;
6
+ }
7
+
8
+ function formatValue(value: unknown): string {
9
+ if (value === null) return 'null';
10
+ if (value === undefined) return 'undefined';
11
+ if (typeof value === 'string') return `"${value}"`;
12
+ if (typeof value === 'object') {
13
+ try {
14
+ return JSON.stringify(value, null, 2);
15
+ } catch {
16
+ return String(value);
17
+ }
18
+ }
19
+ return String(value);
20
+ }
21
+
22
+ function isMultiline(value: unknown): boolean {
23
+ const str = formatValue(value);
24
+ return str.length > 60 || str.includes('\n');
25
+ }
26
+
27
+ export const BlackboardInspector: React.FC<BlackboardInspectorProps> = ({ data }) => {
28
+ const entries = Object.entries(data);
29
+
30
+ return (
31
+ <div className="blackboard-inspector">
32
+ <div className="blackboard-inspector__header">
33
+ Blackboard
34
+ <span className="blackboard-inspector__count">{entries.length} keys</span>
35
+ </div>
36
+ <div className="blackboard-inspector__body">
37
+ {entries.length === 0 ? (
38
+ <div className="blackboard-inspector__empty">
39
+ Blackboard is empty. Run a workflow to populate it.
40
+ </div>
41
+ ) : (
42
+ <table className="blackboard-inspector__table">
43
+ <thead>
44
+ <tr>
45
+ <th>Key</th>
46
+ <th>Value</th>
47
+ </tr>
48
+ </thead>
49
+ <tbody>
50
+ {entries.map(([key, value]) => (
51
+ <tr key={key}>
52
+ <td className="bb-key">{key}</td>
53
+ <td className={`bb-value ${isMultiline(value) ? 'bb-value--multi' : ''}`}>
54
+ <pre>{formatValue(value)}</pre>
55
+ </td>
56
+ </tr>
57
+ ))}
58
+ </tbody>
59
+ </table>
60
+ )}
61
+ </div>
62
+ </div>
63
+ );
64
+ };
@@ -0,0 +1,64 @@
1
+ .example-selector {
2
+ display: flex;
3
+ align-items: center;
4
+ gap: 10px;
5
+ padding: 8px 20px;
6
+ background: var(--color-surface-1);
7
+ border-bottom: 1px solid var(--color-border);
8
+ overflow-x: auto;
9
+ }
10
+
11
+ .example-selector__label {
12
+ font-size: 12px;
13
+ font-weight: 500;
14
+ color: var(--color-text-muted);
15
+ flex-shrink: 0;
16
+ }
17
+
18
+ .example-selector__list {
19
+ display: flex;
20
+ gap: 6px;
21
+ flex-wrap: nowrap;
22
+ }
23
+
24
+ .example-selector__btn {
25
+ font-family: 'Inter', sans-serif;
26
+ font-size: 12px;
27
+ font-weight: 500;
28
+ padding: 4px 12px;
29
+ border: 1px solid var(--color-border);
30
+ border-radius: 6px;
31
+ background: var(--color-surface-2);
32
+ color: var(--color-text-muted);
33
+ cursor: pointer;
34
+ transition: all 0.15s;
35
+ white-space: nowrap;
36
+ display: flex;
37
+ align-items: center;
38
+ gap: 5px;
39
+ }
40
+
41
+ .example-selector__btn:hover {
42
+ border-color: var(--color-accent);
43
+ color: var(--color-text);
44
+ }
45
+
46
+ .example-selector__btn--active {
47
+ background: rgba(129, 140, 248, 0.15);
48
+ border-color: var(--color-accent);
49
+ color: var(--color-accent);
50
+ }
51
+
52
+ .example-selector__btn--viewonly {
53
+ opacity: 0.75;
54
+ }
55
+
56
+ .example-selector__tag {
57
+ font-size: 9px;
58
+ padding: 1px 4px;
59
+ border-radius: 3px;
60
+ background: rgba(251, 191, 36, 0.2);
61
+ color: #fbbf24;
62
+ text-transform: uppercase;
63
+ letter-spacing: 0.05em;
64
+ }
@@ -0,0 +1,34 @@
1
+ import React from 'react';
2
+ import type { YamlExample } from '../lib/yaml-examples';
3
+ import './ExampleSelector.css';
4
+
5
+ interface ExampleSelectorProps {
6
+ examples: YamlExample[];
7
+ selectedId: string;
8
+ onSelect: (example: YamlExample) => void;
9
+ }
10
+
11
+ export const ExampleSelector: React.FC<ExampleSelectorProps> = ({
12
+ examples,
13
+ selectedId,
14
+ onSelect,
15
+ }) => {
16
+ return (
17
+ <div className="example-selector">
18
+ <span className="example-selector__label">Examples:</span>
19
+ <div className="example-selector__list">
20
+ {examples.map((ex) => (
21
+ <button
22
+ key={ex.id}
23
+ className={`example-selector__btn ${selectedId === ex.id ? 'example-selector__btn--active' : ''} ${!ex.executable ? 'example-selector__btn--viewonly' : ''}`}
24
+ onClick={() => onSelect(ex)}
25
+ title={ex.description + (ex.executable ? '' : ' (view only)')}
26
+ >
27
+ {ex.name}
28
+ {!ex.executable && <span className="example-selector__tag">view</span>}
29
+ </button>
30
+ ))}
31
+ </div>
32
+ </div>
33
+ );
34
+ };
@@ -0,0 +1,107 @@
1
+ .execution-log {
2
+ display: flex;
3
+ flex-direction: column;
4
+ height: 100%;
5
+ overflow: hidden;
6
+ }
7
+
8
+ .execution-log__header {
9
+ display: flex;
10
+ align-items: center;
11
+ justify-content: space-between;
12
+ padding: 8px 12px;
13
+ font-size: 12px;
14
+ font-weight: 600;
15
+ color: var(--color-text-muted);
16
+ border-bottom: 1px solid var(--color-border);
17
+ background: var(--color-surface-1);
18
+ flex-shrink: 0;
19
+ }
20
+
21
+ .execution-log__count {
22
+ font-weight: 400;
23
+ font-family: 'JetBrains Mono', monospace;
24
+ font-size: 11px;
25
+ }
26
+
27
+ .execution-log__body {
28
+ flex: 1;
29
+ overflow-y: auto;
30
+ padding: 4px 0;
31
+ font-family: 'JetBrains Mono', monospace;
32
+ font-size: 11px;
33
+ }
34
+
35
+ .execution-log__empty {
36
+ padding: 20px;
37
+ text-align: center;
38
+ color: var(--color-text-muted);
39
+ font-family: 'Inter', sans-serif;
40
+ font-size: 12px;
41
+ }
42
+
43
+ .log-entry {
44
+ display: flex;
45
+ align-items: baseline;
46
+ gap: 8px;
47
+ padding: 2px 12px;
48
+ color: var(--color-text-muted);
49
+ line-height: 1.6;
50
+ }
51
+
52
+ .log-entry:hover {
53
+ background: var(--color-surface-2);
54
+ }
55
+
56
+ .log-entry__time {
57
+ flex-shrink: 0;
58
+ width: 60px;
59
+ color: #475569;
60
+ text-align: right;
61
+ }
62
+
63
+ .log-entry__icon {
64
+ flex-shrink: 0;
65
+ width: 24px;
66
+ text-align: center;
67
+ font-weight: 600;
68
+ }
69
+
70
+ .log-entry__type {
71
+ flex-shrink: 0;
72
+ width: 120px;
73
+ color: #818cf8;
74
+ overflow: hidden;
75
+ text-overflow: ellipsis;
76
+ white-space: nowrap;
77
+ }
78
+
79
+ .log-entry__name {
80
+ flex: 1;
81
+ overflow: hidden;
82
+ text-overflow: ellipsis;
83
+ white-space: nowrap;
84
+ color: var(--color-text);
85
+ }
86
+
87
+ .log-entry__status {
88
+ flex-shrink: 0;
89
+ font-weight: 500;
90
+ }
91
+
92
+ .log-entry__error {
93
+ color: #f87171;
94
+ flex: 1;
95
+ overflow: hidden;
96
+ text-overflow: ellipsis;
97
+ white-space: nowrap;
98
+ }
99
+
100
+ .log-entry--success .log-entry__icon { color: #34d399; }
101
+ .log-entry--failure .log-entry__icon { color: #f87171; }
102
+ .log-entry--running .log-entry__icon { color: #fbbf24; }
103
+ .log-entry--error .log-entry__icon { color: #f87171; }
104
+
105
+ .log-entry--success .log-entry__status { color: #34d399; }
106
+ .log-entry--failure .log-entry__status { color: #f87171; }
107
+ .log-entry--running .log-entry__status { color: #fbbf24; }