opencode-top 3.1.2 → 3.2.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 (104) hide show
  1. package/bin/octop.js +2 -9
  2. package/dist/cli.d.ts +3 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +22432 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/cli.mjs +22572 -0
  7. package/dist/core/agents.d.ts +11 -0
  8. package/dist/core/agents.d.ts.map +1 -0
  9. package/dist/core/agents.js +58 -0
  10. package/dist/core/agents.js.map +1 -0
  11. package/dist/core/session.d.ts +19 -0
  12. package/dist/core/session.d.ts.map +1 -0
  13. package/dist/core/session.js +261 -0
  14. package/dist/core/session.js.map +1 -0
  15. package/dist/core/types.d.ts +140 -0
  16. package/dist/core/types.d.ts.map +1 -0
  17. package/dist/core/types.js +29 -0
  18. package/dist/core/types.js.map +1 -0
  19. package/dist/data/pricing.d.ts +4 -0
  20. package/dist/data/pricing.d.ts.map +1 -0
  21. package/dist/data/pricing.js +76 -0
  22. package/dist/data/pricing.js.map +1 -0
  23. package/dist/data/sqlite.d.ts +5 -0
  24. package/dist/data/sqlite.d.ts.map +1 -0
  25. package/dist/data/sqlite.js +222 -0
  26. package/dist/data/sqlite.js.map +1 -0
  27. package/dist/index.d.ts +7 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/{src/index.ts → dist/index.js} +1 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/ui/App.d.ts +6 -0
  32. package/dist/ui/App.d.ts.map +1 -0
  33. package/dist/ui/App.js +101 -0
  34. package/dist/ui/App.js.map +1 -0
  35. package/dist/ui/components/AgentChainGraph.d.ts +9 -0
  36. package/dist/ui/components/AgentChainGraph.d.ts.map +1 -0
  37. package/dist/ui/components/AgentChainGraph.js +41 -0
  38. package/dist/ui/components/AgentChainGraph.js.map +1 -0
  39. package/dist/ui/components/AgentTree.d.ts +13 -0
  40. package/dist/ui/components/AgentTree.d.ts.map +1 -0
  41. package/dist/ui/components/AgentTree.js +50 -0
  42. package/dist/ui/components/AgentTree.js.map +1 -0
  43. package/dist/ui/components/DetailsPanel.d.ts +9 -0
  44. package/dist/ui/components/DetailsPanel.d.ts.map +1 -0
  45. package/dist/ui/components/DetailsPanel.js +82 -0
  46. package/dist/ui/components/DetailsPanel.js.map +1 -0
  47. package/dist/ui/components/MessagesPanel.d.ts +12 -0
  48. package/dist/ui/components/MessagesPanel.d.ts.map +1 -0
  49. package/dist/ui/components/MessagesPanel.js +107 -0
  50. package/dist/ui/components/MessagesPanel.js.map +1 -0
  51. package/dist/ui/components/SparkLine.d.ts +10 -0
  52. package/dist/ui/components/SparkLine.d.ts.map +1 -0
  53. package/dist/ui/components/SparkLine.js +12 -0
  54. package/dist/ui/components/SparkLine.js.map +1 -0
  55. package/dist/ui/components/StatusBar.d.ts +9 -0
  56. package/dist/ui/components/StatusBar.d.ts.map +1 -0
  57. package/dist/ui/components/StatusBar.js +9 -0
  58. package/dist/ui/components/StatusBar.js.map +1 -0
  59. package/dist/ui/components/TabBar.d.ts +10 -0
  60. package/dist/ui/components/TabBar.d.ts.map +1 -0
  61. package/dist/ui/components/TabBar.js +17 -0
  62. package/dist/ui/components/TabBar.js.map +1 -0
  63. package/dist/ui/screens/OverviewScreen.d.ts +12 -0
  64. package/dist/ui/screens/OverviewScreen.d.ts.map +1 -0
  65. package/dist/ui/screens/OverviewScreen.js +94 -0
  66. package/dist/ui/screens/OverviewScreen.js.map +1 -0
  67. package/dist/ui/screens/SessionsScreen.d.ts +12 -0
  68. package/dist/ui/screens/SessionsScreen.d.ts.map +1 -0
  69. package/dist/ui/screens/SessionsScreen.js +98 -0
  70. package/dist/ui/screens/SessionsScreen.js.map +1 -0
  71. package/dist/ui/screens/TimelineScreen.d.ts +11 -0
  72. package/dist/ui/screens/TimelineScreen.d.ts.map +1 -0
  73. package/dist/ui/screens/TimelineScreen.js +128 -0
  74. package/dist/ui/screens/TimelineScreen.js.map +1 -0
  75. package/dist/ui/screens/ToolsScreen.d.ts +12 -0
  76. package/dist/ui/screens/ToolsScreen.d.ts.map +1 -0
  77. package/dist/ui/screens/ToolsScreen.js +113 -0
  78. package/dist/ui/screens/ToolsScreen.js.map +1 -0
  79. package/dist/ui/theme.d.ts +21 -0
  80. package/dist/ui/theme.d.ts.map +1 -0
  81. package/dist/ui/theme.js +21 -0
  82. package/dist/ui/theme.js.map +1 -0
  83. package/package.json +3 -1
  84. package/patches/ink+5.2.1.patch +13 -0
  85. package/bin/octop.mjs +0 -13
  86. package/src/cli.ts +0 -60
  87. package/src/core/agents.ts +0 -78
  88. package/src/core/session.ts +0 -315
  89. package/src/core/types.ts +0 -156
  90. package/src/data/pricing.ts +0 -82
  91. package/src/data/sqlite.ts +0 -347
  92. package/src/ui/App.tsx +0 -154
  93. package/src/ui/components/AgentChainGraph.tsx +0 -95
  94. package/src/ui/components/AgentTree.tsx +0 -101
  95. package/src/ui/components/DetailsPanel.tsx +0 -211
  96. package/src/ui/components/MessagesPanel.tsx +0 -323
  97. package/src/ui/components/SparkLine.tsx +0 -18
  98. package/src/ui/components/StatusBar.tsx +0 -24
  99. package/src/ui/components/TabBar.tsx +0 -42
  100. package/src/ui/screens/OverviewScreen.tsx +0 -327
  101. package/src/ui/screens/SessionsScreen.tsx +0 -168
  102. package/src/ui/screens/TimelineScreen.tsx +0 -222
  103. package/src/ui/screens/ToolsScreen.tsx +0 -260
  104. package/src/ui/theme.ts +0 -21
@@ -1,222 +0,0 @@
1
- import React, { useState, useMemo, useCallback, memo } from "react";
2
- import { Box, Text, useInput } from "ink";
3
- import { colors } from "../theme";
4
- import { StatusBar } from "../components/StatusBar";
5
- import type { Workflow, Interaction, MessagePart } from "../../core/types";
6
-
7
- interface TimelineScreenProps {
8
- workflows: Workflow[];
9
- isActive: boolean;
10
- contentHeight: number;
11
- }
12
-
13
- type TimelineLine =
14
- | { kind: "session-header"; sessionId: string; title: string; agent: string | null; time: number | null }
15
- | { kind: "interaction-header"; interactionId: string; modelId: string; time: number | null; agent: string | null }
16
- | { kind: "tool-call"; part: MessagePart & { type: "tool" }; interactionId: string }
17
- | { kind: "text-snippet"; text: string; interactionId: string }
18
- | { kind: "reasoning-snippet"; text: string; interactionId: string }
19
- | { kind: "spacer" };
20
-
21
- function formatTime(ts: number | null): string {
22
- if (!ts) return "??:??";
23
- return new Date(ts).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit" });
24
- }
25
-
26
- function buildTimeline(workflows: Workflow[]): TimelineLine[] {
27
- const lines: TimelineLine[] = [];
28
-
29
- const allSessions = workflows.flatMap((w) => [
30
- w.mainSession,
31
- ...w.subAgentSessions,
32
- ]);
33
-
34
- // Sort by timeCreated
35
- allSessions.sort((a, b) => (a.timeCreated ?? 0) - (b.timeCreated ?? 0));
36
-
37
- for (const session of allSessions) {
38
- const agentName = session.interactions[0]?.agent ?? null;
39
- lines.push({
40
- kind: "session-header",
41
- sessionId: session.id,
42
- title: session.title ?? session.id.slice(0, 12),
43
- agent: agentName,
44
- time: session.timeCreated,
45
- });
46
-
47
- for (const interaction of session.interactions) {
48
- if (interaction.role !== "assistant") continue;
49
-
50
- lines.push({
51
- kind: "interaction-header",
52
- interactionId: interaction.id,
53
- modelId: interaction.modelId,
54
- time: interaction.time.created,
55
- agent: interaction.agent,
56
- });
57
-
58
- for (const part of interaction.parts) {
59
- if (part.type === "tool") {
60
- lines.push({
61
- kind: "tool-call",
62
- part: part as MessagePart & { type: "tool" },
63
- interactionId: interaction.id,
64
- });
65
- } else if (part.type === "text" && part.text.trim().length > 0) {
66
- lines.push({
67
- kind: "text-snippet",
68
- text: part.text.slice(0, 120).replace(/\n/g, " "),
69
- interactionId: interaction.id,
70
- });
71
- } else if (part.type === "reasoning" && part.text.trim().length > 0) {
72
- lines.push({
73
- kind: "reasoning-snippet",
74
- text: part.text.slice(0, 100).replace(/\n/g, " "),
75
- interactionId: interaction.id,
76
- });
77
- }
78
- }
79
- }
80
-
81
- lines.push({ kind: "spacer" });
82
- }
83
-
84
- return lines;
85
- }
86
-
87
- function renderLine(line: TimelineLine, idx: number): React.ReactElement {
88
- switch (line.kind) {
89
- case "session-header":
90
- return (
91
- <Box key={idx} flexDirection="row">
92
- <Text color={colors.accent} bold>
93
- ▶ {truncate(line.title, 40)}
94
- </Text>
95
- {line.agent && <Text color={colors.cyan}> [{line.agent}]</Text>}
96
- <Box flexGrow={1} />
97
- <Text color={colors.textDim}>{formatTime(line.time)}</Text>
98
- </Box>
99
- );
100
-
101
- case "interaction-header":
102
- return (
103
- <Box key={idx} flexDirection="row">
104
- <Text color={colors.textDim}> </Text>
105
- <Text color={colors.purple}>◆ </Text>
106
- <Text color={colors.info}>{truncate(line.modelId, 30)}</Text>
107
- {line.agent && <Text color={colors.cyan}> [{line.agent}]</Text>}
108
- <Box flexGrow={1} />
109
- <Text color={colors.textDim}>{formatTime(line.time)}</Text>
110
- </Box>
111
- );
112
-
113
- case "tool-call": {
114
- const p = line.part;
115
- const statusIcon = p.status === "completed" ? "✓" : p.status === "error" ? "✗" : "◌";
116
- const statusColor =
117
- p.status === "completed" ? colors.success : p.status === "error" ? colors.error : colors.warning;
118
- const durationMs = p.timeEnd > 0 && p.timeStart > 0 ? p.timeEnd - p.timeStart : 0;
119
- const durationStr = durationMs > 0 ? ` ${(durationMs / 1000).toFixed(1)}s` : "";
120
- return (
121
- <Box key={idx} flexDirection="row">
122
- <Text color={colors.textDim}> </Text>
123
- <Text color={statusColor}>{statusIcon} </Text>
124
- <Text color={colors.text}>{truncate(p.toolName, 20)}</Text>
125
- {p.title && <Text color={colors.textDim}> {truncate(p.title, 25)}</Text>}
126
- <Box flexGrow={1} />
127
- <Text color={colors.textDim}>{durationStr}</Text>
128
- </Box>
129
- );
130
- }
131
-
132
- case "text-snippet":
133
- return (
134
- <Box key={idx} flexDirection="row">
135
- <Text color={colors.textDim}> │ </Text>
136
- <Text color={colors.text}>{truncate(line.text, 80)}</Text>
137
- </Box>
138
- );
139
-
140
- case "reasoning-snippet":
141
- return (
142
- <Box key={idx} flexDirection="row">
143
- <Text color={colors.textDim}> ⚡ </Text>
144
- <Text color={colors.accentDim}>{truncate(line.text, 80)}</Text>
145
- </Box>
146
- );
147
-
148
- case "spacer":
149
- return <Box key={idx} />;
150
- }
151
- }
152
-
153
- function TimelineScreenInner({ workflows, isActive, contentHeight }: TimelineScreenProps) {
154
- const [scrollOffset, setScrollOffset] = useState(0);
155
-
156
- const allLines = useMemo(() => buildTimeline(workflows), [workflows]);
157
-
158
- const visibleHeight = contentHeight - 4; // header row + status bar + borders
159
-
160
- const clampOffset = useCallback(
161
- (offset: number) => Math.max(0, Math.min(offset, Math.max(0, allLines.length - visibleHeight))),
162
- [allLines.length, visibleHeight]
163
- );
164
-
165
- useInput(
166
- (input, key) => {
167
- if (key.upArrow || input === "k") {
168
- setScrollOffset((o) => clampOffset(o - 1));
169
- return;
170
- }
171
- if (key.downArrow || input === "j") {
172
- setScrollOffset((o) => clampOffset(o + 1));
173
- return;
174
- }
175
- if (input === "g") {
176
- setScrollOffset(0);
177
- return;
178
- }
179
- if (input === "G") {
180
- setScrollOffset(clampOffset(allLines.length));
181
- return;
182
- }
183
- if (key.pageUp) {
184
- setScrollOffset((o) => clampOffset(o - visibleHeight));
185
- return;
186
- }
187
- if (key.pageDown) {
188
- setScrollOffset((o) => clampOffset(o + visibleHeight));
189
- return;
190
- }
191
- },
192
- { isActive }
193
- );
194
-
195
- const visibleLines = allLines.slice(scrollOffset, scrollOffset + visibleHeight);
196
-
197
- return (
198
- <Box flexDirection="column" height={contentHeight}>
199
- <Box paddingX={1} flexDirection="row">
200
- <Text color={colors.accent} bold>
201
- Timeline
202
- </Text>
203
- <Box flexGrow={1} />
204
- <Text color={colors.textDim}>
205
- {scrollOffset + 1}-{Math.min(scrollOffset + visibleHeight, allLines.length)}/
206
- {allLines.length}
207
- </Text>
208
- </Box>
209
- <Box flexDirection="column" flexGrow={1} overflow="hidden">
210
- {visibleLines.map((line, i) => renderLine(line, scrollOffset + i))}
211
- </Box>
212
- <StatusBar hints="j/k:scroll g/G:top/bottom PgUp/PgDn:page 1:sessions 3:tools 4:overview q:quit" />
213
- </Box>
214
- );
215
- }
216
-
217
- export const TimelineScreen = memo(TimelineScreenInner);
218
-
219
- function truncate(s: string, max: number): string {
220
- if (s.length <= max) return s;
221
- return s.slice(0, max - 1) + "…";
222
- }
@@ -1,260 +0,0 @@
1
- import React, { useState, useMemo, memo } from "react";
2
- import { Box, Text, useInput } from "ink";
3
- import { colors } from "../theme";
4
- import { StatusBar } from "../components/StatusBar";
5
- import type { Workflow, ToolUsage } from "../../core/types";
6
- import { getToolUsage } from "../../core/session";
7
-
8
- interface ToolsScreenProps {
9
- workflows: Workflow[];
10
- isActive: boolean;
11
- contentHeight: number;
12
- terminalWidth: number;
13
- }
14
-
15
- type SortKey = "calls" | "failures" | "avgTime";
16
-
17
- function aggregateToolUsage(workflows: Workflow[]): ToolUsage[] {
18
- const merged = new Map<
19
- string,
20
- { calls: number; successes: number; failures: number; totalDurationMs: number; recentErrors: string[] }
21
- >();
22
-
23
- for (const workflow of workflows) {
24
- const allSessions = [workflow.mainSession, ...workflow.subAgentSessions];
25
- for (const session of allSessions) {
26
- for (const tool of getToolUsage(session)) {
27
- const existing = merged.get(tool.name) ?? {
28
- calls: 0,
29
- successes: 0,
30
- failures: 0,
31
- totalDurationMs: 0,
32
- recentErrors: [],
33
- };
34
- existing.calls += tool.calls;
35
- existing.successes += tool.successes;
36
- existing.failures += tool.failures;
37
- existing.totalDurationMs += tool.totalDurationMs;
38
- for (const err of tool.recentErrors) {
39
- if (existing.recentErrors.length < 3) existing.recentErrors.push(err);
40
- }
41
- merged.set(tool.name, existing);
42
- }
43
- }
44
- }
45
-
46
- return Array.from(merged.entries()).map(([name, s]) => ({
47
- name,
48
- calls: s.calls,
49
- successes: s.successes,
50
- failures: s.failures,
51
- totalDurationMs: s.totalDurationMs,
52
- avgDurationMs: s.calls > 0 ? s.totalDurationMs / s.calls : 0,
53
- recentErrors: s.recentErrors,
54
- }));
55
- }
56
-
57
- function SuccessBar({ successes, calls, width = 12 }: { successes: number; calls: number; width?: number }) {
58
- const pct = calls > 0 ? successes / calls : 0;
59
- const filled = Math.round(pct * width);
60
- const empty = width - filled;
61
- const color = pct >= 0.9 ? colors.success : pct >= 0.7 ? colors.warning : colors.error;
62
- return (
63
- <Text>
64
- <Text color={color}>{"█".repeat(filled)}</Text>
65
- <Text color={colors.border}>{"░".repeat(empty)}</Text>
66
- <Text color={colors.textDim}> {Math.round(pct * 100)}%</Text>
67
- </Text>
68
- );
69
- }
70
-
71
- function formatDuration(ms: number): string {
72
- if (ms === 0) return "—";
73
- if (ms < 1000) return `${ms.toFixed(0)}ms`;
74
- return `${(ms / 1000).toFixed(1)}s`;
75
- }
76
-
77
- function ToolsScreenInner({ workflows, isActive, contentHeight, terminalWidth }: ToolsScreenProps) {
78
- const [selectedIndex, setSelectedIndex] = useState(0);
79
- const [sortKey, setSortKey] = useState<SortKey>("calls");
80
-
81
- const allTools = useMemo(() => aggregateToolUsage(workflows), [workflows]);
82
-
83
- const sortedTools = useMemo(() => {
84
- const copy = [...allTools];
85
- switch (sortKey) {
86
- case "calls":
87
- return copy.sort((a, b) => b.calls - a.calls);
88
- case "failures":
89
- return copy.sort((a, b) => b.failures - a.failures);
90
- case "avgTime":
91
- return copy.sort((a, b) => b.avgDurationMs - a.avgDurationMs);
92
- }
93
- }, [allTools, sortKey]);
94
-
95
- const listHeight = contentHeight - 4; // header + status bar + padding
96
- const clampedIndex = Math.min(selectedIndex, Math.max(0, sortedTools.length - 1));
97
- const selectedTool = sortedTools[clampedIndex] ?? null;
98
-
99
- // Pagination
100
- const startIndex = clampedIndex >= listHeight ? clampedIndex - listHeight + 1 : 0;
101
- const visibleTools = sortedTools.slice(startIndex, startIndex + listHeight);
102
-
103
- useInput(
104
- (input, key) => {
105
- if (key.upArrow || input === "k") {
106
- setSelectedIndex((i) => Math.max(0, i - 1));
107
- return;
108
- }
109
- if (key.downArrow || input === "j") {
110
- setSelectedIndex((i) => Math.min(sortedTools.length - 1, i + 1));
111
- return;
112
- }
113
- if (key.tab) {
114
- setSortKey((k) => {
115
- if (k === "calls") return "failures";
116
- if (k === "failures") return "avgTime";
117
- return "calls";
118
- });
119
- return;
120
- }
121
- },
122
- { isActive }
123
- );
124
-
125
- const sortLabels: Record<SortKey, string> = {
126
- calls: "Calls",
127
- failures: "Failures",
128
- avgTime: "Avg Time",
129
- };
130
-
131
- return (
132
- <Box flexDirection="column" width={terminalWidth} height={contentHeight}>
133
- <Box paddingX={1} flexDirection="row">
134
- <Text color={colors.accent} bold>
135
- Tools
136
- </Text>
137
- <Box flexGrow={1} />
138
- <Text color={colors.textDim}>
139
- sort:{" "}
140
- {(["calls", "failures", "avgTime"] as SortKey[]).map((k) => (
141
- <Text key={k} color={sortKey === k ? colors.accent : colors.textDim}>
142
- [{k === sortKey ? sortLabels[k] : k}]{" "}
143
- </Text>
144
- ))}
145
- Tab:cycle
146
- </Text>
147
- </Box>
148
-
149
- <Box flexDirection="row" flexGrow={1}>
150
- {/* Left: tool list */}
151
- <Box width={36} flexDirection="column" borderStyle="single" borderColor={colors.border}>
152
- <Box paddingX={1} flexDirection="row">
153
- <Text color={colors.textDim} bold>
154
- Tool
155
- </Text>
156
- <Box flexGrow={1} />
157
- <Text color={colors.textDim}>calls </Text>
158
- <Text color={colors.textDim}>err</Text>
159
- </Box>
160
- {visibleTools.map((tool, i) => {
161
- const isSelected = tool.name === selectedTool?.name;
162
- return (
163
- <Box key={tool.name} flexDirection="row" paddingX={1}>
164
- <Text
165
- color={isSelected ? colors.accent : colors.textDim}
166
- bold={isSelected}
167
- >
168
- {isSelected ? "▶ " : " "}{truncate(tool.name, 20)}
169
- </Text>
170
- <Box flexGrow={1} />
171
- <Text color={colors.info}>{tool.calls}</Text>
172
- <Text color={colors.textDim}> </Text>
173
- <Text color={tool.failures > 0 ? colors.error : colors.textDim}>{tool.failures}</Text>
174
- </Box>
175
- );
176
- })}
177
- {allTools.length === 0 && (
178
- <Box paddingX={1}>
179
- <Text color={colors.textDim}>No tool data yet</Text>
180
- </Box>
181
- )}
182
- </Box>
183
-
184
- {/* Right: detail panel */}
185
- <Box flexGrow={1} flexDirection="column" borderStyle="single" borderColor={colors.border} paddingX={1}>
186
- {selectedTool ? (
187
- <>
188
- <Text color={colors.cyan} bold>
189
- {selectedTool.name}
190
- </Text>
191
- <Box marginTop={1} flexDirection="column">
192
- <Box flexDirection="row">
193
- <Box width={14}>
194
- <Text color={colors.textDim}>Total calls</Text>
195
- </Box>
196
- <Text color={colors.text}>{selectedTool.calls}</Text>
197
- </Box>
198
- <Box flexDirection="row">
199
- <Box width={14}>
200
- <Text color={colors.textDim}>Successes</Text>
201
- </Box>
202
- <Text color={colors.success}>{selectedTool.successes}</Text>
203
- </Box>
204
- <Box flexDirection="row">
205
- <Box width={14}>
206
- <Text color={colors.textDim}>Failures</Text>
207
- </Box>
208
- <Text color={selectedTool.failures > 0 ? colors.error : colors.textDim}>
209
- {selectedTool.failures}
210
- </Text>
211
- </Box>
212
- <Box flexDirection="row">
213
- <Box width={14}>
214
- <Text color={colors.textDim}>Avg time</Text>
215
- </Box>
216
- <Text color={colors.info}>{formatDuration(selectedTool.avgDurationMs)}</Text>
217
- </Box>
218
- </Box>
219
-
220
- <Box marginTop={1}>
221
- <Text color={colors.textDim}>Success rate </Text>
222
- <SuccessBar successes={selectedTool.successes} calls={selectedTool.calls} />
223
- </Box>
224
-
225
- {selectedTool.recentErrors.length > 0 && (
226
- <>
227
- <Box marginTop={1}>
228
- <Text color={colors.warning} bold>
229
- Recent Errors
230
- </Text>
231
- </Box>
232
- {selectedTool.recentErrors.map((err, i) => (
233
- <Box key={i} flexDirection="row">
234
- <Text color={colors.error}>• </Text>
235
- <Text color={colors.text}>{truncate(err, 60)}</Text>
236
- </Box>
237
- ))}
238
- </>
239
- )}
240
- </>
241
- ) : (
242
- <Text color={colors.textDim}>No tools found. Run some OpenCode sessions first.</Text>
243
- )}
244
- </Box>
245
- </Box>
246
-
247
- <StatusBar
248
- hints="j/k:nav Tab:cycle-sort 1:sessions 3:overview q:quit"
249
- info={`${allTools.length} tools`}
250
- />
251
- </Box>
252
- );
253
- }
254
-
255
- export const ToolsScreen = memo(ToolsScreenInner);
256
-
257
- function truncate(s: string, max: number): string {
258
- if (s.length <= max) return s;
259
- return s.slice(0, max - 1) + "…";
260
- }
package/src/ui/theme.ts DELETED
@@ -1,21 +0,0 @@
1
- export const colors = {
2
- bg: "#1a1a2e",
3
- bgSecondary: "#16213e",
4
- border: "#0f3460",
5
- accent: "#e94560",
6
- accentDim: "#533483",
7
- text: "#eaeaea",
8
- textDim: "#888",
9
- success: "#4ade80",
10
- warning: "#fbbf24",
11
- error: "#f87171",
12
- info: "#60a5fa",
13
- purple: "#a855f7",
14
- cyan: "#22d3ee",
15
- } as const;
16
-
17
- export const tokens = {
18
- radius: 1,
19
- paddingX: 1,
20
- paddingY: 0,
21
- } as const;