@tokentop/ttop 0.2.2 → 0.4.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/README.md +2 -5
- package/package.json +8 -5
- package/src/agents/aggregator.ts +39 -19
- package/src/agents/costing.ts +11 -9
- package/src/agents/index.ts +7 -9
- package/src/agents/types.ts +2 -2
- package/src/cli.ts +28 -23
- package/src/config/schema.test.ts +42 -40
- package/src/config/schema.ts +26 -26
- package/src/demo/simulator.ts +161 -122
- package/src/plugins/agents/index.ts +2 -2
- package/src/plugins/auth-sources.ts +42 -38
- package/src/plugins/lifecycle.ts +42 -38
- package/src/plugins/loader.test.ts +49 -49
- package/src/plugins/loader.ts +67 -63
- package/src/plugins/notification-bus.ts +21 -16
- package/src/plugins/notifications/index.ts +2 -2
- package/src/plugins/notifications/terminal-bell.ts +36 -23
- package/src/plugins/notifications/visual-flash.ts +31 -19
- package/src/plugins/npm-installer.ts +23 -25
- package/src/plugins/plugin-context-factory.ts +10 -10
- package/src/plugins/plugin-host.ts +12 -5
- package/src/plugins/providers/anthropic.ts +64 -57
- package/src/plugins/providers/antigravity.ts +81 -69
- package/src/plugins/providers/codex.ts +42 -42
- package/src/plugins/providers/gemini.ts +106 -87
- package/src/plugins/providers/github-copilot.ts +49 -45
- package/src/plugins/providers/index.ts +10 -12
- package/src/plugins/providers/minimax.ts +50 -44
- package/src/plugins/providers/openai-api.ts +82 -62
- package/src/plugins/providers/opencode-zen.ts +46 -39
- package/src/plugins/providers/perplexity.ts +55 -46
- package/src/plugins/providers/zai.ts +68 -57
- package/src/plugins/registry.ts +80 -71
- package/src/plugins/sandbox-guard.test.ts +45 -48
- package/src/plugins/sandbox-guard.ts +20 -25
- package/src/plugins/sandbox.ts +26 -48
- package/src/plugins/themes/catppuccin-latte.ts +29 -29
- package/src/plugins/themes/catppuccin-mocha.ts +29 -29
- package/src/plugins/themes/claude-code.ts +29 -29
- package/src/plugins/themes/dracula.ts +29 -29
- package/src/plugins/themes/github-light.ts +29 -29
- package/src/plugins/themes/gruvbox-dark.ts +29 -29
- package/src/plugins/themes/gruvbox-light.ts +29 -29
- package/src/plugins/themes/index.ts +15 -15
- package/src/plugins/themes/kanagawa.ts +29 -29
- package/src/plugins/themes/nord.ts +29 -29
- package/src/plugins/themes/one-dark.ts +29 -29
- package/src/plugins/themes/opencode.ts +31 -31
- package/src/plugins/themes/rose-pine-dawn.ts +29 -29
- package/src/plugins/themes/rose-pine.ts +29 -29
- package/src/plugins/themes/solarized-light.ts +29 -29
- package/src/plugins/themes/tokyo-night.ts +29 -29
- package/src/plugins/types/agent.ts +5 -5
- package/src/plugins/types/base.ts +35 -24
- package/src/plugins/types/index.ts +9 -9
- package/src/plugins/types/notification.ts +12 -12
- package/src/plugins/types/provider.ts +12 -8
- package/src/plugins/types/theme.ts +31 -23
- package/src/plugins/update-checker.ts +6 -6
- package/src/pricing/estimator.test.ts +39 -44
- package/src/pricing/estimator.ts +7 -13
- package/src/pricing/fallback.test.ts +30 -34
- package/src/pricing/fallback.ts +54 -27
- package/src/pricing/index.ts +18 -12
- package/src/pricing/models-dev.ts +6 -6
- package/src/storage/database.ts +56 -46
- package/src/storage/db.ts +12 -12
- package/src/storage/index.ts +44 -45
- package/src/storage/migrations/index.ts +2 -2
- package/src/storage/paths.ts +13 -13
- package/src/storage/persistence-service.ts +206 -0
- package/src/storage/repos/agentSessions.ts +33 -23
- package/src/storage/repos/providerSnapshots.ts +5 -5
- package/src/storage/repos/usageEvents.ts +40 -28
- package/src/storage/retention.ts +146 -0
- package/src/storage/types.ts +5 -5
- package/src/tui/App.tsx +180 -137
- package/src/tui/components/CommandPalette.tsx +46 -34
- package/src/tui/components/DebugConsole.tsx +39 -33
- package/src/tui/components/DebugPanel.tsx +314 -183
- package/src/tui/components/FooterHints.tsx +2 -2
- package/src/tui/components/GhostProviderCard.tsx +37 -35
- package/src/tui/components/Header.tsx +68 -35
- package/src/tui/components/HelpOverlay.tsx +99 -29
- package/src/tui/components/InlineGauge.tsx +10 -7
- package/src/tui/components/InlineSparkline.tsx +17 -11
- package/src/tui/components/KpiStrip.tsx +128 -98
- package/src/tui/components/LimitGauge.tsx +96 -59
- package/src/tui/components/ModalBackdrop.tsx +4 -4
- package/src/tui/components/ProviderAggregateStrip.tsx +69 -38
- package/src/tui/components/ProviderCard.tsx +232 -226
- package/src/tui/components/ProviderDetailPanel.tsx +133 -58
- package/src/tui/components/ProviderLimitsPanel.tsx +126 -89
- package/src/tui/components/ProvidersList.tsx +215 -126
- package/src/tui/components/SessionDetailsDrawer.tsx +191 -94
- package/src/tui/components/SessionsTable.tsx +320 -169
- package/src/tui/components/SettingsModal.tsx +593 -269
- package/src/tui/components/SidebarBreakdown.tsx +30 -16
- package/src/tui/components/Skeleton.tsx +24 -17
- package/src/tui/components/SmartSidebar.tsx +360 -258
- package/src/tui/components/Sparkline.tsx +64 -52
- package/src/tui/components/Spinner.tsx +3 -3
- package/src/tui/components/StatusBar.tsx +27 -27
- package/src/tui/components/ThemePicker.tsx +67 -66
- package/src/tui/components/Toast.tsx +9 -6
- package/src/tui/components/UsageGauge.tsx +10 -13
- package/src/tui/components/index.ts +5 -5
- package/src/tui/contexts/AgentSessionContext.tsx +164 -151
- package/src/tui/contexts/ConfigContext.tsx +15 -13
- package/src/tui/contexts/DashboardRuntimeContext.tsx +26 -26
- package/src/tui/contexts/DemoModeContext.tsx +22 -15
- package/src/tui/contexts/DrawerContext.tsx +11 -9
- package/src/tui/contexts/InputContext.tsx +2 -2
- package/src/tui/contexts/LogContext.tsx +62 -47
- package/src/tui/contexts/PluginContext.tsx +238 -183
- package/src/tui/contexts/RealTimeActivityContext.tsx +70 -36
- package/src/tui/contexts/StorageContext.tsx +95 -149
- package/src/tui/contexts/ThemeContext.tsx +21 -16
- package/src/tui/contexts/TimeWindowContext.tsx +72 -61
- package/src/tui/contexts/ToastContext.tsx +10 -7
- package/src/tui/contexts/index.ts +9 -5
- package/src/tui/createApp.tsx +8 -12
- package/src/tui/debug/captureFrame.ts +25 -28
- package/src/tui/debug/snapshot.tsx +179 -162
- package/src/tui/driver/assertions.ts +30 -22
- package/src/tui/driver/cli.ts +231 -221
- package/src/tui/driver/coverage.ts +27 -44
- package/src/tui/driver/demo-snapshot.ts +33 -33
- package/src/tui/driver/diff.ts +26 -32
- package/src/tui/driver/driver.ts +43 -48
- package/src/tui/driver/index.ts +36 -40
- package/src/tui/driver/recorder.ts +37 -48
- package/src/tui/driver/test-workflow.ts +51 -51
- package/src/tui/driver/test.ts +25 -25
- package/src/tui/hooks/index.ts +24 -20
- package/src/tui/hooks/useAnimatedValue.ts +13 -18
- package/src/tui/hooks/useDashboardKeyboard.ts +118 -98
- package/src/tui/hooks/useDashboardState.ts +26 -24
- package/src/tui/hooks/useEmaActivity.ts +82 -74
- package/src/tui/hooks/useEntranceAnimation.ts +11 -11
- package/src/tui/hooks/useExitAnimation.ts +7 -3
- package/src/tui/hooks/usePulse.ts +4 -4
- package/src/tui/hooks/useSafeRenderer.ts +5 -5
- package/src/tui/hooks/useSessionEnrichedProviders.ts +150 -0
- package/src/tui/hooks/useValueFlash.ts +10 -14
- package/src/tui/index.tsx +29 -6
- package/src/tui/shutdown.ts +19 -0
- package/src/tui/utils/providerColor.ts +1 -1
- package/src/tui/views/Dashboard.tsx +164 -102
- package/src/tui/views/HistoricalTrendsView.tsx +579 -250
- package/src/tui/views/ProjectsView.tsx +495 -243
- package/src/tui/views/RealTimeDashboard.tsx +176 -116
- package/src/tui/views/SettingsView.tsx +225 -189
- package/src/tui/views/index.ts +2 -2
- package/src/types/opentui-test-utils.d.ts +7 -7
- package/src/utils/clipboard.ts +29 -29
- package/src/utils/currency.test.ts +53 -53
- package/src/utils/currency.ts +35 -32
- package/src/version.ts +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import * as fs from
|
|
2
|
-
import * as path from
|
|
3
|
-
import type { Driver } from
|
|
1
|
+
import * as fs from "fs/promises";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import type { Driver } from "./driver.ts";
|
|
4
4
|
|
|
5
5
|
export interface RecordedCommand {
|
|
6
6
|
action: string;
|
|
@@ -35,7 +35,7 @@ export interface Recorder {
|
|
|
35
35
|
export function createRecorder(
|
|
36
36
|
width: number,
|
|
37
37
|
height: number,
|
|
38
|
-
options: RecorderOptions = {}
|
|
38
|
+
options: RecorderOptions = {},
|
|
39
39
|
): Recorder {
|
|
40
40
|
const { captureFrames = false, name = `recording-${Date.now()}` } = options;
|
|
41
41
|
|
|
@@ -109,11 +109,7 @@ export function createRecorder(
|
|
|
109
109
|
export interface ReplayOptions {
|
|
110
110
|
speed?: number;
|
|
111
111
|
stepMode?: boolean;
|
|
112
|
-
onStep?: (
|
|
113
|
-
cmd: RecordedCommand,
|
|
114
|
-
index: number,
|
|
115
|
-
total: number
|
|
116
|
-
) => Promise<void>;
|
|
112
|
+
onStep?: (cmd: RecordedCommand, index: number, total: number) => Promise<void>;
|
|
117
113
|
onFrame?: (frame: string, cmd: RecordedCommand) => void;
|
|
118
114
|
}
|
|
119
115
|
|
|
@@ -128,7 +124,7 @@ export interface ReplayResult {
|
|
|
128
124
|
export async function replayRecording(
|
|
129
125
|
driver: Driver,
|
|
130
126
|
recording: Recording,
|
|
131
|
-
options: ReplayOptions = {}
|
|
127
|
+
options: ReplayOptions = {},
|
|
132
128
|
): Promise<ReplayResult> {
|
|
133
129
|
const { speed = 1, stepMode = false, onStep, onFrame } = options;
|
|
134
130
|
|
|
@@ -184,66 +180,61 @@ export async function replayRecording(
|
|
|
184
180
|
return result;
|
|
185
181
|
}
|
|
186
182
|
|
|
187
|
-
async function executeCommand(
|
|
188
|
-
driver: Driver,
|
|
189
|
-
cmd: RecordedCommand
|
|
190
|
-
): Promise<void> {
|
|
183
|
+
async function executeCommand(driver: Driver, cmd: RecordedCommand): Promise<void> {
|
|
191
184
|
const params = cmd.params ?? {};
|
|
192
185
|
|
|
193
186
|
switch (cmd.action) {
|
|
194
|
-
case
|
|
195
|
-
await driver.sendKeys(String(params.keys ??
|
|
187
|
+
case "sendKeys":
|
|
188
|
+
await driver.sendKeys(String(params.keys ?? ""));
|
|
196
189
|
break;
|
|
197
190
|
|
|
198
|
-
case
|
|
191
|
+
case "pressKey":
|
|
199
192
|
await driver.pressKey(
|
|
200
|
-
String(params.key ??
|
|
201
|
-
params.modifiers as Record<string, boolean> | undefined
|
|
193
|
+
String(params.key ?? ""),
|
|
194
|
+
params.modifiers as Record<string, boolean> | undefined,
|
|
202
195
|
);
|
|
203
196
|
break;
|
|
204
197
|
|
|
205
|
-
case
|
|
198
|
+
case "pressTab":
|
|
206
199
|
await driver.pressTab();
|
|
207
200
|
break;
|
|
208
201
|
|
|
209
|
-
case
|
|
202
|
+
case "pressEnter":
|
|
210
203
|
await driver.pressEnter();
|
|
211
204
|
break;
|
|
212
205
|
|
|
213
|
-
case
|
|
206
|
+
case "pressEscape":
|
|
214
207
|
await driver.pressEscape();
|
|
215
208
|
break;
|
|
216
209
|
|
|
217
|
-
case
|
|
218
|
-
await driver.pressArrow(params.direction as
|
|
210
|
+
case "pressArrow":
|
|
211
|
+
await driver.pressArrow(params.direction as "up" | "down" | "left" | "right");
|
|
219
212
|
break;
|
|
220
213
|
|
|
221
|
-
case
|
|
214
|
+
case "typeText":
|
|
222
215
|
await driver.typeText(
|
|
223
|
-
String(params.text ??
|
|
224
|
-
typeof params.delay ===
|
|
216
|
+
String(params.text ?? ""),
|
|
217
|
+
typeof params.delay === "number" ? params.delay : 0,
|
|
225
218
|
);
|
|
226
219
|
break;
|
|
227
220
|
|
|
228
|
-
case
|
|
221
|
+
case "resize":
|
|
229
222
|
await driver.resize(
|
|
230
|
-
typeof params.cols ===
|
|
231
|
-
typeof params.rows ===
|
|
223
|
+
typeof params.cols === "number" ? params.cols : 100,
|
|
224
|
+
typeof params.rows === "number" ? params.rows : 30,
|
|
232
225
|
);
|
|
233
226
|
break;
|
|
234
227
|
|
|
235
|
-
case
|
|
228
|
+
case "waitForStable":
|
|
236
229
|
await driver.waitForStable({
|
|
237
|
-
maxIterations:
|
|
238
|
-
|
|
239
|
-
intervalMs:
|
|
240
|
-
typeof params.intervalMs === 'number' ? params.intervalMs : 50,
|
|
230
|
+
maxIterations: typeof params.maxIterations === "number" ? params.maxIterations : 10,
|
|
231
|
+
intervalMs: typeof params.intervalMs === "number" ? params.intervalMs : 50,
|
|
241
232
|
});
|
|
242
233
|
break;
|
|
243
234
|
|
|
244
|
-
case
|
|
245
|
-
await driver.waitForText(String(params.text ??
|
|
246
|
-
timeout: typeof params.timeout ===
|
|
235
|
+
case "waitForText":
|
|
236
|
+
await driver.waitForText(String(params.text ?? ""), {
|
|
237
|
+
timeout: typeof params.timeout === "number" ? params.timeout : 5000,
|
|
247
238
|
});
|
|
248
239
|
break;
|
|
249
240
|
|
|
@@ -253,37 +244,35 @@ async function executeCommand(
|
|
|
253
244
|
}
|
|
254
245
|
}
|
|
255
246
|
|
|
256
|
-
const DEFAULT_RECORDINGS_DIR =
|
|
247
|
+
const DEFAULT_RECORDINGS_DIR = "./recordings";
|
|
257
248
|
|
|
258
249
|
export async function saveRecording(
|
|
259
250
|
recording: Recording,
|
|
260
|
-
dir: string = DEFAULT_RECORDINGS_DIR
|
|
251
|
+
dir: string = DEFAULT_RECORDINGS_DIR,
|
|
261
252
|
): Promise<string> {
|
|
262
253
|
await fs.mkdir(dir, { recursive: true });
|
|
263
254
|
const filePath = path.join(dir, `${recording.name}.json`);
|
|
264
|
-
await fs.writeFile(filePath, JSON.stringify(recording, null, 2),
|
|
255
|
+
await fs.writeFile(filePath, JSON.stringify(recording, null, 2), "utf-8");
|
|
265
256
|
return path.resolve(filePath);
|
|
266
257
|
}
|
|
267
258
|
|
|
268
259
|
export async function loadRecording(
|
|
269
260
|
name: string,
|
|
270
|
-
dir: string = DEFAULT_RECORDINGS_DIR
|
|
261
|
+
dir: string = DEFAULT_RECORDINGS_DIR,
|
|
271
262
|
): Promise<Recording | null> {
|
|
272
263
|
const filePath = path.join(dir, `${name}.json`);
|
|
273
264
|
try {
|
|
274
|
-
const content = await fs.readFile(filePath,
|
|
265
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
275
266
|
return JSON.parse(content) as Recording;
|
|
276
267
|
} catch {
|
|
277
268
|
return null;
|
|
278
269
|
}
|
|
279
270
|
}
|
|
280
271
|
|
|
281
|
-
export async function listRecordings(
|
|
282
|
-
dir: string = DEFAULT_RECORDINGS_DIR
|
|
283
|
-
): Promise<string[]> {
|
|
272
|
+
export async function listRecordings(dir: string = DEFAULT_RECORDINGS_DIR): Promise<string[]> {
|
|
284
273
|
try {
|
|
285
274
|
const files = await fs.readdir(dir);
|
|
286
|
-
return files.filter((f) => f.endsWith(
|
|
275
|
+
return files.filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", ""));
|
|
287
276
|
} catch {
|
|
288
277
|
return [];
|
|
289
278
|
}
|
|
@@ -291,7 +280,7 @@ export async function listRecordings(
|
|
|
291
280
|
|
|
292
281
|
export async function deleteRecording(
|
|
293
282
|
name: string,
|
|
294
|
-
dir: string = DEFAULT_RECORDINGS_DIR
|
|
283
|
+
dir: string = DEFAULT_RECORDINGS_DIR,
|
|
295
284
|
): Promise<boolean> {
|
|
296
285
|
const filePath = path.join(dir, `${name}.json`);
|
|
297
286
|
try {
|
|
@@ -1,115 +1,115 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
import
|
|
3
|
-
import * as
|
|
4
|
-
import
|
|
2
|
+
import * as fs from "fs/promises";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import { createDriver } from "./driver.ts";
|
|
5
5
|
|
|
6
|
-
const SNAPSHOTS_DIR =
|
|
6
|
+
const SNAPSHOTS_DIR = "./snapshots";
|
|
7
7
|
|
|
8
8
|
async function saveSnapshot(frame: string, name: string): Promise<string> {
|
|
9
9
|
await fs.mkdir(SNAPSHOTS_DIR, { recursive: true });
|
|
10
10
|
const filePath = path.join(SNAPSHOTS_DIR, `${name}.txt`);
|
|
11
|
-
await fs.writeFile(filePath, frame,
|
|
11
|
+
await fs.writeFile(filePath, frame, "utf-8");
|
|
12
12
|
return path.resolve(filePath);
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
async function main() {
|
|
16
16
|
// Use 80x24 to match your terminal
|
|
17
17
|
const driver = await createDriver({ width: 80, height: 24 });
|
|
18
|
-
|
|
19
|
-
console.log(
|
|
18
|
+
|
|
19
|
+
console.log("Launching app...");
|
|
20
20
|
await driver.launch();
|
|
21
21
|
await driver.waitForStable();
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
// Step 1: Navigate to Settings (key 5)
|
|
24
|
-
console.log(
|
|
25
|
-
await driver.pressKey(
|
|
24
|
+
console.log("Navigating to Settings...");
|
|
25
|
+
await driver.pressKey("5");
|
|
26
26
|
await driver.waitForStable();
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
// Step 2: Find and capture Alerts settings
|
|
29
29
|
// Settings page likely has tabs/sections - need to find Alerts
|
|
30
|
-
console.log(
|
|
30
|
+
console.log("Looking for Alerts section...");
|
|
31
31
|
let frame = await driver.capture();
|
|
32
|
-
await saveSnapshot(frame,
|
|
33
|
-
console.log(
|
|
34
|
-
|
|
32
|
+
await saveSnapshot(frame, "workflow-01-settings");
|
|
33
|
+
console.log("Saved settings view");
|
|
34
|
+
|
|
35
35
|
// Try navigating within settings to find Alerts (try arrow keys or tab)
|
|
36
36
|
// First let's see what's on the settings page
|
|
37
|
-
if (!frame.includes(
|
|
37
|
+
if (!frame.includes("Alerts") && !frame.includes("alerts")) {
|
|
38
38
|
// Try pressing tab or arrows to navigate
|
|
39
39
|
await driver.pressTab();
|
|
40
40
|
await driver.waitForStable();
|
|
41
41
|
frame = await driver.capture();
|
|
42
42
|
}
|
|
43
|
-
|
|
44
|
-
await saveSnapshot(frame,
|
|
45
|
-
console.log(
|
|
46
|
-
|
|
43
|
+
|
|
44
|
+
await saveSnapshot(frame, "workflow-02-alerts-settings");
|
|
45
|
+
console.log("Saved alerts settings");
|
|
46
|
+
|
|
47
47
|
// Step 3: Navigate back to Dashboard (key 1)
|
|
48
|
-
console.log(
|
|
49
|
-
await driver.pressKey(
|
|
48
|
+
console.log("Navigating to Dashboard...");
|
|
49
|
+
await driver.pressKey("1");
|
|
50
50
|
await driver.waitForStable();
|
|
51
51
|
frame = await driver.capture();
|
|
52
|
-
await saveSnapshot(frame,
|
|
53
|
-
console.log(
|
|
54
|
-
|
|
52
|
+
await saveSnapshot(frame, "workflow-03-dashboard");
|
|
53
|
+
console.log("Saved dashboard view");
|
|
54
|
+
|
|
55
55
|
// Step 4: Change time scale to 7d
|
|
56
56
|
// Need to find how to change timescale - likely a dropdown or key
|
|
57
|
-
console.log(
|
|
57
|
+
console.log("Changing time scale to 7d...");
|
|
58
58
|
// Try common keys for time scale selection
|
|
59
59
|
// Check if there's a visible control for this
|
|
60
|
-
|
|
60
|
+
|
|
61
61
|
// Try 't' for time or look for other controls
|
|
62
|
-
await driver.pressKey(
|
|
62
|
+
await driver.pressKey("t");
|
|
63
63
|
await driver.waitForStable();
|
|
64
64
|
frame = await driver.capture();
|
|
65
|
-
await saveSnapshot(frame,
|
|
66
|
-
|
|
65
|
+
await saveSnapshot(frame, "workflow-04-timescale-menu");
|
|
66
|
+
|
|
67
67
|
// If a menu opened, try to select 7d
|
|
68
|
-
if (frame.includes(
|
|
68
|
+
if (frame.includes("7d") || frame.includes("7 d")) {
|
|
69
69
|
// Navigate to 7d option
|
|
70
|
-
await driver.pressArrow(
|
|
70
|
+
await driver.pressArrow("down");
|
|
71
71
|
await driver.waitForStable();
|
|
72
|
-
await driver.pressArrow(
|
|
72
|
+
await driver.pressArrow("down");
|
|
73
73
|
await driver.waitForStable();
|
|
74
74
|
frame = await driver.capture();
|
|
75
|
-
await saveSnapshot(frame,
|
|
75
|
+
await saveSnapshot(frame, "workflow-05-timescale-selecting");
|
|
76
76
|
await driver.pressEnter();
|
|
77
77
|
await driver.waitForStable();
|
|
78
78
|
}
|
|
79
|
-
|
|
79
|
+
|
|
80
80
|
frame = await driver.capture();
|
|
81
|
-
await saveSnapshot(frame,
|
|
82
|
-
console.log(
|
|
83
|
-
|
|
81
|
+
await saveSnapshot(frame, "workflow-06-after-timescale");
|
|
82
|
+
console.log("Saved after timescale change");
|
|
83
|
+
|
|
84
84
|
// Step 5: Filter with "opus"
|
|
85
85
|
console.log('Filtering with "opus"...');
|
|
86
86
|
// Try '/' for search/filter (common pattern)
|
|
87
|
-
await driver.pressKey(
|
|
87
|
+
await driver.pressKey("/");
|
|
88
88
|
await driver.waitForStable();
|
|
89
89
|
frame = await driver.capture();
|
|
90
|
-
await saveSnapshot(frame,
|
|
91
|
-
|
|
90
|
+
await saveSnapshot(frame, "workflow-07-filter-input");
|
|
91
|
+
|
|
92
92
|
// Type "opus"
|
|
93
|
-
await driver.typeText(
|
|
93
|
+
await driver.typeText("opus");
|
|
94
94
|
await driver.waitForStable();
|
|
95
95
|
frame = await driver.capture();
|
|
96
|
-
await saveSnapshot(frame,
|
|
97
|
-
|
|
96
|
+
await saveSnapshot(frame, "workflow-08-filter-opus");
|
|
97
|
+
|
|
98
98
|
// Press enter to apply filter
|
|
99
99
|
await driver.pressEnter();
|
|
100
100
|
await driver.waitForStable();
|
|
101
|
-
|
|
101
|
+
|
|
102
102
|
// Final result
|
|
103
103
|
frame = await driver.capture();
|
|
104
|
-
await saveSnapshot(frame,
|
|
105
|
-
console.log(
|
|
106
|
-
|
|
104
|
+
await saveSnapshot(frame, "workflow-09-final-result");
|
|
105
|
+
console.log("Saved final filtered result");
|
|
106
|
+
|
|
107
107
|
await driver.close();
|
|
108
|
-
|
|
109
|
-
console.log(
|
|
108
|
+
|
|
109
|
+
console.log("\n✓ Workflow complete! Check ./snapshots/workflow-*.txt");
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
main().catch((err) => {
|
|
113
|
-
console.error(
|
|
113
|
+
console.error("Workflow failed:", err);
|
|
114
114
|
process.exit(1);
|
|
115
115
|
});
|
package/src/tui/driver/test.ts
CHANGED
|
@@ -1,47 +1,47 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
import { createDriver } from
|
|
2
|
+
import { createDriver } from "./driver.ts";
|
|
3
3
|
|
|
4
4
|
async function main() {
|
|
5
|
-
console.log(
|
|
5
|
+
console.log("Creating driver...");
|
|
6
6
|
const driver = await createDriver({ width: 100, height: 30 });
|
|
7
|
-
|
|
8
|
-
console.log(
|
|
7
|
+
|
|
8
|
+
console.log("Launching app...");
|
|
9
9
|
await driver.launch();
|
|
10
|
-
|
|
11
|
-
console.log(
|
|
10
|
+
|
|
11
|
+
console.log("Waiting for initial render...");
|
|
12
12
|
await driver.waitForStable();
|
|
13
|
-
|
|
14
|
-
console.log(
|
|
13
|
+
|
|
14
|
+
console.log("Capturing initial frame...");
|
|
15
15
|
const frame1 = await driver.capture();
|
|
16
|
-
console.log(
|
|
16
|
+
console.log("=== INITIAL FRAME ===");
|
|
17
17
|
console.log(frame1);
|
|
18
|
-
console.log(
|
|
19
|
-
|
|
18
|
+
console.log("=== END FRAME ===\n");
|
|
19
|
+
|
|
20
20
|
console.log('Pressing "2" to switch to Providers view...');
|
|
21
|
-
await driver.pressKey(
|
|
21
|
+
await driver.pressKey("2");
|
|
22
22
|
await driver.waitForStable();
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
const frame2 = await driver.capture();
|
|
25
|
-
console.log(
|
|
25
|
+
console.log("=== PROVIDERS VIEW ===");
|
|
26
26
|
console.log(frame2);
|
|
27
|
-
console.log(
|
|
28
|
-
|
|
27
|
+
console.log("=== END FRAME ===\n");
|
|
28
|
+
|
|
29
29
|
console.log('Pressing "3" to switch to Trends view...');
|
|
30
|
-
await driver.pressKey(
|
|
30
|
+
await driver.pressKey("3");
|
|
31
31
|
await driver.waitForStable();
|
|
32
|
-
|
|
32
|
+
|
|
33
33
|
const frame3 = await driver.capture();
|
|
34
|
-
console.log(
|
|
34
|
+
console.log("=== TRENDS VIEW ===");
|
|
35
35
|
console.log(frame3);
|
|
36
|
-
console.log(
|
|
37
|
-
|
|
38
|
-
console.log(
|
|
36
|
+
console.log("=== END FRAME ===\n");
|
|
37
|
+
|
|
38
|
+
console.log("Closing driver...");
|
|
39
39
|
await driver.close();
|
|
40
|
-
|
|
41
|
-
console.log(
|
|
40
|
+
|
|
41
|
+
console.log("Done!");
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
main().catch((err) => {
|
|
45
|
-
console.error(
|
|
45
|
+
console.error("Test failed:", err);
|
|
46
46
|
process.exit(1);
|
|
47
47
|
});
|
package/src/tui/hooks/index.ts
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export {
|
|
11
|
-
export type {
|
|
12
|
-
|
|
13
|
-
export {
|
|
14
|
-
export
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
export
|
|
1
|
+
export type { EasingFunction, UseAnimatedValueOptions } from "./useAnimatedValue.ts";
|
|
2
|
+
export {
|
|
3
|
+
easings,
|
|
4
|
+
useAnimatedCount,
|
|
5
|
+
useAnimatedCurrency,
|
|
6
|
+
useAnimatedTokens,
|
|
7
|
+
useAnimatedValue,
|
|
8
|
+
} from "./useAnimatedValue.ts";
|
|
9
|
+
export type { DashboardDeltas, UseDashboardStateResult } from "./useDashboardState.ts";
|
|
10
|
+
export { useDashboardState } from "./useDashboardState.ts";
|
|
11
|
+
export type { ActivityDebugData, ActivityState, UseActivityRateResult } from "./useEmaActivity.ts";
|
|
12
|
+
export { useEmaActivity } from "./useEmaActivity.ts";
|
|
13
|
+
export type { UseEntranceAnimationOptions } from "./useEntranceAnimation.ts";
|
|
14
|
+
export { applyEntranceFade, useEntranceAnimation } from "./useEntranceAnimation.ts";
|
|
15
|
+
export type {
|
|
16
|
+
ExitAnimationItem,
|
|
17
|
+
UseExitAnimationOptions,
|
|
18
|
+
UseExitAnimationResult,
|
|
19
|
+
} from "./useExitAnimation.ts";
|
|
20
|
+
export { useExitAnimation } from "./useExitAnimation.ts";
|
|
21
|
+
export type { UsePulseOptions } from "./usePulse.ts";
|
|
22
|
+
export { getPulseColor, usePulse } from "./usePulse.ts";
|
|
23
|
+
export type { UseValueFlashOptions, UseValueFlashResult } from "./useValueFlash.ts";
|
|
24
|
+
export { interpolateColor, useValueFlash } from "./useValueFlash.ts";
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
2
|
|
|
3
3
|
export type EasingFunction = (t: number) => number;
|
|
4
4
|
|
|
5
5
|
export const easings = {
|
|
6
6
|
linear: (t: number) => t,
|
|
7
7
|
easeOutQuad: (t: number) => 1 - (1 - t) * (1 - t),
|
|
8
|
-
easeOutCubic: (t: number) => 1 -
|
|
9
|
-
easeOutExpo: (t: number) => (t === 1 ? 1 : 1 -
|
|
10
|
-
easeInOutQuad: (t: number) => t < 0.5 ? 2 * t * t : 1 -
|
|
8
|
+
easeOutCubic: (t: number) => 1 - (1 - t) ** 3,
|
|
9
|
+
easeOutExpo: (t: number) => (t === 1 ? 1 : 1 - 2 ** (-10 * t)),
|
|
10
|
+
easeInOutQuad: (t: number) => (t < 0.5 ? 2 * t * t : 1 - (-2 * t + 2) ** 2 / 2),
|
|
11
11
|
} as const;
|
|
12
12
|
|
|
13
13
|
export interface UseAnimatedValueOptions {
|
|
@@ -19,14 +19,9 @@ export interface UseAnimatedValueOptions {
|
|
|
19
19
|
|
|
20
20
|
export function useAnimatedValue(
|
|
21
21
|
targetValue: number,
|
|
22
|
-
options: UseAnimatedValueOptions = {}
|
|
22
|
+
options: UseAnimatedValueOptions = {},
|
|
23
23
|
): number {
|
|
24
|
-
const {
|
|
25
|
-
durationMs = 300,
|
|
26
|
-
easing = 'easeOutQuad',
|
|
27
|
-
precision = 2,
|
|
28
|
-
frameRate = 60,
|
|
29
|
-
} = options;
|
|
24
|
+
const { durationMs = 300, easing = "easeOutQuad", precision = 2, frameRate = 60 } = options;
|
|
30
25
|
|
|
31
26
|
const [displayValue, setDisplayValue] = useState(targetValue);
|
|
32
27
|
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
@@ -34,7 +29,7 @@ export function useAnimatedValue(
|
|
|
34
29
|
const startTimeRef = useRef<number>(Date.now());
|
|
35
30
|
const targetRef = useRef(targetValue);
|
|
36
31
|
|
|
37
|
-
const easingFn = typeof easing ===
|
|
32
|
+
const easingFn = typeof easing === "function" ? easing : easings[easing];
|
|
38
33
|
const frameInterval = Math.round(1000 / frameRate);
|
|
39
34
|
|
|
40
35
|
useEffect(() => {
|
|
@@ -54,11 +49,11 @@ export function useAnimatedValue(
|
|
|
54
49
|
const elapsed = Date.now() - startTimeRef.current;
|
|
55
50
|
const progress = Math.min(elapsed / durationMs, 1);
|
|
56
51
|
const easedProgress = easingFn(progress);
|
|
57
|
-
|
|
52
|
+
|
|
58
53
|
const startVal = startValueRef.current;
|
|
59
54
|
const currentValue = startVal + (targetRef.current - startVal) * easedProgress;
|
|
60
55
|
const roundedValue = Number(currentValue.toFixed(precision));
|
|
61
|
-
|
|
56
|
+
|
|
62
57
|
setDisplayValue(roundedValue);
|
|
63
58
|
|
|
64
59
|
if (progress >= 1) {
|
|
@@ -83,7 +78,7 @@ export function useAnimatedValue(
|
|
|
83
78
|
|
|
84
79
|
export function useAnimatedCurrency(
|
|
85
80
|
targetValue: number,
|
|
86
|
-
options: Omit<UseAnimatedValueOptions,
|
|
81
|
+
options: Omit<UseAnimatedValueOptions, "precision"> = {},
|
|
87
82
|
): string {
|
|
88
83
|
const animatedValue = useAnimatedValue(targetValue, { ...options, precision: 2 });
|
|
89
84
|
return `$${animatedValue.toFixed(2)}`;
|
|
@@ -91,10 +86,10 @@ export function useAnimatedCurrency(
|
|
|
91
86
|
|
|
92
87
|
export function useAnimatedTokens(
|
|
93
88
|
targetValue: number,
|
|
94
|
-
options: Omit<UseAnimatedValueOptions,
|
|
89
|
+
options: Omit<UseAnimatedValueOptions, "precision"> = {},
|
|
95
90
|
): string {
|
|
96
91
|
const animatedValue = useAnimatedValue(targetValue, { ...options, precision: 0 });
|
|
97
|
-
|
|
92
|
+
|
|
98
93
|
if (animatedValue >= 1_000_000) {
|
|
99
94
|
return `${(animatedValue / 1_000_000).toFixed(1)}M`;
|
|
100
95
|
}
|
|
@@ -106,7 +101,7 @@ export function useAnimatedTokens(
|
|
|
106
101
|
|
|
107
102
|
export function useAnimatedCount(
|
|
108
103
|
targetValue: number,
|
|
109
|
-
options: Omit<UseAnimatedValueOptions,
|
|
104
|
+
options: Omit<UseAnimatedValueOptions, "precision"> = {},
|
|
110
105
|
): string {
|
|
111
106
|
const animatedValue = useAnimatedValue(targetValue, { ...options, precision: 0 });
|
|
112
107
|
return Math.round(animatedValue).toLocaleString();
|