diff --git a/packages/core/src/renderer.ts b/packages/core/src/renderer.ts index c8db3ea67..c3cbd402e 100644 --- a/packages/core/src/renderer.ts +++ b/packages/core/src/renderer.ts @@ -429,6 +429,7 @@ export class CliRenderer extends EventEmitter implements RenderContext { private _cachedPalette: TerminalColors | null = null private _paletteDetectionPromise: Promise | null = null private _onDestroy?: () => void + private _themeMode: "dark" | "light" | null = null private inputHandlers: ((sequence: string) => boolean)[] = [] private prependedInputHandlers: ((sequence: string) => boolean)[] = [] @@ -848,6 +849,10 @@ export class CliRenderer extends EventEmitter implements RenderContext { return this._capabilities } + public get themeMode(): "dark" | "light" | null { + return this._themeMode + } + public getDebugInputs(): Array<{ timestamp: string; sequence: string }> { return [...this._debugInputs] } @@ -1060,6 +1065,24 @@ export class CliRenderer extends EventEmitter implements RenderContext { return false }).bind(this) + private themeModeHandler: (sequence: string) => boolean = ((sequence: string) => { + if (sequence === "\x1b[?997;1n") { + if (this._themeMode !== "dark") { + this._themeMode = "dark" + this.emit("theme_mode", "dark") + } + return true + } + if (sequence === "\x1b[?997;2n") { + if (this._themeMode !== "light") { + this._themeMode = "light" + this.emit("theme_mode", "light") + } + return true + } + return false + }).bind(this) + private setupInput(): void { for (const handler of this.prependedInputHandlers) { this.addInputHandler(handler) @@ -1078,6 +1101,7 @@ export class CliRenderer extends EventEmitter implements RenderContext { }) this.addInputHandler(this.capabilityHandler) this.addInputHandler(this.focusHandler) + this.addInputHandler(this.themeModeHandler) this.addInputHandler((sequence: string) => { return this._keyHandler.processInput(sequence) }) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index e948de941..fcf552342 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -44,12 +44,15 @@ export enum DebugOverlayCorner { export type WidthMethod = "wcwidth" | "unicode" +export type ThemeMode = "dark" | "light" + export interface RendererEvents { resize: (width: number, height: number) => void key: (data: Buffer) => void "memory:snapshot": (snapshot: { heapUsed: number; heapTotal: number; arrayBuffers: number }) => void selection: (selection: Selection) => void "debugOverlay:toggle": (enabled: boolean) => void + theme_mode: (mode: ThemeMode) => void } export interface RenderContext extends EventEmitter { diff --git a/packages/core/src/zig/terminal.zig b/packages/core/src/zig/terminal.zig index 6d08380aa..12a6c9988 100644 --- a/packages/core/src/zig/terminal.zig +++ b/packages/core/src/zig/terminal.zig @@ -165,8 +165,7 @@ pub fn resetState(self: *Terminal, tty: anytype) !void { } if (self.state.color_scheme_updates) { - try tty.writeAll(ansi.ANSI.colorSchemeReset); - self.state.color_scheme_updates = false; + try self.setColorSchemeUpdates(tty, false); } self.setTerminalTitle(tty, ""); @@ -270,6 +269,11 @@ pub fn enableDetectedFeatures(self: *Terminal, tty: anytype, use_kitty_keyboard: if (self.caps.focus_tracking) { try self.setFocusTracking(tty, true); } + + if (self.caps.color_scheme_updates) { + try self.setColorSchemeUpdates(tty, true); + try tty.writeAll(ansi.ANSI.colorSchemeRequest); + } } fn checkEnvironmentOverrides(self: *Terminal) void { @@ -488,6 +492,12 @@ pub fn setModifyOtherKeys(self: *Terminal, tty: anytype, enable: bool) !void { self.state.modify_other_keys = enable; } +pub fn setColorSchemeUpdates(self: *Terminal, tty: anytype, enable: bool) !void { + const seq = if (enable) ansi.ANSI.colorSchemeSet else ansi.ANSI.colorSchemeReset; + try tty.writeAll(seq); + self.state.color_scheme_updates = enable; +} + /// The responses look like these: /// kitty - '\x1B[?1016;2$y\x1B[?2027;0$y\x1B[?2031;2$y\x1B[?1004;1$y\x1B[?2026;2$y\x1B[1;2R\x1B[1;3R\x1BP>|kitty(0.40.1)\x1B\\\x1B[?0u\x1B_Gi=1;EINVAL:Zero width/height not allowed\x1B\\\x1B[?62;c' /// ghostty - '\x1B[?1016;1$y\x1B[?2027;1$y\x1B[?2031;2$y\x1B[?1004;1$y\x1B[?2004;2$y\x1B[?2026;2$y\x1B[1;1R\x1B[1;1R\x1BP>|ghostty 1.1.3\x1B\\\x1B[?0u\x1B_Gi=1;OK\x1B\\\x1B[?62;22c'