@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,4 +1,4 @@
|
|
|
1
|
-
import { useColors } from
|
|
1
|
+
import { useColors } from "../contexts/ThemeContext.tsx";
|
|
2
2
|
|
|
3
3
|
export interface HintItem {
|
|
4
4
|
key: string;
|
|
@@ -15,7 +15,7 @@ export function FooterHints({ hints }: FooterHintsProps) {
|
|
|
15
15
|
return (
|
|
16
16
|
<box flexDirection="row" height={1} paddingLeft={1}>
|
|
17
17
|
<text fg={colors.textSubtle}>
|
|
18
|
-
{hints.map((h, i) => `${h.key} ${h.action}${i < hints.length - 1 ?
|
|
18
|
+
{hints.map((h, i) => `${h.key} ${h.action}${i < hints.length - 1 ? " " : ""}`).join("")}
|
|
19
19
|
</text>
|
|
20
20
|
</box>
|
|
21
21
|
);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import { useColors } from
|
|
1
|
+
import type { BoxRenderable } from "@opentui/core";
|
|
2
|
+
import { forwardRef } from "react";
|
|
3
|
+
import { useColors } from "../contexts/ThemeContext.tsx";
|
|
4
4
|
|
|
5
5
|
interface GhostProviderCardProps {
|
|
6
6
|
name: string;
|
|
@@ -8,38 +8,40 @@ interface GhostProviderCardProps {
|
|
|
8
8
|
onFocus?: () => void;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export const GhostProviderCard = forwardRef<BoxRenderable, GhostProviderCardProps>(
|
|
12
|
-
name,
|
|
13
|
-
|
|
14
|
-
onFocus,
|
|
15
|
-
}, ref) => {
|
|
16
|
-
const colors = useColors();
|
|
11
|
+
export const GhostProviderCard = forwardRef<BoxRenderable, GhostProviderCardProps>(
|
|
12
|
+
({ name, focused = false, onFocus }, ref) => {
|
|
13
|
+
const colors = useColors();
|
|
17
14
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
{
|
|
34
|
-
</
|
|
35
|
-
<text fg={colors.textSubtle}>○</text>
|
|
36
|
-
</box>
|
|
15
|
+
return (
|
|
16
|
+
<box
|
|
17
|
+
ref={ref}
|
|
18
|
+
border
|
|
19
|
+
borderStyle="rounded"
|
|
20
|
+
borderColor={focused ? colors.textSubtle : colors.textSubtle}
|
|
21
|
+
padding={1}
|
|
22
|
+
flexDirection="column"
|
|
23
|
+
gap={1}
|
|
24
|
+
width={44}
|
|
25
|
+
minHeight={8}
|
|
26
|
+
{...(onFocus ? { onMouseDown: onFocus } : {})}
|
|
27
|
+
>
|
|
28
|
+
<box flexDirection="row" justifyContent="space-between" alignItems="center">
|
|
29
|
+
<text fg={colors.textSubtle}>{name}</text>
|
|
30
|
+
<text fg={colors.textSubtle}>○</text>
|
|
31
|
+
</box>
|
|
37
32
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
33
|
+
<box
|
|
34
|
+
flexDirection="column"
|
|
35
|
+
flexGrow={1}
|
|
36
|
+
justifyContent="center"
|
|
37
|
+
alignItems="center"
|
|
38
|
+
gap={1}
|
|
39
|
+
>
|
|
40
|
+
<text fg={colors.textSubtle}>Not configured</text>
|
|
41
|
+
<text fg={colors.textMuted}>Set API key to enable</text>
|
|
42
|
+
</box>
|
|
41
43
|
</box>
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
GhostProviderCard.displayName =
|
|
44
|
+
);
|
|
45
|
+
},
|
|
46
|
+
);
|
|
47
|
+
GhostProviderCard.displayName = "GhostProviderCard";
|
|
@@ -1,51 +1,56 @@
|
|
|
1
|
-
import { useTerminalDimensions } from
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { useTerminalDimensions } from "@opentui/react";
|
|
2
|
+
import { VERSION } from "../../version.ts";
|
|
3
|
+
import { useColors, useTheme } from "../contexts/ThemeContext.tsx";
|
|
4
4
|
|
|
5
5
|
interface HeaderProps {
|
|
6
6
|
title?: string;
|
|
7
7
|
subtitle?: string;
|
|
8
|
-
activeView?:
|
|
8
|
+
activeView?: "dashboard" | "providers" | "trends" | "projects";
|
|
9
9
|
demoMode?: boolean;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
const ASCII_LOGO = [
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
"████████╗ ██████╗ ██╗ ██╗███████╗███╗ ██╗████████╗ ██████╗ ██████╗ ",
|
|
14
|
+
"╚══██╔══╝██╔═══██╗██║ ██╔╝██╔════╝████╗ ██║╚══██╔══╝██╔═══██╗██╔══██╗",
|
|
15
|
+
" ██║ ██║ ██║█████╔╝ █████╗ ██╔██╗ ██║ ██║ ██║ ██║██████╔╝",
|
|
16
|
+
" ██║ ██║ ██║██╔═██╗ ██╔══╝ ██║╚██╗██║ ██║ ██║ ██║██╔═══╝ ",
|
|
17
|
+
" ██║ ╚██████╔╝██║ ██╗███████╗██║ ╚████║ ██║ ╚██████╔╝██║ ",
|
|
18
|
+
" ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═════╝ ╚═╝ ",
|
|
19
19
|
];
|
|
20
20
|
|
|
21
21
|
const MIN_HEIGHT_FOR_LARGE_LOGO = 35;
|
|
22
22
|
|
|
23
|
-
export function Header({
|
|
23
|
+
export function Header({
|
|
24
|
+
title = "tokentop",
|
|
25
|
+
subtitle,
|
|
26
|
+
activeView,
|
|
27
|
+
demoMode = false,
|
|
28
|
+
}: HeaderProps) {
|
|
24
29
|
const colors = useColors();
|
|
25
30
|
const { components } = useTheme();
|
|
26
31
|
const { height } = useTerminalDimensions();
|
|
27
32
|
|
|
28
|
-
const isDashboard = activeView ===
|
|
29
|
-
const isProviders = activeView ===
|
|
30
|
-
const isTrends = activeView ===
|
|
31
|
-
const isProjects = activeView ===
|
|
33
|
+
const isDashboard = activeView === "dashboard";
|
|
34
|
+
const isProviders = activeView === "providers";
|
|
35
|
+
const isTrends = activeView === "trends";
|
|
36
|
+
const isProjects = activeView === "projects";
|
|
32
37
|
const useLargeLogo = height >= MIN_HEIGHT_FOR_LARGE_LOGO;
|
|
33
38
|
|
|
34
39
|
const titleColor = components.header?.titleColor;
|
|
35
40
|
const titleAccentColor = components.header?.titleAccentColor;
|
|
36
|
-
const hasSplitTitle = titleColor && titleAccentColor && title ===
|
|
41
|
+
const hasSplitTitle = titleColor && titleAccentColor && title === "tokentop";
|
|
37
42
|
|
|
38
43
|
const headerHeight = useLargeLogo ? 7 : 1;
|
|
39
44
|
|
|
40
45
|
return (
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
<box
|
|
47
|
+
flexDirection="column"
|
|
48
|
+
backgroundColor={colors.foreground}
|
|
49
|
+
flexShrink={0}
|
|
50
|
+
height={headerHeight}
|
|
51
|
+
paddingX={1}
|
|
52
|
+
overflow="hidden"
|
|
53
|
+
>
|
|
49
54
|
{useLargeLogo && (
|
|
50
55
|
<box flexDirection="column" alignItems="center" height={6}>
|
|
51
56
|
{ASCII_LOGO.map((line, idx) => {
|
|
@@ -58,7 +63,11 @@ export function Header({ title = 'tokentop', subtitle, activeView, demoMode = fa
|
|
|
58
63
|
</text>
|
|
59
64
|
);
|
|
60
65
|
}
|
|
61
|
-
return
|
|
66
|
+
return (
|
|
67
|
+
<text key={idx} height={1} fg={colors.primary}>
|
|
68
|
+
{line}
|
|
69
|
+
</text>
|
|
70
|
+
);
|
|
62
71
|
})}
|
|
63
72
|
</box>
|
|
64
73
|
)}
|
|
@@ -67,8 +76,12 @@ export function Header({ title = 'tokentop', subtitle, activeView, demoMode = fa
|
|
|
67
76
|
<box flexDirection="row" gap={1} alignItems="center" height={1}>
|
|
68
77
|
{hasSplitTitle ? (
|
|
69
78
|
<text height={1}>
|
|
70
|
-
<span fg={titleColor}
|
|
71
|
-
|
|
79
|
+
<span fg={titleColor}>
|
|
80
|
+
<strong>token</strong>
|
|
81
|
+
</span>
|
|
82
|
+
<span fg={titleAccentColor}>
|
|
83
|
+
<strong>top</strong>
|
|
84
|
+
</span>
|
|
72
85
|
</text>
|
|
73
86
|
) : (
|
|
74
87
|
<text height={1}>
|
|
@@ -78,47 +91,67 @@ export function Header({ title = 'tokentop', subtitle, activeView, demoMode = fa
|
|
|
78
91
|
</text>
|
|
79
92
|
)}
|
|
80
93
|
{demoMode && (
|
|
81
|
-
<text height={1} fg={colors.warning}
|
|
94
|
+
<text height={1} fg={colors.warning}>
|
|
95
|
+
<strong>DEMO</strong>
|
|
96
|
+
</text>
|
|
82
97
|
)}
|
|
83
98
|
{subtitle && (
|
|
84
|
-
<text height={1} fg={colors.textMuted}>
|
|
99
|
+
<text height={1} fg={colors.textMuted}>
|
|
100
|
+
{subtitle}
|
|
101
|
+
</text>
|
|
85
102
|
)}
|
|
86
103
|
</box>
|
|
87
104
|
)}
|
|
88
105
|
{useLargeLogo && <box width={10} />}
|
|
89
106
|
{activeView && (
|
|
90
|
-
<box
|
|
107
|
+
<box
|
|
108
|
+
flexDirection="row"
|
|
109
|
+
gap={useLargeLogo ? 1 : 0}
|
|
110
|
+
marginLeft={useLargeLogo ? 0 : 2}
|
|
111
|
+
alignItems="center"
|
|
112
|
+
height={1}
|
|
113
|
+
>
|
|
91
114
|
<text height={1}>
|
|
92
115
|
{isDashboard ? (
|
|
93
|
-
<span bg={colors.primary} fg={colors.background}
|
|
116
|
+
<span bg={colors.primary} fg={colors.background}>
|
|
117
|
+
<strong> 1 DASHBOARD </strong>
|
|
118
|
+
</span>
|
|
94
119
|
) : (
|
|
95
120
|
<span fg={colors.textMuted}> 1 DASHBOARD </span>
|
|
96
121
|
)}
|
|
97
122
|
</text>
|
|
98
123
|
<text height={1}>
|
|
99
124
|
{isProviders ? (
|
|
100
|
-
<span bg={colors.primary} fg={colors.background}
|
|
125
|
+
<span bg={colors.primary} fg={colors.background}>
|
|
126
|
+
<strong> 2 PROVIDERS </strong>
|
|
127
|
+
</span>
|
|
101
128
|
) : (
|
|
102
129
|
<span fg={colors.textMuted}> 2 PROVIDERS </span>
|
|
103
130
|
)}
|
|
104
131
|
</text>
|
|
105
132
|
<text height={1}>
|
|
106
133
|
{isTrends ? (
|
|
107
|
-
<span bg={colors.primary} fg={colors.background}
|
|
134
|
+
<span bg={colors.primary} fg={colors.background}>
|
|
135
|
+
<strong> 3 TRENDS </strong>
|
|
136
|
+
</span>
|
|
108
137
|
) : (
|
|
109
138
|
<span fg={colors.textMuted}> 3 TRENDS </span>
|
|
110
139
|
)}
|
|
111
140
|
</text>
|
|
112
141
|
<text height={1}>
|
|
113
142
|
{isProjects ? (
|
|
114
|
-
<span bg={colors.primary} fg={colors.background}
|
|
143
|
+
<span bg={colors.primary} fg={colors.background}>
|
|
144
|
+
<strong> 4 PROJECTS </strong>
|
|
145
|
+
</span>
|
|
115
146
|
) : (
|
|
116
147
|
<span fg={colors.textMuted}> 4 PROJECTS </span>
|
|
117
148
|
)}
|
|
118
149
|
</text>
|
|
119
150
|
</box>
|
|
120
151
|
)}
|
|
121
|
-
<text height={1} fg={colors.textMuted}>
|
|
152
|
+
<text height={1} fg={colors.textMuted}>
|
|
153
|
+
v{VERSION}
|
|
154
|
+
</text>
|
|
122
155
|
</box>
|
|
123
156
|
</box>
|
|
124
157
|
);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { useTerminalDimensions } from
|
|
2
|
-
import { useColors } from
|
|
3
|
-
import { ModalBackdrop, Z_INDEX } from
|
|
1
|
+
import { useTerminalDimensions } from "@opentui/react";
|
|
2
|
+
import { useColors } from "../contexts/ThemeContext.tsx";
|
|
3
|
+
import { ModalBackdrop, Z_INDEX } from "./ModalBackdrop.tsx";
|
|
4
4
|
|
|
5
5
|
export function HelpOverlay() {
|
|
6
6
|
const colors = useColors();
|
|
@@ -9,36 +9,106 @@ export function HelpOverlay() {
|
|
|
9
9
|
|
|
10
10
|
return (
|
|
11
11
|
<ModalBackdrop zIndex={Z_INDEX.MODAL}>
|
|
12
|
-
<box
|
|
13
|
-
width={46}
|
|
14
|
-
height={isCompact ? 18 : 20}
|
|
15
|
-
border
|
|
16
|
-
borderStyle="double"
|
|
17
|
-
borderColor={colors.primary}
|
|
18
|
-
flexDirection="column"
|
|
12
|
+
<box
|
|
13
|
+
width={46}
|
|
14
|
+
height={isCompact ? 18 : 20}
|
|
15
|
+
border
|
|
16
|
+
borderStyle="double"
|
|
17
|
+
borderColor={colors.primary}
|
|
18
|
+
flexDirection="column"
|
|
19
19
|
padding={1}
|
|
20
20
|
backgroundColor={colors.background}
|
|
21
21
|
overflow="hidden"
|
|
22
22
|
>
|
|
23
|
-
<box justifyContent="center" height={1}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
<box height={1} marginTop={1}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
<box
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
23
|
+
<box justifyContent="center" height={1}>
|
|
24
|
+
<text height={1}>
|
|
25
|
+
<strong>Help</strong>
|
|
26
|
+
</text>
|
|
27
|
+
</box>
|
|
28
|
+
|
|
29
|
+
<box height={1} marginTop={1}>
|
|
30
|
+
<text height={1} fg={colors.primary}>
|
|
31
|
+
<strong>Navigation</strong>
|
|
32
|
+
</text>
|
|
33
|
+
</box>
|
|
34
|
+
<box flexDirection="row" height={1}>
|
|
35
|
+
<text width={10} height={1} fg={colors.textMuted}>
|
|
36
|
+
↑↓ j/k
|
|
37
|
+
</text>
|
|
38
|
+
<text height={1}>Navigate</text>
|
|
39
|
+
</box>
|
|
40
|
+
<box flexDirection="row" height={1}>
|
|
41
|
+
<text width={10} height={1} fg={colors.textMuted}>
|
|
42
|
+
Enter
|
|
43
|
+
</text>
|
|
44
|
+
<text height={1}>View details</text>
|
|
45
|
+
</box>
|
|
46
|
+
|
|
47
|
+
<box height={1} marginTop={1}>
|
|
48
|
+
<text height={1} fg={colors.primary}>
|
|
49
|
+
<strong>Actions</strong>
|
|
50
|
+
</text>
|
|
51
|
+
</box>
|
|
52
|
+
<box flexDirection="row" height={1}>
|
|
53
|
+
<text width={10} height={1} fg={colors.textMuted}>
|
|
54
|
+
/
|
|
55
|
+
</text>
|
|
56
|
+
<text height={1}>Filter</text>
|
|
57
|
+
</box>
|
|
58
|
+
<box flexDirection="row" height={1}>
|
|
59
|
+
<text width={10} height={1} fg={colors.textMuted}>
|
|
60
|
+
s
|
|
61
|
+
</text>
|
|
62
|
+
<text height={1}>Sort</text>
|
|
63
|
+
</box>
|
|
64
|
+
<box flexDirection="row" height={1}>
|
|
65
|
+
<text width={10} height={1} fg={colors.textMuted}>
|
|
66
|
+
t
|
|
67
|
+
</text>
|
|
68
|
+
<text height={1}>Time window</text>
|
|
69
|
+
</box>
|
|
70
|
+
|
|
71
|
+
<box height={1} marginTop={1}>
|
|
72
|
+
<text height={1} fg={colors.primary}>
|
|
73
|
+
<strong>Global</strong>
|
|
74
|
+
</text>
|
|
75
|
+
</box>
|
|
76
|
+
<box flexDirection="row" height={1}>
|
|
77
|
+
<text width={10} height={1} fg={colors.textMuted}>
|
|
78
|
+
1-4
|
|
79
|
+
</text>
|
|
80
|
+
<text height={1}>Switch views</text>
|
|
81
|
+
</box>
|
|
82
|
+
<box flexDirection="row" height={1}>
|
|
83
|
+
<text width={10} height={1} fg={colors.textMuted}>
|
|
84
|
+
,
|
|
85
|
+
</text>
|
|
86
|
+
<text height={1}>Settings</text>
|
|
87
|
+
</box>
|
|
88
|
+
<box flexDirection="row" height={1}>
|
|
89
|
+
<text width={10} height={1} fg={colors.textMuted}>
|
|
90
|
+
:
|
|
91
|
+
</text>
|
|
92
|
+
<text height={1}>Command palette</text>
|
|
93
|
+
</box>
|
|
94
|
+
<box flexDirection="row" height={1}>
|
|
95
|
+
<text width={10} height={1} fg={colors.textMuted}>
|
|
96
|
+
~
|
|
97
|
+
</text>
|
|
98
|
+
<text height={1}>Debug panel</text>
|
|
99
|
+
</box>
|
|
100
|
+
<box flexDirection="row" height={1}>
|
|
101
|
+
<text width={10} height={1} fg={colors.textMuted}>
|
|
102
|
+
q
|
|
103
|
+
</text>
|
|
104
|
+
<text height={1}>Quit</text>
|
|
105
|
+
</box>
|
|
106
|
+
|
|
107
|
+
<box justifyContent="center" height={1} marginTop={1}>
|
|
108
|
+
<text height={1} fg={colors.textMuted}>
|
|
109
|
+
? or Esc to close
|
|
110
|
+
</text>
|
|
111
|
+
</box>
|
|
42
112
|
</box>
|
|
43
113
|
</ModalBackdrop>
|
|
44
114
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useColors } from
|
|
1
|
+
import { useColors } from "../contexts/ThemeContext.tsx";
|
|
2
2
|
|
|
3
3
|
interface InlineGaugeProps {
|
|
4
4
|
percent: number | null;
|
|
@@ -10,7 +10,7 @@ export function InlineGauge({ percent, width = 20, color }: InlineGaugeProps) {
|
|
|
10
10
|
const colors = useColors();
|
|
11
11
|
|
|
12
12
|
if (percent === null || percent === undefined) {
|
|
13
|
-
const emptyBar =
|
|
13
|
+
const emptyBar = "·".repeat(width);
|
|
14
14
|
return <text fg={colors.textSubtle}>{emptyBar}</text>;
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -18,12 +18,15 @@ export function InlineGauge({ percent, width = 20, color }: InlineGaugeProps) {
|
|
|
18
18
|
const filledWidth = Math.round((clamped / 100) * width);
|
|
19
19
|
const emptyWidth = width - filledWidth;
|
|
20
20
|
|
|
21
|
-
const fillColor =
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
const fillColor =
|
|
22
|
+
clamped >= 90
|
|
23
|
+
? colors.gaugeDanger
|
|
24
|
+
: clamped >= 70
|
|
25
|
+
? colors.gaugeWarning
|
|
26
|
+
: (color ?? colors.gaugeFill);
|
|
24
27
|
|
|
25
|
-
const filledBar =
|
|
26
|
-
const emptyBar =
|
|
28
|
+
const filledBar = "█".repeat(filledWidth);
|
|
29
|
+
const emptyBar = "·".repeat(emptyWidth);
|
|
27
30
|
|
|
28
31
|
return (
|
|
29
32
|
<text>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import type { UsageSnapshot } from "../contexts/PluginContext.tsx";
|
|
2
|
+
import { useColors } from "../contexts/ThemeContext.tsx";
|
|
3
3
|
|
|
4
4
|
interface InlineSparklineProps {
|
|
5
5
|
history: UsageSnapshot[];
|
|
@@ -30,10 +30,10 @@ export function InlineSparkline({ history, width = 8 }: InlineSparklineProps) {
|
|
|
30
30
|
const colors = useColors();
|
|
31
31
|
|
|
32
32
|
if (history.length < 2) {
|
|
33
|
-
return <text fg={colors.textSubtle}>{
|
|
33
|
+
return <text fg={colors.textSubtle}>{"⣀".repeat(width)}</text>;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
const values = history.map(s => s.usedPercent ?? 0);
|
|
36
|
+
const values = history.map((s) => s.usedPercent ?? 0);
|
|
37
37
|
const effectiveWidth = width * 2;
|
|
38
38
|
const displayData = values.slice(-effectiveWidth);
|
|
39
39
|
|
|
@@ -50,12 +50,16 @@ export function InlineSparkline({ history, width = 8 }: InlineSparklineProps) {
|
|
|
50
50
|
const rightNorm = normalize(rightRaw);
|
|
51
51
|
|
|
52
52
|
const dominantRaw = Math.max(leftRaw, rightRaw);
|
|
53
|
-
const color =
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
const color =
|
|
54
|
+
dominantRaw >= 90
|
|
55
|
+
? colors.error
|
|
56
|
+
: dominantRaw >= 70
|
|
57
|
+
? colors.warning
|
|
58
|
+
: dominantRaw > 0
|
|
59
|
+
? colors.success
|
|
60
|
+
: colors.textSubtle;
|
|
57
61
|
|
|
58
|
-
const char = dominantRaw <= 0 ?
|
|
62
|
+
const char = dominantRaw <= 0 ? "⣀" : valueToBraille(leftNorm, rightNorm);
|
|
59
63
|
|
|
60
64
|
if (groups.length > 0 && groups[groups.length - 1]!.color === color) {
|
|
61
65
|
groups[groups.length - 1]!.chars += char;
|
|
@@ -69,9 +73,11 @@ export function InlineSparkline({ history, width = 8 }: InlineSparklineProps) {
|
|
|
69
73
|
|
|
70
74
|
return (
|
|
71
75
|
<text>
|
|
72
|
-
{padding > 0 && <span fg={colors.textSubtle}>{
|
|
76
|
+
{padding > 0 && <span fg={colors.textSubtle}>{"⣀".repeat(padding)}</span>}
|
|
73
77
|
{groups.map((group, i) => (
|
|
74
|
-
<span key={i} fg={group.color}>
|
|
78
|
+
<span key={i} fg={group.color}>
|
|
79
|
+
{group.chars}
|
|
80
|
+
</span>
|
|
75
81
|
))}
|
|
76
82
|
</text>
|
|
77
83
|
);
|