@arvorco/relentless 0.6.1 → 0.7.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/.claude/skills/specify/SKILL.md +2 -2
- package/CHANGELOG.md +59 -0
- package/package.json +1 -1
- package/src/agents/amp.ts +3 -6
- package/src/agents/claude.ts +4 -7
- package/src/agents/codex.ts +3 -6
- package/src/agents/droid.ts +3 -6
- package/src/agents/exec.ts +56 -10
- package/src/agents/gemini.ts +3 -6
- package/src/agents/opencode.ts +3 -6
- package/src/agents/types.ts +2 -0
- package/src/config/schema.ts +2 -2
- package/src/tui/App.tsx +132 -16
- package/src/tui/TUIRunner.tsx +68 -4
- package/src/tui/components/CostBadge.tsx +59 -0
- package/src/tui/components/MessageItem.tsx +113 -0
- package/src/tui/components/MessageQueuePanel.tsx +126 -0
- package/src/tui/components/OutputPanel.tsx +270 -0
- package/src/tui/components/QueueInput.tsx +28 -11
- package/src/tui/components/RateLimitIndicator.tsx +97 -0
- package/src/tui/components/StatusBar.tsx +188 -0
- package/src/tui/components/TaskItem.tsx +131 -0
- package/src/tui/components/TaskPanel.tsx +189 -0
- package/src/tui/components/TokenCounter.tsx +48 -0
- package/src/tui/hooks/useAnimation.ts +220 -0
- package/src/tui/hooks/useCostTracking.ts +199 -0
- package/src/tui/hooks/useResponsiveLayout.ts +94 -0
- package/src/tui/hooks/useTUI.ts +57 -1
- package/src/tui/index.tsx +24 -0
- package/src/tui/layouts/LayoutSwitcher.tsx +95 -0
- package/src/tui/layouts/ThreeColumnLayout.tsx +97 -0
- package/src/tui/layouts/VerticalLayout.tsx +69 -0
- package/src/tui/layouts/index.ts +9 -0
- package/src/tui/theme.ts +152 -21
- package/src/tui/types.ts +95 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LayoutSwitcher Component
|
|
3
|
+
*
|
|
4
|
+
* Automatically switches between 3-column and vertical layouts
|
|
5
|
+
* based on terminal size
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React from "react";
|
|
9
|
+
import { useResponsiveLayout } from "../hooks/useResponsiveLayout.js";
|
|
10
|
+
import { ThreeColumnLayout } from "./ThreeColumnLayout.js";
|
|
11
|
+
import { VerticalLayout } from "./VerticalLayout.js";
|
|
12
|
+
import type { LayoutMode } from "../types.js";
|
|
13
|
+
|
|
14
|
+
interface LayoutSwitcherProps {
|
|
15
|
+
/** Content for left panel (Tasks) in 3-column mode */
|
|
16
|
+
taskPanel: React.ReactNode;
|
|
17
|
+
/** Content for center panel (Output) in 3-column mode */
|
|
18
|
+
outputPanel: React.ReactNode;
|
|
19
|
+
/** Content for right panel (Queue) in 3-column mode */
|
|
20
|
+
queuePanel: React.ReactNode;
|
|
21
|
+
/** Status bar content */
|
|
22
|
+
statusBar: React.ReactNode;
|
|
23
|
+
|
|
24
|
+
// Vertical layout specific props
|
|
25
|
+
/** Header content for vertical layout */
|
|
26
|
+
header?: React.ReactNode;
|
|
27
|
+
/** Current story info for vertical layout */
|
|
28
|
+
currentStory?: React.ReactNode;
|
|
29
|
+
/** Story grid for vertical layout */
|
|
30
|
+
storyGrid?: React.ReactNode;
|
|
31
|
+
/** Agent status for vertical layout */
|
|
32
|
+
agentStatus?: React.ReactNode;
|
|
33
|
+
/** Additional controls for vertical layout */
|
|
34
|
+
controls?: React.ReactNode;
|
|
35
|
+
|
|
36
|
+
/** Force a specific layout mode (overrides responsive detection) */
|
|
37
|
+
forceMode?: LayoutMode;
|
|
38
|
+
/** Callback when layout mode changes */
|
|
39
|
+
onLayoutChange?: (mode: LayoutMode) => void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function LayoutSwitcher({
|
|
43
|
+
taskPanel,
|
|
44
|
+
outputPanel,
|
|
45
|
+
queuePanel,
|
|
46
|
+
statusBar,
|
|
47
|
+
header,
|
|
48
|
+
currentStory,
|
|
49
|
+
storyGrid,
|
|
50
|
+
agentStatus,
|
|
51
|
+
controls,
|
|
52
|
+
forceMode,
|
|
53
|
+
onLayoutChange,
|
|
54
|
+
}: LayoutSwitcherProps): React.ReactElement {
|
|
55
|
+
const layout = useResponsiveLayout();
|
|
56
|
+
const mode = forceMode ?? layout.mode;
|
|
57
|
+
|
|
58
|
+
// Notify parent of layout changes
|
|
59
|
+
React.useEffect(() => {
|
|
60
|
+
onLayoutChange?.(mode);
|
|
61
|
+
}, [mode, onLayoutChange]);
|
|
62
|
+
|
|
63
|
+
// Use 3-column layout for larger terminals
|
|
64
|
+
if (mode === "three-column" || mode === "compressed") {
|
|
65
|
+
return (
|
|
66
|
+
<ThreeColumnLayout
|
|
67
|
+
leftPanel={taskPanel}
|
|
68
|
+
centerPanel={outputPanel}
|
|
69
|
+
rightPanel={queuePanel}
|
|
70
|
+
statusBar={statusBar}
|
|
71
|
+
mode={mode}
|
|
72
|
+
height={layout.height}
|
|
73
|
+
/>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Fall back to vertical layout for narrow terminals
|
|
78
|
+
return (
|
|
79
|
+
<VerticalLayout
|
|
80
|
+
header={header ?? null}
|
|
81
|
+
currentStory={currentStory ?? null}
|
|
82
|
+
outputPanel={outputPanel}
|
|
83
|
+
queuePanel={queuePanel}
|
|
84
|
+
storyGrid={storyGrid ?? null}
|
|
85
|
+
agentStatus={agentStatus ?? null}
|
|
86
|
+
statusBar={statusBar}
|
|
87
|
+
controls={controls ?? null}
|
|
88
|
+
height={layout.height}
|
|
89
|
+
/>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export { useResponsiveLayout } from "../hooks/useResponsiveLayout.js";
|
|
94
|
+
export { ThreeColumnLayout } from "./ThreeColumnLayout.js";
|
|
95
|
+
export { VerticalLayout } from "./VerticalLayout.js";
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ThreeColumnLayout Component
|
|
3
|
+
*
|
|
4
|
+
* mIRC-inspired 3-column layout with Tasks | Output | Queue
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React from "react";
|
|
8
|
+
import { Box } from "ink";
|
|
9
|
+
import type { LayoutMode } from "../types.js";
|
|
10
|
+
|
|
11
|
+
interface ThreeColumnLayoutProps {
|
|
12
|
+
/** Left panel content (Tasks) */
|
|
13
|
+
leftPanel: React.ReactNode;
|
|
14
|
+
/** Center panel content (Output) */
|
|
15
|
+
centerPanel: React.ReactNode;
|
|
16
|
+
/** Right panel content (Queue) */
|
|
17
|
+
rightPanel: React.ReactNode;
|
|
18
|
+
/** Bottom content (Status bar) */
|
|
19
|
+
statusBar: React.ReactNode;
|
|
20
|
+
/** Layout mode determines column widths */
|
|
21
|
+
mode: LayoutMode;
|
|
22
|
+
/** Terminal height for calculating panel heights */
|
|
23
|
+
height: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Get width percentages based on layout mode
|
|
28
|
+
*/
|
|
29
|
+
function getWidths(mode: LayoutMode): { left: string; center: string; right: string } {
|
|
30
|
+
switch (mode) {
|
|
31
|
+
case "three-column":
|
|
32
|
+
return { left: "25%", center: "50%", right: "25%" };
|
|
33
|
+
case "compressed":
|
|
34
|
+
return { left: "20%", center: "60%", right: "20%" };
|
|
35
|
+
default:
|
|
36
|
+
return { left: "100%", center: "100%", right: "100%" };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function ThreeColumnLayout({
|
|
41
|
+
leftPanel,
|
|
42
|
+
centerPanel,
|
|
43
|
+
rightPanel,
|
|
44
|
+
statusBar,
|
|
45
|
+
mode,
|
|
46
|
+
height,
|
|
47
|
+
}: ThreeColumnLayoutProps): React.ReactElement {
|
|
48
|
+
const widths = getWidths(mode);
|
|
49
|
+
|
|
50
|
+
// Calculate available height for panels (reserve 1 line for status bar)
|
|
51
|
+
const panelHeight = Math.max(10, height - 2);
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<Box flexDirection="column" width="100%" height={height}>
|
|
55
|
+
{/* Main content area - 3 columns */}
|
|
56
|
+
<Box flexDirection="row" width="100%" height={panelHeight}>
|
|
57
|
+
{/* Left panel - Tasks */}
|
|
58
|
+
<Box
|
|
59
|
+
width={widths.left}
|
|
60
|
+
height="100%"
|
|
61
|
+
flexDirection="column"
|
|
62
|
+
borderStyle="single"
|
|
63
|
+
borderColor="cyan"
|
|
64
|
+
>
|
|
65
|
+
{leftPanel}
|
|
66
|
+
</Box>
|
|
67
|
+
|
|
68
|
+
{/* Center panel - Output */}
|
|
69
|
+
<Box
|
|
70
|
+
width={widths.center}
|
|
71
|
+
height="100%"
|
|
72
|
+
flexDirection="column"
|
|
73
|
+
borderStyle="single"
|
|
74
|
+
borderColor="white"
|
|
75
|
+
>
|
|
76
|
+
{centerPanel}
|
|
77
|
+
</Box>
|
|
78
|
+
|
|
79
|
+
{/* Right panel - Queue */}
|
|
80
|
+
<Box
|
|
81
|
+
width={widths.right}
|
|
82
|
+
height="100%"
|
|
83
|
+
flexDirection="column"
|
|
84
|
+
borderStyle="single"
|
|
85
|
+
borderColor="magenta"
|
|
86
|
+
>
|
|
87
|
+
{rightPanel}
|
|
88
|
+
</Box>
|
|
89
|
+
</Box>
|
|
90
|
+
|
|
91
|
+
{/* Status bar */}
|
|
92
|
+
<Box width="100%" height={1}>
|
|
93
|
+
{statusBar}
|
|
94
|
+
</Box>
|
|
95
|
+
</Box>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VerticalLayout Component
|
|
3
|
+
*
|
|
4
|
+
* Fallback vertical layout for narrow terminals (<100 cols)
|
|
5
|
+
* Stacks panels vertically similar to current behavior
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React from "react";
|
|
9
|
+
import { Box } from "ink";
|
|
10
|
+
|
|
11
|
+
interface VerticalLayoutProps {
|
|
12
|
+
/** Header content */
|
|
13
|
+
header: React.ReactNode;
|
|
14
|
+
/** Current story info */
|
|
15
|
+
currentStory: React.ReactNode;
|
|
16
|
+
/** Output panel content */
|
|
17
|
+
outputPanel: React.ReactNode;
|
|
18
|
+
/** Queue panel content */
|
|
19
|
+
queuePanel: React.ReactNode;
|
|
20
|
+
/** Story grid content */
|
|
21
|
+
storyGrid: React.ReactNode;
|
|
22
|
+
/** Agent status footer */
|
|
23
|
+
agentStatus: React.ReactNode;
|
|
24
|
+
/** Status bar */
|
|
25
|
+
statusBar: React.ReactNode;
|
|
26
|
+
/** Additional controls (queue input, etc.) */
|
|
27
|
+
controls: React.ReactNode;
|
|
28
|
+
/** Terminal height */
|
|
29
|
+
height: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function VerticalLayout({
|
|
33
|
+
header,
|
|
34
|
+
currentStory,
|
|
35
|
+
outputPanel,
|
|
36
|
+
queuePanel,
|
|
37
|
+
storyGrid,
|
|
38
|
+
agentStatus,
|
|
39
|
+
statusBar,
|
|
40
|
+
controls,
|
|
41
|
+
}: VerticalLayoutProps): React.ReactElement {
|
|
42
|
+
return (
|
|
43
|
+
<Box flexDirection="column" width="100%">
|
|
44
|
+
{/* Header */}
|
|
45
|
+
{header}
|
|
46
|
+
|
|
47
|
+
{/* Current story info */}
|
|
48
|
+
{currentStory}
|
|
49
|
+
|
|
50
|
+
{/* Agent output */}
|
|
51
|
+
{outputPanel}
|
|
52
|
+
|
|
53
|
+
{/* Queue panel */}
|
|
54
|
+
{queuePanel}
|
|
55
|
+
|
|
56
|
+
{/* Story grid */}
|
|
57
|
+
{storyGrid}
|
|
58
|
+
|
|
59
|
+
{/* Agent status */}
|
|
60
|
+
{agentStatus}
|
|
61
|
+
|
|
62
|
+
{/* Status bar */}
|
|
63
|
+
{statusBar}
|
|
64
|
+
|
|
65
|
+
{/* Controls (queue input, etc.) */}
|
|
66
|
+
{controls}
|
|
67
|
+
</Box>
|
|
68
|
+
);
|
|
69
|
+
}
|
package/src/tui/theme.ts
CHANGED
|
@@ -11,31 +11,162 @@ export const colors = {
|
|
|
11
11
|
error: "red",
|
|
12
12
|
dim: "gray",
|
|
13
13
|
accent: "magenta",
|
|
14
|
+
/** Panel-specific colors */
|
|
15
|
+
panel: {
|
|
16
|
+
taskBorder: "cyan",
|
|
17
|
+
outputBorder: "white",
|
|
18
|
+
queueBorder: "magenta",
|
|
19
|
+
statusBorder: "gray",
|
|
20
|
+
headerBg: "gray",
|
|
21
|
+
},
|
|
22
|
+
/** Message type colors */
|
|
23
|
+
message: {
|
|
24
|
+
command: "magenta",
|
|
25
|
+
prompt: "white",
|
|
26
|
+
system: "gray",
|
|
27
|
+
info: "cyan",
|
|
28
|
+
success: "green",
|
|
29
|
+
error: "red",
|
|
30
|
+
warning: "yellow",
|
|
31
|
+
},
|
|
32
|
+
/** Status colors */
|
|
33
|
+
status: {
|
|
34
|
+
active: "green",
|
|
35
|
+
pending: "gray",
|
|
36
|
+
blocked: "red",
|
|
37
|
+
complete: "green",
|
|
38
|
+
inProgress: "yellow",
|
|
39
|
+
rateLimited: "red",
|
|
40
|
+
},
|
|
41
|
+
/** Cost display colors */
|
|
42
|
+
cost: {
|
|
43
|
+
free: "green",
|
|
44
|
+
cheap: "cyan",
|
|
45
|
+
medium: "yellow",
|
|
46
|
+
expensive: "red",
|
|
47
|
+
},
|
|
14
48
|
} as const;
|
|
15
49
|
|
|
16
50
|
export const symbols = {
|
|
17
|
-
complete: "
|
|
18
|
-
pending: "
|
|
19
|
-
inProgress: "
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
51
|
+
complete: "\u2713",
|
|
52
|
+
pending: "\u25CB",
|
|
53
|
+
inProgress: "\u25C9",
|
|
54
|
+
blocked: "\uD83D\uDD12",
|
|
55
|
+
arrow: "\u2192",
|
|
56
|
+
bullet: "\u2022",
|
|
57
|
+
lightning: "\u26A1",
|
|
58
|
+
rocket: "\uD83D\uDE80",
|
|
59
|
+
party: "\uD83C\uDF89",
|
|
60
|
+
warning: "\u26A0\uFE0F",
|
|
61
|
+
error: "\u274C",
|
|
62
|
+
clock: "\u23F3",
|
|
63
|
+
/** Panel indicators */
|
|
64
|
+
panel: {
|
|
65
|
+
expand: "+",
|
|
66
|
+
collapse: "-",
|
|
67
|
+
fullscreen: "\u25A3",
|
|
68
|
+
minimize: "\u25A2",
|
|
69
|
+
},
|
|
70
|
+
/** Pulse animation frames */
|
|
71
|
+
pulse: [">", ">>", ">>>", ">>"],
|
|
72
|
+
/** Priority badges */
|
|
73
|
+
priority: {
|
|
74
|
+
critical: "!",
|
|
75
|
+
high: "\u2191",
|
|
76
|
+
medium: "\u2022",
|
|
77
|
+
low: "\u2193",
|
|
78
|
+
},
|
|
79
|
+
/** Research indicator */
|
|
80
|
+
research: "\uD83D\uDD0D",
|
|
28
81
|
} as const;
|
|
29
82
|
|
|
30
83
|
export const borders = {
|
|
31
|
-
horizontal: "
|
|
32
|
-
vertical: "
|
|
33
|
-
topLeft: "
|
|
34
|
-
topRight: "
|
|
35
|
-
bottomLeft: "
|
|
36
|
-
bottomRight: "
|
|
37
|
-
teeLeft: "
|
|
38
|
-
teeRight: "
|
|
39
|
-
cross: "
|
|
40
|
-
doubleLine: "
|
|
84
|
+
horizontal: "\u2500",
|
|
85
|
+
vertical: "\u2502",
|
|
86
|
+
topLeft: "\u250C",
|
|
87
|
+
topRight: "\u2510",
|
|
88
|
+
bottomLeft: "\u2514",
|
|
89
|
+
bottomRight: "\u2518",
|
|
90
|
+
teeLeft: "\u251C",
|
|
91
|
+
teeRight: "\u2524",
|
|
92
|
+
cross: "\u253C",
|
|
93
|
+
doubleLine: "\u2550",
|
|
94
|
+
/** Rounded corners for panels */
|
|
95
|
+
rounded: {
|
|
96
|
+
topLeft: "\u256D",
|
|
97
|
+
topRight: "\u256E",
|
|
98
|
+
bottomLeft: "\u2570",
|
|
99
|
+
bottomRight: "\u256F",
|
|
100
|
+
},
|
|
101
|
+
/** Double-line borders for emphasis */
|
|
102
|
+
double: {
|
|
103
|
+
horizontal: "\u2550",
|
|
104
|
+
vertical: "\u2551",
|
|
105
|
+
topLeft: "\u2554",
|
|
106
|
+
topRight: "\u2557",
|
|
107
|
+
bottomLeft: "\u255A",
|
|
108
|
+
bottomRight: "\u255D",
|
|
109
|
+
},
|
|
110
|
+
} as const;
|
|
111
|
+
|
|
112
|
+
/** Animation timing constants */
|
|
113
|
+
export const animation = {
|
|
114
|
+
/** Pulse animation interval (ms) */
|
|
115
|
+
pulseInterval: 300,
|
|
116
|
+
/** Typing effect speed (ms per character) */
|
|
117
|
+
typingSpeed: 30,
|
|
118
|
+
/** Cursor blink interval (ms) */
|
|
119
|
+
blinkInterval: 500,
|
|
120
|
+
/** Number transition duration (ms) */
|
|
121
|
+
numberTransition: 500,
|
|
122
|
+
/** Countdown update interval (ms) */
|
|
123
|
+
countdownInterval: 1000,
|
|
124
|
+
/** Status message display duration (ms) */
|
|
125
|
+
statusMessageDuration: 2000,
|
|
126
|
+
} as const;
|
|
127
|
+
|
|
128
|
+
/** Panel layout constants */
|
|
129
|
+
export const layout = {
|
|
130
|
+
/** Minimum panel widths */
|
|
131
|
+
minWidth: {
|
|
132
|
+
task: 20,
|
|
133
|
+
output: 40,
|
|
134
|
+
queue: 20,
|
|
135
|
+
},
|
|
136
|
+
/** Default panel heights (in lines) */
|
|
137
|
+
defaultHeight: {
|
|
138
|
+
header: 2,
|
|
139
|
+
statusBar: 1,
|
|
140
|
+
output: 12,
|
|
141
|
+
queue: 8,
|
|
142
|
+
},
|
|
143
|
+
/** Padding values */
|
|
144
|
+
padding: {
|
|
145
|
+
panel: 1,
|
|
146
|
+
content: 1,
|
|
147
|
+
section: 1,
|
|
148
|
+
},
|
|
149
|
+
} as const;
|
|
150
|
+
|
|
151
|
+
/** Badge styles for different states/modes */
|
|
152
|
+
export const badges = {
|
|
153
|
+
mode: {
|
|
154
|
+
free: { text: "FREE", color: "green" },
|
|
155
|
+
cheap: { text: "CHEAP", color: "cyan" },
|
|
156
|
+
good: { text: "GOOD", color: "yellow" },
|
|
157
|
+
genius: { text: "GENIUS", color: "magenta" },
|
|
158
|
+
},
|
|
159
|
+
complexity: {
|
|
160
|
+
simple: { text: "S", color: "green" },
|
|
161
|
+
medium: { text: "M", color: "yellow" },
|
|
162
|
+
complex: { text: "C", color: "red" },
|
|
163
|
+
expert: { text: "E", color: "magenta" },
|
|
164
|
+
},
|
|
165
|
+
priority: {
|
|
166
|
+
1: { text: "P1", color: "red" },
|
|
167
|
+
2: { text: "P2", color: "red" },
|
|
168
|
+
3: { text: "P3", color: "yellow" },
|
|
169
|
+
4: { text: "P4", color: "yellow" },
|
|
170
|
+
5: { text: "P5", color: "gray" },
|
|
171
|
+
},
|
|
41
172
|
} as const;
|
package/src/tui/types.ts
CHANGED
|
@@ -15,6 +15,10 @@ export interface Story {
|
|
|
15
15
|
criteriaCount: number;
|
|
16
16
|
research?: boolean;
|
|
17
17
|
phase?: string;
|
|
18
|
+
/** Dependencies that must complete first */
|
|
19
|
+
dependencies?: string[];
|
|
20
|
+
/** Whether blocked by unfinished dependencies */
|
|
21
|
+
blocked?: boolean;
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
export interface AgentState {
|
|
@@ -25,6 +29,46 @@ export interface AgentState {
|
|
|
25
29
|
resetTime?: Date;
|
|
26
30
|
}
|
|
27
31
|
|
|
32
|
+
/** Token usage tracking */
|
|
33
|
+
export interface TokenUsage {
|
|
34
|
+
/** Input tokens used */
|
|
35
|
+
inputTokens: number;
|
|
36
|
+
/** Output tokens used */
|
|
37
|
+
outputTokens: number;
|
|
38
|
+
/** Total tokens used */
|
|
39
|
+
totalTokens: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Cost tracking data */
|
|
43
|
+
export interface CostData {
|
|
44
|
+
/** Actual cost incurred so far (in dollars) */
|
|
45
|
+
actual: number;
|
|
46
|
+
/** Estimated total cost (in dollars) */
|
|
47
|
+
estimated: number;
|
|
48
|
+
/** Token usage breakdown */
|
|
49
|
+
tokens: TokenUsage;
|
|
50
|
+
/** Cost per story */
|
|
51
|
+
perStory: number;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Message in the queue panel */
|
|
55
|
+
export interface MessageItem {
|
|
56
|
+
/** Unique identifier */
|
|
57
|
+
id: string;
|
|
58
|
+
/** Timestamp when added */
|
|
59
|
+
timestamp: Date;
|
|
60
|
+
/** Message content */
|
|
61
|
+
content: string;
|
|
62
|
+
/** Message type for color coding */
|
|
63
|
+
type: "command" | "prompt" | "system" | "info" | "error" | "success";
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Layout mode for responsive design */
|
|
67
|
+
export type LayoutMode = "three-column" | "compressed" | "vertical";
|
|
68
|
+
|
|
69
|
+
/** Output panel display mode */
|
|
70
|
+
export type OutputMode = "normal" | "fullscreen";
|
|
71
|
+
|
|
28
72
|
export interface TUIState {
|
|
29
73
|
/** Feature name */
|
|
30
74
|
feature: string;
|
|
@@ -75,6 +119,23 @@ export interface TUIState {
|
|
|
75
119
|
confirmClearActive: boolean;
|
|
76
120
|
/** Status message to display (e.g., "Queue already empty") */
|
|
77
121
|
statusMessage?: string;
|
|
122
|
+
|
|
123
|
+
// Enhanced state for new TUI
|
|
124
|
+
|
|
125
|
+
/** Cost and token tracking data */
|
|
126
|
+
costData?: CostData;
|
|
127
|
+
/** Messages in the queue panel (mIRC-style) */
|
|
128
|
+
messages: MessageItem[];
|
|
129
|
+
/** Current layout mode */
|
|
130
|
+
layoutMode: LayoutMode;
|
|
131
|
+
/** Output panel display mode */
|
|
132
|
+
outputMode: OutputMode;
|
|
133
|
+
/** Total elapsed time since start (seconds) */
|
|
134
|
+
totalElapsedSeconds: number;
|
|
135
|
+
/** Start time of current session */
|
|
136
|
+
startTime?: Date;
|
|
137
|
+
/** Savings compared to SOTA (percentage) */
|
|
138
|
+
savingsPercent?: number;
|
|
78
139
|
}
|
|
79
140
|
|
|
80
141
|
export interface TUIActions {
|
|
@@ -100,4 +161,38 @@ export interface TUIActions {
|
|
|
100
161
|
setComplete: (complete: boolean) => void;
|
|
101
162
|
/** Set error */
|
|
102
163
|
setError: (error: string | undefined) => void;
|
|
164
|
+
/** Add a message to the queue panel */
|
|
165
|
+
addMessage: (message: Omit<MessageItem, "id" | "timestamp">) => void;
|
|
166
|
+
/** Update cost tracking data */
|
|
167
|
+
updateCostData: (data: Partial<CostData>) => void;
|
|
168
|
+
/** Toggle output fullscreen mode */
|
|
169
|
+
toggleOutputMode: () => void;
|
|
170
|
+
/** Set layout mode */
|
|
171
|
+
setLayoutMode: (mode: LayoutMode) => void;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Props for panel components */
|
|
175
|
+
export interface PanelProps {
|
|
176
|
+
/** Panel title */
|
|
177
|
+
title?: string;
|
|
178
|
+
/** Panel width (percentage or absolute) */
|
|
179
|
+
width?: string | number;
|
|
180
|
+
/** Panel height (lines) */
|
|
181
|
+
height?: number;
|
|
182
|
+
/** Border color */
|
|
183
|
+
borderColor?: string;
|
|
184
|
+
/** Whether panel is active/focused */
|
|
185
|
+
active?: boolean;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** Props for status bar sections */
|
|
189
|
+
export interface StatusSectionProps {
|
|
190
|
+
/** Section label */
|
|
191
|
+
label?: string;
|
|
192
|
+
/** Section value */
|
|
193
|
+
value: string;
|
|
194
|
+
/** Value color */
|
|
195
|
+
color?: string;
|
|
196
|
+
/** Whether to show separator after */
|
|
197
|
+
separator?: boolean;
|
|
103
198
|
}
|