feat(appearance): apply UI zoom level to terminal and Monaco editors
Extend the existing appearance zoom setting so it also scales xterm.js terminals and Monaco editors (diff viewer, three-pane merge editor, file workspace editor), which previously rendered at a hard-coded 13px regardless of zoom. - Terminals read zoom at init and update term.options.fontSize live on zoom change, refitting after a double rAF so xterm's renderer has recomputed cell metrics. Font size is rounded to an integer to avoid subpixel blur in the canvas renderer. - Monaco editors derive fontSize from zoomLevel; three-pane merge editor memoizes its options object to avoid redundant updateOptions calls on unrelated re-renders.
This commit is contained in:
@@ -8,8 +8,15 @@ import {
|
||||
terminalResize,
|
||||
terminalKill,
|
||||
} from "@/lib/api"
|
||||
import { useZoomLevel } from "@/hooks/use-appearance"
|
||||
import type { TerminalEvent } from "@/lib/types"
|
||||
import type { ITheme } from "@xterm/xterm"
|
||||
import type { ITheme, Terminal as XTermTerminal } from "@xterm/xterm"
|
||||
|
||||
const TERMINAL_BASE_FONT_SIZE = 13
|
||||
|
||||
function computeTerminalFontSize(zoomLevel: number): number {
|
||||
return Math.round((TERMINAL_BASE_FONT_SIZE * zoomLevel) / 100)
|
||||
}
|
||||
|
||||
const DARK_THEME: ITheme = {
|
||||
background: "#1a1a1a",
|
||||
@@ -108,11 +115,13 @@ export function TerminalView({
|
||||
}: TerminalViewProps) {
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
const fitAddonRef = useRef<{ fit: () => void } | null>(null)
|
||||
const termRef = useRef<{ focus: () => void } | null>(null)
|
||||
const termRef = useRef<XTermTerminal | null>(null)
|
||||
const lastResizeRef = useRef<{ cols: number; rows: number } | null>(null)
|
||||
const isActiveRef = useRef(isActive)
|
||||
const isVisibleRef = useRef(isVisible)
|
||||
const onProcessExitedRef = useRef(onProcessExited)
|
||||
const { zoomLevel } = useZoomLevel()
|
||||
const zoomLevelRef = useRef(zoomLevel)
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
@@ -140,7 +149,7 @@ export function TerminalView({
|
||||
|
||||
const term = new Terminal({
|
||||
cursorBlink: true,
|
||||
fontSize: 13,
|
||||
fontSize: computeTerminalFontSize(zoomLevelRef.current),
|
||||
fontFamily: "Menlo, Monaco, 'Courier New', monospace",
|
||||
theme: getTerminalTheme(containerRef.current),
|
||||
allowProposedApi: true,
|
||||
@@ -292,6 +301,25 @@ export function TerminalView({
|
||||
}
|
||||
}, [isActive, isVisible])
|
||||
|
||||
// React to zoom level changes. Updates the ref synchronously so async init()
|
||||
// always reads the latest zoom, and pushes the new font size to already-mounted
|
||||
// terminals. Double rAF ensures xterm's renderer has recomputed cell metrics
|
||||
// before we refit.
|
||||
useEffect(() => {
|
||||
zoomLevelRef.current = zoomLevel
|
||||
const term = termRef.current
|
||||
if (!term) return
|
||||
term.options.fontSize = computeTerminalFontSize(zoomLevel)
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
const el = containerRef.current
|
||||
if (el && el.clientWidth > 0 && el.clientHeight > 0) {
|
||||
fitAddonRef.current?.fit()
|
||||
}
|
||||
})
|
||||
})
|
||||
}, [zoomLevel])
|
||||
|
||||
return (
|
||||
<div
|
||||
className="absolute inset-0 h-full w-full p-2"
|
||||
|
||||
Reference in New Issue
Block a user