Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
069ebbe
feat(file-editor): add configurable typography
austinywang Aug 18, 2026
25fcf14
fix: quote typography source path in pbxproj
austinywang Aug 18, 2026
dd6db0b
fix(file-editor): address typography review feedback
austinywang Aug 18, 2026
4f51c91
fix(file-editor): preserve paragraph styling on reset
austinywang Aug 19, 2026
609aeeb
fix: clear Swift warning and test import failures
austinywang Aug 19, 2026
47c26a8
fix(settings): localize file editor search aliases
austinywang Aug 24, 2026
5d8989e
fix(file-editor): guard typography synchronization
austinywang Aug 25, 2026
65855bc
perf(file-editor): scope settings observation to editor
austinywang Aug 25, 2026
b9f40f5
fix(file-editor): defer family setting persistence
austinywang Aug 25, 2026
b2d6951
fix(file-editor): preserve explicit zoom overrides
austinywang Aug 25, 2026
700ca65
fix(settings): persist file editor font family on blur
austinywang Aug 25, 2026
9b65423
perf(file-editor): skip unchanged typography normalization
austinywang Aug 25, 2026
4a01797
refactor(file-editor): isolate typography input type
austinywang Aug 25, 2026
3de0ece
ci: refresh required CLA status
austinywang Sep 1, 2026
0f4ec0c
test(file-editor): exercise representable synchronization
austinywang Sep 1, 2026
9a213b5
refactor(file-editor): remove layout namespace
austinywang Sep 3, 2026
a9a2ded
refactor(file-editor): keep scoped settings with editor chrome
austinywang Sep 7, 2026
3d0a6a2
chore(file-editor): normalize rebased project wiring
austinywang Sep 7, 2026
9844853
fix(file-editor): preserve layout constants used by main tests
austinywang Sep 7, 2026
5b3c0a8
test(file-editor): supply gutter color in editor fixture
austinywang Sep 7, 2026
2ebc503
Merge branch 'main' into issue-10319-file-editor-font-settings
austinywang Sep 8, 2026
0b14997
Merge branch 'main' into issue-10319-file-editor-font-settings
austinywang Sep 8, 2026
82bd80c
Merge branch 'main' into issue-10319-file-editor-font-settings
austinywang Sep 8, 2026
86f9484
fix(file-editor): preserve typography after content updates
austinywang Sep 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,35 @@ import Foundation
/// is distinct from the rendered markdown viewer, whose settings live in
/// ``MarkdownCatalogSection``.
public struct FileEditorCatalogSection: SettingCatalogSection {
/// Default font size, in points, for newly opened editors.
///
/// The editor's zoom shortcuts still adjust an individual editor on top of
/// this default and Cmd-0 returns to it.
public let fontSize = DefaultsKey<Int>(
id: "fileEditor.fontSize",
defaultValue: 13,
userDefaultsKey: "fileEditor.fontSize"
)

/// Default font family for newly opened editors.
///
/// An empty value keeps the established monospaced system font.
public let fontFamily = DefaultsKey<String>(
id: "fileEditor.fontFamily",
defaultValue: "",
userDefaultsKey: "fileEditor.fontFamily"
)

/// Paragraph line-height multiplier for the editor.
///
/// `1.0` preserves the natural font leading; larger values add readable
/// space between lines.
public let lineHeight = DefaultsKey<Double>(
id: "fileEditor.lineHeight",
defaultValue: 1.0,
userDefaultsKey: "fileEditor.lineHeight"
)

/// Inclusive tab-stop range accepted by the editor and JSON schema.
///
/// Keep this as a package-level constant so AppKit, the settings UI, and
Expand All @@ -15,7 +44,6 @@ public struct FileEditorCatalogSection: SettingCatalogSection {

/// Inclusive tab-stop range accepted by the editor and JSON schema.
public var tabWidthRange: ClosedRange<Int> { Self.supportedTabWidthRange }

/// Whether long lines soft-wrap at the editor's right edge.
///
/// `false` (the default) preserves the established behavior: lines extend
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,33 @@ extension Array where Element == CuratedSettingEntry {
.init(section: .app, id: "preferred-editor", title: "Open Files With", synonyms: "app.preferredEditor editor open file code vscode visual studio zed sublime subl cursor"),
.init(section: .app, id: "supported-file-previews", title: "Open Supported Files in cmux", synonyms: "app.openSupportedFilesInCmux cmd click file preview pdf image video audio quicklook quick look editor external"),
.init(section: .app, id: "markdown-viewer", title: "Open Markdown in cmux Viewer", synonyms: "app.openMarkdownInCmuxViewer md markdown mdx viewer preview readme"),
.init(
section: .app,
id: "file-editor-font-size",
title: String(localized: "settings.app.fileEditorFontSize", defaultValue: "File Editor Font Size"),
synonyms: String(
localized: "settings.search.alias.setting.app.file-editor-font-size",
defaultValue: "fileEditor.fontSize file editor font size points zoom scale text bigger smaller larger default"
)
),
.init(
section: .app,
id: "file-editor-font-family",
title: String(localized: "settings.app.fileEditorFontFamily", defaultValue: "File Editor Font"),
synonyms: String(
localized: "settings.search.alias.setting.app.file-editor-font-family",
defaultValue: "fileEditor.fontFamily file editor font family typeface system mono custom"
)
),
.init(
section: .app,
id: "file-editor-line-height",
title: String(localized: "settings.app.fileEditorLineHeight", defaultValue: "File Editor Line Height"),
synonyms: String(
localized: "settings.search.alias.setting.app.file-editor-line-height",
defaultValue: "fileEditor.lineHeight file editor line height leading spacing paragraph line multiplier"
)
),
.init(section: .app, id: "file-editor-word-wrap", title: String(localized: "settings.app.fileEditorWordWrap", defaultValue: "File Editor Word Wrap"), synonyms: "fileEditor.wordWrap " + String(localized: "settings.search.fileEditor.wordWrap", defaultValue: "file editor word wrap soft wrap reflow lines text horizontal scroll preview")),
.init(
section: .app,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import SwiftUI
/// Keep Workspace Open When Closing Last Surface, Focus Pane on
/// First Click, File Drops, Open Files With, Open Supported Files in
/// cmux, Terminal Config link, Open Markdown in cmux Viewer,
/// Markdown Viewer typography, iMessage Mode, Reorder on Notification, Dock Badge, Menu Bar
/// Markdown Viewer typography, File Editor typography, iMessage Mode, Reorder on Notification, Dock Badge, Menu Bar
/// Only, Show in Menu Bar, Unread Pane Ring, Pane Flash, Desktop
/// Notifications, Notification Sound, Notification Command, Send
/// anonymous telemetry, Warn Before Quit, Warn Before Closing Tab /
Expand Down Expand Up @@ -41,6 +41,11 @@ public struct AppSection: View {
@State private var markdownMaxWidth: DefaultsValueModel<Int>
@State private var canvasPaneGap: DefaultsValueModel<Int>
@State private var canvasSnapping: DefaultsValueModel<Bool>
@State private var fileEditorFontSize: DefaultsValueModel<Int>
@State private var fileEditorFontFamily: DefaultsValueModel<String>
@State private var fileEditorFontFamilyDraft = ""
@State private var fileEditorFontFamilyDraftLoaded = false
@State private var fileEditorLineHeight: DefaultsValueModel<Double>
@State private var fileEditorWordWrap: DefaultsValueModel<Bool>
@State private var fileEditorSyntaxHighlighting: DefaultsValueModel<Bool>
@State private var fileEditorLineNumbers: DefaultsValueModel<Bool>
Expand Down Expand Up @@ -103,6 +108,9 @@ public struct AppSection: View {
_markdownMaxWidth = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.markdown.maxWidth))
_canvasPaneGap = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.canvas.paneGap))
_canvasSnapping = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.canvas.snappingEnabled))
_fileEditorFontSize = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.fileEditor.fontSize))
_fileEditorFontFamily = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.fileEditor.fontFamily))
_fileEditorLineHeight = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.fileEditor.lineHeight))
_fileEditorWordWrap = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.fileEditor.wordWrap))
_fileEditorSyntaxHighlighting = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.fileEditor.syntaxHighlighting))
_fileEditorLineNumbers = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.fileEditor.lineNumbers))
Expand Down Expand Up @@ -155,15 +163,24 @@ public struct AppSection: View {
mainCard
}
.task {
startSettingsObservation([language, appearance, appIcon, placement, inheritDir, minimalMode, keepWorkspaceOpen, firstClick, focusHistoryIncludesPanesAndTabs, fileDrop, preferredEditor, openSupported, openMarkdown, globalFontMagnification, markdownFontSize, markdownFontFamily, markdownMaxWidth, canvasPaneGap, canvasSnapping, fileEditorWordWrap, fileEditorSyntaxHighlighting, fileEditorLineNumbers, fileEditorIndentGuides, fileEditorCurrentLineHighlight, fileEditorTabWidth, iMessage, reorder, dockBadge, menuBarOnly, showInMenuBar, paneRing, paneFlash, desktopNotifications, agentPermissionPrompt, agentTurnComplete, agentIdleReminder, soundName, soundCommand, customSoundFile, soundOverrides, telemetry, confirmQuit, warnCloseTab, warnCloseX, hideCloseButton, renameSelects, paletteAllSurfaces])
startSettingsObservation([language, appearance, appIcon, placement, inheritDir, minimalMode, keepWorkspaceOpen, firstClick, focusHistoryIncludesPanesAndTabs, fileDrop, preferredEditor, openSupported, openMarkdown, globalFontMagnification, markdownFontSize, markdownFontFamily, markdownMaxWidth, canvasPaneGap, canvasSnapping, fileEditorFontSize, fileEditorFontFamily, fileEditorLineHeight, fileEditorWordWrap, fileEditorSyntaxHighlighting, fileEditorLineNumbers, fileEditorIndentGuides, fileEditorCurrentLineHighlight, fileEditorTabWidth, iMessage, reorder, dockBadge, menuBarOnly, showInMenuBar, paneRing, paneFlash, desktopNotifications, agentPermissionPrompt, agentTurnComplete, agentIdleReminder, soundName, soundCommand, customSoundFile, soundOverrides, telemetry, confirmQuit, warnCloseTab, warnCloseX, hideCloseButton, renameSelects, paletteAllSurfaces])
if soundAgents.isEmpty {
soundAgents = await hostActions.notificationSoundAgentOptions()
}
if !fileEditorFontFamilyDraftLoaded {
fileEditorFontFamilyDraft = fileEditorFontFamily.current
fileEditorFontFamilyDraftLoaded = true
}
if languageAtAppear == nil { languageAtAppear = language.current }; if telemetryAtAppear == nil { telemetryAtAppear = telemetry.current }
}
.onChange(of: soundOverrides.current) { _, newValue in
soundOverridesModel.accept(newValue)
}
.onChange(of: fileEditorFontFamily.current) { _, newValue in
if fileEditorFontFamilyDraft != newValue {
fileEditorFontFamilyDraft = newValue
}
}
}

private var globalFontMagnificationSubtitle: String {
Expand All @@ -184,6 +201,12 @@ public struct AppSection: View {
globalFontMagnification.set(clamped) { NotificationCenter.default.post(name: GlobalFontMagnification.didChangeNotification, object: nil) }
}

/// Persists the loaded font-family draft when editing ends or submits.
private func commitFileEditorFontFamilyDraft() {
guard fileEditorFontFamilyDraftLoaded else { return }
fileEditorFontFamily.set(fileEditorFontFamilyDraft)
}

@ViewBuilder
private var mainCard: some View {
SettingsCard {
Expand Down Expand Up @@ -521,6 +544,75 @@ public struct AppSection: View {
}
SettingsCardDivider()

// File Editor Font Size
SettingsCardRow(
configurationReview: .json("fileEditor.fontSize"),
String(localized: "settings.app.fileEditorFontSize", defaultValue: "File Editor Font Size"),
subtitle: String(localized: "settings.app.fileEditorFontSize.subtitle", defaultValue: "Default font size, in points, for newly opened plain-text editors. Cmd-+ / Cmd-- / Cmd-0 zoom the current editor."),
controlWidth: Self.columnWidth
) {
Stepper(
value: Binding(get: { fileEditorFontSize.current }, set: { fileEditorFontSize.set($0) }),
in: 8...36
) {
Text(verbatim: "\(fileEditorFontSize.current)")
.monospacedDigit()
.frame(width: 28, alignment: .trailing)
}
.controlSize(.small)
.accessibilityIdentifier("SettingsFileEditorFontSizeStepper")
.accessibilityLabel(
String(localized: "settings.app.fileEditorFontSize", defaultValue: "File Editor Font Size")
)
}
SettingsCardDivider()

// File Editor Font Family
SettingsCardRow(
configurationReview: .json("fileEditor.fontFamily"),
String(localized: "settings.app.fileEditorFontFamily", defaultValue: "File Editor Font"),
subtitle: String(localized: "settings.app.fileEditorFontFamily.subtitle", defaultValue: "Default font family for newly opened plain-text editors. Leave empty for the system monospaced font.")
) {
TextField(
String(localized: "settings.app.fileEditorFontFamily.placeholder", defaultValue: "System Mono"),
text: $fileEditorFontFamilyDraft,
onEditingChanged: { isEditing in
if !isEditing {
commitFileEditorFontFamilyDraft()
}
},
onCommit: { commitFileEditorFontFamilyDraft() }
)
.textFieldStyle(.roundedBorder)
.frame(width: 200)
.accessibilityIdentifier("SettingsFileEditorFontFamilyTextField")
.onDisappear { commitFileEditorFontFamilyDraft() }
}
SettingsCardDivider()

// File Editor Line Height
SettingsCardRow(
configurationReview: .json("fileEditor.lineHeight"),
String(localized: "settings.app.fileEditorLineHeight", defaultValue: "File Editor Line Height"),
subtitle: String(localized: "settings.app.fileEditorLineHeight.subtitle", defaultValue: "Line-height multiplier for plain-text editors. 1.0 preserves the font's natural spacing.")
) {
Stepper(
value: Binding(get: { fileEditorLineHeight.current }, set: { fileEditorLineHeight.set($0) }),
in: 0.5...3.0,
step: 0.1
) {
Text(verbatim: String(format: "%.1f", fileEditorLineHeight.current))
.monospacedDigit()
.frame(width: 32, alignment: .trailing)
}
.controlSize(.small)
.accessibilityIdentifier("SettingsFileEditorLineHeightStepper")
.accessibilityLabel(
String(localized: "settings.app.fileEditorLineHeight", defaultValue: "File Editor Line Height")
)
}
SettingsCardDivider()

// File Editor Word Wrap
SettingsCardRow(
configurationReview: .json("fileEditor.wordWrap"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ struct SettingsRowAnchorResolutionTests {
"canvas.snappingEnabled",
"cloud.beta.machines.enabled",
"customSidebars.renderer",
"fileEditor.fontFamily",
"fileEditor.fontSize",
"fileEditor.lineHeight",
"fileEditor.currentLineHighlight",
"fileEditor.indentGuides",
"fileEditor.lineNumbers",
Expand Down
Loading
Loading