( dailyTokens: DailyModelTokens[], models: string[], terminalWidth: number, )
| 701 | }; |
| 702 | |
| 703 | function generateTokenChart( |
| 704 | dailyTokens: DailyModelTokens[], |
| 705 | models: string[], |
| 706 | terminalWidth: number, |
| 707 | ): ChartOutput | null { |
| 708 | if (dailyTokens.length < 2 || models.length === 0) { |
| 709 | return null; |
| 710 | } |
| 711 | |
| 712 | // Y-axis labels take about 6 characters, plus some padding |
| 713 | // Cap at ~52 to align with heatmap width (1 year of data) |
| 714 | const yAxisWidth = 7; |
| 715 | const availableWidth = terminalWidth - yAxisWidth; |
| 716 | const chartWidth = Math.min(52, Math.max(20, availableWidth)); |
| 717 | |
| 718 | // Distribute data across the available chart width |
| 719 | let recentData: DailyModelTokens[]; |
| 720 | if (dailyTokens.length >= chartWidth) { |
| 721 | // More data than space: take most recent N days |
| 722 | recentData = dailyTokens.slice(-chartWidth); |
| 723 | } else { |
| 724 | // Less data than space: expand by repeating each point |
| 725 | const repeatCount = Math.floor(chartWidth / dailyTokens.length); |
| 726 | recentData = []; |
| 727 | for (const day of dailyTokens) { |
| 728 | for (let i = 0; i < repeatCount; i++) { |
| 729 | recentData.push(day); |
| 730 | } |
| 731 | } |
| 732 | } |
| 733 | |
| 734 | // Color palette for different models - use theme colors |
| 735 | const theme = getTheme(resolveThemeSetting(getGlobalConfig().theme)); |
| 736 | const colors = [themeColorToAnsi(theme.suggestion), themeColorToAnsi(theme.success), themeColorToAnsi(theme.warning)]; |
| 737 | |
| 738 | // Prepare series data for each model |
| 739 | const series: number[][] = []; |
| 740 | const legend: ChartLegend[] = []; |
| 741 | |
| 742 | // Only show top 3 models to keep chart readable |
| 743 | const topModels = models.slice(0, 3); |
| 744 | |
| 745 | for (let i = 0; i < topModels.length; i++) { |
| 746 | const model = topModels[i]!; |
| 747 | const data = recentData.map(day => day.tokensByModel[model] || 0); |
| 748 | |
| 749 | // Only include if there's actual data |
| 750 | if (data.some(v => v > 0)) { |
| 751 | series.push(data); |
| 752 | // Use theme colors that match the chart |
| 753 | const bulletColors = [theme.suggestion, theme.success, theme.warning]; |
| 754 | legend.push({ |
| 755 | model: renderModelName(model), |
| 756 | coloredBullet: applyColor(figures.bullet, bulletColors[i % bulletColors.length] as Color), |
| 757 | }); |
| 758 | } |
| 759 | } |
| 760 |
no test coverage detected