diff --git a/apps/desktop/src/app/contrib/surfaces.tsx b/apps/desktop/src/app/contrib/surfaces.tsx
index 2e40092ffc..750248fbfe 100644
--- a/apps/desktop/src/app/contrib/surfaces.tsx
+++ b/apps/desktop/src/app/contrib/surfaces.tsx
@@ -56,7 +56,7 @@ export const SidebarSurface = memo(function SidebarSurface({
export const TerminalSurface = memo(function TerminalSurface() {
return (
-
+
)
diff --git a/apps/desktop/src/app/right-sidebar/terminal/instance.tsx b/apps/desktop/src/app/right-sidebar/terminal/instance.tsx
index 933916b640..9d81495642 100644
--- a/apps/desktop/src/app/right-sidebar/terminal/instance.tsx
+++ b/apps/desktop/src/app/right-sidebar/terminal/instance.tsx
@@ -12,7 +12,13 @@ import { useTerminalSession } from './use-terminal-session'
// Absolute-stacked so inactive tabs keep layout size (a display:none host goes
// 0×0 and renders garbled on re-show); visibility toggles which one is seen.
-const INSTANCE_CLASS = 'absolute inset-0 flex flex-col bg-(--ui-editor-surface-background) px-2 pb-2 pt-0'
+const INSTANCE_CLASS = 'absolute inset-0 flex flex-col bg-(--ui-terminal-surface-background) px-2 pb-2 pt-0'
+
+// xterm host. The screen/viewport overrides matter for the DOM renderer (the
+// WebGL fast-path paints the canvas from ITheme.background instead) — both
+// resolve to the same token, so the two renderers can't disagree.
+const HOST_CLASS =
+ 'h-full min-h-0 overflow-hidden text-(--ui-text-secondary) [&_.xterm]:h-full [&_.xterm-screen]:bg-(--ui-terminal-surface-background)! [&_.xterm-viewport]:bg-(--ui-terminal-surface-background)!'
interface TerminalInstanceProps {
id: string
@@ -81,10 +87,7 @@ export function TerminalInstance({
)}
{/* Outer div paints the terminal inset; inner div is the xterm host so the
canvas sizes to the content area and p-2 stays as terminal padding. */}
-
+
)
}
@@ -107,10 +110,7 @@ export function AgentTerminalInstance({ active, id, procId }: AgentTerminalInsta
// routes ⌘W here and closes the focused agent tab (not a preview).
data-terminal=""
>
-
+
)
}
diff --git a/apps/desktop/src/app/right-sidebar/terminal/persistent.tsx b/apps/desktop/src/app/right-sidebar/terminal/persistent.tsx
index 3fe2edb04a..b5e75978b0 100644
--- a/apps/desktop/src/app/right-sidebar/terminal/persistent.tsx
+++ b/apps/desktop/src/app/right-sidebar/terminal/persistent.tsx
@@ -207,7 +207,7 @@ export function PersistentTerminal({ onAddSelectionToChat }: PersistentTerminalP
zIndex: 4,
// Match the live skin surface so the header strip (transparent) and body
// read as one cohesive pane instead of revealing a near-black slab behind.
- backgroundColor: 'var(--ui-editor-surface-background)',
+ backgroundColor: 'var(--ui-terminal-surface-background)',
contain: 'layout size paint'
}
diff --git a/apps/desktop/src/app/right-sidebar/terminal/rail.tsx b/apps/desktop/src/app/right-sidebar/terminal/rail.tsx
index ced1675032..7b5aee0ffa 100644
--- a/apps/desktop/src/app/right-sidebar/terminal/rail.tsx
+++ b/apps/desktop/src/app/right-sidebar/terminal/rail.tsx
@@ -44,7 +44,7 @@ export function TerminalRail() {
return (
{
const ansi = renderedMode === 'dark' ? (theme.darkTerminal ?? theme.terminal) : theme.terminal
- const surface = resolveSurfaceColor('#ffffff')
+ const base = terminalTheme(renderedMode, ansi)
+ // Fall back to the palette's own background, not white — a hardcoded
+ // '#ffffff' flashes a white slab in dark mode whenever the probe can't read
+ // the token (pre-paint mount). Same contract as the user terminal.
+ const surface = resolveSurfaceColor(base.background ?? '#ffffff')
- return { ...terminalTheme(renderedMode, ansi), background: surface, cursorAccent: surface }
+ return { ...base, background: surface, cursorAccent: surface }
}
// eslint-disable-next-line no-restricted-syntax -- legitimate non-atom ref write (see eslint rule comment)
diff --git a/apps/desktop/src/styles.css b/apps/desktop/src/styles.css
index 4017644307..065f5bd562 100644
--- a/apps/desktop/src/styles.css
+++ b/apps/desktop/src/styles.css
@@ -327,6 +327,12 @@
--ui-sidebar-surface-background: var(--ui-bg-sidebar);
--ui-chat-surface-background: var(--ui-bg-chrome);
--ui-editor-surface-background: var(--ui-bg-chrome);
+ /* The integrated terminal wears the transcript's background, not the
+ editor's — it reads as part of the conversation surface. Its own token so
+ the two can diverge without dragging the terminal along, and so a skin
+ gets one knob for it. xterm's canvas can't use a var, so the probe in
+ terminal/selection.ts resolves this same name to a concrete rgb. */
+ --ui-terminal-surface-background: var(--ui-chat-surface-background);
/* Inline chat widgets (clarify, artifact card) — the card fill in light
mode, nudged down in dark so a widget settles into the transcript
instead of glowing above it (see the `.dark` override). */