@tokscale/cli 1.4.3 → 2.0.1

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 (188) hide show
  1. package/dist/index.d.ts +3 -0
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +128 -0
  4. package/dist/index.js.map +1 -0
  5. package/package.json +19 -26
  6. package/dist/auth.d.ts +0 -17
  7. package/dist/auth.d.ts.map +0 -1
  8. package/dist/auth.js +0 -162
  9. package/dist/auth.js.map +0 -1
  10. package/dist/cli.d.ts +0 -9
  11. package/dist/cli.d.ts.map +0 -1
  12. package/dist/cli.js +0 -1550
  13. package/dist/cli.js.map +0 -1
  14. package/dist/credentials.d.ts +0 -50
  15. package/dist/credentials.d.ts.map +0 -1
  16. package/dist/credentials.js +0 -151
  17. package/dist/credentials.js.map +0 -1
  18. package/dist/cursor.d.ts +0 -167
  19. package/dist/cursor.d.ts.map +0 -1
  20. package/dist/cursor.js +0 -906
  21. package/dist/cursor.js.map +0 -1
  22. package/dist/date-utils.d.ts +0 -10
  23. package/dist/date-utils.d.ts.map +0 -1
  24. package/dist/date-utils.js +0 -47
  25. package/dist/date-utils.js.map +0 -1
  26. package/dist/graph-types.d.ts +0 -142
  27. package/dist/graph-types.d.ts.map +0 -1
  28. package/dist/graph-types.js +0 -6
  29. package/dist/graph-types.js.map +0 -1
  30. package/dist/native-runner.d.ts +0 -11
  31. package/dist/native-runner.d.ts.map +0 -1
  32. package/dist/native-runner.js +0 -77
  33. package/dist/native-runner.js.map +0 -1
  34. package/dist/native.d.ts +0 -106
  35. package/dist/native.d.ts.map +0 -1
  36. package/dist/native.js +0 -302
  37. package/dist/native.js.map +0 -1
  38. package/dist/sessions/types.d.ts +0 -28
  39. package/dist/sessions/types.d.ts.map +0 -1
  40. package/dist/sessions/types.js +0 -27
  41. package/dist/sessions/types.js.map +0 -1
  42. package/dist/spinner.d.ts +0 -75
  43. package/dist/spinner.d.ts.map +0 -1
  44. package/dist/spinner.js +0 -203
  45. package/dist/spinner.js.map +0 -1
  46. package/dist/submit.d.ts +0 -23
  47. package/dist/submit.d.ts.map +0 -1
  48. package/dist/submit.js +0 -294
  49. package/dist/submit.js.map +0 -1
  50. package/dist/table.d.ts +0 -42
  51. package/dist/table.d.ts.map +0 -1
  52. package/dist/table.js +0 -181
  53. package/dist/table.js.map +0 -1
  54. package/dist/tui/App.d.ts +0 -4
  55. package/dist/tui/App.d.ts.map +0 -1
  56. package/dist/tui/App.js +0 -333
  57. package/dist/tui/App.js.map +0 -1
  58. package/dist/tui/components/BarChart.d.ts +0 -17
  59. package/dist/tui/components/BarChart.d.ts.map +0 -1
  60. package/dist/tui/components/BarChart.js +0 -146
  61. package/dist/tui/components/BarChart.js.map +0 -1
  62. package/dist/tui/components/DailyView.d.ts +0 -13
  63. package/dist/tui/components/DailyView.d.ts.map +0 -1
  64. package/dist/tui/components/DailyView.js +0 -86
  65. package/dist/tui/components/DailyView.js.map +0 -1
  66. package/dist/tui/components/DateBreakdownPanel.d.ts +0 -7
  67. package/dist/tui/components/DateBreakdownPanel.d.ts.map +0 -1
  68. package/dist/tui/components/DateBreakdownPanel.js +0 -36
  69. package/dist/tui/components/DateBreakdownPanel.js.map +0 -1
  70. package/dist/tui/components/Footer.d.ts +0 -28
  71. package/dist/tui/components/Footer.d.ts.map +0 -1
  72. package/dist/tui/components/Footer.js +0 -130
  73. package/dist/tui/components/Footer.js.map +0 -1
  74. package/dist/tui/components/Header.d.ts +0 -9
  75. package/dist/tui/components/Header.d.ts.map +0 -1
  76. package/dist/tui/components/Header.js +0 -20
  77. package/dist/tui/components/Header.js.map +0 -1
  78. package/dist/tui/components/Legend.d.ts +0 -7
  79. package/dist/tui/components/Legend.d.ts.map +0 -1
  80. package/dist/tui/components/Legend.js +0 -16
  81. package/dist/tui/components/Legend.js.map +0 -1
  82. package/dist/tui/components/LoadingSpinner.d.ts +0 -8
  83. package/dist/tui/components/LoadingSpinner.d.ts.map +0 -1
  84. package/dist/tui/components/LoadingSpinner.js +0 -55
  85. package/dist/tui/components/LoadingSpinner.js.map +0 -1
  86. package/dist/tui/components/ModelRow.d.ts +0 -13
  87. package/dist/tui/components/ModelRow.d.ts.map +0 -1
  88. package/dist/tui/components/ModelRow.js +0 -15
  89. package/dist/tui/components/ModelRow.js.map +0 -1
  90. package/dist/tui/components/ModelView.d.ts +0 -13
  91. package/dist/tui/components/ModelView.d.ts.map +0 -1
  92. package/dist/tui/components/ModelView.js +0 -96
  93. package/dist/tui/components/ModelView.js.map +0 -1
  94. package/dist/tui/components/OverviewView.d.ts +0 -14
  95. package/dist/tui/components/OverviewView.d.ts.map +0 -1
  96. package/dist/tui/components/OverviewView.js +0 -65
  97. package/dist/tui/components/OverviewView.js.map +0 -1
  98. package/dist/tui/components/StatsView.d.ts +0 -14
  99. package/dist/tui/components/StatsView.d.ts.map +0 -1
  100. package/dist/tui/components/StatsView.js +0 -102
  101. package/dist/tui/components/StatsView.js.map +0 -1
  102. package/dist/tui/components/TokenBreakdown.d.ts +0 -14
  103. package/dist/tui/components/TokenBreakdown.d.ts.map +0 -1
  104. package/dist/tui/components/TokenBreakdown.js +0 -10
  105. package/dist/tui/components/TokenBreakdown.js.map +0 -1
  106. package/dist/tui/components/index.d.ts +0 -16
  107. package/dist/tui/components/index.d.ts.map +0 -1
  108. package/dist/tui/components/index.js +0 -13
  109. package/dist/tui/components/index.js.map +0 -1
  110. package/dist/tui/config/settings.d.ts +0 -15
  111. package/dist/tui/config/settings.d.ts.map +0 -1
  112. package/dist/tui/config/settings.js +0 -147
  113. package/dist/tui/config/settings.js.map +0 -1
  114. package/dist/tui/config/themes.d.ts +0 -15
  115. package/dist/tui/config/themes.d.ts.map +0 -1
  116. package/dist/tui/config/themes.js +0 -82
  117. package/dist/tui/config/themes.js.map +0 -1
  118. package/dist/tui/hooks/useData.d.ts +0 -19
  119. package/dist/tui/hooks/useData.d.ts.map +0 -1
  120. package/dist/tui/hooks/useData.js +0 -468
  121. package/dist/tui/hooks/useData.js.map +0 -1
  122. package/dist/tui/index.d.ts +0 -4
  123. package/dist/tui/index.d.ts.map +0 -1
  124. package/dist/tui/index.js +0 -36
  125. package/dist/tui/index.js.map +0 -1
  126. package/dist/tui/types/index.d.ts +0 -137
  127. package/dist/tui/types/index.d.ts.map +0 -1
  128. package/dist/tui/types/index.js +0 -26
  129. package/dist/tui/types/index.js.map +0 -1
  130. package/dist/tui/utils/cleanup.d.ts +0 -22
  131. package/dist/tui/utils/cleanup.d.ts.map +0 -1
  132. package/dist/tui/utils/cleanup.js +0 -59
  133. package/dist/tui/utils/cleanup.js.map +0 -1
  134. package/dist/tui/utils/colors.d.ts +0 -19
  135. package/dist/tui/utils/colors.d.ts.map +0 -1
  136. package/dist/tui/utils/colors.js +0 -71
  137. package/dist/tui/utils/colors.js.map +0 -1
  138. package/dist/tui/utils/format.d.ts +0 -7
  139. package/dist/tui/utils/format.d.ts.map +0 -1
  140. package/dist/tui/utils/format.js +0 -45
  141. package/dist/tui/utils/format.js.map +0 -1
  142. package/dist/tui/utils/responsive.d.ts +0 -5
  143. package/dist/tui/utils/responsive.d.ts.map +0 -1
  144. package/dist/tui/utils/responsive.js +0 -5
  145. package/dist/tui/utils/responsive.js.map +0 -1
  146. package/dist/wrapped.d.ts +0 -43
  147. package/dist/wrapped.d.ts.map +0 -1
  148. package/dist/wrapped.js +0 -719
  149. package/dist/wrapped.js.map +0 -1
  150. package/src/auth.ts +0 -211
  151. package/src/cli.ts +0 -1892
  152. package/src/credentials.ts +0 -176
  153. package/src/cursor.ts +0 -1044
  154. package/src/date-utils.ts +0 -51
  155. package/src/graph-types.ts +0 -175
  156. package/src/native-runner.js +0 -4
  157. package/src/native-runner.ts +0 -91
  158. package/src/native.ts +0 -633
  159. package/src/sessions/types.ts +0 -59
  160. package/src/spinner.ts +0 -283
  161. package/src/submit.ts +0 -360
  162. package/src/table.ts +0 -233
  163. package/src/tui/App.tsx +0 -453
  164. package/src/tui/components/BarChart.tsx +0 -205
  165. package/src/tui/components/DailyView.tsx +0 -132
  166. package/src/tui/components/DateBreakdownPanel.tsx +0 -79
  167. package/src/tui/components/Footer.tsx +0 -380
  168. package/src/tui/components/Header.tsx +0 -68
  169. package/src/tui/components/Legend.tsx +0 -39
  170. package/src/tui/components/LoadingSpinner.tsx +0 -81
  171. package/src/tui/components/ModelRow.tsx +0 -47
  172. package/src/tui/components/ModelView.tsx +0 -147
  173. package/src/tui/components/OverviewView.tsx +0 -121
  174. package/src/tui/components/StatsView.tsx +0 -249
  175. package/src/tui/components/TokenBreakdown.tsx +0 -46
  176. package/src/tui/components/index.ts +0 -15
  177. package/src/tui/config/settings.ts +0 -183
  178. package/src/tui/config/themes.ts +0 -115
  179. package/src/tui/hooks/useData.ts +0 -558
  180. package/src/tui/index.tsx +0 -44
  181. package/src/tui/opentui.d.ts +0 -166
  182. package/src/tui/types/index.ts +0 -173
  183. package/src/tui/utils/cleanup.ts +0 -65
  184. package/src/tui/utils/colors.ts +0 -78
  185. package/src/tui/utils/format.ts +0 -36
  186. package/src/tui/utils/responsive.ts +0 -8
  187. package/src/types.d.ts +0 -28
  188. 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
- }