diff --git a/.changeset/terminal-integrated-font.md b/.changeset/terminal-integrated-font.md new file mode 100644 index 00000000000..fae71fea107 --- /dev/null +++ b/.changeset/terminal-integrated-font.md @@ -0,0 +1,5 @@ +--- +"kilo-vscode": patch +--- + +Agent Manager terminals now use `terminal.integrated.fontFamily` and `terminal.integrated.fontSize` (including Nerd Font glyphs) instead of the editor font. diff --git a/packages/kilo-vscode/src/agent-manager/AgentManagerProvider.ts b/packages/kilo-vscode/src/agent-manager/AgentManagerProvider.ts index fe0c08b60c3..2091211251c 100644 --- a/packages/kilo-vscode/src/agent-manager/AgentManagerProvider.ts +++ b/packages/kilo-vscode/src/agent-manager/AgentManagerProvider.ts @@ -36,6 +36,7 @@ import { parseToolRequest, startFromTool, type ToolRequest } from "./tool-start" import { stopSessionProcesses } from "../kilo-provider/background-process" import { startSession } from "./mcp-warmup" +import { readTerminalFont, watchTerminalFont } from "./terminal-font" import { buildKeybindingMap } from "./format-keybinding" import { resolveVersionModels, buildInitialMessages, type CreatedVersion } from "./multi-version" import { Semaphore } from "./semaphore" @@ -73,6 +74,7 @@ export class AgentManagerProvider implements Disposable { private cachedWorktreeStats: { type: "agentManager.worktreeStats"; stats: WorktreeStats[] } | undefined private cachedLocalStats: { type: "agentManager.localStats"; stats: LocalStats } | undefined private unsubTool: (() => void) | undefined + private unsubFont: (() => void) | undefined private closing: Promise | undefined private onVisibilityChange: ((visible: boolean) => void) | undefined @@ -96,6 +98,10 @@ export class AgentManagerProvider implements Disposable { getWorktreePath: (id) => this.getStateManager()?.getWorktree(id)?.path, log: (...args) => this.log("[XTerm]", ...args), post: (msg) => this.postToWebview(msg), + getTerminalFont: () => readTerminalFont(), + }) + this.unsubFont = watchTerminalFont((font) => { + this.postToWebview({ type: "agentManager.terminal.fontChanged", font }) }) this.run = new RunController({ root: () => this.getRoot(), @@ -1791,6 +1797,7 @@ export class AgentManagerProvider implements Disposable { await this.stateReady?.catch((err) => this.log("dispose: stateReady rejected:", err)) await this.state?.flush().catch((err) => this.log("dispose: state flush failed:", err)) this.unsubTool?.() + this.unsubFont?.() this.connectionService.unregisterFocused("agent-manager") this.connectionService.registerOpen("agent-manager", []) this.diffs.stop() diff --git a/packages/kilo-vscode/src/agent-manager/terminal-font.ts b/packages/kilo-vscode/src/agent-manager/terminal-font.ts new file mode 100644 index 00000000000..d49f27ddba3 --- /dev/null +++ b/packages/kilo-vscode/src/agent-manager/terminal-font.ts @@ -0,0 +1,59 @@ +/** + * Helpers to read and watch the user's integrated-terminal font settings. + * + * VS Code's integrated terminal mirrors editor font settings when terminal + * font settings are unset. We replicate that same logic here so the Agent + * Manager xterm instances look identical to VS Code's own terminal. + */ + +import * as vscode from "vscode" + +export interface TerminalFont { + fontFamily: string + fontSize: number + fontWeight?: string + fontWeightBold?: string + lineHeight?: number + letterSpacing?: number +} + +const FALLBACK = "Menlo, Monaco, 'Courier New', monospace" + +/** Resolve the user's integrated-terminal font, mirroring VS Code's own + * fallback to the editor font when the terminal font is unset. */ +export function readTerminalFont(): TerminalFont { + const term = vscode.workspace.getConfiguration("terminal.integrated") + const editor = vscode.workspace.getConfiguration("editor") + const family = term.get("fontFamily")?.trim() || editor.get("fontFamily")?.trim() || FALLBACK + const size = term.get("fontSize") || editor.get("fontSize") || 13 + return { + fontFamily: family, + fontSize: size, + fontWeight: term.get("fontWeight") || undefined, + fontWeightBold: term.get("fontWeightBold") || undefined, + lineHeight: term.get("lineHeight") || undefined, + letterSpacing: term.get("letterSpacing") || undefined, + } +} + +/** True when a config change touches any terminal/editor font setting. */ +export function affectsTerminalFont(e: vscode.ConfigurationChangeEvent): boolean { + return ( + e.affectsConfiguration("terminal.integrated.fontFamily") || + e.affectsConfiguration("terminal.integrated.fontSize") || + e.affectsConfiguration("terminal.integrated.fontWeight") || + e.affectsConfiguration("terminal.integrated.fontWeightBold") || + e.affectsConfiguration("terminal.integrated.lineHeight") || + e.affectsConfiguration("terminal.integrated.letterSpacing") || + e.affectsConfiguration("editor.fontFamily") || + e.affectsConfiguration("editor.fontSize") + ) +} + +/** Subscribe to terminal-font config changes. Returns a cleanup function. */ +export function watchTerminalFont(callback: (font: TerminalFont) => void): () => void { + const sub = vscode.workspace.onDidChangeConfiguration((e) => { + if (affectsTerminalFont(e)) callback(readTerminalFont()) + }) + return () => sub.dispose() +} diff --git a/packages/kilo-vscode/src/agent-manager/terminal-routing.ts b/packages/kilo-vscode/src/agent-manager/terminal-routing.ts index 38082ab51ff..7c8c7e46cc2 100644 --- a/packages/kilo-vscode/src/agent-manager/terminal-routing.ts +++ b/packages/kilo-vscode/src/agent-manager/terminal-routing.ts @@ -15,7 +15,7 @@ */ import type { KiloClient } from "@kilocode/sdk/v2/client" -import type { AgentManagerInMessage, AgentManagerOutMessage } from "./types" +import type { AgentManagerInMessage, AgentManagerOutMessage, TerminalFont } from "./types" import { TerminalManager } from "./terminal-manager" interface ServerConfig { @@ -36,6 +36,8 @@ export interface TerminalRoutingDeps { log(...args: unknown[]): void /** Send a message back to the webview. */ post(message: AgentManagerOutMessage): void + /** Return the current terminal font settings. */ + getTerminalFont(): TerminalFont } /** True iff the message belongs to the terminal-tab subsystem. */ @@ -106,6 +108,7 @@ export class TerminalRouter { terminalId: created.terminalId, title: created.title, wsUrl: created.wsUrl, + font: this.deps.getTerminalFont(), }) } catch (err) { const message = err instanceof Error ? err.message : String(err) diff --git a/packages/kilo-vscode/src/agent-manager/types.ts b/packages/kilo-vscode/src/agent-manager/types.ts index f6f312fe466..360b32c7217 100644 --- a/packages/kilo-vscode/src/agent-manager/types.ts +++ b/packages/kilo-vscode/src/agent-manager/types.ts @@ -14,6 +14,9 @@ import type { ApplyConflict } from "./GitOps" import type { BranchListItem, WorktreeSetupErrorCode } from "./git-import" import type { ExternalWorktreeItem } from "./WorktreeManager" import type { RunStatus } from "./run/manager" +import type { TerminalFont } from "./terminal-font" + +export type { TerminalFont } // --------------------------------------------------------------------------- // Shared payload types @@ -147,6 +150,7 @@ interface TerminalCreatedMessage { terminalId: string title: string wsUrl: string + font: TerminalFont } interface TerminalClosedMessage { @@ -160,6 +164,11 @@ interface TerminalErrorMessage { message: string } +interface TerminalFontChangedMessage { + type: "agentManager.terminal.fontChanged" + font: TerminalFont +} + interface ErrorOutMessage { type: "error" message: string @@ -314,6 +323,7 @@ export type AgentManagerOutMessage = | TerminalCreatedMessage | TerminalClosedMessage | TerminalErrorMessage + | TerminalFontChangedMessage // --------------------------------------------------------------------------- // Webview → Extension messages (onMessage) diff --git a/packages/kilo-vscode/tests/unit/agent-manager-arch.test.ts b/packages/kilo-vscode/tests/unit/agent-manager-arch.test.ts index 71733ccdd94..a0c196bfc09 100644 --- a/packages/kilo-vscode/tests/unit/agent-manager-arch.test.ts +++ b/packages/kilo-vscode/tests/unit/agent-manager-arch.test.ts @@ -653,6 +653,10 @@ const VSCODE_ALLOWED: Record = { "run/task.ts": { note: "vscode adapter for Agent Manager run scripts", }, + // Reads terminal.integrated.* and editor.font* config for xterm font settings + "terminal-font.ts": { + note: "vscode config reader for integrated terminal font settings", + }, } /** diff --git a/packages/kilo-vscode/webview-ui/agent-manager/terminal/TerminalTab.tsx b/packages/kilo-vscode/webview-ui/agent-manager/terminal/TerminalTab.tsx index 14cb63a4de8..8ad304f46ce 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/terminal/TerminalTab.tsx +++ b/packages/kilo-vscode/webview-ui/agent-manager/terminal/TerminalTab.tsx @@ -11,7 +11,7 @@ */ import { Component, createEffect, onCleanup, onMount } from "solid-js" -import { Terminal } from "@xterm/xterm" +import { Terminal, type FontWeight } from "@xterm/xterm" import { FitAddon } from "@xterm/addon-fit" import { WebLinksAddon } from "@xterm/addon-web-links" import { ClipboardAddon } from "@xterm/addon-clipboard" @@ -20,10 +20,14 @@ import "@xterm/xterm/css/xterm.css" import { useVSCode } from "../../src/context/vscode" import { useLanguage } from "../../src/context/language" import { formatReviewCommentsMarkdown } from "../../src/utils/review-comment-markdown" +import type { TerminalFont } from "./state" interface Props { terminalId: string wsUrl: string + /** Terminal font settings forwarded from the extension host. Used on + * initial mount; live changes arrive via `agentManager.terminal.fontChanged`. */ + font: TerminalFont /** Whether this terminal is currently the focused tab. * * The xterm subtree always stays in the paint tree (see the layer / @@ -126,12 +130,16 @@ export const TerminalTab: Component = (props) => { const term = new Terminal({ convertEol: true, cursorBlink: true, - fontFamily: cssVar("--vscode-editor-font-family", "Menlo, Monaco, 'Courier New', monospace"), - fontSize: Number.parseFloat(cssVar("--font-size-base", "13px")), + fontFamily: props.font.fontFamily, + fontSize: props.font.fontSize, scrollback: 5000, theme: readTheme(), allowProposedApi: true, }) + if (props.font.fontWeight) term.options.fontWeight = props.font.fontWeight as FontWeight + if (props.font.fontWeightBold) term.options.fontWeightBold = props.font.fontWeightBold as FontWeight + if (props.font.lineHeight) term.options.lineHeight = props.font.lineHeight + if (props.font.letterSpacing) term.options.letterSpacing = props.font.letterSpacing const fit = new FitAddon() term.loadAddon(fit) // Clickable URLs in terminal output (Cmd/Ctrl+click to open). @@ -288,12 +296,26 @@ export const TerminalTab: Component = (props) => { return } + if (message.type === "agentManager.terminal.fontChanged") { + const font = message.font as TerminalFont + term.options.fontFamily = font.fontFamily + term.options.fontSize = font.fontSize + if (font.fontWeight) term.options.fontWeight = font.fontWeight as FontWeight + if (font.fontWeightBold) term.options.fontWeightBold = font.fontWeightBold as FontWeight + if (font.lineHeight) term.options.lineHeight = font.lineHeight + if (font.letterSpacing) term.options.letterSpacing = font.letterSpacing + fit.fit() + syncSize() + scheduleRepaint() + return + } + + // fontSizeChanged/ready control the Kilo chat UI font — do not apply + // them to the terminal, which has its own independent font settings. + // Keep the repaint for any downstream layout side-effects. const size = message.type === "fontSizeChanged" ? message.fontSize : message.type === "ready" ? message.fontSize : undefined if (size === undefined) return - term.options.fontSize = size - fit.fit() - syncSize() scheduleRepaint() }) diff --git a/packages/kilo-vscode/webview-ui/agent-manager/terminal/render.tsx b/packages/kilo-vscode/webview-ui/agent-manager/terminal/render.tsx index 97651314098..2fe5e2c44a0 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/terminal/render.tsx +++ b/packages/kilo-vscode/webview-ui/agent-manager/terminal/render.tsx @@ -85,7 +85,7 @@ export function renderTerminalLayer(props: { state: TerminalStateControls }): JS const visible = () => slotVisible(term.id, term.contextKey) return (
- +
) }} diff --git a/packages/kilo-vscode/webview-ui/agent-manager/terminal/state.ts b/packages/kilo-vscode/webview-ui/agent-manager/terminal/state.ts index 7899c1f4d78..64604167ada 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/terminal/state.ts +++ b/packages/kilo-vscode/webview-ui/agent-manager/terminal/state.ts @@ -11,6 +11,16 @@ import { createMemo, createSignal } from "solid-js" import type { Accessor } from "solid-js" import { LOCAL } from "../navigate" +/** Font settings forwarded from the extension host (mirrors terminal-font.ts). */ +export interface TerminalFont { + fontFamily: string + fontSize: number + fontWeight?: string + fontWeightBold?: string + lineHeight?: number + letterSpacing?: number +} + /** Prefix used for terminal tab IDs in the webview (mirrors terminal-manager.ts). */ export const TERMINAL_PREFIX = "terminal:" @@ -21,6 +31,7 @@ export interface TerminalTabState { id: string title: string wsUrl: string + font: TerminalFont } /** Terminal row enriched with the sidebar context it belongs to. Used by @@ -35,6 +46,7 @@ export interface TerminalCreatedEvent { terminalId: string title: string wsUrl: string + font: TerminalFont } export interface TerminalStateControls { @@ -335,7 +347,7 @@ export function createTerminalMessageHandler(deps: TerminalMessageHandlerDeps) { if (msg.type === "agentManager.terminal.created") { const ev = msg as unknown as TerminalCreatedEvent const contextKey = ev.worktreeId === null ? LOCAL : ev.worktreeId - deps.state.add(ev.worktreeId, { id: ev.terminalId, title: ev.title, wsUrl: ev.wsUrl }) + deps.state.add(ev.worktreeId, { id: ev.terminalId, title: ev.title, wsUrl: ev.wsUrl, font: ev.font }) deps.onCreated?.(contextKey, ev.terminalId) deps.saveTabMemory() deps.setSelection(contextKey)