@tokscale/cli 1.4.3 → 2.0.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/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +128 -0
- package/dist/index.js.map +1 -0
- package/package.json +19 -26
- package/dist/auth.d.ts +0 -17
- package/dist/auth.d.ts.map +0 -1
- package/dist/auth.js +0 -162
- package/dist/auth.js.map +0 -1
- package/dist/cli.d.ts +0 -9
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -1550
- package/dist/cli.js.map +0 -1
- package/dist/credentials.d.ts +0 -50
- package/dist/credentials.d.ts.map +0 -1
- package/dist/credentials.js +0 -151
- package/dist/credentials.js.map +0 -1
- package/dist/cursor.d.ts +0 -167
- package/dist/cursor.d.ts.map +0 -1
- package/dist/cursor.js +0 -906
- package/dist/cursor.js.map +0 -1
- package/dist/date-utils.d.ts +0 -10
- package/dist/date-utils.d.ts.map +0 -1
- package/dist/date-utils.js +0 -47
- package/dist/date-utils.js.map +0 -1
- package/dist/graph-types.d.ts +0 -142
- package/dist/graph-types.d.ts.map +0 -1
- package/dist/graph-types.js +0 -6
- package/dist/graph-types.js.map +0 -1
- package/dist/native-runner.d.ts +0 -11
- package/dist/native-runner.d.ts.map +0 -1
- package/dist/native-runner.js +0 -77
- package/dist/native-runner.js.map +0 -1
- package/dist/native.d.ts +0 -106
- package/dist/native.d.ts.map +0 -1
- package/dist/native.js +0 -302
- package/dist/native.js.map +0 -1
- package/dist/sessions/types.d.ts +0 -28
- package/dist/sessions/types.d.ts.map +0 -1
- package/dist/sessions/types.js +0 -27
- package/dist/sessions/types.js.map +0 -1
- package/dist/spinner.d.ts +0 -75
- package/dist/spinner.d.ts.map +0 -1
- package/dist/spinner.js +0 -203
- package/dist/spinner.js.map +0 -1
- package/dist/submit.d.ts +0 -23
- package/dist/submit.d.ts.map +0 -1
- package/dist/submit.js +0 -294
- package/dist/submit.js.map +0 -1
- package/dist/table.d.ts +0 -42
- package/dist/table.d.ts.map +0 -1
- package/dist/table.js +0 -181
- package/dist/table.js.map +0 -1
- package/dist/tui/App.d.ts +0 -4
- package/dist/tui/App.d.ts.map +0 -1
- package/dist/tui/App.js +0 -333
- package/dist/tui/App.js.map +0 -1
- package/dist/tui/components/BarChart.d.ts +0 -17
- package/dist/tui/components/BarChart.d.ts.map +0 -1
- package/dist/tui/components/BarChart.js +0 -146
- package/dist/tui/components/BarChart.js.map +0 -1
- package/dist/tui/components/DailyView.d.ts +0 -13
- package/dist/tui/components/DailyView.d.ts.map +0 -1
- package/dist/tui/components/DailyView.js +0 -86
- package/dist/tui/components/DailyView.js.map +0 -1
- package/dist/tui/components/DateBreakdownPanel.d.ts +0 -7
- package/dist/tui/components/DateBreakdownPanel.d.ts.map +0 -1
- package/dist/tui/components/DateBreakdownPanel.js +0 -36
- package/dist/tui/components/DateBreakdownPanel.js.map +0 -1
- package/dist/tui/components/Footer.d.ts +0 -28
- package/dist/tui/components/Footer.d.ts.map +0 -1
- package/dist/tui/components/Footer.js +0 -130
- package/dist/tui/components/Footer.js.map +0 -1
- package/dist/tui/components/Header.d.ts +0 -9
- package/dist/tui/components/Header.d.ts.map +0 -1
- package/dist/tui/components/Header.js +0 -20
- package/dist/tui/components/Header.js.map +0 -1
- package/dist/tui/components/Legend.d.ts +0 -7
- package/dist/tui/components/Legend.d.ts.map +0 -1
- package/dist/tui/components/Legend.js +0 -16
- package/dist/tui/components/Legend.js.map +0 -1
- package/dist/tui/components/LoadingSpinner.d.ts +0 -8
- package/dist/tui/components/LoadingSpinner.d.ts.map +0 -1
- package/dist/tui/components/LoadingSpinner.js +0 -55
- package/dist/tui/components/LoadingSpinner.js.map +0 -1
- package/dist/tui/components/ModelRow.d.ts +0 -13
- package/dist/tui/components/ModelRow.d.ts.map +0 -1
- package/dist/tui/components/ModelRow.js +0 -15
- package/dist/tui/components/ModelRow.js.map +0 -1
- package/dist/tui/components/ModelView.d.ts +0 -13
- package/dist/tui/components/ModelView.d.ts.map +0 -1
- package/dist/tui/components/ModelView.js +0 -96
- package/dist/tui/components/ModelView.js.map +0 -1
- package/dist/tui/components/OverviewView.d.ts +0 -14
- package/dist/tui/components/OverviewView.d.ts.map +0 -1
- package/dist/tui/components/OverviewView.js +0 -65
- package/dist/tui/components/OverviewView.js.map +0 -1
- package/dist/tui/components/StatsView.d.ts +0 -14
- package/dist/tui/components/StatsView.d.ts.map +0 -1
- package/dist/tui/components/StatsView.js +0 -102
- package/dist/tui/components/StatsView.js.map +0 -1
- package/dist/tui/components/TokenBreakdown.d.ts +0 -14
- package/dist/tui/components/TokenBreakdown.d.ts.map +0 -1
- package/dist/tui/components/TokenBreakdown.js +0 -10
- package/dist/tui/components/TokenBreakdown.js.map +0 -1
- package/dist/tui/components/index.d.ts +0 -16
- package/dist/tui/components/index.d.ts.map +0 -1
- package/dist/tui/components/index.js +0 -13
- package/dist/tui/components/index.js.map +0 -1
- package/dist/tui/config/settings.d.ts +0 -15
- package/dist/tui/config/settings.d.ts.map +0 -1
- package/dist/tui/config/settings.js +0 -147
- package/dist/tui/config/settings.js.map +0 -1
- package/dist/tui/config/themes.d.ts +0 -15
- package/dist/tui/config/themes.d.ts.map +0 -1
- package/dist/tui/config/themes.js +0 -82
- package/dist/tui/config/themes.js.map +0 -1
- package/dist/tui/hooks/useData.d.ts +0 -19
- package/dist/tui/hooks/useData.d.ts.map +0 -1
- package/dist/tui/hooks/useData.js +0 -468
- package/dist/tui/hooks/useData.js.map +0 -1
- package/dist/tui/index.d.ts +0 -4
- package/dist/tui/index.d.ts.map +0 -1
- package/dist/tui/index.js +0 -36
- package/dist/tui/index.js.map +0 -1
- package/dist/tui/types/index.d.ts +0 -137
- package/dist/tui/types/index.d.ts.map +0 -1
- package/dist/tui/types/index.js +0 -26
- package/dist/tui/types/index.js.map +0 -1
- package/dist/tui/utils/cleanup.d.ts +0 -22
- package/dist/tui/utils/cleanup.d.ts.map +0 -1
- package/dist/tui/utils/cleanup.js +0 -59
- package/dist/tui/utils/cleanup.js.map +0 -1
- package/dist/tui/utils/colors.d.ts +0 -19
- package/dist/tui/utils/colors.d.ts.map +0 -1
- package/dist/tui/utils/colors.js +0 -71
- package/dist/tui/utils/colors.js.map +0 -1
- package/dist/tui/utils/format.d.ts +0 -7
- package/dist/tui/utils/format.d.ts.map +0 -1
- package/dist/tui/utils/format.js +0 -45
- package/dist/tui/utils/format.js.map +0 -1
- package/dist/tui/utils/responsive.d.ts +0 -5
- package/dist/tui/utils/responsive.d.ts.map +0 -1
- package/dist/tui/utils/responsive.js +0 -5
- package/dist/tui/utils/responsive.js.map +0 -1
- package/dist/wrapped.d.ts +0 -43
- package/dist/wrapped.d.ts.map +0 -1
- package/dist/wrapped.js +0 -719
- package/dist/wrapped.js.map +0 -1
- package/src/auth.ts +0 -211
- package/src/cli.ts +0 -1892
- package/src/credentials.ts +0 -176
- package/src/cursor.ts +0 -1044
- package/src/date-utils.ts +0 -51
- package/src/graph-types.ts +0 -175
- package/src/native-runner.js +0 -4
- package/src/native-runner.ts +0 -91
- package/src/native.ts +0 -633
- package/src/sessions/types.ts +0 -59
- package/src/spinner.ts +0 -283
- package/src/submit.ts +0 -360
- package/src/table.ts +0 -233
- package/src/tui/App.tsx +0 -453
- package/src/tui/components/BarChart.tsx +0 -205
- package/src/tui/components/DailyView.tsx +0 -132
- package/src/tui/components/DateBreakdownPanel.tsx +0 -79
- package/src/tui/components/Footer.tsx +0 -380
- package/src/tui/components/Header.tsx +0 -68
- package/src/tui/components/Legend.tsx +0 -39
- package/src/tui/components/LoadingSpinner.tsx +0 -81
- package/src/tui/components/ModelRow.tsx +0 -47
- package/src/tui/components/ModelView.tsx +0 -147
- package/src/tui/components/OverviewView.tsx +0 -121
- package/src/tui/components/StatsView.tsx +0 -249
- package/src/tui/components/TokenBreakdown.tsx +0 -46
- package/src/tui/components/index.ts +0 -15
- package/src/tui/config/settings.ts +0 -183
- package/src/tui/config/themes.ts +0 -115
- package/src/tui/hooks/useData.ts +0 -558
- package/src/tui/index.tsx +0 -44
- package/src/tui/opentui.d.ts +0 -166
- package/src/tui/types/index.ts +0 -173
- package/src/tui/utils/cleanup.ts +0 -65
- package/src/tui/utils/colors.ts +0 -78
- package/src/tui/utils/format.ts +0 -36
- package/src/tui/utils/responsive.ts +0 -8
- package/src/types.d.ts +0 -28
- package/src/wrapped.ts +0 -848
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
import { For, Show, createMemo } from "solid-js";
|
|
2
|
-
import { formatTokensCompact } from "../utils/format.js";
|
|
3
|
-
import { isNarrow, isVeryNarrow } from "../utils/responsive.js";
|
|
4
|
-
|
|
5
|
-
export interface ChartDataPoint {
|
|
6
|
-
date: string;
|
|
7
|
-
models: { modelId: string; tokens: number; color: string }[];
|
|
8
|
-
total: number;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
interface BarChartProps {
|
|
12
|
-
data: ChartDataPoint[];
|
|
13
|
-
width: number;
|
|
14
|
-
height: number;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const BLOCKS = [" ", "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"];
|
|
18
|
-
const MONTH_NAMES = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
19
|
-
const REPEAT_CACHE_MAX_SIZE = 256;
|
|
20
|
-
|
|
21
|
-
const repeatCache = new Map<string, string>();
|
|
22
|
-
function getRepeatedString(char: string, count: number): string {
|
|
23
|
-
const key = `${char}:${count}`;
|
|
24
|
-
let cached = repeatCache.get(key);
|
|
25
|
-
if (!cached) {
|
|
26
|
-
if (repeatCache.size >= REPEAT_CACHE_MAX_SIZE) {
|
|
27
|
-
const firstKey = repeatCache.keys().next().value;
|
|
28
|
-
if (firstKey) repeatCache.delete(firstKey);
|
|
29
|
-
}
|
|
30
|
-
cached = char.repeat(count);
|
|
31
|
-
repeatCache.set(key, cached);
|
|
32
|
-
}
|
|
33
|
-
return cached;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function BarChart(props: BarChartProps) {
|
|
37
|
-
const data = () => props.data;
|
|
38
|
-
const width = () => props.width;
|
|
39
|
-
const height = () => props.height;
|
|
40
|
-
|
|
41
|
-
const isNarrowTerminal = () => isNarrow(width());
|
|
42
|
-
const isVeryNarrowTerminal = () => isVeryNarrow(width());
|
|
43
|
-
|
|
44
|
-
const safeHeight = () => Math.max(height(), 1);
|
|
45
|
-
|
|
46
|
-
const maxTotal = createMemo(() => {
|
|
47
|
-
const arr = data();
|
|
48
|
-
if (arr.length === 0) return 1;
|
|
49
|
-
let max = arr[0].total;
|
|
50
|
-
for (let i = 1; i < arr.length; i++) {
|
|
51
|
-
if (arr[i].total > max) max = arr[i].total;
|
|
52
|
-
}
|
|
53
|
-
return Math.max(max, 1);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
const chartWidth = () => Math.max(width() - 8, 20);
|
|
57
|
-
|
|
58
|
-
const getBarWidth = (index: number, total: number) => {
|
|
59
|
-
const cw = chartWidth();
|
|
60
|
-
if (total === 0) return 1;
|
|
61
|
-
const start = Math.floor((index * cw) / total);
|
|
62
|
-
const end = Math.floor(((index + 1) * cw) / total);
|
|
63
|
-
return Math.max(1, end - start);
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
const visibleData = () => data();
|
|
67
|
-
|
|
68
|
-
const sortedModelsMap = createMemo(() => {
|
|
69
|
-
const vd = visibleData();
|
|
70
|
-
const map = new Map<string, { modelId: string; tokens: number; color: string }[]>();
|
|
71
|
-
for (const point of vd) {
|
|
72
|
-
const models = point.models ?? [];
|
|
73
|
-
const sorted = [...models].sort((a, b) => a.modelId.localeCompare(b.modelId));
|
|
74
|
-
map.set(point.date, sorted);
|
|
75
|
-
}
|
|
76
|
-
return map;
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
const rowIndices = createMemo(() => {
|
|
80
|
-
const sh = safeHeight();
|
|
81
|
-
const indices: number[] = new Array(sh);
|
|
82
|
-
for (let i = 0; i < sh; i++) {
|
|
83
|
-
indices[i] = sh - 1 - i;
|
|
84
|
-
}
|
|
85
|
-
return indices;
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
const dateLabels = createMemo(() => {
|
|
89
|
-
const vd = visibleData();
|
|
90
|
-
if (vd.length === 0) return [];
|
|
91
|
-
|
|
92
|
-
const labelInterval = Math.max(1, Math.floor(vd.length / (isVeryNarrowTerminal() ? 2 : 3)));
|
|
93
|
-
const labels: string[] = [];
|
|
94
|
-
|
|
95
|
-
for (let i = 0; i < vd.length; i += labelInterval) {
|
|
96
|
-
const dateStr = vd[i].date;
|
|
97
|
-
const parts = dateStr.split("-");
|
|
98
|
-
if (parts.length === 3) {
|
|
99
|
-
const month = parseInt(parts[1], 10);
|
|
100
|
-
const day = parseInt(parts[2], 10);
|
|
101
|
-
labels.push(isVeryNarrowTerminal() ? `${month}/${day}` : `${MONTH_NAMES[month - 1]} ${day}`);
|
|
102
|
-
} else {
|
|
103
|
-
labels.push(dateStr.slice(5));
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return labels;
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
const axisWidth = () => chartWidth();
|
|
110
|
-
const labelPadding = () => {
|
|
111
|
-
const labels = dateLabels();
|
|
112
|
-
return labels.length > 0 ? Math.floor(axisWidth() / labels.length) : 0;
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
const chartTitle = () => isVeryNarrowTerminal() ? "Tokens" : "Tokens per Day";
|
|
116
|
-
|
|
117
|
-
const getBarContent = (point: ChartDataPoint, row: number, barIndex: number): { char: string; color: string } => {
|
|
118
|
-
const mt = maxTotal();
|
|
119
|
-
const sh = safeHeight();
|
|
120
|
-
const rowThreshold = ((row + 1) / sh) * mt;
|
|
121
|
-
const prevThreshold = (row / sh) * mt;
|
|
122
|
-
const thresholdDiff = rowThreshold - prevThreshold;
|
|
123
|
-
const bw = getBarWidth(barIndex, visibleData().length);
|
|
124
|
-
|
|
125
|
-
if (point.total <= prevThreshold) {
|
|
126
|
-
return { char: getRepeatedString(" ", bw), color: "dim" };
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const sortedModels = sortedModelsMap().get(point.date) ?? [];
|
|
130
|
-
if (sortedModels.length === 0) {
|
|
131
|
-
return { char: getRepeatedString(" ", bw), color: "dim" };
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
let currentHeight = 0;
|
|
135
|
-
let maxOverlap = 0;
|
|
136
|
-
let color = sortedModels[0].color;
|
|
137
|
-
|
|
138
|
-
const rowStart = prevThreshold;
|
|
139
|
-
const rowEnd = rowThreshold;
|
|
140
|
-
|
|
141
|
-
for (const m of sortedModels) {
|
|
142
|
-
const mStart = currentHeight;
|
|
143
|
-
const mEnd = currentHeight + m.tokens;
|
|
144
|
-
currentHeight += m.tokens;
|
|
145
|
-
|
|
146
|
-
const overlapStart = Math.max(mStart, rowStart);
|
|
147
|
-
const overlapEnd = Math.min(mEnd, rowEnd);
|
|
148
|
-
const overlap = Math.max(0, overlapEnd - overlapStart);
|
|
149
|
-
|
|
150
|
-
if (overlap > maxOverlap) {
|
|
151
|
-
maxOverlap = overlap;
|
|
152
|
-
color = m.color;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (point.total >= rowThreshold) {
|
|
157
|
-
return { char: getRepeatedString("█", bw), color };
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
const ratio = thresholdDiff > 0 ? (point.total - prevThreshold) / thresholdDiff : 1;
|
|
161
|
-
const blockIndex = Math.min(8, Math.max(1, Math.floor(ratio * 8)));
|
|
162
|
-
return { char: getRepeatedString(BLOCKS[blockIndex], bw), color };
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
return (
|
|
166
|
-
<Show when={data().length > 0} fallback={<text dim>No chart data</text>}>
|
|
167
|
-
<box flexDirection="column">
|
|
168
|
-
<text bold>{chartTitle()}</text>
|
|
169
|
-
<For each={rowIndices()}>
|
|
170
|
-
{(row) => {
|
|
171
|
-
const yLabelWidth = isVeryNarrowTerminal() ? 5 : 6;
|
|
172
|
-
const yLabel = row === safeHeight() - 1
|
|
173
|
-
? formatTokensCompact(maxTotal()).padStart(yLabelWidth)
|
|
174
|
-
: " ".repeat(yLabelWidth);
|
|
175
|
-
return (
|
|
176
|
-
<box flexDirection="row">
|
|
177
|
-
<text dim>{yLabel}│</text>
|
|
178
|
-
<For each={visibleData()}>
|
|
179
|
-
{(point, barIndex) => {
|
|
180
|
-
const bar = getBarContent(point, row, barIndex());
|
|
181
|
-
return bar.color === "dim"
|
|
182
|
-
? <text dim>{bar.char}</text>
|
|
183
|
-
: <text fg={bar.color}>{bar.char}</text>;
|
|
184
|
-
}}
|
|
185
|
-
</For>
|
|
186
|
-
</box>
|
|
187
|
-
);
|
|
188
|
-
}}
|
|
189
|
-
</For>
|
|
190
|
-
<box flexDirection="row">
|
|
191
|
-
<text dim>{isVeryNarrowTerminal() ? " 0│" : " 0│"}</text>
|
|
192
|
-
<text dim>{getRepeatedString("─", axisWidth())}</text>
|
|
193
|
-
</box>
|
|
194
|
-
<Show when={dateLabels().length > 0}>
|
|
195
|
-
<box flexDirection="row">
|
|
196
|
-
<text dim>{isVeryNarrowTerminal() ? " " : " "}</text>
|
|
197
|
-
<text dim>
|
|
198
|
-
{dateLabels().map((l) => l.padEnd(labelPadding())).join("")}
|
|
199
|
-
</text>
|
|
200
|
-
</box>
|
|
201
|
-
</Show>
|
|
202
|
-
</box>
|
|
203
|
-
</Show>
|
|
204
|
-
);
|
|
205
|
-
}
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import { For, createMemo, type Accessor } from "solid-js";
|
|
2
|
-
import type { TUIData, SortType } from "../hooks/useData.js";
|
|
3
|
-
import { formatTokensCompact, formatCostFull } from "../utils/format.js";
|
|
4
|
-
import { isNarrow } from "../utils/responsive.js";
|
|
5
|
-
|
|
6
|
-
const STRIPE_BG = "#232328";
|
|
7
|
-
|
|
8
|
-
const INPUT_COL_WIDTH = 12;
|
|
9
|
-
const OUTPUT_COL_WIDTH = 12;
|
|
10
|
-
const CACHE_READ_COL_WIDTH = 10;
|
|
11
|
-
const CACHE_WRITE_COL_WIDTH = 10;
|
|
12
|
-
const TOTAL_COL_WIDTH = 14;
|
|
13
|
-
const COST_COL_WIDTH = 12;
|
|
14
|
-
const METRIC_COLUMNS_WIDTH_FULL = INPUT_COL_WIDTH + OUTPUT_COL_WIDTH + CACHE_READ_COL_WIDTH + CACHE_WRITE_COL_WIDTH + TOTAL_COL_WIDTH + COST_COL_WIDTH;
|
|
15
|
-
const METRIC_COLUMNS_WIDTH_NARROW = TOTAL_COL_WIDTH + COST_COL_WIDTH;
|
|
16
|
-
const SIDE_PADDING = 2;
|
|
17
|
-
const MIN_DATE_COLUMN = 14;
|
|
18
|
-
|
|
19
|
-
interface DailyViewProps {
|
|
20
|
-
data: TUIData;
|
|
21
|
-
sortBy: SortType;
|
|
22
|
-
sortDesc: boolean;
|
|
23
|
-
selectedIndex: Accessor<number>;
|
|
24
|
-
height: number;
|
|
25
|
-
width?: number;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function DailyView(props: DailyViewProps) {
|
|
29
|
-
const isNarrowTerminal = () => isNarrow(props.width);
|
|
30
|
-
const terminalWidth = () => props.width ?? process.stdout.columns ?? 80;
|
|
31
|
-
|
|
32
|
-
const dateColumnWidths = createMemo(() => {
|
|
33
|
-
const metricWidth = isNarrowTerminal() ? METRIC_COLUMNS_WIDTH_NARROW : METRIC_COLUMNS_WIDTH_FULL;
|
|
34
|
-
const minDate = MIN_DATE_COLUMN;
|
|
35
|
-
const available = Math.max(terminalWidth() - SIDE_PADDING - metricWidth, minDate);
|
|
36
|
-
const dateColumn = Math.max(minDate, available);
|
|
37
|
-
|
|
38
|
-
return {
|
|
39
|
-
column: dateColumn,
|
|
40
|
-
text: dateColumn,
|
|
41
|
-
};
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
const sortedEntries = createMemo(() => {
|
|
45
|
-
const entries = props.data.dailyEntries;
|
|
46
|
-
const sortBy = props.sortBy;
|
|
47
|
-
const sortDesc = props.sortDesc;
|
|
48
|
-
|
|
49
|
-
return [...entries].sort((a, b) => {
|
|
50
|
-
let cmp = 0;
|
|
51
|
-
if (sortBy === "cost") cmp = a.cost - b.cost;
|
|
52
|
-
else if (sortBy === "tokens") cmp = a.total - b.total;
|
|
53
|
-
else if (sortBy === "date") cmp = a.date.localeCompare(b.date);
|
|
54
|
-
return sortDesc ? -cmp : cmp;
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
const visibleEntries = createMemo(() => {
|
|
59
|
-
const maxRows = Math.max(props.height - 3, 0);
|
|
60
|
-
return sortedEntries().slice(0, maxRows);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
const formattedRows = createMemo(() => {
|
|
64
|
-
const dateColWidth = dateColumnWidths().column;
|
|
65
|
-
const narrow = isNarrowTerminal();
|
|
66
|
-
return visibleEntries().map((entry) => ({
|
|
67
|
-
entry,
|
|
68
|
-
dateColWidth,
|
|
69
|
-
narrow,
|
|
70
|
-
input: formatTokensCompact(entry.input),
|
|
71
|
-
output: formatTokensCompact(entry.output),
|
|
72
|
-
cacheRead: formatTokensCompact(entry.cacheRead),
|
|
73
|
-
cacheWrite: formatTokensCompact(entry.cacheWrite),
|
|
74
|
-
total: formatTokensCompact(entry.total),
|
|
75
|
-
cost: formatCostFull(entry.cost),
|
|
76
|
-
}));
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
const sortArrow = () => (props.sortDesc ? "▼" : "▲");
|
|
80
|
-
const dateHeader = () => (props.sortBy === "date" ? `${sortArrow()} Date` : "Date");
|
|
81
|
-
const totalHeader = () => (props.sortBy === "tokens" ? `${sortArrow()} Total` : "Total");
|
|
82
|
-
const costHeader = () => (props.sortBy === "cost" ? `${sortArrow()} Cost` : "Cost");
|
|
83
|
-
|
|
84
|
-
const renderHeader = () => {
|
|
85
|
-
const dateColWidth = dateColumnWidths().column;
|
|
86
|
-
if (isNarrowTerminal()) {
|
|
87
|
-
return `${"Date".padEnd(dateColWidth)}${totalHeader().padStart(TOTAL_COL_WIDTH)}${costHeader().padStart(COST_COL_WIDTH)}`;
|
|
88
|
-
}
|
|
89
|
-
return `${(" " + dateHeader()).padEnd(dateColWidth)}${"Input".padStart(INPUT_COL_WIDTH)}${"Output".padStart(OUTPUT_COL_WIDTH)}${"C.Read".padStart(CACHE_READ_COL_WIDTH)}${"C.Write".padStart(CACHE_WRITE_COL_WIDTH)}${totalHeader().padStart(TOTAL_COL_WIDTH)}${costHeader().padStart(COST_COL_WIDTH)}`;
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
const renderRowData = (row: typeof formattedRows extends () => (infer T)[] ? T : never) => {
|
|
93
|
-
if (row.narrow) {
|
|
94
|
-
return `${row.entry.date.padEnd(row.dateColWidth)}${row.total.padStart(TOTAL_COL_WIDTH)}`;
|
|
95
|
-
}
|
|
96
|
-
return `${row.entry.date.padEnd(row.dateColWidth)}${row.input.padStart(INPUT_COL_WIDTH)}${row.output.padStart(OUTPUT_COL_WIDTH)}${row.cacheRead.padStart(CACHE_READ_COL_WIDTH)}${row.cacheWrite.padStart(CACHE_WRITE_COL_WIDTH)}${row.total.padStart(TOTAL_COL_WIDTH)}`;
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
return (
|
|
100
|
-
<box flexDirection="column">
|
|
101
|
-
<box flexDirection="row">
|
|
102
|
-
<text fg="cyan" bold>
|
|
103
|
-
{renderHeader()}
|
|
104
|
-
</text>
|
|
105
|
-
</box>
|
|
106
|
-
|
|
107
|
-
<For each={formattedRows()}>
|
|
108
|
-
{(row, i) => {
|
|
109
|
-
const isActive = createMemo(() => i() === props.selectedIndex());
|
|
110
|
-
const rowBg = createMemo(() => isActive() ? "blue" : (i() % 2 === 1 ? STRIPE_BG : undefined));
|
|
111
|
-
|
|
112
|
-
return (
|
|
113
|
-
<box flexDirection="row">
|
|
114
|
-
<text
|
|
115
|
-
bg={rowBg()}
|
|
116
|
-
fg={isActive() ? "white" : undefined}
|
|
117
|
-
>
|
|
118
|
-
{renderRowData(row)}
|
|
119
|
-
</text>
|
|
120
|
-
<text
|
|
121
|
-
fg="green"
|
|
122
|
-
bg={rowBg()}
|
|
123
|
-
>
|
|
124
|
-
{row.cost.padStart(COST_COL_WIDTH)}
|
|
125
|
-
</text>
|
|
126
|
-
</box>
|
|
127
|
-
);
|
|
128
|
-
}}
|
|
129
|
-
</For>
|
|
130
|
-
</box>
|
|
131
|
-
);
|
|
132
|
-
}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { For, createMemo } from "solid-js";
|
|
2
|
-
import type { DailyModelBreakdown } from "../types/index.js";
|
|
3
|
-
import { getSourceColor, getSourceDisplayName } from "../utils/colors.js";
|
|
4
|
-
import { formatTokens, formatCost } from "../utils/format.js";
|
|
5
|
-
import { ModelRow } from "./ModelRow.js";
|
|
6
|
-
|
|
7
|
-
function formatDateDisplay(dateStr: string): string {
|
|
8
|
-
const date = new Date(dateStr + "T00:00:00");
|
|
9
|
-
return date.toLocaleDateString("en-US", { weekday: "short", month: "short", day: "numeric", year: "numeric" });
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface DateBreakdownPanelProps {
|
|
13
|
-
breakdown: DailyModelBreakdown;
|
|
14
|
-
isNarrow: boolean;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function DateBreakdownPanel(props: DateBreakdownPanelProps) {
|
|
18
|
-
const groupedBySource = createMemo(() => {
|
|
19
|
-
if (!props.breakdown?.models) return new Map();
|
|
20
|
-
const groups = new Map<string, typeof props.breakdown.models>();
|
|
21
|
-
for (const model of props.breakdown.models) {
|
|
22
|
-
const existing = groups.get(model.source) || [];
|
|
23
|
-
existing.push(model);
|
|
24
|
-
groups.set(model.source, existing);
|
|
25
|
-
}
|
|
26
|
-
for (const [, models] of groups) {
|
|
27
|
-
models.sort((a, b) => {
|
|
28
|
-
const totalA = a.tokens.input + a.tokens.output + (a.tokens.cacheRead || 0) + (a.tokens.cacheWrite || 0);
|
|
29
|
-
const totalB = b.tokens.input + b.tokens.output + (b.tokens.cacheRead || 0) + (b.tokens.cacheWrite || 0);
|
|
30
|
-
return totalB - totalA;
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
return groups;
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
return (
|
|
37
|
-
<box flexDirection="column" marginTop={1} paddingX={1}>
|
|
38
|
-
<box flexDirection="row" justifyContent="space-between">
|
|
39
|
-
<text bold fg="white">{formatDateDisplay(props.breakdown.date)}</text>
|
|
40
|
-
<box flexDirection="row" gap={2}>
|
|
41
|
-
<text fg="cyan">{formatTokens(props.breakdown.totalTokens)}</text>
|
|
42
|
-
<text fg="green" bold>{formatCost(props.breakdown.cost)}</text>
|
|
43
|
-
</box>
|
|
44
|
-
</box>
|
|
45
|
-
|
|
46
|
-
<box flexDirection="column" marginTop={1}>
|
|
47
|
-
<For each={Array.from(groupedBySource().entries())}>
|
|
48
|
-
{([source, models]) => (
|
|
49
|
-
<box flexDirection="column">
|
|
50
|
-
<box flexDirection="row" gap={1}>
|
|
51
|
-
<text fg={getSourceColor(source)} bold>{`● ${getSourceDisplayName(source)}`}</text>
|
|
52
|
-
<text dim>{`(${models.length} model${models.length > 1 ? "s" : ""})`}</text>
|
|
53
|
-
</box>
|
|
54
|
-
<For each={models}>
|
|
55
|
-
{(model) => (
|
|
56
|
-
<ModelRow
|
|
57
|
-
modelId={model.modelId}
|
|
58
|
-
tokens={{
|
|
59
|
-
input: model.tokens.input,
|
|
60
|
-
output: model.tokens.output,
|
|
61
|
-
cacheRead: model.tokens.cacheRead,
|
|
62
|
-
cacheWrite: model.tokens.cacheWrite,
|
|
63
|
-
}}
|
|
64
|
-
compact={props.isNarrow}
|
|
65
|
-
indent={2}
|
|
66
|
-
/>
|
|
67
|
-
)}
|
|
68
|
-
</For>
|
|
69
|
-
</box>
|
|
70
|
-
)}
|
|
71
|
-
</For>
|
|
72
|
-
</box>
|
|
73
|
-
|
|
74
|
-
<box flexDirection="row" marginTop={1}>
|
|
75
|
-
<text dim>Click another day or same day to close</text>
|
|
76
|
-
</box>
|
|
77
|
-
</box>
|
|
78
|
-
);
|
|
79
|
-
}
|