Skip to content

feat: fix theming issues and set up new themes#178

Draft
pbrczan wants to merge 4 commits intomasterfrom
peterbrczan-nojira-theme-alignments
Draft

feat: fix theming issues and set up new themes#178
pbrczan wants to merge 4 commits intomasterfrom
peterbrczan-nojira-theme-alignments

Conversation

@pbrczan
Copy link
Copy Markdown
Collaborator

@pbrczan pbrczan commented May 8, 2026


Kimchi Summary

What changed

Adds six new community themes (Night Owl, Nord, One Dark, Monokai, Catppuccin Macchiato, Lucent Orng) and a transparent theme. Enables all themes to optionally declare oscFg/oscBg colors for terminal-wide foreground/background control via OSC sequences. Fixes background rendering gaps when ANSI reset codes appear inside blocks, and adds auto-contrast text generation for the kimchi-minimal theme. Theme files can now be hot-reloaded when edited externally.

Why

Previously only the hardcoded kimchi theme could override terminal colors, leaving other themes with inconsistent backgrounds and unreadable text on some terminal emulators. ANSI reset codes inside rendered blocks could also cancel background fills, creating visual gaps. These changes unify terminal-wide theming across all bundled themes and make kimchi-minimal adapt its text color to the probed terminal background for better readability.

Key changes

  • src/extensions/terminal-colors.ts: Reads oscFg/oscBg dynamically from the active theme JSON instead of using hardcoded values; adds fillViewport to paint backgrounds by writing explicit spaces (works around BCE-disabled terminals like Terminal.app); adds iTerm2-specific OSC 1337 support; restores the original terminal palette when switching to a theme without OSC values.
  • src/cli.ts: Paints the initial viewport with the active theme's oscBg before the first render frame; registers new bundled themes (night-owl, nord, one-dark, monokai, catppuccin-macchiato, lucent-orng, transparent).
  • src/extensions/kimchi-minimal-tints.ts: Computes terminal background luminance on startup and sets fgColors for the text token to #333333 on light backgrounds or #CCCCCC on dark backgrounds; subscribes to onThemeFileChange to re-apply tints without restarting.
  • src/theme-file-watcher.ts: New debounced file watcher for themes/*.json that triggers callbacks when the active theme file is modified on disk.
  • patches/@mariozechner__pi-tui.patch: Re-injects background ANSI open codes after inline \x1b[49m and \x1b[0m resets in applyBackgroundToLine, ensuring block backgrounds cover the full line width including padding.
  • src/terminal-bg-probe.ts: Adds hexToFgAnsi helper for truecolor/256-color foreground ANSI generation.
  • Existing themes: Added oscFg/oscBg to kimchi, dark, light, and kimchi-light; added empty oscFg/oscBg to kimchi-minimal to opt out of OSC theming.
  • New themes: themes/night-owl.json, themes/nord.json, themes/one-dark.json, themes/monokai.json, themes/catppuccin-macchiato.json, themes/lucent-orng.json, themes/transparent.json.

Impact

  • Behavior change: Themes that define oscBg (including existing kimchi, dark, and light) now set the terminal emulator's default background on activation and restore the original palette when switching away.
  • Behavior change: kimchi-minimal now explicitly overrides the text foreground color based on probed terminal luminance instead of inheriting the terminal's default.
  • Hot-reload: Editing the active bundled theme .json while kimchi is running triggers an immediate re-application of colors after the internal reload.

@pbrczan pbrczan self-assigned this May 8, 2026
@kimchi-review
Copy link
Copy Markdown

kimchi-review Bot commented May 8, 2026

Kimchi Code Review

Property Value
Commit f22faef
Author @pbrczan
Files changed 0
Review status Completed
Comments 8 (3 info, 5 warning)
Duration 282s

Summary

📊 Review Score: 76/100 (overall code quality — 0 lowest, 100 highest)
⏱️ Estimated effort to review: 4/5 (1 = trivial, 5 = very complex)

🧪 Tests: no — No tests were added or modified for the new theme file watcher, hot-reload logic, terminal color extensions, or CLI viewport painting. The new themes are purely data files without accompanying validation tests.

🔒 Security concerns found: Unsanitized themeName from user-controlled settings.json is interpolated directly into resolve() in both src/cli.ts and src/extensions/terminal-colors.ts, allowing directory traversal (e.g., ../foo) to read arbitrary .json files outside the themes directory. Impact is limited because this is a local CLI tool, but it is still an unsafe path construction.

📝 Found 8 issue(s). See inline comments for details.

What to expect

Kimchi will analyze the changes in this pull request and post:

  • A summary of the overall changes
  • Inline comments on specific lines with findings categorized by issue type

The review typically completes within a few minutes. This comment will be updated once the review is ready.

Interact with Kimchi
  • @kimchi review — re-trigger a full review on the latest commit
  • @kimchi summary — regenerate the PR summary
  • @kimchi ignore — skip this PR (no review will be posted)
  • Reply to any inline comment to ask follow-up questions or request clarification
Configuration

Reviews are configured by your organization admin.
Review instructions, excluded directories, and severity thresholds can be adjusted per repository in the Kimchi dashboard.


Powered by Kimchi — AI-powered code review by CAST AI

@pbrczan pbrczan marked this pull request as draft May 8, 2026 10:17
Copy link
Copy Markdown

@kimchi-review kimchi-review Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📊 Review Score: 76/100 (overall code quality — 0 lowest, 100 highest)
⏱️ Estimated effort to review: 4/5 (1 = trivial, 5 = very complex)

🧪 Tests: no — No tests were added or modified for the new theme file watcher, hot-reload logic, terminal color extensions, or CLI viewport painting. The new themes are purely data files without accompanying validation tests.

🔒 Security concerns found: Unsanitized themeName from user-controlled settings.json is interpolated directly into resolve() in both src/cli.ts and src/extensions/terminal-colors.ts, allowing directory traversal (e.g., ../foo) to read arbitrary .json files outside the themes directory. Impact is limited because this is a local CLI tool, but it is still an unsafe path construction.

📝 Found 8 issue(s). See inline comments for details.

Comment thread src/theme-file-watcher.ts
} catch (err) {
console.warn("[theme-file-watcher] listener error:", err)
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️🐛 Bug

The fire function suppresses events where changedTheme === lastActiveTheme. Because ensureWatcher seeds lastActiveTheme with the currently active theme, editing the active theme file never triggers listeners on the first (or any subsequent) change. The watcher effectively only works once immediately after a theme switch, breaking the hot-reload contract.

💡 Suggestion: Remove the lastActiveTheme guard from fire; the 30 ms debounce timer is already sufficient to coalesce duplicate fs.watch events from a single write.

Comment thread src/cli.ts
// per-process at session_start from the OSC 11 probe.
const themesDir = resolve(agentDir, "themes")
const bundledThemes = ["kimchi.json", "kimchi-minimal.json", "kimchi-light.json", "dark.json", "light.json"]
const bundledThemes = [
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️🐛 Bug

theme-file-watcher.ts relies on process.env.KIMCHI_CODING_AGENT_THEMES_DIR to know which directory to watch, but cli.ts computes themesDir locally and never assigns it to that environment variable. Consequently, ensureWatcher exits silently and no file watching is established.

💡 Suggestion: After computing themesDir in cli.ts, add process.env.KIMCHI_CODING_AGENT_THEMES_DIR = themesDir so the theme file watcher can locate the directory.

Comment thread src/cli.ts
// Paint the initial viewport background before pi-mono renders its first frame.
// This ensures blank areas of the terminal reflect the theme color from the start,
// on every terminal regardless of OSC 10/11 support.
if (!acpMode && process.stdout.isTTY) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️🔒 Security

The themeName value is taken from user-editable settings.json and interpolated into resolve(themesDir, \${themeName}.json`)without sanitization. A crafted value like../package` escapes the themes directory and can read arbitrary files.

💡 Suggestion: Sanitize the name with path.basename(themeName) before resolving, or validate that the resolved path starts with themesDir.

const raw = readFileSync(path, "utf-8")
const theme = JSON.parse(raw)
const vars: Record<string, string> = theme.vars ?? {}
const resolveVar = (val: string): string => vars[val] ?? val
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️🔒 Security

Same path traversal issue as in cli.ts: themeName is passed directly into resolve(dir, "themes", \${themeName}.json`)` without stripping directory traversal segments.

💡 Suggestion: Use path.basename(themeName) before resolving the theme file path, or constrain the lookup to the known themes directory.

Comment thread src/theme-file-watcher.ts
}
}

export function onThemeFileChange(listener: ThemeFileChangeListener): () => void {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️🐛 Bug

The fs.watch instance is created without an 'error' event listener. If the watched directory is deleted or permissions change while kimchi is running, the emitted error is uncaught and can crash the process.

💡 Suggestion: Add an error handler: watcher.on('error', (err) => { console.warn('[theme-file-watcher] watch error:', err); watcher?.close(); watcher = undefined; }).

function hexToItermFormat(hex: string): string | null {
if (!hex || hex === "") return null
const match = hex.match(/^#?([0-9a-fA-F]{6})$/i)
if (!match) return null
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️🔧 Maintainability

isIterm2() returns true when process.env.TERM === "xterm-256color-italic", which is not iTerm2-specific. Other terminals may use this TERM value and will receive unrecognized OSC 1337 sequences.

💡 Suggestion: Rely only on process.env.TERM_PROGRAM === "iTerm.app" for iTerm2-specific OSC 1337 behavior.

Comment thread themes/night-owl.json
"cardBg": "#0d1f35",
"infoBg": "#1a2d42"
}
} No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️🔧 Maintainability

The file is missing a trailing newline.

💡 Suggestion: Add a newline at the end of the file.

Comment thread themes/transparent.json
"oscFg": "",
"oscBg": ""
}
} No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️🔧 Maintainability

The file is missing a trailing newline.

💡 Suggestion: Add a newline at the end of the file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant