opencode-top 3.1.2 → 3.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 (103) 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 +22570 -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 +2 -1
  84. package/bin/octop.mjs +0 -13
  85. package/src/cli.ts +0 -60
  86. package/src/core/agents.ts +0 -78
  87. package/src/core/session.ts +0 -315
  88. package/src/core/types.ts +0 -156
  89. package/src/data/pricing.ts +0 -82
  90. package/src/data/sqlite.ts +0 -347
  91. package/src/ui/App.tsx +0 -154
  92. package/src/ui/components/AgentChainGraph.tsx +0 -95
  93. package/src/ui/components/AgentTree.tsx +0 -101
  94. package/src/ui/components/DetailsPanel.tsx +0 -211
  95. package/src/ui/components/MessagesPanel.tsx +0 -323
  96. package/src/ui/components/SparkLine.tsx +0 -18
  97. package/src/ui/components/StatusBar.tsx +0 -24
  98. package/src/ui/components/TabBar.tsx +0 -42
  99. package/src/ui/screens/OverviewScreen.tsx +0 -327
  100. package/src/ui/screens/SessionsScreen.tsx +0 -168
  101. package/src/ui/screens/TimelineScreen.tsx +0 -222
  102. package/src/ui/screens/ToolsScreen.tsx +0 -260
  103. 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;