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:
xintaofei
2026-04-19 09:07:51 +08:00
parent eeeee2141c
commit 0fa2a0895f
4 changed files with 68 additions and 21 deletions

View File

@@ -7,7 +7,10 @@ import type { editor as MonacoEditorNs, IRange } from "monaco-editor"
import { ArrowLeft, ArrowRight, CheckCheck } from "lucide-react"
import { useTranslations } from "next-intl"
import { defineMonacoThemes, useMonacoThemeSync } from "@/lib/monaco-themes"
import { useZoomLevel } from "@/hooks/use-appearance"
import { cn } from "@/lib/utils"
const EDITOR_BASE_FONT_SIZE = 13
import { Button } from "@/components/ui/button"
import {
ResizableHandle,
@@ -55,6 +58,7 @@ export function ThreePaneMergeEditor({
}: ThreePaneMergeEditorProps) {
const t = useTranslations("MergePage")
const editorTheme = useMonacoThemeSync()
const { zoomLevel } = useZoomLevel()
const { registerEditor } = useSyncScroll()
const leftEditorRef = useRef<MonacoEditorNs.IStandaloneCodeEditor | null>(
@@ -497,23 +501,30 @@ export function ThreePaneMergeEditor({
// ---------------------------------------------------------------------------
// Editor options
// ---------------------------------------------------------------------------
const editorOptions: MonacoEditorNs.IStandaloneEditorConstructionOptions = {
fontSize: 13,
minimap: { enabled: false },
scrollBeyondLastLine: false,
automaticLayout: true,
lineNumbers: "on",
glyphMargin: true,
folding: false,
wordWrap: "off",
overviewRulerLanes: 0,
}
const editorOptions =
useMemo<MonacoEditorNs.IStandaloneEditorConstructionOptions>(
() => ({
fontSize: (EDITOR_BASE_FONT_SIZE * zoomLevel) / 100,
minimap: { enabled: false },
scrollBeyondLastLine: false,
automaticLayout: true,
lineNumbers: "on",
glyphMargin: true,
folding: false,
wordWrap: "off",
overviewRulerLanes: 0,
}),
[zoomLevel]
)
const readonlyOptions = {
...editorOptions,
readOnly: true,
domReadOnly: true,
}
const readonlyOptions = useMemo(
() => ({
...editorOptions,
readOnly: true,
domReadOnly: true,
}),
[editorOptions]
)
const loadingEl = (
<div className="flex h-full items-center justify-center text-xs text-muted-foreground">