You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feat(editor): one typography resolver for every Monaco surface + opt-in weight, line height, ligatures, size and macOS smoothing - #36577
Code typography now comes from one resolver. Before this, five Monaco sites (file editor, diff viewer, combined diff, notebook cells, automation prompt editor), the GitHub comment code excerpt and the editor zoom overlay each built fontFamily/fontSize on their own from terminalFontSize. None of them passed fontWeight, fontLigatures or lineHeight.
src/renderer/src/lib/editor-typography.ts (with useEditorTypography()) replaces the font-family part of editor-font-zoom.ts; that file now holds only the zoom-step math. Every one of those surfaces reads the resolver. The DOM excerpts use a CSS twin (editorTypographyTextStyle), so they match Monaco: the same family stack, weight, line height and liga/calt handling.
New opt-in settings sit next to Editor Font Family under Settings › General › Editor. Any field left empty renders exactly as today.
Setting
Empty (default)
Set
Editor Font Size
follows the terminal font size (today's behavior)
10–24 px base. ⌘= / ⌘− editor zoom still applies on top, and the zoom overlay % uses this base
Editor Font Weight
Monaco regular (400, today's behavior)
100–900
Editor Line Height
Monaco automatic spacing (today's behavior)
1–3 × font size; a value of 8 or more is read as pixels, and 0 means automatic. Applies to the file editor, diffs, the combined diff, notebook cells and excerpts
Editor Font Ligatures
Off (today's behavior)
Off / Auto / On, with the same semantics as the terminal setting, judged against the editor font
Editor Font Family (existing)
follows the terminal font in code editors; Markdown code keeps the app monospace
now also used for Markdown code (inline and fenced, in the rich editor and the preview)
Font Smoothing (macOS only, hidden elsewhere)
Grayscale (today's app-wide antialiased)
Native (-webkit-font-smoothing: auto) for terminals and code editors
Root cause, font smoothing
body { -webkit-font-smoothing: antialiased } is inherited by .xterm and .monaco-editor. I measured this in the running app: switching to native smoothing makes terminal glyphs about 14% brighter on average, with 29% more lit pixels.
WebGL terminals bake the smoothing into their glyph atlas, and clearTextureAtlas() does not pick up the change. xterm also shares atlases between terminals that use the same font config. So a toggle calls a new rebuildAllTerminalWebglRenderers(): every pane detaches first, then they all reattach, so no pane can get the old shared atlas back. The rebuild runs only when the attribute actually changes, so it costs nothing while the setting is unused.
Supersedes / builds on: #20188 by @PedroOsnaghi (editor font weight and line height), #30088 by @nivaldomcj (opt-in Editor Font Size, and the zoom-overlay base), #13056 by @adaex (font smoothing). Each of those wires one option into some of the sites. This PR fixes the shared root cause instead, so all options reach every Monaco and DOM code surface through one path.
Checked in code and measured in the running app (computed font-family, font-size, font-weight, line-height, font-feature-settings) with defaults and with family FiraCode Nerd Font, size 16, weight 600, line height 24px, ligatures On:
Surface
Follows family / size / weight / line height / ligatures
File editor (also Markdown source mode)
Yes: 16px, 600, 24px, liga on
Single-file diff, side-by-side and inline (Source Control, branch and commit diffs, external-change compare)
Yes: same resolver, size base − 0.5 (15.5px), 23px lines
Combined "all changes" diff, branch/commit all-diffs and the pull request files view, side-by-side and inline
Markdown code blocks and inline code (rich editor and preview)
Family only, and only when Editor Font Family is set. Size stays relative to the prose and follows editor zoom; weight, line height and ligatures stay prose-styled
Terminal, chat transcripts, sidebar comments
No, by design: they keep the terminal or app fonts
Mobile
No: the mobile app renders natively with its own fonts
Setting semantics follow common code-editor conventions
Line height: a value of 8 or more is read as pixels and 0 means automatic, so a value like 22 no longer clamps to 3× the font size. Pixel values are converted against the base size, so they still scale with editor zoom. They are clamped to 1–3× the font size so glyphs can't overlap.
Markdown code uses the explicitly chosen editor font. When Editor Font Family is empty, Markdown keeps today's app monospace instead of switching every document to the terminal font.
Kept as is: the editor follows the terminal font and size when unset (Orca's existing direction); weight 100–900; ligatures Off / Auto / On. Custom OpenType feature strings (for example stylistic sets) are left for a follow-up.
Markdown code with an explicit Editor Font Family (light: rich editor unset → set; dark: preview unset → set)
Visual proof
Captured on macOS from the real dev build, driven over CDP in the background. The sample repo uses FiraCode Nerd Font as the Editor Font Family. "Before" is origin/main. "After" uses Editor Font Size 16, Weight 600, Line Height 1.8 and Ligatures On.
File editor (dark)
File editor (light)
Diff viewer and combined diff (dark)
Diff viewer (light)
Settings › General › Editor (before / after dark / after light)
Font Smoothing: Grayscale vs Native (WebGL terminal and editor)
No change when nothing is set: with every new field empty, the file editor screenshots from origin/main and from this branch are byte-identical PNGs (dark), and so are the light diff-viewer screenshots. Computed styles match too: weight 400, "calt" 0, "liga" 0, 21px lines in the editor and 20px in the diff.
Perf
Scanned: the full diff against origin/main, using the six perf-scan questions:
Unused work before the skip gate:useEditorTypography uses a shallow selector over seven keys, and the useMemo result is stable.
Multiplied owners: one hook per mounted Monaco surface, no new listeners or timers.
Thread: all work runs synchronously on the renderer.
Growth: the WebGL rebuild is O(live terminals) and runs once per toggle.
Host: the smoothing setting is gated to macOS; elsewhere it is a no-op.
Teardown: the warm-up cancel on dispose fixes the atlas canvas leak, and the Markdown variable is removed when unset.
Findings: none introduced. The notebook cell, notebook preview and automation prompt editor no longer subscribe to the whole settings object. One pre-existing cost is unchanged and identical on origin/main: the combined diff re-renders every section and calls updateOptions once per section on every settings write. It is left for a follow-up.
Measured. Dev builds, base (origin/main) and branch launched side by side with ORCA_BACKGROUND_LAUNCH=1, isolated profiles, driven over CDP. Renders were counted with a React commit hook (PerformedWork per named component). updateOptions calls were counted on every live Monaco instance. Workload: 10 unrelated settings writes.
Surface
updateOptions calls (base / branch)
Component renders (base / branch)
File editor
10 / 10
MonacoEditor 30 / 30
Single-file diff
10 / 10
DiffViewer 30 / 30
Combined diff (11 sections)
110 / 110
DiffSectionItem 330 / 330
Notebook
0 / 0
IpynbCodePreview 30 / 30
Control: one typography write (terminal font size)
2 / 2
MonacoEditor 2 / 2
The 10 calls per 10 writes in the file editor and single-file diff happen on base too. The editor wrapper re-applies its options object on every render, and the effect this PR owns does not fire on unrelated writes.
Editor mount time: median time from openFile to the first painted line, 10 fresh files per round, three interleaved rounds. Base 205 / 234 / 267 ms, branch 220 / 224 / 205 ms. No measurable difference within dev-build noise.
Font Smoothing toggle, 8 retained WebGL terminals (branch): an unrelated write keeps every terminal canvas, so no rebuild. Each toggle recreates 17 canvases. The whole settings write plus the rebuild takes a 138–238 ms long task (one outlier at 652 ms). The same write on base, where the key is ignored, is a 65–151 ms long task. So the rebuild costs about 80–150 ms once per user toggle, scaling with the number of live terminals.
Merge-risk assessment
Low (was low–medium). Phase-2 stress and cross-platform testing exercised the risky WebGL rebuild path and fixed the one real problem it found.
Blast radius: every Monaco surface now takes its typography options from one place. With nothing set, the resolved values equal today's: same family, same size math, fontWeight: 'normal', lineHeight: 0 (Monaco auto) and ligatures off. The file-editor and light diff-viewer screenshots are byte-identical to origin/main.
Follow-up delta (pixel/0 line heights, Markdown code font): low. With Editor Font Family empty, the CSS variable is removed and Markdown code computes to exactly the previous --font-mono stack (measured in light and dark). Line-height values from 1 to 3 resolve exactly as before. The shared NumberField gained an opt-in clearOnZero, and it now skips writing a value equal to the current one; that is the only change for the other 17 users.
Migration: none. All five keys are optional and have no defaults, so no persisted value changes.
Back-compat: older clients ignore the unknown optional keys. Corrupt or hand-edited values fall back to today's behavior (covered by a unit test). A native smoothing value copied onto Linux or Windows has no effect: the root attribute stays unset.
Perf:useEditorTypography uses a shallow selector, so unrelated settings writes don't re-render editors or call updateOptions. Smoothing does nothing until a user opts in. A toggle rebuilds the live WebGL renderers once, the same cost as switching GPU acceleration.
Evidence from the WebGL rebuild stress run. This was a macOS dev build with 68 terminal panes:
An 8-pane split tab, four hidden tabs holding 17, 17, 16 and 9 panes, and a floating terminal.
Every visible pane was printing seq 1 40000 while Font Smoothing was toggled 20 times at 250 ms intervals. Then another 490 toggles were run with no output.
Results:
No lost scrollback: every visible pane's buffer is contiguous with 0 gaps and ends at 40000 followed by the done marker.
No blank panes: checked in screenshots after the toggles. A hidden 17-pane tab revealed afterwards rendered every pane with WebGL and the new smoothing.
Renderer count stable: WebGL renderers held at 9 and .xterm-screen canvases at 18 throughout. Context losses stayed at 0, and there were no Chromium "too many WebGL contexts" warnings.
Heap: no lasting growth after forced GC. It moved between 144 and 187 MB across 490 toggles, ending lower than it started.
Context loss still recovers: I forced WEBGL_lose_context on 3 live panes. They fell back to the DOM renderer as the existing policy intends. Toggling smoothing afterwards skipped them, and all 9 visible panes still echoed new output.
Leak found and fixed (commit fix(terminal): cancel WebGL atlas warm-up…):
Cause: xterm's WebGL glyph atlas keeps running its idle warm-up queue after dispose(). Orca's xterm patch makes that warm-up draw into the terminal element, so a disposed atlas re-appended its hidden scratch canvas and leaked one canvas per atlas.
Not new to this PR: it already happened on every GPU-acceleration toggle, context-loss retry and resize. Before the fix, 10 GPU toggles added 170 orphan canvases and 120 smoothing toggles added about 1,000.
Fix:config/patches/xterm-src/@xterm__addon-webgl@0.20.0-beta.300.src.patch now clears the queue on dispose and before a re-warm. I regenerated the built patch with regenerate-xterm-patches.mjs --write, which also updated the lockfile hash. Orphan canvases now stay at 1 (the live atlas) across 10 slow toggles, 120 rapid toggles and 10 GPU toggles.
Mobile: the mobile app's own copy of this patch warms up without a DOM container, so it can't leak this way and is unchanged.
Cross-platform:
Linux: omarchy (Arch, Hyprland, native Wayland) running this branch.
Windows: awin (Windows 11) running this branch.
Result on both: General › Editor shows Editor Font Size, Weight, Line Height and Ligatures correctly. There is no Font Smoothing row, and searching settings for "smoothing" finds nothing.
Negative user-facing tradeoffs
GitHub comment code excerpts look different: they now render in the editor font and size. They were meant to, but a font-mono class overrode the inline style. They also no longer show ligatures by default, to match Monaco.
Commented-line highlight color in code excerpts: commented lines in GitHub comment code excerpts used to be tinted emerald green (bg-emerald-500/10). They now use the theme's --ring color (bg-ring/15), the same color as the comment-range highlight in Monaco diffs, so the tint looks different from before.
Notebook previews with ligature fonts: the code cell preview used the browser default (ligatures on); it now matches the cell editor (off unless enabled).
Weight and ligatures don't follow the terminal by default. That would have changed every existing editor: the terminal's default weight is 500, and its ligatures are Auto. Users who want them to match set them explicitly.
WebGL rebuild on toggle: changing Font Smoothing recreates WebGL terminal renderers, which can cause a brief repaint. Terminals in a dashboard pop-out window pick up the change on their next renderer rebuild, not immediately.
Combined-diff height estimate: before a section is measured, its height is estimated at a fixed 19px per line, and that was already wrong under editor zoom. With a tall Editor Line Height, unmeasured sections can jump once Monaco reports their real height.
Native Font Smoothing covers code surfaces only: it applies to terminals, Monaco, code excerpts and notebook previews. UI chrome stays grayscale.
Markdown code font changes for users with an explicit Editor Font Family: inline and fenced Markdown code switches from the app monospace to that font.
Line height 3.1–7.9 renders as 3×: those values are neither a usable multiplier nor pixels.
Pixel line heights are clamped to 1–3× the font size: a pixel value smaller than the font, for example, renders at 1×.
No translations yet: the new strings are English only; other locales fall back to English until translated.
Validation
pnpm tc:web passes.
oxlint is clean on the changed files; oxfmt was run.
pnpm sync:localization-catalog, verify:localization-extraction and verify:localization-runtime-catalog pass.
Focused vitest from a non-nested worktree: the new lib/editor-typography.test.ts plus the pane-manager, settings, editor (DiffSection*, Monaco*, Ipynb*), automations, app-shell and useIpcEvents* suites. 372 files / 2889 tests pass. An earlier wider run, which also covered terminal-pane, hit one failure that comes from the node-pty environment (fish-color-scheme-child-stdin.node-pty.test.ts); this diff does not touch that code.
In the app, launched with ORCA_BACKGROUND_LAUNCH=1, an isolated profile and CDP:
Opened a file and its diff and the combined "all changes" diff, in light and dark, with defaults and with overrides. Checked the computed font-weight, font-feature-settings and line heights.
Emptied a field from the Settings UI: the value is cleared to undefined and the placeholder falls back to the terminal value.
Toggled Font Smoothing Grayscale → Native → Grayscale on a live WebGL terminal and measured the glyph pixels: 73.4 → 83.6 → 73.4.
Toggled Font Smoothing while the terminal was hidden behind an editor tab (retained WebGL), then revealed it: the terminal was correctly native (83.6).
Confirmed that clearTextureAtlas() alone does not apply the change (73.4 → 73.4). That is why this PR rebuilds the renderers.
Rebased on origin/main. pnpm tc:web passes, and the focused vitest run (editor typography and all settings suites, 182 files / 1276 tests) passes. The localization extraction and runtime-catalog checks pass.
In the app (background launch, isolated profile, CDP): measured the file editor, the single-file diff and the combined diff in both side-by-side and inline modes, notebook cells, and Markdown code in the rich editor and the preview, with defaults and with overrides (table above).
Settings UI: entering 22 stores 22 (pixels), 1.5 stores 1.5, and 0 or an empty field clears to automatic.
Review: an Opus sub-agent and Codex (gpt-6.1-sol, xhigh). Fixed: fenced code in the rich editor (rendered directly in <pre>) missed the font; rounding the ratio skewed pixel heights; typed pixel values were stored as a ratio, so they drifted when the font size changed; a hand-edited terminal size of 0 could divide by zero; the line-height field kept a stale 1-3 suffix. Not changed: the 1–3× clamp on pixel values is deliberate.
Phase 2 (risk reduction)
WebGL stress, leak, context-loss and hidden-tab tests as described under Merge-risk assessment. The scripts drove the dev build over CDP with VITE_EXPOSE_STORE=true so they could inspect pane managers.
Cross-platform screenshots of Settings › General › Editor:
Linux (omarchy):
Windows (awin):
The xterm patch fix had two independent reviews: an Opus sub-agent and Codex (gpt-6.1-sol, xhigh). Neither found anything to fix. Codex confirmed the CJS and ESM bundles and the source maps match the source patch, and that the lockfile hashes agree. Opus pointed out the leak also happened on resize, where atlases are rebuilt twice per pane; this fix covers that too.
Review
I ran two independent Opus reviews and a Codex (gpt-6.1-sol, xhigh) review, and fixed what they found:
Retained hidden terminals kept the shared atlas alive. The global rebuild now detaches them too, and they reattach when shown.
The notebook cell's maximum height was captured at mount. It now uses the live line height.
Notebook previews and code excerpts didn't get Native smoothing. They now carry an editor-typography-surface class.
Unknown editorLigatures values could enable ligatures. Anything except on/auto now resolves to off.
Blurring an already-empty number field wrote the setting again. It no longer does.
The editor rows reused terminal translation keys and hard-coded the weight and line-height ranges. They now have their own keys and use the shared constants.
Two reviewers also argued that CSS smoothing can't reach xterm's detached atlas canvas. The in-app measurements above show that it does once the renderer is rebuilt.
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 6 remain after this review.
Walkthrough
The change adds editor font size, weight, line height, ligature, and code font smoothing settings. A shared resolver supplies typography to Monaco editors, diff views, notebook cells, code excerpts, and automation prompt editors. Settings search and controls are updated. On supported platforms, changing code font smoothing updates the document attribute and rebuilds terminal WebGL renderers.
Merge Risk: Low · previous review 74486
Notebook and Markdown zoom percentages can differ from the rendered size; this is a localized UI inconsistency suitable for bounded follow-up.
Pre-merge checks: 3 · 2
Failed checks: 2 warnings
Check name
Status
Explanation
Resolution
Linked Issues check
The changes satisfy [#30084], [#35973], and [#25910]. The shared resolver applies editor size, ligatures, and smoothing to the reported Monaco and code surfaces, and the smoothing path rebuilds live W…
Use normalized terminalFontWeight as the fallback when editorFontWeight is unset or invalid. Keep explicit editor-weight overrides. Add tests for terminal-weight inheritance and override behavior.
Docstring Coverage
Docstring coverage is 46.15% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta…
Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed checks: 3 passed
Check name
Status
Explanation
Title check
The title clearly identifies the main change: a shared editor typography resolver with opt-in typography and macOS smoothing settings. It is long, but it remains specific and related to the changeset.
Out of Scope Changes check
The changes remain connected to the linked editor-typography objectives. The shared resolver, settings, tests, DOM styles, Monaco updates, and WebGL lifecycle changes support [#20183], [#35973], [#300…
Description check
The description follows the repository template closely. It has an ELI5-style summary in the Summary section, a detailed What Changed and Why explanation (root cause, resolver design, and the WebGL re…
Full details: Out of Scope Changes check
Explanation
The changes remain connected to the linked editor-typography objectives. The shared resolver, settings, tests, DOM styles, Monaco updates, and WebGL lifecycle changes support [#20183], [#35973], [#30084], or [#25910]. The added line-height support is an adjacent Monaco typography change identified as suitable to solve with editor weight. The changes do not implement the separately excluded File Explorer sizing or UI-wide IDE weight.
Full details: Description check
Explanation
The description follows the repository template closely. It has an ELI5-style summary in the Summary section, a detailed What Changed and Why explanation (root cause, resolver design, and the WebGL rebuild mechanism with alternatives such as #20188, #30088, #13056), a Visual Proof section with before/after screenshots for file editor, diff, settings, and smoothing, a Testing section with platforms actually tested (macOS, Linux, Windows) and the validation commands run, and Negative user-facing tradeoffs. The template's ELI5 heading is not present as a labeled section, and the 'Linked Issue' section uses 'Closes #' lines for four issues, which satisfies the linked issue requirement. The 'Review' heading and 'Agent skill upstream boundary' checkbox are not addressed, and the AI Disclosure section is not filled in. The checklist is not ticked. These gaps are non-critical relative to the substance of the description.
Full details: Linked Issues check
Explanation
The changes satisfy [#30084], [#35973], and [#25910]. The shared resolver applies editor size, ligatures, and smoothing to the reported Monaco and code surfaces, and the smoothing path rebuilds live WebGL renderers. The changes do not satisfy [#20183]. An empty editorFontWeight resolves to Monaco regular ('normal') instead of normalized terminalFontWeight, although [#20183] requires terminal-weight inheritance by default.
Resolution
Use normalized terminalFontWeight as the fallback when editorFontWeight is unset or invalid. Keep explicit editor-weight overrides. Add tests for terminal-weight inheritance and override behavior.
Full details: Docstring Coverage
Explanation
Docstring coverage is 46.15% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 26 functions across 29 files. (1 skipped: 1 unsupported.)
Resolution
Write docstrings for the functions missing them to satisfy the coverage threshold.
Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 4 remain after this review.
set -o pipefail
printf'%s\n''--- autoHeight definitions and call sites in editor surfaces ---'
rg -n -F --glob '*.tsx' -e 'autoHeight' src/renderer/src/components/editor/EditorConflictReviewSurface.tsx src/renderer/src/components/editor/EditorEditFileSurface.tsx src/renderer/src/components/editor
printf'%s\n''--- surrounding autoHeight declarations ---'
python3 - <<'PY'from pathlib import Pathfor name in [ 'src/renderer/src/components/editor/EditorConflictReviewSurface.tsx', 'src/renderer/src/components/editor/EditorEditFileSurface.tsx',]: lines = Path(name).read_text().splitlines() hits = [i for i, line in enumerate(lines) if 'autoHeight' in line] print(f'--- {name} ---') for i in hits: lo, hi = max(0, i-5), min(len(lines), i+6) print(f'lines {lo+1}-{hi}') for n in range(lo, hi): print(f'{n+1:5} {lines[n]}')printf '%s\n' '--- typography hook and its settings consumers ---'rg -n -F --glob '*.{ts,tsx}' -e 'function useEditorTypography' -e 'export function useEditorTypography' -e 'useEditorTypography' src/renderer/src/hooks src/renderer/src/lib src/renderer/src/components/settings | head -160 || test "$?" -eq 1
Repository: stablyai/orca
Length of output: 3863
Script executed:
nl -ba src/renderer/src/components/editor/use-monaco-editor-mount.ts | sed -n '255,292p'
Repository: stablyai/orca
Length of output: 1465
Read the current line height in the retained auto-height listener.
The mounted MonacoEditor updates typography without replacing its content-size listener. That listener still clamps with the mount-time line height. In the reachable conflict-review path with autoHeight: true, increasing the line height can restore the old, smaller cap. The current cap check then hides the scrollbar because it uses the new line height, so content can be clipped.
Route notebook and Markdown preview sizing through useEditorTypography().
IpynbViewer and MarkdownPreviewSurface still use fixed bases (13 and 14). When editorFontSize or terminalFontSize differs, their rendered sizes can differ from the overlay percentage calculated from resolveEditorBaseFontSize(settings).
Apply the same change in use-markdown-preview-source-foundation.ts, replacing computeEditorFontSize(14, editorFontZoomLevel) with useEditorTypography().fontSize.
…t-in weight, line height, ligatures, size and macOS smoothing
Monaco sites built font options ad hoc and never received fontWeight,
fontLigatures or lineHeight; the editor size was tied to the terminal.
editor-typography.ts now feeds every Monaco and DOM code surface. New
optional settings default to today's rendering. A macOS-only Font
Smoothing setting rebuilds WebGL terminal renderers so the change
reaches their shared glyph atlases.
…rers stop leaking canvases
The warm-up queue kept drawing into a disposed atlas, which re-appended its
scratch canvas to the terminal element. Every WebGL renderer rebuild (GPU
toggle, context-loss retry, font smoothing toggle) leaked one canvas per pane.
Upstream stablyai#36979 moved the structured agent session capability constants
under src/shared/agent-session/, and stablyai#36577 replaced DiffSectionBody's
diffEditorFontSize with typography. The two context-menu fork tests type
their hoisted item list instead of asserting it, which the v1.4.246
casting gate flags.
audichuang
added a commit
to audichuang/orca
that referenced
this pull request
Nov 3, 2026
Upstream stablyai#36979 moved the structured agent session capability constants
under src/shared/agent-session/, and stablyai#36577 replaced DiffSectionBody's
diffEditorFontSize with typography. The two context-menu fork tests type
their hoisted item list instead of asserting it, which the v1.4.246
casting gate flags.
audichuang
added a commit
to audichuang/orca
that referenced
this pull request
Nov 4, 2026
Upstream stablyai#36979 moved the structured agent session capability constants
under src/shared/agent-session/, and stablyai#36577 replaced DiffSectionBody's
diffEditorFontSize with typography. The two context-menu fork tests type
their hoisted item list instead of asserting it, which the v1.4.246
casting gate flags.
audichuang
added a commit
to audichuang/orca
that referenced
this pull request
Nov 5, 2026
Upstream stablyai#36979 moved the structured agent session capability constants
under src/shared/agent-session/, and stablyai#36577 replaced DiffSectionBody's
diffEditorFontSize with typography. The two context-menu fork tests type
their hoisted item list instead of asserting it, which the v1.4.246
casting gate flags.
audichuang
added a commit
to audichuang/orca
that referenced
this pull request
Nov 5, 2026
Upstream stablyai#36979 moved the structured agent session capability constants
under src/shared/agent-session/, and stablyai#36577 replaced DiffSectionBody's
diffEditorFontSize with typography. The two context-menu fork tests type
their hoisted item list instead of asserting it, which the v1.4.246
casting gate flags.
audichuang
added a commit
to audichuang/orca
that referenced
this pull request
Nov 6, 2026
Upstream stablyai#36979 moved the structured agent session capability constants
under src/shared/agent-session/, and stablyai#36577 replaced DiffSectionBody's
diffEditorFontSize with typography. The two context-menu fork tests type
their hoisted item list instead of asserting it, which the v1.4.246
casting gate flags.
audichuang
added a commit
to audichuang/orca
that referenced
this pull request
Nov 7, 2026
Upstream stablyai#36979 moved the structured agent session capability constants
under src/shared/agent-session/, and stablyai#36577 replaced DiffSectionBody's
diffEditorFontSize with typography. The two context-menu fork tests type
their hoisted item list instead of asserting it, which the v1.4.246
casting gate flags.
audichuang
added a commit
to audichuang/orca
that referenced
this pull request
Nov 8, 2026
Upstream stablyai#36979 moved the structured agent session capability constants
under src/shared/agent-session/, and stablyai#36577 replaced DiffSectionBody's
diffEditorFontSize with typography. The two context-menu fork tests type
their hoisted item list instead of asserting it, which the v1.4.246
casting gate flags.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Code typography now comes from one resolver. Before this, five Monaco sites (file editor, diff viewer, combined diff, notebook cells, automation prompt editor), the GitHub comment code excerpt and the editor zoom overlay each built
fontFamily/fontSizeon their own fromterminalFontSize. None of them passedfontWeight,fontLigaturesorlineHeight.src/renderer/src/lib/editor-typography.ts(withuseEditorTypography()) replaces the font-family part ofeditor-font-zoom.ts; that file now holds only the zoom-step math. Every one of those surfaces reads the resolver. The DOM excerpts use a CSS twin (editorTypographyTextStyle), so they match Monaco: the same family stack, weight, line height and liga/calt handling.New opt-in settings sit next to Editor Font Family under Settings › General › Editor. Any field left empty renders exactly as today.
antialiased)-webkit-font-smoothing: auto) for terminals and code editorsRoot cause, font smoothing
body { -webkit-font-smoothing: antialiased }is inherited by.xtermand.monaco-editor. I measured this in the running app: switching to native smoothing makes terminal glyphs about 14% brighter on average, with 29% more lit pixels.WebGL terminals bake the smoothing into their glyph atlas, and
clearTextureAtlas()does not pick up the change. xterm also shares atlases between terminals that use the same font config. So a toggle calls a newrebuildAllTerminalWebglRenderers(): every pane detaches first, then they all reattach, so no pane can get the old shared atlas back. The rebuild runs only when the attribute actually changes, so it costs nothing while the setting is unused.Closes #20183
Closes #35973
Closes #30084
Closes #25910
Supersedes / builds on: #20188 by @PedroOsnaghi (editor font weight and line height), #30088 by @nivaldomcj (opt-in Editor Font Size, and the zoom-overlay base), #13056 by @adaex (font smoothing). Each of those wires one option into some of the sites. This PR fixes the shared root cause instead, so all options reach every Monaco and DOM code surface through one path.
Not included:
Which surfaces follow the editor setting
Checked in code and measured in the running app (computed
font-family,font-size,font-weight,line-height,font-feature-settings) with defaults and with family FiraCode Nerd Font, size 16, weight 600, line height 24px, ligatures On:Setting semantics follow common code-editor conventions
22no longer clamps to 3× the font size. Pixel values are converted against the base size, so they still scale with editor zoom. They are clamped to 1–3× the font size so glyphs can't overlap.Markdown code with an explicit Editor Font Family (light: rich editor unset → set; dark: preview unset → set)




Visual proof
Captured on macOS from the real dev build, driven over CDP in the background. The sample repo uses FiraCode Nerd Font as the Editor Font Family. "Before" is
origin/main. "After" uses Editor Font Size 16, Weight 600, Line Height 1.8 and Ligatures On.File editor (dark)

File editor (light)

Diff viewer and combined diff (dark)

Diff viewer (light)

Settings › General › Editor (before / after dark / after light)

Font Smoothing: Grayscale vs Native (WebGL terminal and editor)

No change when nothing is set: with every new field empty, the file editor screenshots from
origin/mainand from this branch are byte-identical PNGs (dark), and so are the light diff-viewer screenshots. Computed styles match too: weight 400,"calt" 0, "liga" 0, 21px lines in the editor and 20px in the diff.Perf
Scanned: the full diff against
origin/main, using the six perf-scan questions:useEditorTypographyuses a shallow selector over seven keys, and theuseMemoresult is stable.Findings: none introduced. The notebook cell, notebook preview and automation prompt editor no longer subscribe to the whole
settingsobject. One pre-existing cost is unchanged and identical onorigin/main: the combined diff re-renders every section and callsupdateOptionsonce per section on every settings write. It is left for a follow-up.Measured. Dev builds, base (
origin/main) and branch launched side by side withORCA_BACKGROUND_LAUNCH=1, isolated profiles, driven over CDP. Renders were counted with a React commit hook (PerformedWorkper named component).updateOptionscalls were counted on every live Monaco instance. Workload: 10 unrelated settings writes.updateOptionscalls (base / branch)The 10 calls per 10 writes in the file editor and single-file diff happen on base too. The editor wrapper re-applies its options object on every render, and the effect this PR owns does not fire on unrelated writes.
openFileto the first painted line, 10 fresh files per round, three interleaved rounds. Base 205 / 234 / 267 ms, branch 220 / 224 / 205 ms. No measurable difference within dev-build noise.Merge-risk assessment
Low (was low–medium). Phase-2 stress and cross-platform testing exercised the risky WebGL rebuild path and fixed the one real problem it found.
fontWeight: 'normal',lineHeight: 0(Monaco auto) and ligatures off. The file-editor and light diff-viewer screenshots are byte-identical toorigin/main.--font-monostack (measured in light and dark). Line-height values from 1 to 3 resolve exactly as before. The sharedNumberFieldgained an opt-inclearOnZero, and it now skips writing a value equal to the current one; that is the only change for the other 17 users.nativesmoothing value copied onto Linux or Windows has no effect: the root attribute stays unset.useEditorTypographyuses a shallow selector, so unrelated settings writes don't re-render editors or callupdateOptions. Smoothing does nothing until a user opts in. A toggle rebuilds the live WebGL renderers once, the same cost as switching GPU acceleration.Evidence from the WebGL rebuild stress run. This was a macOS dev build with 68 terminal panes:
seq 1 40000while Font Smoothing was toggled 20 times at 250 ms intervals. Then another 490 toggles were run with no output.Results:
40000followed by the done marker..xterm-screencanvases at 18 throughout. Context losses stayed at 0, and there were no Chromium "too many WebGL contexts" warnings.WEBGL_lose_contexton 3 live panes. They fell back to the DOM renderer as the existing policy intends. Toggling smoothing afterwards skipped them, and all 9 visible panes still echoed new output.Leak found and fixed (commit
fix(terminal): cancel WebGL atlas warm-up…):dispose(). Orca's xterm patch makes that warm-up draw into the terminal element, so a disposed atlas re-appended its hidden scratch canvas and leaked one canvas per atlas.config/patches/xterm-src/@xterm__addon-webgl@0.20.0-beta.300.src.patchnow clears the queue on dispose and before a re-warm. I regenerated the built patch withregenerate-xterm-patches.mjs --write, which also updated the lockfile hash. Orphan canvases now stay at 1 (the live atlas) across 10 slow toggles, 120 rapid toggles and 10 GPU toggles.Cross-platform:
Negative user-facing tradeoffs
font-monoclass overrode the inline style. They also no longer show ligatures by default, to match Monaco.bg-emerald-500/10). They now use the theme's--ringcolor (bg-ring/15), the same color as the comment-range highlight in Monaco diffs, so the tint looks different from before.Validation
pnpm tc:webpasses.oxlintis clean on the changed files;oxfmtwas run.pnpm sync:localization-catalog,verify:localization-extractionandverify:localization-runtime-catalogpass.lib/editor-typography.test.tsplus the pane-manager, settings, editor (DiffSection*, Monaco*, Ipynb*), automations, app-shell anduseIpcEvents*suites. 372 files / 2889 tests pass. An earlier wider run, which also coveredterminal-pane, hit one failure that comes from the node-pty environment (fish-color-scheme-child-stdin.node-pty.test.ts); this diff does not touch that code.ORCA_BACKGROUND_LAUNCH=1, an isolated profile and CDP:font-weight,font-feature-settingsand line heights.undefinedand the placeholder falls back to the terminal value.clearTextureAtlas()alone does not apply the change (73.4 → 73.4). That is why this PR rebuilds the renderers.Follow-up delta (surface coverage, line-height semantics, Markdown code font)
origin/main.pnpm tc:webpasses, and the focused vitest run (editor typography and all settings suites, 182 files / 1276 tests) passes. The localization extraction and runtime-catalog checks pass.22stores 22 (pixels),1.5stores 1.5, and0or an empty field clears to automatic.<pre>) missed the font; rounding the ratio skewed pixel heights; typed pixel values were stored as a ratio, so they drifted when the font size changed; a hand-edited terminal size of 0 could divide by zero; the line-height field kept a stale1-3suffix. Not changed: the 1–3× clamp on pixel values is deliberate.Phase 2 (risk reduction)
VITE_EXPOSE_STORE=trueso they could inspect pane managers.Linux (omarchy):

Windows (awin):

Review
I ran two independent Opus reviews and a Codex (gpt-6.1-sol, xhigh) review, and fixed what they found:
editor-typography-surfaceclass.editorLigaturesvalues could enable ligatures. Anything except on/auto now resolves to off.Two reviewers also argued that CSS smoothing can't reach xterm's detached atlas canvas. The in-app measurements above show that it does once the renderer is rebuilt.