@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.
Files changed (160) hide show
  1. package/README.md +2 -5
  2. package/package.json +8 -5
  3. package/src/agents/aggregator.ts +39 -19
  4. package/src/agents/costing.ts +11 -9
  5. package/src/agents/index.ts +7 -9
  6. package/src/agents/types.ts +2 -2
  7. package/src/cli.ts +28 -23
  8. package/src/config/schema.test.ts +42 -40
  9. package/src/config/schema.ts +26 -26
  10. package/src/demo/simulator.ts +161 -122
  11. package/src/plugins/agents/index.ts +2 -2
  12. package/src/plugins/auth-sources.ts +42 -38
  13. package/src/plugins/lifecycle.ts +42 -38
  14. package/src/plugins/loader.test.ts +49 -49
  15. package/src/plugins/loader.ts +67 -63
  16. package/src/plugins/notification-bus.ts +21 -16
  17. package/src/plugins/notifications/index.ts +2 -2
  18. package/src/plugins/notifications/terminal-bell.ts +36 -23
  19. package/src/plugins/notifications/visual-flash.ts +31 -19
  20. package/src/plugins/npm-installer.ts +23 -25
  21. package/src/plugins/plugin-context-factory.ts +10 -10
  22. package/src/plugins/plugin-host.ts +12 -5
  23. package/src/plugins/providers/anthropic.ts +64 -57
  24. package/src/plugins/providers/antigravity.ts +81 -69
  25. package/src/plugins/providers/codex.ts +42 -42
  26. package/src/plugins/providers/gemini.ts +106 -87
  27. package/src/plugins/providers/github-copilot.ts +49 -45
  28. package/src/plugins/providers/index.ts +10 -12
  29. package/src/plugins/providers/minimax.ts +50 -44
  30. package/src/plugins/providers/openai-api.ts +82 -62
  31. package/src/plugins/providers/opencode-zen.ts +46 -39
  32. package/src/plugins/providers/perplexity.ts +55 -46
  33. package/src/plugins/providers/zai.ts +68 -57
  34. package/src/plugins/registry.ts +80 -71
  35. package/src/plugins/sandbox-guard.test.ts +45 -48
  36. package/src/plugins/sandbox-guard.ts +20 -25
  37. package/src/plugins/sandbox.ts +26 -48
  38. package/src/plugins/themes/catppuccin-latte.ts +29 -29
  39. package/src/plugins/themes/catppuccin-mocha.ts +29 -29
  40. package/src/plugins/themes/claude-code.ts +29 -29
  41. package/src/plugins/themes/dracula.ts +29 -29
  42. package/src/plugins/themes/github-light.ts +29 -29
  43. package/src/plugins/themes/gruvbox-dark.ts +29 -29
  44. package/src/plugins/themes/gruvbox-light.ts +29 -29
  45. package/src/plugins/themes/index.ts +15 -15
  46. package/src/plugins/themes/kanagawa.ts +29 -29
  47. package/src/plugins/themes/nord.ts +29 -29
  48. package/src/plugins/themes/one-dark.ts +29 -29
  49. package/src/plugins/themes/opencode.ts +31 -31
  50. package/src/plugins/themes/rose-pine-dawn.ts +29 -29
  51. package/src/plugins/themes/rose-pine.ts +29 -29
  52. package/src/plugins/themes/solarized-light.ts +29 -29
  53. package/src/plugins/themes/tokyo-night.ts +29 -29
  54. package/src/plugins/types/agent.ts +5 -5
  55. package/src/plugins/types/base.ts +35 -24
  56. package/src/plugins/types/index.ts +9 -9
  57. package/src/plugins/types/notification.ts +12 -12
  58. package/src/plugins/types/provider.ts +12 -8
  59. package/src/plugins/types/theme.ts +31 -23
  60. package/src/plugins/update-checker.ts +6 -6
  61. package/src/pricing/estimator.test.ts +39 -44
  62. package/src/pricing/estimator.ts +7 -13
  63. package/src/pricing/fallback.test.ts +30 -34
  64. package/src/pricing/fallback.ts +54 -27
  65. package/src/pricing/index.ts +18 -12
  66. package/src/pricing/models-dev.ts +6 -6
  67. package/src/storage/database.ts +56 -46
  68. package/src/storage/db.ts +12 -12
  69. package/src/storage/index.ts +44 -45
  70. package/src/storage/migrations/index.ts +2 -2
  71. package/src/storage/paths.ts +13 -13
  72. package/src/storage/persistence-service.ts +206 -0
  73. package/src/storage/repos/agentSessions.ts +33 -23
  74. package/src/storage/repos/providerSnapshots.ts +5 -5
  75. package/src/storage/repos/usageEvents.ts +40 -28
  76. package/src/storage/retention.ts +146 -0
  77. package/src/storage/types.ts +5 -5
  78. package/src/tui/App.tsx +180 -137
  79. package/src/tui/components/CommandPalette.tsx +46 -34
  80. package/src/tui/components/DebugConsole.tsx +39 -33
  81. package/src/tui/components/DebugPanel.tsx +314 -183
  82. package/src/tui/components/FooterHints.tsx +2 -2
  83. package/src/tui/components/GhostProviderCard.tsx +37 -35
  84. package/src/tui/components/Header.tsx +68 -35
  85. package/src/tui/components/HelpOverlay.tsx +99 -29
  86. package/src/tui/components/InlineGauge.tsx +10 -7
  87. package/src/tui/components/InlineSparkline.tsx +17 -11
  88. package/src/tui/components/KpiStrip.tsx +128 -98
  89. package/src/tui/components/LimitGauge.tsx +96 -59
  90. package/src/tui/components/ModalBackdrop.tsx +4 -4
  91. package/src/tui/components/ProviderAggregateStrip.tsx +69 -38
  92. package/src/tui/components/ProviderCard.tsx +232 -226
  93. package/src/tui/components/ProviderDetailPanel.tsx +133 -58
  94. package/src/tui/components/ProviderLimitsPanel.tsx +126 -89
  95. package/src/tui/components/ProvidersList.tsx +215 -126
  96. package/src/tui/components/SessionDetailsDrawer.tsx +191 -94
  97. package/src/tui/components/SessionsTable.tsx +320 -169
  98. package/src/tui/components/SettingsModal.tsx +593 -269
  99. package/src/tui/components/SidebarBreakdown.tsx +30 -16
  100. package/src/tui/components/Skeleton.tsx +24 -17
  101. package/src/tui/components/SmartSidebar.tsx +360 -258
  102. package/src/tui/components/Sparkline.tsx +64 -52
  103. package/src/tui/components/Spinner.tsx +3 -3
  104. package/src/tui/components/StatusBar.tsx +27 -27
  105. package/src/tui/components/ThemePicker.tsx +67 -66
  106. package/src/tui/components/Toast.tsx +9 -6
  107. package/src/tui/components/UsageGauge.tsx +10 -13
  108. package/src/tui/components/index.ts +5 -5
  109. package/src/tui/contexts/AgentSessionContext.tsx +164 -151
  110. package/src/tui/contexts/ConfigContext.tsx +15 -13
  111. package/src/tui/contexts/DashboardRuntimeContext.tsx +26 -26
  112. package/src/tui/contexts/DemoModeContext.tsx +22 -15
  113. package/src/tui/contexts/DrawerContext.tsx +11 -9
  114. package/src/tui/contexts/InputContext.tsx +2 -2
  115. package/src/tui/contexts/LogContext.tsx +62 -47
  116. package/src/tui/contexts/PluginContext.tsx +238 -183
  117. package/src/tui/contexts/RealTimeActivityContext.tsx +70 -36
  118. package/src/tui/contexts/StorageContext.tsx +95 -149
  119. package/src/tui/contexts/ThemeContext.tsx +21 -16
  120. package/src/tui/contexts/TimeWindowContext.tsx +72 -61
  121. package/src/tui/contexts/ToastContext.tsx +10 -7
  122. package/src/tui/contexts/index.ts +9 -5
  123. package/src/tui/createApp.tsx +8 -12
  124. package/src/tui/debug/captureFrame.ts +25 -28
  125. package/src/tui/debug/snapshot.tsx +179 -162
  126. package/src/tui/driver/assertions.ts +30 -22
  127. package/src/tui/driver/cli.ts +231 -221
  128. package/src/tui/driver/coverage.ts +27 -44
  129. package/src/tui/driver/demo-snapshot.ts +33 -33
  130. package/src/tui/driver/diff.ts +26 -32
  131. package/src/tui/driver/driver.ts +43 -48
  132. package/src/tui/driver/index.ts +36 -40
  133. package/src/tui/driver/recorder.ts +37 -48
  134. package/src/tui/driver/test-workflow.ts +51 -51
  135. package/src/tui/driver/test.ts +25 -25
  136. package/src/tui/hooks/index.ts +24 -20
  137. package/src/tui/hooks/useAnimatedValue.ts +13 -18
  138. package/src/tui/hooks/useDashboardKeyboard.ts +118 -98
  139. package/src/tui/hooks/useDashboardState.ts +26 -24
  140. package/src/tui/hooks/useEmaActivity.ts +82 -74
  141. package/src/tui/hooks/useEntranceAnimation.ts +11 -11
  142. package/src/tui/hooks/useExitAnimation.ts +7 -3
  143. package/src/tui/hooks/usePulse.ts +4 -4
  144. package/src/tui/hooks/useSafeRenderer.ts +5 -5
  145. package/src/tui/hooks/useSessionEnrichedProviders.ts +150 -0
  146. package/src/tui/hooks/useValueFlash.ts +10 -14
  147. package/src/tui/index.tsx +29 -6
  148. package/src/tui/shutdown.ts +19 -0
  149. package/src/tui/utils/providerColor.ts +1 -1
  150. package/src/tui/views/Dashboard.tsx +164 -102
  151. package/src/tui/views/HistoricalTrendsView.tsx +579 -250
  152. package/src/tui/views/ProjectsView.tsx +495 -243
  153. package/src/tui/views/RealTimeDashboard.tsx +176 -116
  154. package/src/tui/views/SettingsView.tsx +225 -189
  155. package/src/tui/views/index.ts +2 -2
  156. package/src/types/opentui-test-utils.d.ts +7 -7
  157. package/src/utils/clipboard.ts +29 -29
  158. package/src/utils/currency.test.ts +53 -53
  159. package/src/utils/currency.ts +35 -32
  160. package/src/version.ts +1 -1
@@ -1,6 +1,6 @@
1
- import * as fs from 'fs/promises';
2
- import * as path from 'path';
3
- import type { Driver } from './driver.ts';
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 'sendKeys':
195
- await driver.sendKeys(String(params.keys ?? ''));
187
+ case "sendKeys":
188
+ await driver.sendKeys(String(params.keys ?? ""));
196
189
  break;
197
190
 
198
- case 'pressKey':
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 'pressTab':
198
+ case "pressTab":
206
199
  await driver.pressTab();
207
200
  break;
208
201
 
209
- case 'pressEnter':
202
+ case "pressEnter":
210
203
  await driver.pressEnter();
211
204
  break;
212
205
 
213
- case 'pressEscape':
206
+ case "pressEscape":
214
207
  await driver.pressEscape();
215
208
  break;
216
209
 
217
- case 'pressArrow':
218
- await driver.pressArrow(params.direction as 'up' | 'down' | 'left' | 'right');
210
+ case "pressArrow":
211
+ await driver.pressArrow(params.direction as "up" | "down" | "left" | "right");
219
212
  break;
220
213
 
221
- case 'typeText':
214
+ case "typeText":
222
215
  await driver.typeText(
223
- String(params.text ?? ''),
224
- typeof params.delay === 'number' ? params.delay : 0
216
+ String(params.text ?? ""),
217
+ typeof params.delay === "number" ? params.delay : 0,
225
218
  );
226
219
  break;
227
220
 
228
- case 'resize':
221
+ case "resize":
229
222
  await driver.resize(
230
- typeof params.cols === 'number' ? params.cols : 100,
231
- typeof params.rows === 'number' ? params.rows : 30
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 'waitForStable':
228
+ case "waitForStable":
236
229
  await driver.waitForStable({
237
- maxIterations:
238
- typeof params.maxIterations === 'number' ? params.maxIterations : 10,
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 'waitForText':
245
- await driver.waitForText(String(params.text ?? ''), {
246
- timeout: typeof params.timeout === 'number' ? params.timeout : 5000,
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 = './recordings';
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), 'utf-8');
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, 'utf-8');
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('.json')).map((f) => f.replace('.json', ''));
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 { createDriver } from './driver.ts';
3
- import * as fs from 'fs/promises';
4
- import * as path from 'path';
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 = './snapshots';
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, 'utf-8');
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('Launching app...');
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('Navigating to Settings...');
25
- await driver.pressKey('5');
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('Looking for Alerts section...');
30
+ console.log("Looking for Alerts section...");
31
31
  let frame = await driver.capture();
32
- await saveSnapshot(frame, 'workflow-01-settings');
33
- console.log('Saved settings view');
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('Alerts') && !frame.includes('alerts')) {
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, 'workflow-02-alerts-settings');
45
- console.log('Saved alerts settings');
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('Navigating to Dashboard...');
49
- await driver.pressKey('1');
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, 'workflow-03-dashboard');
53
- console.log('Saved dashboard view');
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('Changing time scale to 7d...');
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('t');
62
+ await driver.pressKey("t");
63
63
  await driver.waitForStable();
64
64
  frame = await driver.capture();
65
- await saveSnapshot(frame, 'workflow-04-timescale-menu');
66
-
65
+ await saveSnapshot(frame, "workflow-04-timescale-menu");
66
+
67
67
  // If a menu opened, try to select 7d
68
- if (frame.includes('7d') || frame.includes('7 d')) {
68
+ if (frame.includes("7d") || frame.includes("7 d")) {
69
69
  // Navigate to 7d option
70
- await driver.pressArrow('down');
70
+ await driver.pressArrow("down");
71
71
  await driver.waitForStable();
72
- await driver.pressArrow('down');
72
+ await driver.pressArrow("down");
73
73
  await driver.waitForStable();
74
74
  frame = await driver.capture();
75
- await saveSnapshot(frame, 'workflow-05-timescale-selecting');
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, 'workflow-06-after-timescale');
82
- console.log('Saved after timescale change');
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, 'workflow-07-filter-input');
91
-
90
+ await saveSnapshot(frame, "workflow-07-filter-input");
91
+
92
92
  // Type "opus"
93
- await driver.typeText('opus');
93
+ await driver.typeText("opus");
94
94
  await driver.waitForStable();
95
95
  frame = await driver.capture();
96
- await saveSnapshot(frame, 'workflow-08-filter-opus');
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, 'workflow-09-final-result');
105
- console.log('Saved final filtered result');
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('\n✓ Workflow complete! Check ./snapshots/workflow-*.txt');
108
+
109
+ console.log("\n✓ Workflow complete! Check ./snapshots/workflow-*.txt");
110
110
  }
111
111
 
112
112
  main().catch((err) => {
113
- console.error('Workflow failed:', err);
113
+ console.error("Workflow failed:", err);
114
114
  process.exit(1);
115
115
  });
@@ -1,47 +1,47 @@
1
1
  #!/usr/bin/env bun
2
- import { createDriver } from './driver.ts';
2
+ import { createDriver } from "./driver.ts";
3
3
 
4
4
  async function main() {
5
- console.log('Creating driver...');
5
+ console.log("Creating driver...");
6
6
  const driver = await createDriver({ width: 100, height: 30 });
7
-
8
- console.log('Launching app...');
7
+
8
+ console.log("Launching app...");
9
9
  await driver.launch();
10
-
11
- console.log('Waiting for initial render...');
10
+
11
+ console.log("Waiting for initial render...");
12
12
  await driver.waitForStable();
13
-
14
- console.log('Capturing initial frame...');
13
+
14
+ console.log("Capturing initial frame...");
15
15
  const frame1 = await driver.capture();
16
- console.log('=== INITIAL FRAME ===');
16
+ console.log("=== INITIAL FRAME ===");
17
17
  console.log(frame1);
18
- console.log('=== END FRAME ===\n');
19
-
18
+ console.log("=== END FRAME ===\n");
19
+
20
20
  console.log('Pressing "2" to switch to Providers view...');
21
- await driver.pressKey('2');
21
+ await driver.pressKey("2");
22
22
  await driver.waitForStable();
23
-
23
+
24
24
  const frame2 = await driver.capture();
25
- console.log('=== PROVIDERS VIEW ===');
25
+ console.log("=== PROVIDERS VIEW ===");
26
26
  console.log(frame2);
27
- console.log('=== END FRAME ===\n');
28
-
27
+ console.log("=== END FRAME ===\n");
28
+
29
29
  console.log('Pressing "3" to switch to Trends view...');
30
- await driver.pressKey('3');
30
+ await driver.pressKey("3");
31
31
  await driver.waitForStable();
32
-
32
+
33
33
  const frame3 = await driver.capture();
34
- console.log('=== TRENDS VIEW ===');
34
+ console.log("=== TRENDS VIEW ===");
35
35
  console.log(frame3);
36
- console.log('=== END FRAME ===\n');
37
-
38
- console.log('Closing driver...');
36
+ console.log("=== END FRAME ===\n");
37
+
38
+ console.log("Closing driver...");
39
39
  await driver.close();
40
-
41
- console.log('Done!');
40
+
41
+ console.log("Done!");
42
42
  }
43
43
 
44
44
  main().catch((err) => {
45
- console.error('Test failed:', err);
45
+ console.error("Test failed:", err);
46
46
  process.exit(1);
47
47
  });
@@ -1,20 +1,24 @@
1
- export { useEmaActivity } from './useEmaActivity.ts';
2
- export type { ActivityState, ActivityDebugData, UseActivityRateResult } from './useEmaActivity.ts';
3
-
4
- export { useDashboardState } from './useDashboardState.ts';
5
- export type { DashboardDeltas, UseDashboardStateResult } from './useDashboardState.ts';
6
-
7
- export { usePulse, getPulseColor } from './usePulse.ts';
8
- export type { UsePulseOptions } from './usePulse.ts';
9
-
10
- export { useValueFlash, interpolateColor } from './useValueFlash.ts';
11
- export type { UseValueFlashOptions, UseValueFlashResult } from './useValueFlash.ts';
12
-
13
- export { useAnimatedValue, useAnimatedCurrency, useAnimatedTokens, useAnimatedCount, easings } from './useAnimatedValue.ts';
14
- export type { UseAnimatedValueOptions, EasingFunction } from './useAnimatedValue.ts';
15
-
16
- export { useEntranceAnimation, applyEntranceFade } from './useEntranceAnimation.ts';
17
- export type { UseEntranceAnimationOptions } from './useEntranceAnimation.ts';
18
-
19
- export { useExitAnimation } from './useExitAnimation.ts';
20
- export type { ExitAnimationItem, UseExitAnimationOptions, UseExitAnimationResult } from './useExitAnimation.ts';
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 { useState, useEffect, useRef } from 'react';
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 - Math.pow(1 - t, 3),
9
- easeOutExpo: (t: number) => (t === 1 ? 1 : 1 - Math.pow(2, -10 * t)),
10
- easeInOutQuad: (t: number) => t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2,
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 === 'function' ? easing : easings[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, 'precision'> = {}
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, 'precision'> = {}
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, 'precision'> = {}
104
+ options: Omit<UseAnimatedValueOptions, "precision"> = {},
110
105
  ): string {
111
106
  const animatedValue = useAnimatedValue(targetValue, { ...options, precision: 0 });
112
107
  return Math.round(animatedValue).toLocaleString();