(data: DailyModelTokens[], _chartWidth: number, yAxisOffset: number)
| 1017 | }; |
| 1018 | } |
| 1019 | function generateXAxisLabels(data: DailyModelTokens[], _chartWidth: number, yAxisOffset: number): string { |
| 1020 | if (data.length === 0) return ''; |
| 1021 | |
| 1022 | // Show 3-4 date labels evenly spaced, but leave room for last label |
| 1023 | const numLabels = Math.min(4, Math.max(2, Math.floor(data.length / 8))); |
| 1024 | // Don't use the very last position - leave room for the label text |
| 1025 | const usableLength = data.length - 6; // Reserve ~6 chars for last label (e.g., "Dec 7") |
| 1026 | const step = Math.floor(usableLength / (numLabels - 1)) || 1; |
| 1027 | const labelPositions: { |
| 1028 | pos: number; |
| 1029 | label: string; |
| 1030 | }[] = []; |
| 1031 | for (let i = 0; i < numLabels; i++) { |
| 1032 | const idx = Math.min(i * step, data.length - 1); |
| 1033 | const date = new Date(data[idx]!.date); |
| 1034 | const label = date.toLocaleDateString('en-US', { |
| 1035 | month: 'short', |
| 1036 | day: 'numeric' |
| 1037 | }); |
| 1038 | labelPositions.push({ |
| 1039 | pos: idx, |
| 1040 | label |
| 1041 | }); |
| 1042 | } |
| 1043 | |
| 1044 | // Build the label string with proper spacing |
| 1045 | let result = ' '.repeat(yAxisOffset); |
| 1046 | let currentPos = 0; |
| 1047 | for (const { |
| 1048 | pos, |
| 1049 | label |
| 1050 | } of labelPositions) { |
| 1051 | const spaces = Math.max(1, pos - currentPos); |
| 1052 | result += ' '.repeat(spaces) + label; |
| 1053 | currentPos = pos + label.length; |
| 1054 | } |
| 1055 | return result; |
| 1056 | } |
| 1057 | |
| 1058 | // Screenshot functionality |
| 1059 | async function handleScreenshot(stats: ClaudeCodeStats, activeTab: 'Overview' | 'Models', setStatus: (status: string | null) => void): Promise<void> { |
no test coverage detected