Skip to content

Commit 81e0b40

Browse files
committed
feat: improve theme detection priority and file watching
- Prioritize IDE theme detection over OSC (better for live updates) - Add OSC terminal color detection as fallback - Add 250ms debouncing for file watcher events - Only watch IDE config files for the active IDE terminal - Remove unused getTerminalColors() function - Remove Ghostty special case (now handled by OSC fallback)
1 parent f216f8c commit 81e0b40

File tree

2 files changed

+36
-58
lines changed

2 files changed

+36
-58
lines changed

cli/src/utils/terminal-color-detection.ts

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -282,41 +282,3 @@ export async function detectTerminalTheme(): Promise<'dark' | 'light' | null> {
282282
}
283283
}
284284

285-
/**
286-
* Get detailed color information from terminal
287-
* @returns Object with foreground, background RGB values and detected theme
288-
*/
289-
export async function getTerminalColors(): Promise<{
290-
foreground: [number, number, number] | null
291-
background: [number, number, number] | null
292-
theme: 'dark' | 'light' | null
293-
} | null> {
294-
if (!terminalSupportsOSC()) {
295-
return null
296-
}
297-
298-
try {
299-
const [bgResponse, fgResponse] = await Promise.all([
300-
queryTerminalOSC(11),
301-
queryTerminalOSC(10),
302-
])
303-
304-
const background = bgResponse ? parseOSCResponse(bgResponse) : null
305-
const foreground = fgResponse ? parseOSCResponse(fgResponse) : null
306-
307-
let theme: 'dark' | 'light' | null = null
308-
if (background) {
309-
theme = themeFromBgColor(background)
310-
} else if (foreground) {
311-
theme = themeFromFgColor(foreground)
312-
}
313-
314-
return {
315-
foreground,
316-
background,
317-
theme,
318-
}
319-
} catch {
320-
return null
321-
}
322-
}

cli/src/utils/theme-system.ts

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,11 @@ const isJetBrainsTerminal = (): boolean => {
296296
return false
297297
}
298298

299+
const isZedTerminal = (): boolean => {
300+
const termProgram = process.env.TERM_PROGRAM?.toLowerCase()
301+
return termProgram === 'zed' || false
302+
}
303+
299304
const detectVSCodeTheme = (): ThemeName | null => {
300305
if (!isVSCodeFamilyTerminal()) {
301306
return null
@@ -659,24 +664,10 @@ export const detectSystemTheme = (): ThemeName => {
659664
return normalizedEnv
660665
}
661666

662-
// Detect Ghostty terminal and default to dark.
663-
if (
664-
(typeof Bun !== 'undefined' &&
665-
Bun.env.GHOSTTY_RESOURCES_DIR !== undefined) ||
666-
process.env.GHOSTTY_RESOURCES_DIR !== undefined ||
667-
(process.env.TERM ?? '').toLowerCase() === 'xterm-ghostty'
668-
) {
669-
return 'dark'
670-
}
671-
672-
// Use OSC-detected theme if available
673-
if (oscDetectedTheme) {
674-
return oscDetectedTheme
675-
}
676-
667+
// Priority: IDE > OSC > Platform > Default
677668
const ideTheme = detectIDETheme()
678669
const platformTheme = detectPlatformTheme()
679-
const preferredTheme = ideTheme ?? platformTheme
670+
const preferredTheme = ideTheme ?? oscDetectedTheme ?? platformTheme ?? 'dark'
680671

681672
if (normalizedEnv === 'opposite') {
682673
return preferredTheme === 'dark' ? 'light' : 'dark'
@@ -904,9 +895,13 @@ export const resolveThemeColor = (
904895
* Watches for system theme changes and updates zustand store
905896
*/
906897

898+
// Debounce timing for file watcher events
899+
const FILE_WATCHER_DEBOUNCE_MS = 250
900+
907901
let lastDetectedTheme: ThemeName | null = null
908902
let themeStoreUpdater: ((name: ThemeName) => void) | null = null
909903
let oscDetectedTheme: ThemeName | null = null
904+
let pendingRecomputeTimer: NodeJS.Timeout | null = null
910905

911906
/**
912907
* Initialize theme store updater
@@ -938,6 +933,20 @@ const recomputeSystemTheme = (source: string) => {
938933
}
939934
}
940935

936+
/**
937+
* Debounced version of recomputeSystemTheme for file watcher events
938+
* Prevents excessive recomputations when files change rapidly
939+
*/
940+
const debouncedRecomputeSystemTheme = (source: string) => {
941+
if (pendingRecomputeTimer) {
942+
clearTimeout(pendingRecomputeTimer)
943+
}
944+
pendingRecomputeTimer = setTimeout(() => {
945+
pendingRecomputeTimer = null
946+
recomputeSystemTheme(source)
947+
}, FILE_WATCHER_DEBOUNCE_MS)
948+
}
949+
941950
// Initialize on module load
942951
lastDetectedTheme = detectSystemTheme()
943952

@@ -957,9 +966,16 @@ const setupFileWatchers = () => {
957966
)
958967
}
959968

960-
// IDE config files that we should watch
961-
const ideConfigPaths = getIDEThemeConfigPaths()
962-
watchTargets.push(...ideConfigPaths)
969+
// IDE config files - only watch for the active IDE terminal
970+
if (isVSCodeFamilyTerminal()) {
971+
watchTargets.push(...resolveVSCodeSettingsPaths())
972+
}
973+
if (isJetBrainsTerminal()) {
974+
watchTargets.push(...resolveJetBrainsLafPaths())
975+
}
976+
if (isZedTerminal()) {
977+
watchTargets.push(...resolveZedSettingsPaths())
978+
}
963979

964980
// Watch parent directories instead of individual files
965981
// Directory watches are more reliable for catching all modifications including plist key deletions
@@ -979,7 +995,7 @@ const setupFileWatchers = () => {
979995
(eventType, filename) => {
980996
// Only respond to changes affecting our target files
981997
if (filename && watchTargets.some((t) => t.endsWith(filename))) {
982-
recomputeSystemTheme(
998+
debouncedRecomputeSystemTheme(
983999
`watch:${join(parentDir, filename)}:${eventType}`,
9841000
)
9851001
}

0 commit comments

Comments
 (0)