diff --git a/.changeset/colorize-footer-diff-stats.md b/.changeset/colorize-footer-diff-stats.md new file mode 100644 index 000000000..02d1737ed --- /dev/null +++ b/.changeset/colorize-footer-diff-stats.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Colorize diff line-change stats in the footer git badge: additions in green, deletions in red, dirty marker in amber diff --git a/apps/kimi-code/src/tui/components/chrome/footer.ts b/apps/kimi-code/src/tui/components/chrome/footer.ts index 350da47ce..58b86ed06 100644 --- a/apps/kimi-code/src/tui/components/chrome/footer.ts +++ b/apps/kimi-code/src/tui/components/chrome/footer.ts @@ -206,7 +206,7 @@ function formatContextStatus(usage: number, tokens?: number, maxTokens?: number) } export function formatFooterGitBadge(status: GitStatus, colors: ColorPalette): string { - const base = chalk.hex(colors.status)(formatGitBadgeBase(status)); + const base = formatColorizedGitBadge(status, colors); if (status.pullRequest === null) return base; const pullRequest = chalk.hex(colors.primary)( @@ -215,6 +215,19 @@ export function formatFooterGitBadge(status: GitStatus, colors: ColorPalette): s return `${base} ${pullRequest}`; } +function formatColorizedGitBadge(status: GitStatus, colors: ColorPalette): string { + const parts: string[] = []; + if (status.diffAdded > 0) parts.push(chalk.hex(colors.success)(`+${String(status.diffAdded)}`)); + if (status.diffDeleted > 0) parts.push(chalk.hex(colors.error)(`-${String(status.diffDeleted)}`)); + if (parts.length === 0 && status.dirty) parts.push(chalk.hex(colors.warning)('±')); + let sync = ''; + if (status.ahead > 0) sync += `↑${status.ahead}`; + if (status.behind > 0) sync += `↓${status.behind}`; + if (sync) parts.push(chalk.hex(colors.status)(sync)); + const branch = chalk.hex(colors.status)(status.branch); + return parts.length === 0 ? branch : `${branch} ${chalk.hex(colors.status)('[')}${parts.join(' ')}${chalk.hex(colors.status)(']')}`; +} + export class FooterComponent implements Component { private state: AppState; private colors: ColorPalette; diff --git a/apps/kimi-code/test/tui/components/panels/footer-context.test.ts b/apps/kimi-code/test/tui/components/panels/footer-context.test.ts index db34ee895..6f1ef4f3a 100644 --- a/apps/kimi-code/test/tui/components/panels/footer-context.test.ts +++ b/apps/kimi-code/test/tui/components/panels/footer-context.test.ts @@ -145,6 +145,58 @@ describe('FooterComponent — context NaN resilience', () => { chalk.level = previousLevel; } }); + + it('colorizes diff additions in success color and deletions in error color', () => { + const previousLevel = chalk.level; + chalk.level = 3; + try { + const out = formatFooterGitBadge( + { + branch: 'main', + dirty: true, + ahead: 0, + behind: 0, + diffAdded: 42, + diffDeleted: 7, + pullRequest: null, + }, + darkColors, + ); + + const plain = strip(out); + expect(plain).toContain('+42'); + expect(plain).toContain('-7'); + expect(out).toContain(hexToSgr(darkColors.success)); + expect(out).toContain(hexToSgr(darkColors.error)); + } finally { + chalk.level = previousLevel; + } + }); + + it('renders dirty marker in warning color when no line stats are available', () => { + const previousLevel = chalk.level; + chalk.level = 3; + try { + const out = formatFooterGitBadge( + { + branch: 'main', + dirty: true, + ahead: 0, + behind: 0, + diffAdded: 0, + diffDeleted: 0, + pullRequest: null, + }, + darkColors, + ); + + const plain = strip(out); + expect(plain).toContain('±'); + expect(out).toContain(hexToSgr(darkColors.warning)); + } finally { + chalk.level = previousLevel; + } + }); }); describe('buildWeightedTips — weighted rotation', () => {