rax-flow 0.1.8 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (224) hide show
  1. package/dist/bin.js +8 -6
  2. package/dist/bin.js.map +1 -1
  3. package/dist/hub/commands/index.d.ts.map +1 -1
  4. package/dist/hub/commands/index.js +7 -4
  5. package/dist/hub/commands/index.js.map +1 -1
  6. package/dist/hub/event-listener.d.ts +6 -0
  7. package/dist/hub/event-listener.d.ts.map +1 -1
  8. package/dist/hub/event-listener.js +10 -6
  9. package/dist/hub/event-listener.js.map +1 -1
  10. package/dist/hub/index.d.ts.map +1 -1
  11. package/dist/hub/index.js +12 -0
  12. package/dist/hub/index.js.map +1 -1
  13. package/dist/setup/components/ApiKeyInput.d.ts +25 -0
  14. package/dist/setup/components/ApiKeyInput.d.ts.map +1 -0
  15. package/dist/setup/components/ApiKeyInput.js +54 -0
  16. package/dist/setup/components/ApiKeyInput.js.map +1 -0
  17. package/dist/setup/components/AsciiBanner.d.ts +22 -0
  18. package/dist/setup/components/AsciiBanner.d.ts.map +1 -0
  19. package/dist/setup/components/AsciiBanner.js +55 -0
  20. package/dist/setup/components/AsciiBanner.js.map +1 -0
  21. package/dist/setup/components/CliDetector.d.ts +17 -0
  22. package/dist/setup/components/CliDetector.d.ts.map +1 -0
  23. package/dist/setup/components/CliDetector.js +79 -0
  24. package/dist/setup/components/CliDetector.js.map +1 -0
  25. package/dist/setup/components/ModeSelector.d.ts +8 -0
  26. package/dist/setup/components/ModeSelector.d.ts.map +1 -0
  27. package/dist/setup/components/ModeSelector.js +76 -0
  28. package/dist/setup/components/ModeSelector.js.map +1 -0
  29. package/dist/setup/components/ProviderSelector.d.ts +18 -0
  30. package/dist/setup/components/ProviderSelector.d.ts.map +1 -0
  31. package/dist/setup/components/ProviderSelector.js +97 -0
  32. package/dist/setup/components/ProviderSelector.js.map +1 -0
  33. package/dist/setup/components/SetupWizard.d.ts +2 -0
  34. package/dist/setup/components/SetupWizard.d.ts.map +1 -0
  35. package/dist/setup/components/SetupWizard.js +212 -0
  36. package/dist/setup/components/SetupWizard.js.map +1 -0
  37. package/dist/setup/components/StepIndicator.d.ts +13 -0
  38. package/dist/setup/components/StepIndicator.d.ts.map +1 -0
  39. package/dist/setup/components/StepIndicator.js +18 -0
  40. package/dist/setup/components/StepIndicator.js.map +1 -0
  41. package/dist/setup/components/SuccessScreen.d.ts +18 -0
  42. package/dist/setup/components/SuccessScreen.d.ts.map +1 -0
  43. package/dist/setup/components/SuccessScreen.js +38 -0
  44. package/dist/setup/components/SuccessScreen.js.map +1 -0
  45. package/dist/setup/index.d.ts +12 -0
  46. package/dist/setup/index.d.ts.map +1 -0
  47. package/dist/setup/index.js +29 -0
  48. package/dist/setup/index.js.map +1 -0
  49. package/dist/setup/utils/cli-detection.d.ts +12 -0
  50. package/dist/setup/utils/cli-detection.d.ts.map +1 -0
  51. package/dist/setup/utils/cli-detection.js +83 -0
  52. package/dist/setup/utils/cli-detection.js.map +1 -0
  53. package/dist/setup/utils/config-writer.d.ts +43 -0
  54. package/dist/setup/utils/config-writer.d.ts.map +1 -0
  55. package/dist/setup/utils/config-writer.js +180 -0
  56. package/dist/setup/utils/config-writer.js.map +1 -0
  57. package/dist/tui/App.d.ts +2 -1
  58. package/dist/tui/App.d.ts.map +1 -1
  59. package/dist/tui/App.js +88 -20
  60. package/dist/tui/App.js.map +1 -1
  61. package/dist/tui/components/AgentStateIcon.d.ts +18 -0
  62. package/dist/tui/components/AgentStateIcon.d.ts.map +1 -0
  63. package/dist/tui/components/AgentStateIcon.js +57 -0
  64. package/dist/tui/components/AgentStateIcon.js.map +1 -0
  65. package/dist/tui/components/AnimatedBranch.d.ts +39 -0
  66. package/dist/tui/components/AnimatedBranch.d.ts.map +1 -0
  67. package/dist/tui/components/AnimatedBranch.js +64 -0
  68. package/dist/tui/components/AnimatedBranch.js.map +1 -0
  69. package/dist/tui/components/ChatPanel.d.ts +2 -1
  70. package/dist/tui/components/ChatPanel.d.ts.map +1 -1
  71. package/dist/tui/components/ChatPanel.js +47 -28
  72. package/dist/tui/components/ChatPanel.js.map +1 -1
  73. package/dist/tui/components/DAGPanel.d.ts +14 -2
  74. package/dist/tui/components/DAGPanel.d.ts.map +1 -1
  75. package/dist/tui/components/DAGPanel.js +48 -27
  76. package/dist/tui/components/DAGPanel.js.map +1 -1
  77. package/dist/tui/components/ExecutionTimeline.d.ts +34 -0
  78. package/dist/tui/components/ExecutionTimeline.d.ts.map +1 -0
  79. package/dist/tui/components/ExecutionTimeline.js +67 -0
  80. package/dist/tui/components/ExecutionTimeline.js.map +1 -0
  81. package/dist/tui/components/Header.d.ts +2 -1
  82. package/dist/tui/components/Header.d.ts.map +1 -1
  83. package/dist/tui/components/Header.js +26 -19
  84. package/dist/tui/components/Header.js.map +1 -1
  85. package/dist/tui/components/HelpOverlay.d.ts +2 -1
  86. package/dist/tui/components/HelpOverlay.d.ts.map +1 -1
  87. package/dist/tui/components/HelpOverlay.js +59 -41
  88. package/dist/tui/components/HelpOverlay.js.map +1 -1
  89. package/dist/tui/components/InputBar.d.ts +3 -1
  90. package/dist/tui/components/InputBar.d.ts.map +1 -1
  91. package/dist/tui/components/InputBar.js +12 -7
  92. package/dist/tui/components/InputBar.js.map +1 -1
  93. package/dist/tui/components/LogsPanel.d.ts +9 -0
  94. package/dist/tui/components/LogsPanel.d.ts.map +1 -0
  95. package/dist/tui/components/LogsPanel.js +56 -0
  96. package/dist/tui/components/LogsPanel.js.map +1 -0
  97. package/dist/tui/components/MemoryPanel.d.ts +21 -0
  98. package/dist/tui/components/MemoryPanel.d.ts.map +1 -0
  99. package/dist/tui/components/MemoryPanel.js +51 -0
  100. package/dist/tui/components/MemoryPanel.js.map +1 -0
  101. package/dist/tui/components/MetricsPanel.d.ts +18 -0
  102. package/dist/tui/components/MetricsPanel.d.ts.map +1 -0
  103. package/dist/tui/components/MetricsPanel.js +27 -0
  104. package/dist/tui/components/MetricsPanel.js.map +1 -0
  105. package/dist/tui/components/StatusPanel.d.ts +3 -1
  106. package/dist/tui/components/StatusPanel.d.ts.map +1 -1
  107. package/dist/tui/components/StatusPanel.js +20 -18
  108. package/dist/tui/components/StatusPanel.js.map +1 -1
  109. package/dist/tui/components/TaskTree.d.ts +28 -0
  110. package/dist/tui/components/TaskTree.d.ts.map +1 -0
  111. package/dist/tui/components/TaskTree.js +29 -0
  112. package/dist/tui/components/TaskTree.js.map +1 -0
  113. package/dist/tui/components/animations/ProgressBar.d.ts +39 -0
  114. package/dist/tui/components/animations/ProgressBar.d.ts.map +1 -0
  115. package/dist/tui/components/animations/ProgressBar.js +39 -0
  116. package/dist/tui/components/animations/ProgressBar.js.map +1 -0
  117. package/dist/tui/components/animations/Pulse.d.ts +17 -0
  118. package/dist/tui/components/animations/Pulse.d.ts.map +1 -0
  119. package/dist/tui/components/animations/Pulse.js +47 -0
  120. package/dist/tui/components/animations/Pulse.js.map +1 -0
  121. package/dist/tui/components/animations/Spinner.d.ts +13 -0
  122. package/dist/tui/components/animations/Spinner.d.ts.map +1 -0
  123. package/dist/tui/components/animations/Spinner.js +36 -0
  124. package/dist/tui/components/animations/Spinner.js.map +1 -0
  125. package/dist/tui/components/animations/StatusAnimator.d.ts +27 -0
  126. package/dist/tui/components/animations/StatusAnimator.d.ts.map +1 -0
  127. package/dist/tui/components/animations/StatusAnimator.js +85 -0
  128. package/dist/tui/components/animations/StatusAnimator.js.map +1 -0
  129. package/dist/tui/components/animations/TypingEffect.d.ts +26 -0
  130. package/dist/tui/components/animations/TypingEffect.d.ts.map +1 -0
  131. package/dist/tui/components/animations/TypingEffect.js +59 -0
  132. package/dist/tui/components/animations/TypingEffect.js.map +1 -0
  133. package/dist/tui/components/animations/index.d.ts +8 -0
  134. package/dist/tui/components/animations/index.d.ts.map +1 -0
  135. package/dist/tui/components/animations/index.js +6 -0
  136. package/dist/tui/components/animations/index.js.map +1 -0
  137. package/dist/tui/hooks/useAnimation.d.ts +42 -0
  138. package/dist/tui/hooks/useAnimation.d.ts.map +1 -0
  139. package/dist/tui/hooks/useAnimation.js +212 -0
  140. package/dist/tui/hooks/useAnimation.js.map +1 -0
  141. package/dist/tui/hooks/useAppState.d.ts +10 -0
  142. package/dist/tui/hooks/useAppState.d.ts.map +1 -1
  143. package/dist/tui/hooks/useAppState.js +178 -75
  144. package/dist/tui/hooks/useAppState.js.map +1 -1
  145. package/dist/tui/services/orchestrator.d.ts +16 -0
  146. package/dist/tui/services/orchestrator.d.ts.map +1 -0
  147. package/dist/tui/services/orchestrator.js +152 -0
  148. package/dist/tui/services/orchestrator.js.map +1 -0
  149. package/dist/tui/styles/borders.d.ts +31 -0
  150. package/dist/tui/styles/borders.d.ts.map +1 -0
  151. package/dist/tui/styles/borders.js +47 -0
  152. package/dist/tui/styles/borders.js.map +1 -0
  153. package/dist/tui/styles/colors.d.ts +18 -0
  154. package/dist/tui/styles/colors.d.ts.map +1 -0
  155. package/dist/tui/styles/colors.js +18 -0
  156. package/dist/tui/styles/colors.js.map +1 -0
  157. package/dist/tui/styles/index.d.ts +6 -0
  158. package/dist/tui/styles/index.d.ts.map +1 -0
  159. package/dist/tui/styles/index.js +6 -0
  160. package/dist/tui/styles/index.js.map +1 -0
  161. package/dist/tui/styles/indicators.d.ts +67 -0
  162. package/dist/tui/styles/indicators.d.ts.map +1 -0
  163. package/dist/tui/styles/indicators.js +42 -0
  164. package/dist/tui/styles/indicators.js.map +1 -0
  165. package/dist/tui/styles/layout.d.ts +21 -0
  166. package/dist/tui/styles/layout.d.ts.map +1 -0
  167. package/dist/tui/styles/layout.js +42 -0
  168. package/dist/tui/styles/layout.js.map +1 -0
  169. package/dist/tui/styles/providers.d.ts +77 -0
  170. package/dist/tui/styles/providers.d.ts.map +1 -0
  171. package/dist/tui/styles/providers.js +31 -0
  172. package/dist/tui/styles/providers.js.map +1 -0
  173. package/dist/tui/utils/animation.d.ts +44 -0
  174. package/dist/tui/utils/animation.d.ts.map +1 -0
  175. package/dist/tui/utils/animation.js +107 -0
  176. package/dist/tui/utils/animation.js.map +1 -0
  177. package/package.json +8 -8
  178. package/src/bin.ts +8 -5
  179. package/src/hub/commands/index.ts +7 -4
  180. package/src/hub/event-listener.ts +14 -10
  181. package/src/hub/index.ts +14 -2
  182. package/src/setup/components/ApiKeyInput.tsx +158 -0
  183. package/src/setup/components/AsciiBanner.tsx +125 -0
  184. package/src/setup/components/CliDetector.tsx +230 -0
  185. package/src/setup/components/ModeSelector.tsx +137 -0
  186. package/src/setup/components/ProviderSelector.tsx +174 -0
  187. package/src/setup/components/SetupWizard.tsx +368 -0
  188. package/src/setup/components/StepIndicator.tsx +74 -0
  189. package/src/setup/components/SuccessScreen.tsx +229 -0
  190. package/src/setup/index.ts +34 -0
  191. package/src/setup/utils/cli-detection.ts +99 -0
  192. package/src/setup/utils/config-writer.ts +249 -0
  193. package/src/tui/App.tsx +117 -53
  194. package/src/tui/components/AgentStateIcon.tsx +84 -0
  195. package/src/tui/components/AnimatedBranch.tsx +134 -0
  196. package/src/tui/components/ChatPanel.tsx +85 -43
  197. package/src/tui/components/DAGPanel.tsx +150 -58
  198. package/src/tui/components/ExecutionTimeline.tsx +225 -0
  199. package/src/tui/components/Header.tsx +76 -43
  200. package/src/tui/components/HelpOverlay.tsx +118 -61
  201. package/src/tui/components/InputBar.tsx +33 -19
  202. package/src/tui/components/LogsPanel.tsx +129 -0
  203. package/src/tui/components/MemoryPanel.tsx +163 -0
  204. package/src/tui/components/MetricsPanel.tsx +149 -0
  205. package/src/tui/components/StatusPanel.tsx +84 -37
  206. package/src/tui/components/TaskTree.tsx +159 -0
  207. package/src/tui/components/animations/ProgressBar.tsx +160 -0
  208. package/src/tui/components/animations/Pulse.tsx +73 -0
  209. package/src/tui/components/animations/Spinner.tsx +54 -0
  210. package/src/tui/components/animations/StatusAnimator.tsx +153 -0
  211. package/src/tui/components/animations/TypingEffect.tsx +119 -0
  212. package/src/tui/components/animations/index.ts +16 -0
  213. package/src/tui/hooks/useAnimation.ts +290 -0
  214. package/src/tui/hooks/useAppState.ts +206 -67
  215. package/src/tui/services/orchestrator.ts +195 -0
  216. package/src/tui/styles/borders.ts +51 -0
  217. package/src/tui/styles/colors.ts +19 -0
  218. package/src/tui/styles/index.ts +20 -0
  219. package/src/tui/styles/indicators.ts +54 -0
  220. package/src/tui/styles/layout.ts +44 -0
  221. package/src/tui/styles/providers.ts +32 -0
  222. package/src/tui/utils/animation.ts +124 -0
  223. package/LICENSE +0 -21
  224. package/tsconfig.tsbuildinfo +0 -1
@@ -1,17 +1,31 @@
1
- import React from "react";
1
+ import React, { useMemo, memo } from "react";
2
2
  import { Box, Text } from "ink";
3
+ import { tuiColors, getStatusIndicator, getSpinnerFrame, treeChars } from "../styles/index.js";
4
+ import { AnimatedProgressBar } from "./animations/index.js";
5
+ import { AgentStateIcon, AgentState } from "./AgentStateIcon.js";
6
+
7
+ interface DAGSubtask {
8
+ id: string;
9
+ name: string;
10
+ state: AgentState;
11
+ }
3
12
 
4
13
  interface DAGNode {
5
14
  id: string;
6
15
  name: string;
7
16
  status: "pending" | "running" | "done" | "error";
8
17
  agent?: string;
18
+ state?: AgentState;
19
+ executionTime?: number;
20
+ detail?: string;
21
+ subtasks?: DAGSubtask[];
9
22
  }
10
23
 
11
24
  interface DAGLevel {
12
25
  name: string;
13
26
  progress: number;
14
27
  nodes: DAGNode[];
28
+ status?: 'done' | 'running' | 'pending';
15
29
  }
16
30
 
17
31
  interface WorkflowState {
@@ -20,97 +34,175 @@ interface WorkflowState {
20
34
  totalProgress: number;
21
35
  }
22
36
 
23
- interface DAGPanelProps {
24
- workflowState: WorkflowState;
37
+ interface DAGNodeItemProps {
38
+ node: DAGNode;
25
39
  tick: number;
26
- isActive: boolean;
40
+ isLast: boolean;
27
41
  }
28
42
 
29
- const statusIcons: Record<string, { icon: string; color: string }> = {
30
- pending: { icon: "", color: "gray" },
31
- running: { icon: "", color: "yellow" },
32
- done: { icon: "", color: "green" },
33
- error: { icon: "", color: "red" },
43
+ const STATE_LABELS: Record<AgentState, string> = {
44
+ idle: "pending",
45
+ thinking: "thinking...",
46
+ processing: "processing",
47
+ running: "running",
48
+ success: "done",
49
+ error: "error",
50
+ waiting: "waiting",
34
51
  };
35
52
 
36
- const spinnerFrames = ["◐", "◓", "◑", "◒"];
53
+ const DAGNodeItem = memo(function DAGNodeItem({ node, tick, isLast }: DAGNodeItemProps) {
54
+ const statusInfo = useMemo(() => getStatusIndicator(node.status), [node.status]);
55
+ const agentState = useMemo((): AgentState => {
56
+ if (node.state) return node.state;
57
+ if (node.status === 'running') return 'running';
58
+ if (node.status === 'done') return 'success';
59
+ if (node.status === 'error') return 'error';
60
+ return 'idle';
61
+ }, [node.state, node.status]);
62
+
63
+ const icon = node.status === "running"
64
+ ? getSpinnerFrame(tick, "dots")
65
+ : statusInfo.icon;
66
+ const prefix = isLast ? treeChars.lastBranch : treeChars.branch;
67
+ const timeStr = node.executionTime ? `${node.executionTime.toFixed(1)}s` : "";
37
68
 
38
- function ProgressBar({ progress, width = 20 }: { progress: number; width?: number }) {
39
- const filled = Math.round((progress / 100) * width);
40
- const empty = width - filled;
41
- const bar = "█".repeat(filled) + "░".repeat(empty);
42
69
  return (
43
- <Text color={progress >= 100 ? "green" : progress > 0 ? "yellow" : "gray"}>
44
- {bar}
45
- </Text>
46
- );
47
- }
48
-
49
- function DAGNodeItem({ node, tick }: { node: DAGNode; tick: number }) {
50
- const { icon, color } = statusIcons[node.status];
51
- const animatedIcon = node.status === "running"
52
- ? spinnerFrames[tick % spinnerFrames.length]
53
- : icon;
54
-
55
- return (
56
- <Box flexDirection="row" marginLeft={2}>
57
- <Text color={color}>{animatedIcon}</Text>
58
- <Text color="white"> {node.name}</Text>
59
- {node.agent && <Text color="gray"> [{node.agent}]</Text>}
70
+ <Box flexDirection="column">
71
+ <Box flexDirection="row">
72
+ <Text color={tuiColors.textTertiary}>{prefix} </Text>
73
+ <Box flexDirection="row">
74
+ <Text color={tuiColors.textSecondary}>[</Text>
75
+ <AgentStateIcon state={agentState} tick={tick} />
76
+ <Text color={tuiColors.textSecondary}>]</Text>
77
+ </Box>
78
+ <Text color={tuiColors.textPrimary}> {node.name.padEnd(18)}</Text>
79
+ {timeStr && (
80
+ <Text color={tuiColors.textTertiary}> ● {timeStr}</Text>
81
+ )}
82
+ </Box>
83
+ {node.detail && (
84
+ <Box flexDirection="row">
85
+ <Text color={tuiColors.textTertiary}> └── {node.detail}</Text>
86
+ </Box>
87
+ )}
88
+ {node.subtasks && node.subtasks.length > 0 && (
89
+ <Box flexDirection="column">
90
+ {node.subtasks.map((subtask, idx) => (
91
+ <Box key={subtask.id} flexDirection="row">
92
+ <Text color={tuiColors.textTertiary}> {idx === node.subtasks!.length - 1 ? '└──' : '├──'} </Text>
93
+ <AgentStateIcon state={subtask.state} tick={tick} />
94
+ <Text color={tuiColors.textTertiary}> {subtask.name}</Text>
95
+ </Box>
96
+ ))}
97
+ </Box>
98
+ )}
60
99
  </Box>
61
100
  );
101
+ });
102
+
103
+ interface DAGLevelViewProps {
104
+ level: DAGLevel;
105
+ tick: number;
106
+ isActive: boolean;
62
107
  }
63
108
 
64
- function DAGLevelView({ level, tick, isActive }: { level: DAGLevel; tick: number; isActive: boolean }) {
109
+ const DAGLevelView = memo(function DAGLevelView({ level, tick, isActive }: DAGLevelViewProps) {
110
+ const levelColor = isActive ? tuiColors.primary : tuiColors.textSecondary;
111
+ const statusColor = level.status === 'done'
112
+ ? tuiColors.success
113
+ : level.status === 'running'
114
+ ? tuiColors.warning
115
+ : tuiColors.textTertiary;
116
+
65
117
  return (
66
118
  <Box flexDirection="column" marginBottom={1}>
67
119
  <Box flexDirection="row">
68
- <Text color={isActive ? "#f97316" : "gray"} bold={isActive}>
69
- {level.name}
120
+ <Text color={levelColor} bold>{level.name.padEnd(12)}</Text>
121
+ <AnimatedProgressBar
122
+ progress={level.progress}
123
+ width={16}
124
+ tick={tick}
125
+ animated={isActive}
126
+ showMarker={isActive}
127
+ glowEffect={isActive}
128
+ />
129
+ </Box>
130
+ <Box flexDirection="row">
131
+ <Text color={isActive ? tuiColors.primary : tuiColors.textTertiary}>
132
+ {isActive ? "┃" : "│"}
70
133
  </Text>
71
- <Text color="gray"> </Text>
72
- <ProgressBar progress={level.progress} width={12} />
73
- <Text color="gray"> {level.progress}%</Text>
74
134
  </Box>
75
- {level.nodes.map((node) => (
76
- <DAGNodeItem key={node.id} node={node} tick={tick} />
135
+ {level.nodes.map((node, idx) => (
136
+ <DAGNodeItem
137
+ key={node.id}
138
+ node={node}
139
+ tick={tick}
140
+ isLast={idx === level.nodes.length - 1}
141
+ />
77
142
  ))}
78
143
  </Box>
79
144
  );
145
+ });
146
+
147
+ interface DAGPanelProps {
148
+ workflowState: WorkflowState;
149
+ tick: number;
150
+ isActive: boolean;
80
151
  }
81
152
 
82
- export function DAGPanel({ workflowState, tick, isActive }: DAGPanelProps) {
83
- const borderColor = isActive ? "#f97316" : "gray";
153
+ export const DAGPanel = memo(function DAGPanel({ workflowState, tick, isActive }: DAGPanelProps) {
154
+ const borderColor = isActive ? tuiColors.primary : tuiColors.border;
155
+
156
+ const levelViews = useMemo(() =>
157
+ workflowState.levels.map((level, index) => (
158
+ <DAGLevelView
159
+ key={level.name}
160
+ level={level}
161
+ tick={tick}
162
+ isActive={index === workflowState.currentLevel}
163
+ />
164
+ )),
165
+ [workflowState.levels, workflowState.currentLevel, tick]
166
+ );
167
+
168
+ const isAnimating = useMemo(() =>
169
+ workflowState.totalProgress > 0 && workflowState.totalProgress < 100,
170
+ [workflowState.totalProgress]
171
+ );
84
172
 
85
173
  return (
86
174
  <Box
87
175
  flexDirection="column"
88
- width={40}
176
+ width={46}
89
177
  borderStyle="single"
90
178
  borderColor={borderColor}
91
179
  paddingX={1}
92
180
  >
93
- <Box borderStyle="single" borderColor="#f97316" marginBottom={1}>
94
- <Text color="#f97316" bold>DAG</Text>
95
- <Text color="gray"> EXECUTION</Text>
181
+ <Box marginBottom={1}>
182
+ <Text color={tuiColors.primary} bold>┌─</Text>
183
+ <Text color={tuiColors.primary} bold> DAG_EXECUTION </Text>
184
+ <Text color={tuiColors.textTertiary}>──────────────────────</Text>
96
185
  </Box>
97
186
 
98
187
  <Box flexDirection="column" flexGrow={1}>
99
- {workflowState.levels.map((level, index) => (
100
- <DAGLevelView
101
- key={level.name}
102
- level={level}
103
- tick={tick}
104
- isActive={index === workflowState.currentLevel}
105
- />
106
- ))}
188
+ {levelViews}
107
189
  </Box>
108
190
 
109
- <Box borderStyle="single" borderColor="gray" marginTop={1}>
110
- <Text color="gray">Total: </Text>
111
- <ProgressBar progress={workflowState.totalProgress} width={10} />
112
- <Text color="white"> {workflowState.totalProgress}%</Text>
191
+ <Box marginTop={1}>
192
+ <Text color={tuiColors.textTertiary}>├─ TOTAL ─────────────────────────</Text>
193
+ </Box>
194
+ <Box flexDirection="row">
195
+ <Text color={tuiColors.textSecondary}>Progress: </Text>
196
+ <AnimatedProgressBar
197
+ progress={workflowState.totalProgress}
198
+ width={16}
199
+ tick={tick}
200
+ animated={isAnimating}
201
+ showMarker
202
+ />
113
203
  </Box>
114
204
  </Box>
115
205
  );
116
- }
206
+ });
207
+
208
+ export type { DAGNode, DAGLevel, WorkflowState, DAGSubtask };
@@ -0,0 +1,225 @@
1
+ import React, { memo, useMemo } from "react";
2
+ import { Box, Text } from "ink";
3
+ import { tuiColors } from "../styles/index.js";
4
+ import { AnimatedBranch } from "./AnimatedBranch.js";
5
+
6
+ interface TimelineAgent {
7
+ name: string;
8
+ startTime: number;
9
+ endTime?: number;
10
+ isActive: boolean;
11
+ color?: string;
12
+ }
13
+
14
+ interface TimelineLevel {
15
+ name: string;
16
+ startTime: number;
17
+ endTime?: number;
18
+ progress: number;
19
+ status: 'done' | 'running' | 'pending';
20
+ }
21
+
22
+ export interface ExecutionTimelineProps {
23
+ tick: number;
24
+ totalDuration: number;
25
+ currentTime: number;
26
+ levels: TimelineLevel[];
27
+ agents: TimelineAgent[];
28
+ isActive?: boolean;
29
+ width?: number;
30
+ }
31
+
32
+ const ACTIVE_MARKER_FRAMES = ["┃", "║", "┃", "║"];
33
+
34
+ const BAR_COLORS: Record<string, string> = {
35
+ done: tuiColors.success,
36
+ running: tuiColors.warning,
37
+ pending: tuiColors.textTertiary,
38
+ };
39
+
40
+ const STATUS_TEXT = {
41
+ done: (endTime: number | undefined) => `done at ${endTime?.toFixed(1)}s`,
42
+ running: () => "running...",
43
+ pending: () => "pending",
44
+ };
45
+
46
+ function formatTime(s: number): string {
47
+ return `${s}s`;
48
+ }
49
+
50
+ interface TimelineBarProps {
51
+ level: TimelineLevel;
52
+ timeScale: number;
53
+ width: number;
54
+ currentTime: number;
55
+ }
56
+
57
+ const TimelineBar = memo(function TimelineBar({ level, timeScale, width, currentTime }: TimelineBarProps) {
58
+ const startCol = Math.floor(level.startTime * timeScale);
59
+ const endCol = level.endTime
60
+ ? Math.floor(level.endTime * timeScale)
61
+ : Math.floor(currentTime * timeScale);
62
+ const barWidth = Math.max(0, endCol - startCol);
63
+ const beforeBar = "░".repeat(startCol);
64
+
65
+ const barChar = level.status === 'done' ? "█" : level.status === 'running' ? "▓" : "░";
66
+ const afterBar = "░".repeat(Math.max(0, width - endCol));
67
+
68
+ const statusText = STATUS_TEXT[level.status](level.endTime);
69
+ const paddedName = useMemo(() => level.name.padEnd(4), [level.name]);
70
+
71
+ return (
72
+ <Box flexDirection="row">
73
+ <Text color={tuiColors.textTertiary}>{paddedName} </Text>
74
+ <Text color={BAR_COLORS[level.status]}>{beforeBar}{barChar.repeat(barWidth)}</Text>
75
+ <Text color={tuiColors.textTertiary}>{afterBar}</Text>
76
+ <Text color={tuiColors.textTertiary}> {statusText}</Text>
77
+ </Box>
78
+ );
79
+ });
80
+
81
+ interface AgentTimelineProps {
82
+ agent: TimelineAgent;
83
+ timeScale: number;
84
+ width: number;
85
+ currentTime: number;
86
+ }
87
+
88
+ const AgentTimeline = memo(function AgentTimeline({ agent, timeScale, width, currentTime }: AgentTimelineProps) {
89
+ const startCol = Math.floor(agent.startTime * timeScale);
90
+ const endCol = agent.endTime
91
+ ? Math.floor(agent.endTime * timeScale)
92
+ : Math.floor(currentTime * timeScale);
93
+ const barWidth = Math.max(0, endCol - startCol);
94
+ const indent = " ".repeat(startCol);
95
+ const bar = agent.isActive ? "─".repeat(barWidth) + "▶" : "─".repeat(barWidth) + "┘";
96
+ const afterBar = " ".repeat(Math.max(0, width - endCol - 1));
97
+
98
+ const agentColor = agent.color || (agent.isActive ? tuiColors.primary : tuiColors.textSecondary);
99
+ const prefix = agent.isActive ? "▶" : "●";
100
+ const paddedName = useMemo(() => agent.name.padEnd(10), [agent.name]);
101
+
102
+ return (
103
+ <Box flexDirection="row">
104
+ <Text color={agentColor}>{prefix} {paddedName} </Text>
105
+ <Text color={tuiColors.textTertiary}>{indent}</Text>
106
+ <Text color={agentColor}>{bar}</Text>
107
+ <Text color={tuiColors.textTertiary}>{afterBar}</Text>
108
+ </Box>
109
+ );
110
+ });
111
+
112
+ export const ExecutionTimeline = memo(function ExecutionTimeline({
113
+ tick,
114
+ totalDuration,
115
+ currentTime,
116
+ levels,
117
+ agents,
118
+ isActive = false,
119
+ width = 50,
120
+ }: ExecutionTimelineProps) {
121
+ const borderColor = isActive ? tuiColors.primary : tuiColors.border;
122
+ const timeScale = width / totalDuration;
123
+ const markerPositions = [0, 5, 10, 15, 20];
124
+
125
+ const activeMarker = isActive ? ACTIVE_MARKER_FRAMES[tick % ACTIVE_MARKER_FRAMES.length] : "│";
126
+
127
+ const markerElements = useMemo(() =>
128
+ markerPositions.map((pos, idx) => (
129
+ <Box key={idx} width={10}>
130
+ <Text color={tuiColors.textTertiary}>{formatTime(pos).padStart(6)}</Text>
131
+ </Box>
132
+ )),
133
+ []
134
+ );
135
+
136
+ const separatorElements = useMemo(() =>
137
+ Array.from({ length: 5 }).map((_, idx) => (
138
+ <Box key={idx} width={10}>
139
+ <Text color={tuiColors.textTertiary}>{idx === 0 ? "│" : " │"}</Text>
140
+ </Box>
141
+ )),
142
+ []
143
+ );
144
+
145
+ const levelElements = useMemo(() =>
146
+ levels.map((level, idx) => (
147
+ <Box key={idx} marginBottom={1}>
148
+ <TimelineBar level={level} timeScale={timeScale} width={width} currentTime={currentTime} />
149
+ </Box>
150
+ )),
151
+ [levels, timeScale, width, currentTime]
152
+ );
153
+
154
+ const agentElements = useMemo(() =>
155
+ agents.map((agent, idx) => (
156
+ <Box key={idx}>
157
+ <AgentTimeline agent={agent} timeScale={timeScale} width={width} currentTime={currentTime} />
158
+ </Box>
159
+ )),
160
+ [agents, timeScale, width, currentTime]
161
+ );
162
+
163
+ return (
164
+ <Box flexDirection="column" borderStyle="single" borderColor={borderColor} paddingX={1}>
165
+ <Box marginBottom={1}>
166
+ <Text color={tuiColors.primary} bold>┌─</Text>
167
+ <Text color={tuiColors.primary} bold> EXECUTION_TIMELINE </Text>
168
+ <Text color={tuiColors.textTertiary}>────────────────</Text>
169
+ </Box>
170
+
171
+ <Box flexDirection="row" marginBottom={1}>
172
+ {markerElements}
173
+ </Box>
174
+
175
+ <Box flexDirection="row" marginBottom={1}>
176
+ {separatorElements}
177
+ </Box>
178
+
179
+ <Box flexDirection="row" marginBottom={1}>
180
+ <Text color={tuiColors.textTertiary}>{"├" + "─".repeat(width - 2) + "┤"}</Text>
181
+ </Box>
182
+
183
+ <Box flexDirection="column">
184
+ {levelElements}
185
+ </Box>
186
+
187
+ <Box flexDirection="row" marginTop={1} marginBottom={1}>
188
+ <AnimatedBranch type="vertical" tick={tick} animated={isActive} />
189
+ </Box>
190
+
191
+ <Box marginTop={1}>
192
+ <Text color={tuiColors.textSecondary}>Agents:</Text>
193
+ </Box>
194
+
195
+ <Box flexDirection="column" marginTop={1}>
196
+ {agentElements}
197
+ </Box>
198
+ </Box>
199
+ );
200
+ });
201
+
202
+ interface MiniTimelineProps {
203
+ progress: number;
204
+ tick: number;
205
+ isActive: boolean;
206
+ width?: number;
207
+ }
208
+
209
+ export const MiniTimeline = memo(function MiniTimeline({ progress, tick, isActive, width = 30 }: MiniTimelineProps) {
210
+ const filled = Math.floor((progress / 100) * width);
211
+ const empty = width - filled;
212
+ const currentPos = Math.min(filled, width - 1);
213
+
214
+ const before = "─".repeat(currentPos);
215
+ const marker = isActive ? "●" : "○";
216
+ const after = "─".repeat(empty);
217
+
218
+ return (
219
+ <Box flexDirection="row">
220
+ <Text color={tuiColors.textTertiary}>├{before}</Text>
221
+ <Text color={isActive ? tuiColors.primary : tuiColors.textSecondary}>{marker}</Text>
222
+ <Text color={tuiColors.textTertiary}>{after}┤</Text>
223
+ </Box>
224
+ );
225
+ });
@@ -1,5 +1,7 @@
1
- import React from "react";
1
+ import React, { memo, useMemo } from "react";
2
2
  import { Box, Text } from "ink";
3
+ import { tuiColors, getStatusIndicator, getProviderTag } from "../styles/index.js";
4
+ import { StatusPulse } from "./animations/index.js";
3
5
 
4
6
  interface HeaderProps {
5
7
  projectName: string;
@@ -10,67 +12,98 @@ interface HeaderProps {
10
12
  activePanel: string;
11
13
  }
12
14
 
13
- const spinnerFrames = ["◐", "◓", "◑", "◒"];
14
- const panelNames: Record<string, string> = {
15
+ const PANEL_NAMES: Record<string, string> = {
15
16
  chat: "CHAT",
16
17
  dag: "DAG",
17
18
  status: "STATUS",
19
+ logs: "LOGS",
20
+ metrics: "METRICS",
21
+ memory: "MEMORY",
18
22
  };
19
23
 
20
- export function Header({ projectName, agentCount, provider, status, tick, activePanel }: HeaderProps) {
21
- const statusColors = {
22
- ready: "green",
23
- running: "yellow",
24
- error: "red",
25
- };
24
+ const PANELS = ["chat", "dag", "logs", "metrics", "memory"] as const;
26
25
 
27
- const statusText = {
28
- ready: "READY",
29
- running: "RUNNING",
30
- error: "ERROR",
31
- };
26
+ export const Header = memo(function Header({
27
+ projectName,
28
+ agentCount,
29
+ provider,
30
+ status,
31
+ tick,
32
+ activePanel
33
+ }: HeaderProps) {
34
+ const statusInfo = useMemo(() => getStatusIndicator(status), [status]);
35
+ const providerInfo = useMemo(() => getProviderTag(provider), [provider]);
32
36
 
33
- const animatedStatus = status === "running"
34
- ? spinnerFrames[tick % spinnerFrames.length]
35
- : status === "error" ? "✗" : "●";
37
+ const statusText = status === "ready" ? "ONLINE" : status === "running" ? "RUNNING" : "ERROR";
38
+ const statusColor = status === "ready"
39
+ ? tuiColors.success
40
+ : status === "running"
41
+ ? tuiColors.warning
42
+ : tuiColors.error;
43
+
44
+ const pulseStatus = status === "ready"
45
+ ? "online"
46
+ : status === "running"
47
+ ? "running"
48
+ : "error";
49
+
50
+ const panelElements = useMemo(() =>
51
+ PANELS.map((panel) => (
52
+ <Box key={panel} marginRight={1}>
53
+ <Text
54
+ color={activePanel === panel ? tuiColors.primary : tuiColors.textTertiary}
55
+ bold={activePanel === panel}
56
+ >
57
+ [{PANEL_NAMES[panel]}]
58
+ </Text>
59
+ </Box>
60
+ )),
61
+ [activePanel]
62
+ );
36
63
 
37
64
  return (
38
- <Box
39
- flexDirection="column"
40
- borderStyle="single"
41
- borderColor="gray"
42
- width="100%"
43
- >
65
+ <Box flexDirection="column" width="100%">
44
66
  <Box
45
67
  flexDirection="row"
46
68
  justifyContent="space-between"
47
69
  alignItems="center"
48
70
  paddingX={1}
71
+ borderStyle="single"
72
+ borderColor={tuiColors.border}
49
73
  >
50
74
  <Box flexDirection="row" alignItems="center">
51
- <Text color="#f97316" bold>■</Text>
52
- <Text color="white" bold> RAX-FLOW</Text>
53
- <Text color="gray"> HUB</Text>
75
+ <Text color={tuiColors.primary} bold>■</Text>
76
+ <Text color={tuiColors.textPrimary} bold> RAX-FLOW</Text>
77
+ <Text color={tuiColors.textSecondary}> HUB</Text>
54
78
  </Box>
55
- <Box flexDirection="row" alignItems="center">
56
- <Text color="gray">Project:</Text>
57
- <Text color="cyan"> {projectName}</Text>
58
- <Text color="gray"> │ Agents:</Text>
59
- <Text color="green"> {agentCount}/12</Text>
60
- <Text color="gray"> │ [{provider}]</Text>
61
- <Text color={statusColors[status]}> {animatedStatus} {statusText[status]}</Text>
79
+ <Box flexDirection="row" alignItems="center" gap={1}>
80
+ <Text color={tuiColors.textTertiary}>Project:</Text>
81
+ <Text color={tuiColors.textPrimary}> {projectName}</Text>
82
+ <Text color={tuiColors.textQuaternary}> │</Text>
83
+ <Text color={tuiColors.textTertiary}> Agents:</Text>
84
+ <Text color={tuiColors.success}> {agentCount}/12</Text>
85
+ <Text color={tuiColors.textQuaternary}> │</Text>
86
+ <Text color={tuiColors.textSecondary}>{providerInfo.code}</Text>
87
+ <Text color={statusColor}> </Text>
88
+ <StatusPulse status={pulseStatus} tick={tick} />
89
+ <Text color={statusColor}> {statusText}</Text>
62
90
  </Box>
63
91
  </Box>
64
- <Box flexDirection="row" paddingX={1}>
65
- {(["chat", "dag", "status"] as const).map((panel) => (
66
- <Box key={panel} marginRight={2}>
67
- <Text color={activePanel === panel ? "#f97316" : "gray"} bold={activePanel === panel}>
68
- [{panelNames[panel]}]
69
- </Text>
70
- </Box>
71
- ))}
72
- <Text color="gray">| Tab: Changer │ ?: Aide │ Ctrl+C: Quitter</Text>
92
+
93
+ <Box borderStyle="single" borderColor={tuiColors.primary} paddingX={1}>
94
+ <Text color={tuiColors.textTertiary} italic>
95
+ Orchestrator that Evolves with Your Host Tools
96
+ </Text>
97
+ </Box>
98
+
99
+ <Box flexDirection="row" paddingX={1} justifyContent="space-between">
100
+ <Box flexDirection="row">
101
+ {panelElements}
102
+ </Box>
103
+ <Box flexDirection="row">
104
+ <Text color={tuiColors.textQuaternary}>Tab:Switch │ ←:View │ ?:Help │ Ctrl+C:Quit</Text>
105
+ </Box>
73
106
  </Box>
74
107
  </Box>
75
108
  );
76
- }
109
+ });