From 622ad8bb7fc4df041e80e3a593ccc2ccb4d8810d Mon Sep 17 00:00:00 2001 From: lawrencecchen <54008264+lawrencecchen@users.noreply.github.com> Date: Mon, 1 Jun 2026 17:18:17 -0700 Subject: [PATCH] Add markdown viewer font size controls --- CLI/cmux.swift | 57 +- .../CmuxSettings/Keys/AppCatalogSection.swift | 6 + .../CmuxSettings/RuntimeNotifications.swift | 5 + .../Values/ShortcutAction+Defaults.swift | 3 + .../CmuxSettings/Values/ShortcutAction.swift | 10 + .../CuratedSettingEntry+Default.swift | 1 + .../CmuxSettingsUI/Sections/AppSection.swift | 62 ++ .../Sections/KeyboardShortcutsSection.swift | 40 + .../SettingsRowAnchorResolutionTests.swift | 1 + README.md | 7 + Resources/Localizable.xcstrings | 750 ++++++++++++++++++ Resources/markdown-viewer/shell.html | 15 +- Sources/App/WorkspaceRuntimeSettings.swift | 42 + Sources/AppDelegate.swift | 12 + Sources/CmuxSettingsJSONPathSupport.swift | 1 + Sources/ContentView.swift | 6 +- Sources/KeyboardShortcutContext.swift | 48 +- Sources/KeyboardShortcutSettings.swift | 12 + ...rdShortcutSettingsFileStore+Template.swift | 1 + .../KeyboardShortcutSettingsFileStore.swift | 15 + Sources/Panels/MarkdownPanel.swift | 66 +- Sources/Panels/MarkdownPanelView.swift | 1 + Sources/Panels/MarkdownWebRenderer.swift | 56 +- Sources/SessionPersistence.swift | 1 + Sources/SettingsNavigation.swift | 2 + Sources/SettingsSearchAliases.swift | 1 + Sources/TerminalController.swift | 15 +- Sources/Workspace.swift | 32 +- cmuxTests/KeyboardShortcutContextTests.swift | 15 +- ...hortcutSettingsFileStoreStartupTests.swift | 36 + cmuxTests/MarkdownPanelTests.swift | 67 +- cmuxTests/SettingsSearchIndexTests.swift | 1 + cmuxUITests/SettingsAppBehaviorUITests.swift | 61 +- cmuxUITests/SettingsUITestSupport.swift | 7 + docs/cli-contract.md | 4 +- docs/configuration.md | 10 + web/data/cmux-shortcuts.ts | 24 + web/data/cmux.schema.json | 10 + web/messages/ar.json | 1 + web/messages/bs.json | 1 + web/messages/da.json | 1 + web/messages/de.json | 1 + web/messages/en.json | 1 + web/messages/es.json | 1 + web/messages/fr.json | 1 + web/messages/it.json | 1 + web/messages/ja.json | 1 + web/messages/km.json | 1 + web/messages/ko.json | 1 + web/messages/no.json | 1 + web/messages/pl.json | 1 + web/messages/pt-BR.json | 1 + web/messages/ru.json | 1 + web/messages/th.json | 1 + web/messages/tr.json | 1 + web/messages/uk.json | 1 + web/messages/zh-CN.json | 1 + web/messages/zh-TW.json | 1 + 58 files changed, 1449 insertions(+), 74 deletions(-) create mode 100644 Packages/CmuxSettings/Sources/CmuxSettings/RuntimeNotifications.swift diff --git a/CLI/cmux.swift b/CLI/cmux.swift index 2282e91c204f..ce2c5b778eeb 100644 --- a/CLI/cmux.swift +++ b/CLI/cmux.swift @@ -4637,10 +4637,12 @@ struct CMUXCLI { let (surfaceOpt, argsAfterSurface) = parseOption(argsAfterWindow, name: "--surface") let (directionOpt, argsAfterDirection) = parseOption(argsAfterSurface, name: "--direction") let (focusOpt, argsAfterFocus) = parseOption(argsAfterDirection, name: "--focus") - args = argsAfterFocus + let (fontSizeOpt, argsAfterFontSize) = parseOption(argsAfterFocus, name: "--font-size") + args = argsAfterFontSize // Determine subcommand. Explicit "open" is supported, otherwise treat // a single positional argument as shorthand path. + let usage = "cmux markdown open [--workspace ] [--surface ] [--window ] [--direction right|down|left|up] [--focus ] [--font-size ]" let subArgs: [String] if let first = args.first, first.lowercased() == "open" { subArgs = Array(args.dropFirst()) @@ -4652,7 +4654,7 @@ struct CMUXCLI { if let first = args.first, first.hasPrefix("-") { throw CLIError( message: - "markdown open: unknown flag '\(first)'. Usage: cmux markdown open [--workspace ] [--surface ] [--window ] [--direction right|down|left|up] [--focus ]" + "markdown open: unknown flag '\(first)'. Usage: \(usage)" ) } else if let first = args.first, looksLikePath(first) || first.contains(".") { subArgs = args @@ -4664,22 +4666,29 @@ struct CMUXCLI { } guard let rawPath = subArgs.first, !rawPath.isEmpty else { - throw CLIError(message: "markdown open requires a file path. Usage: cmux markdown open ") + throw CLIError(message: "markdown open requires a file path. Usage: \(usage)") } let trailingArgs = Array(subArgs.dropFirst()) if let unknownFlag = trailingArgs.first(where: { $0.hasPrefix("-") }) { throw CLIError( message: - "markdown open: unknown flag '\(unknownFlag)'. Usage: cmux markdown open [--workspace ] [--surface ] [--window ] [--direction right|down|left|up] [--focus ]" + "markdown open: unknown flag '\(unknownFlag)'. Usage: \(usage)" ) } if let extraArg = trailingArgs.first { throw CLIError( message: - "markdown open: unexpected argument '\(extraArg)'. Usage: cmux markdown open [--workspace ] [--surface ] [--window ] [--direction right|down|left|up] [--focus ]" + "markdown open: unexpected argument '\(extraArg)'. Usage: \(usage)" ) } + let fontSizePoints: Double? + if let fontSizeOpt { + fontSizePoints = try parseMarkdownViewerFontSize(fontSizeOpt) + } else { + fontSizePoints = nil + } + let absolutePath = resolvePath(rawPath) // Build params @@ -4702,6 +4711,9 @@ struct CMUXCLI { } } try applyFocusOption(focusOpt, defaultValue: false, to: ¶ms) + if let fontSizePoints { + params["font_size"] = fontSizePoints + } let payload = try client.sendV2(method: "markdown.open", params: params) @@ -4711,7 +4723,9 @@ struct CMUXCLI { let surfaceText = formatHandle(payload, kind: "surface", idFormat: idFormat) ?? "unknown" let paneText = formatHandle(payload, kind: "pane", idFormat: idFormat) ?? "unknown" let filePath = (payload["path"] as? String) ?? absolutePath - print("OK surface=\(surfaceText) pane=\(paneText) path=\(filePath)") + let fontSize = doubleFromAny(payload["font_size"]) ?? fontSizePoints + let fontSizeText = fontSize.map { " font_size=\(formatMarkdownViewerFontSize($0))" } ?? "" + print("OK surface=\(surfaceText) pane=\(paneText) path=\(filePath)\(fontSizeText)") } } @@ -14436,12 +14450,14 @@ struct CMUXCLI { --window Target window --direction Split direction (default: right) --focus Focus the markdown panel (default: false) + --font-size Rendered markdown font size Examples: cmux markdown open plan.md cmux markdown ~/project/CHANGELOG.md cmux markdown open ./docs/design.md --workspace 0 cmux markdown open plan.md --direction down + cmux markdown open plan.md --font-size 12 """ default: return nil @@ -14500,6 +14516,33 @@ struct CMUXCLI { return (value, remaining) } + private func parseMarkdownViewerFontSize(_ rawValue: String) throws -> Double { + let trimmed = rawValue.trimmingCharacters(in: .whitespacesAndNewlines) + guard let size = Double(trimmed), + isUsableMarkdownViewerFontSize(size) else { + throw CLIError(message: "--font-size must be a positive number no larger than 96") + } + return roundedMarkdownViewerMetric(size) + } + + private func isUsableMarkdownViewerFontSize(_ size: Double) -> Bool { + size.isFinite && size > 0 && size <= 96 + } + + private func roundedMarkdownViewerMetric(_ value: Double) -> Double { + (value * 100).rounded() / 100 + } + + private func formatMarkdownViewerFontSize(_ value: Double) -> String { + let rounded = roundedMarkdownViewerMetric(value) + if rounded.rounded() == rounded { + return String(Int(rounded)) + } + return String(format: "%.2f", rounded) + .replacingOccurrences(of: #"0+$"#, with: "", options: .regularExpression) + .replacingOccurrences(of: #"\.$"#, with: "", options: .regularExpression) + } + private func parseRepeatedOption(_ args: [String], name: String) -> ([String], [String]) { var remaining: [String] = [] var values: [String] = [] @@ -31402,7 +31445,7 @@ export default function cmuxPiSessionExtension(pi: ExtensionAPI) { respawn-pane [--workspace ] [--surface ] [--window ] [--command ] display-message [-p|--print] - markdown [open] [--focus ] (open markdown file in formatted viewer panel with live reload) + markdown [open] [--focus ] [--font-size ] (open markdown file in formatted viewer panel with live reload) diff [patch-file|-] [--source ] [--cwd ] [--base ] [--focus ] [--no-focus] [--title ] [--layout ] [--font-size ] (open patch input or git source in a browser split) browser [--surface | ] ... diff --git a/Packages/CmuxSettings/Sources/CmuxSettings/Keys/AppCatalogSection.swift b/Packages/CmuxSettings/Sources/CmuxSettings/Keys/AppCatalogSection.swift index 3d275d2f90da..bd565611066e 100644 --- a/Packages/CmuxSettings/Sources/CmuxSettings/Keys/AppCatalogSection.swift +++ b/Packages/CmuxSettings/Sources/CmuxSettings/Keys/AppCatalogSection.swift @@ -79,6 +79,12 @@ public struct AppCatalogSection: SettingCatalogSection { userDefaultsKey: "openMarkdownInCmuxViewer" ) + public let markdownViewerFontSize = DefaultsKey( + id: "app.markdownViewerFontSize", + defaultValue: 15, + userDefaultsKey: "markdownViewerFontSize" + ) + public let iMessageMode = DefaultsKey( id: "app.iMessageMode", defaultValue: false, diff --git a/Packages/CmuxSettings/Sources/CmuxSettings/RuntimeNotifications.swift b/Packages/CmuxSettings/Sources/CmuxSettings/RuntimeNotifications.swift new file mode 100644 index 000000000000..f95822e25ff8 --- /dev/null +++ b/Packages/CmuxSettings/Sources/CmuxSettings/RuntimeNotifications.swift @@ -0,0 +1,5 @@ +import Foundation + +public enum CmuxSettingsRuntimeNotifications { + public static let markdownViewerFontSizeDidChange = Notification.Name("cmux.markdownViewerFontSizeDidChange") +} diff --git a/Packages/CmuxSettings/Sources/CmuxSettings/Values/ShortcutAction+Defaults.swift b/Packages/CmuxSettings/Sources/CmuxSettings/Values/ShortcutAction+Defaults.swift index 5f3ff0beb91c..b44e659b0c01 100644 --- a/Packages/CmuxSettings/Sources/CmuxSettings/Values/ShortcutAction+Defaults.swift +++ b/Packages/CmuxSettings/Sources/CmuxSettings/Values/ShortcutAction+Defaults.swift @@ -78,6 +78,9 @@ extension ShortcutAction { case .browserZoomIn: return ShortcutStroke(key: "=", command: true) case .browserZoomOut: return ShortcutStroke(key: "-", command: true) case .browserZoomReset: return ShortcutStroke(key: "0", command: true) + case .markdownZoomIn: return ShortcutStroke(key: "=", command: true) + case .markdownZoomOut: return ShortcutStroke(key: "-", command: true) + case .markdownZoomReset: return ShortcutStroke(key: "0", command: true) case .find: return ShortcutStroke(key: "f", command: true) case .findInDirectory: return ShortcutStroke(key: "f", command: true, shift: true) case .findNext: return ShortcutStroke(key: "g", command: true) diff --git a/Packages/CmuxSettings/Sources/CmuxSettings/Values/ShortcutAction.swift b/Packages/CmuxSettings/Sources/CmuxSettings/Values/ShortcutAction.swift index c504aa3e2909..cbfb6aaef09b 100644 --- a/Packages/CmuxSettings/Sources/CmuxSettings/Values/ShortcutAction.swift +++ b/Packages/CmuxSettings/Sources/CmuxSettings/Values/ShortcutAction.swift @@ -87,6 +87,9 @@ public enum ShortcutAction: String, CaseIterable, Sendable, Hashable, SettingCod case browserZoomIn case browserZoomOut case browserZoomReset + case markdownZoomIn + case markdownZoomOut + case markdownZoomReset case find case findInDirectory case findNext @@ -106,6 +109,7 @@ extension ShortcutAction { case navigation case panes case browser + case markdown public var title: String { switch self { @@ -114,6 +118,7 @@ extension ShortcutAction { case .navigation: return "Navigation" case .panes: return "Panes" case .browser: return "Browser & Find" + case .markdown: return "Markdown Viewer" } } } @@ -148,6 +153,8 @@ extension ShortcutAction { .hideFind, .useSelectionForFind, .toggleBrowserDeveloperTools, .showBrowserJavaScriptConsole, .toggleReactGrab: return .browser + case .markdownZoomIn, .markdownZoomOut, .markdownZoomReset: + return .markdown } } @@ -221,6 +228,9 @@ extension ShortcutAction { case .browserZoomIn: return "Zoom In" case .browserZoomOut: return "Zoom Out" case .browserZoomReset: return "Actual Size" + case .markdownZoomIn: return "Markdown Viewer: Zoom In" + case .markdownZoomOut: return "Markdown Viewer: Zoom Out" + case .markdownZoomReset: return "Markdown Viewer: Actual Size" case .find: return "Find…" case .findInDirectory: return "Find in Directory…" case .findNext: return "Find Next" diff --git a/Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/CuratedSettingEntry+Default.swift b/Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/CuratedSettingEntry+Default.swift index 3b024cc26743..b7cee177f03f 100644 --- a/Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/CuratedSettingEntry+Default.swift +++ b/Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/CuratedSettingEntry+Default.swift @@ -41,6 +41,7 @@ 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: "markdown-font-size", title: "Markdown Viewer Font Size", synonyms: "app.markdownViewerFontSize markdown viewer font size text scale zoom points preview"), .init(section: .app, id: "terminal-config", title: "Terminal Config", synonyms: "ghostty config merged generated preview terminal configuration window open config"), .init(section: .app, id: "imessage-mode", title: "iMessage Mode", synonyms: "app.iMessageMode imessage message messages chat prompt prompts submitted texting reorder move workspace top agent send"), .init(section: .app, id: "reorder-notification", title: "Reorder on Notification", synonyms: "app.reorderOnNotification notification reorder move workspace top unread sort"), diff --git a/Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AppSection.swift b/Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AppSection.swift index 31decb1a2c04..cdcd3371d6b4 100644 --- a/Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AppSection.swift +++ b/Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AppSection.swift @@ -18,6 +18,7 @@ import UniformTypeIdentifiers @MainActor public struct AppSection: View { private let catalog: SettingCatalog + private let defaultsStore: UserDefaultsSettingsStore private let hostActions: SettingsHostActions // Every bound value-model lives here as view state, constructed once @@ -35,6 +36,7 @@ public struct AppSection: View { @State private var preferredEditor: DefaultsValueModel @State private var openSupported: DefaultsValueModel @State private var openMarkdown: DefaultsValueModel + @State private var markdownFontSize: DefaultsValueModel @State private var iMessage: DefaultsValueModel @State private var reorder: DefaultsValueModel @State private var dockBadge: DefaultsValueModel @@ -62,6 +64,7 @@ public struct AppSection: View { hostActions: SettingsHostActions ) { self.catalog = catalog + self.defaultsStore = defaultsStore self.hostActions = hostActions _language = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.app.language)) _appearance = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.app.appearance)) @@ -75,6 +78,7 @@ public struct AppSection: View { _preferredEditor = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.app.preferredEditor)) _openSupported = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.app.openSupportedFilesInCmux)) _openMarkdown = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.app.openMarkdownInCmuxViewer)) + _markdownFontSize = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.app.markdownViewerFontSize)) _iMessage = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.app.iMessageMode)) _reorder = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.app.reorderOnNotification)) _dockBadge = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.notifications.dockBadge)) @@ -96,6 +100,7 @@ public struct AppSection: View { private static let columnWidth: CGFloat = 196 private static let notificationSoundControlWidth: CGFloat = 280 + private static let markdownFontSizeRange: ClosedRange = 1...96 /// Languages legacy `AppLanguage` exposes (cmuxApp.swift line /// 4338). The shared `CmuxSettings.AppLanguage` adds `.vi` for a @@ -311,6 +316,35 @@ public struct AppSection: View { } SettingsCardDivider() + // Markdown Viewer Font Size + SettingsCardRow( + configurationReview: .json("app.markdownViewerFontSize"), + String(localized: "settings.app.markdownViewerFontSize", defaultValue: "Markdown Viewer Font Size"), + subtitle: String(localized: "settings.app.markdownViewerFontSize.subtitle", defaultValue: "Default rendered markdown size for new viewer panels. Panel zoom shortcuts override this per panel until reset."), + controlWidth: 140 + ) { + Stepper( + value: Binding( + get: { Self.clampedMarkdownFontSize(markdownFontSize.current) }, + set: { setMarkdownFontSize($0) } + ), + in: Self.markdownFontSizeRange, + step: 1 + ) { + Text( + String.localizedStringWithFormat( + String(localized: "settings.fontSize.valuePoints", defaultValue: "%@ pt"), + Self.formatMarkdownFontSize(markdownFontSize.current) + ) + ) + .foregroundStyle(.secondary) + .monospacedDigit() + .frame(minWidth: 44, alignment: .trailing) + } + .accessibilityIdentifier("SettingsMarkdownViewerFontSizeStepper") + } + SettingsCardDivider() + // iMessage Mode SettingsCardRow( configurationReview: .json("app.iMessageMode"), @@ -369,6 +403,7 @@ public struct AppSection: View { Toggle("", isOn: Binding(get: { showInMenuBar.current }, set: { showInMenuBar.set($0) })) .labelsHidden() .controlSize(.small) + .accessibilityIdentifier("SettingsShowInMenuBarToggle") } .disabled(menuBarOnly.current) SettingsCardDivider() @@ -715,6 +750,33 @@ public struct AppSection: View { } } + private static func clampedMarkdownFontSize(_ value: Double) -> Double { + guard value.isFinite else { return 15 } + return min(max(value, markdownFontSizeRange.lowerBound), markdownFontSizeRange.upperBound) + } + + private static func formatMarkdownFontSize(_ value: Double) -> String { + let clamped = clampedMarkdownFontSize(value) + if clamped.rounded() == clamped { + return "\(Int(clamped))" + } + return String(format: "%.2f", clamped) + } + + private func setMarkdownFontSize(_ value: Double) { + let clamped = Self.clampedMarkdownFontSize(value) + let key = catalog.app.markdownViewerFontSize + Task { [defaultsStore] in + await defaultsStore.set(clamped, for: key) + await MainActor.run { + NotificationCenter.default.post( + name: CmuxSettingsRuntimeNotifications.markdownViewerFontSizeDidChange, + object: nil + ) + } + } + } + private func confirmQuitSubtitle(_ mode: ConfirmQuitMode) -> String { // Mirrors legacy confirmQuitModeSubtitle keys/text. switch mode { diff --git a/Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/KeyboardShortcutsSection.swift b/Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/KeyboardShortcutsSection.swift index 650082d4db1c..724b269bae48 100644 --- a/Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/KeyboardShortcutsSection.swift +++ b/Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/KeyboardShortcutsSection.swift @@ -290,6 +290,44 @@ public struct KeyboardShortcutsSection: View { // MARK: - Conflict helpers + private enum ShortcutConflictContext: Equatable { + case application + case nonBrowserPanel + case browserPanel + case markdownPanel + case rightSidebarFocus + + func overlaps(_ other: ShortcutConflictContext) -> Bool { + if self == .application || other == .application { + return true + } + if self == .nonBrowserPanel && other == .markdownPanel { + return true + } + if self == .markdownPanel && other == .nonBrowserPanel { + return true + } + return self == other + } + } + + private static func shortcutContext(for action: ShortcutAction) -> ShortcutConflictContext { + switch action { + case .switchRightSidebarToFiles, .switchRightSidebarToFind, .switchRightSidebarToSessions, + .switchRightSidebarToFeed, .switchRightSidebarToDock: + return .rightSidebarFocus + case .renameTab, .renameWorkspace: + return .nonBrowserPanel + case .browserBack, .browserForward, .browserReload, .toggleBrowserDeveloperTools, + .showBrowserJavaScriptConsole, .browserZoomIn, .browserZoomOut, .browserZoomReset: + return .browserPanel + case .markdownZoomIn, .markdownZoomOut, .markdownZoomReset: + return .markdownPanel + default: + return .application + } + } + /// Mirrors legacy `KeyboardShortcutSettings.Action.conflicts(with:proposedAction:configuredShortcut:)` /// at a coarser grain: only treat two actions as conflicting when the /// *configured* (effective) shortcut of the other action is not @@ -305,7 +343,9 @@ public struct KeyboardShortcutsSection: View { } private func detectConflict(for action: ShortcutAction, stroke: StoredShortcut) -> ShortcutAction? { + let context = Self.shortcutContext(for: action) for other in ShortcutAction.allCases where other != action { + guard context.overlaps(Self.shortcutContext(for: other)) else { continue } let override = bindings[other.rawValue] let effective = override ?? other.defaultStroke.map { StoredShortcut(first: $0) } guard let effective, !effective.isUnbound else { continue } diff --git a/Packages/CmuxSettingsUI/Tests/CmuxSettingsUITests/SettingsRowAnchorResolutionTests.swift b/Packages/CmuxSettingsUI/Tests/CmuxSettingsUITests/SettingsRowAnchorResolutionTests.swift index b45d574eb7dc..8abc2354b94a 100644 --- a/Packages/CmuxSettingsUI/Tests/CmuxSettingsUITests/SettingsRowAnchorResolutionTests.swift +++ b/Packages/CmuxSettingsUI/Tests/CmuxSettingsUITests/SettingsRowAnchorResolutionTests.swift @@ -45,6 +45,7 @@ struct SettingsRowAnchorResolutionTests { "app.language", "app.menuBarOnly", "app.minimalMode", + "app.markdownViewerFontSize", "app.newWorkspacePlacement", "app.openMarkdownInCmuxViewer", "app.openSupportedFilesInCmux", diff --git a/README.md b/README.md index 2dd588da552d..06768968f5e8 100644 --- a/README.md +++ b/README.md @@ -197,6 +197,13 @@ Command palette navigation shortcuts, including ⌃ P, are also customizable and | ⌥ ⌘ I | Toggle Developer Tools (Safari default) | | ⌥ ⌘ C | Show JavaScript Console (Safari default) | +### Markdown Viewer + +| Shortcut | Action | +|----------|--------| +| ⌘ = / ⌘ - | Increase / decrease rendered markdown size | +| ⌘ 0 | Reset rendered markdown size | + ### Notifications | Shortcut | Action | diff --git a/Resources/Localizable.xcstrings b/Resources/Localizable.xcstrings index b9f4f9667b4d..24e676f50630 100644 --- a/Resources/Localizable.xcstrings +++ b/Resources/Localizable.xcstrings @@ -108808,6 +108808,131 @@ } } }, + "settings.search.alias.setting.app.markdown-font-size": { + "extractionState": "manual", + "localizations": { + "ar": { + "stringUnit": { + "state": "translated", + "value": "app.markdownViewerFontSize markdown viewer font size text scale zoom points preview عارض Markdown حجم الخط النص مقياس تكبير تصغير إعادة تعيين معاينة" + } + }, + "bs": { + "stringUnit": { + "state": "translated", + "value": "app.markdownViewerFontSize markdown viewer font size text scale zoom points preview Markdown preglednik veličina fonta tekst skala zum uvećaj umanji resetuj pregled" + } + }, + "da": { + "stringUnit": { + "state": "translated", + "value": "app.markdownViewerFontSize markdown viewer font size text scale zoom points preview Markdown-visning skriftstørrelse tekst skalering zoom zoom ind zoom ud nulstil forhåndsvisning" + } + }, + "de": { + "stringUnit": { + "state": "translated", + "value": "app.markdownViewerFontSize markdown viewer font size text scale zoom points preview Markdown-Viewer Schriftgröße Text Skalierung Zoom einzoomen auszoomen zurücksetzen Vorschau" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "app.markdownViewerFontSize markdown viewer font size text scale zoom points preview" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "app.markdownViewerFontSize markdown viewer font size text scale zoom points preview visor Markdown tamaño fuente texto escala zoom ampliar reducir restablecer vista previa" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "app.markdownViewerFontSize markdown viewer font size text scale zoom points preview visionneuse Markdown taille police texte échelle zoom avant arrière réinitialiser aperçu" + } + }, + "it": { + "stringUnit": { + "state": "translated", + "value": "app.markdownViewerFontSize markdown viewer font size text scale zoom points preview visualizzatore Markdown dimensione font testo scala zoom ingrandisci riduci ripristina anteprima" + } + }, + "ja": { + "stringUnit": { + "state": "translated", + "value": "app.markdownViewerFontSize markdown viewer font size text scale zoom points preview Markdown ビューア フォントサイズ 文字 サイズ 拡大 縮小 リセット" + } + }, + "km": { + "stringUnit": { + "state": "translated", + "value": "app.markdownViewerFontSize markdown viewer font size text scale zoom points preview កម្មវិធីមើល Markdown ទំហំអក្សរ អត្ថបទ មាត្រដ្ឋាន ពង្រីក បង្រួម កំណត់ឡើងវិញ មើលជាមុន" + } + }, + "ko": { + "stringUnit": { + "state": "translated", + "value": "app.markdownViewerFontSize markdown viewer font size text scale zoom points preview Markdown 뷰어 글꼴 크기 텍스트 배율 확대 축소 재설정 미리보기" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "app.markdownViewerFontSize markdown viewer font size text scale zoom points preview Markdown-visning skriftstørrelse tekst skalering zoom zoom inn zoom ut tilbakestill forhåndsvisning" + } + }, + "pl": { + "stringUnit": { + "state": "translated", + "value": "app.markdownViewerFontSize markdown viewer font size text scale zoom points preview przeglądarka Markdown rozmiar czcionki tekst skala zoom powiększ pomniejsz resetuj podgląd" + } + }, + "pt-BR": { + "stringUnit": { + "state": "translated", + "value": "app.markdownViewerFontSize markdown viewer font size text scale zoom points preview visualizador Markdown tamanho fonte texto escala zoom aumentar diminuir redefinir prévia" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "app.markdownViewerFontSize markdown viewer font size text scale zoom points preview просмотр Markdown размер шрифта текст масштаб увеличить уменьшить сброс предпросмотр" + } + }, + "th": { + "stringUnit": { + "state": "translated", + "value": "app.markdownViewerFontSize markdown viewer font size text scale zoom points preview ตัวแสดง Markdown ขนาดฟอนต์ ข้อความ สเกล ซูม ซูมเข้า ซูมออก รีเซ็ต แสดงตัวอย่าง" + } + }, + "tr": { + "stringUnit": { + "state": "translated", + "value": "app.markdownViewerFontSize markdown viewer font size text scale zoom points preview Markdown görüntüleyici yazı tipi boyutu metin ölçek yakınlaştır uzaklaştır sıfırla önizleme" + } + }, + "uk": { + "stringUnit": { + "state": "translated", + "value": "app.markdownViewerFontSize markdown viewer font size text scale zoom points preview переглядач Markdown розмір шрифту текст масштаб збільшити зменшити скинути попередній перегляд" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "app.markdownViewerFontSize markdown viewer font size text scale zoom points preview Markdown 查看器 字体大小 文本 缩放 放大 缩小 重置 预览" + } + }, + "zh-Hant": { + "stringUnit": { + "state": "translated", + "value": "app.markdownViewerFontSize markdown viewer font size text scale zoom points preview Markdown 檢視器 字體大小 文字 縮放 放大 縮小 重設 預覽" + } + } + } + }, "settings.search.alias.setting.app.reorder-notification": { "extractionState": "manual", "localizations": { @@ -121246,6 +121371,381 @@ } } }, + "shortcut.markdownZoomIn.label": { + "extractionState": "manual", + "localizations": { + "ar": { + "stringUnit": { + "state": "translated", + "value": "عارض Markdown: تكبير" + } + }, + "bs": { + "stringUnit": { + "state": "translated", + "value": "Markdown preglednik: Uvećaj" + } + }, + "da": { + "stringUnit": { + "state": "translated", + "value": "Markdown-visning: Zoom ind" + } + }, + "de": { + "stringUnit": { + "state": "translated", + "value": "Markdown-Viewer: Einzoomen" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Markdown Viewer: Zoom In" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Visor de Markdown: Ampliar" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Visionneuse Markdown: Zoom avant" + } + }, + "it": { + "stringUnit": { + "state": "translated", + "value": "Visualizzatore Markdown: Ingrandisci" + } + }, + "ja": { + "stringUnit": { + "state": "translated", + "value": "Markdown ビューア: 拡大" + } + }, + "km": { + "stringUnit": { + "state": "translated", + "value": "កម្មវិធីមើល Markdown: Zoom In" + } + }, + "ko": { + "stringUnit": { + "state": "translated", + "value": "Markdown 뷰어: 확대" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Markdown-visning: Zoom inn" + } + }, + "pl": { + "stringUnit": { + "state": "translated", + "value": "Przeglądarka Markdown: Powiększ" + } + }, + "pt-BR": { + "stringUnit": { + "state": "translated", + "value": "Visualizador de Markdown: Aumentar Zoom" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Просмотр Markdown: Увеличить" + } + }, + "th": { + "stringUnit": { + "state": "translated", + "value": "ตัวแสดง Markdown: ซูมเข้า" + } + }, + "tr": { + "stringUnit": { + "state": "translated", + "value": "Markdown Görüntüleyici: Yakınlaştır" + } + }, + "uk": { + "stringUnit": { + "state": "translated", + "value": "Переглядач Markdown: Збільшити" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "Markdown 查看器: 放大" + } + }, + "zh-Hant": { + "stringUnit": { + "state": "translated", + "value": "Markdown 檢視器: 放大" + } + } + } + }, + "shortcut.markdownZoomOut.label": { + "extractionState": "manual", + "localizations": { + "ar": { + "stringUnit": { + "state": "translated", + "value": "عارض Markdown: تصغير" + } + }, + "bs": { + "stringUnit": { + "state": "translated", + "value": "Markdown preglednik: Umanji" + } + }, + "da": { + "stringUnit": { + "state": "translated", + "value": "Markdown-visning: Zoom ud" + } + }, + "de": { + "stringUnit": { + "state": "translated", + "value": "Markdown-Viewer: Auszoomen" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Markdown Viewer: Zoom Out" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Visor de Markdown: Reducir" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Visionneuse Markdown: Zoom arrière" + } + }, + "it": { + "stringUnit": { + "state": "translated", + "value": "Visualizzatore Markdown: Riduci" + } + }, + "ja": { + "stringUnit": { + "state": "translated", + "value": "Markdown ビューア: 縮小" + } + }, + "km": { + "stringUnit": { + "state": "translated", + "value": "កម្មវិធីមើល Markdown: Zoom Out" + } + }, + "ko": { + "stringUnit": { + "state": "translated", + "value": "Markdown 뷰어: 축소" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Markdown-visning: Zoom ut" + } + }, + "pl": { + "stringUnit": { + "state": "translated", + "value": "Przeglądarka Markdown: Pomniejsz" + } + }, + "pt-BR": { + "stringUnit": { + "state": "translated", + "value": "Visualizador de Markdown: Diminuir Zoom" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Просмотр Markdown: Уменьшить" + } + }, + "th": { + "stringUnit": { + "state": "translated", + "value": "ตัวแสดง Markdown: ซูมออก" + } + }, + "tr": { + "stringUnit": { + "state": "translated", + "value": "Markdown Görüntüleyici: Uzaklaştır" + } + }, + "uk": { + "stringUnit": { + "state": "translated", + "value": "Переглядач Markdown: Зменшити" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "Markdown 查看器: 缩小" + } + }, + "zh-Hant": { + "stringUnit": { + "state": "translated", + "value": "Markdown 檢視器: 縮小" + } + } + } + }, + "shortcut.markdownZoomReset.label": { + "extractionState": "manual", + "localizations": { + "ar": { + "stringUnit": { + "state": "translated", + "value": "عارض Markdown: الحجم الفعلي" + } + }, + "bs": { + "stringUnit": { + "state": "translated", + "value": "Markdown preglednik: Stvarna veličina" + } + }, + "da": { + "stringUnit": { + "state": "translated", + "value": "Markdown-visning: Faktisk størrelse" + } + }, + "de": { + "stringUnit": { + "state": "translated", + "value": "Markdown-Viewer: Originalgröße" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Markdown Viewer: Actual Size" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Visor de Markdown: Tamaño real" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Visionneuse Markdown: Taille réelle" + } + }, + "it": { + "stringUnit": { + "state": "translated", + "value": "Visualizzatore Markdown: Dimensione reale" + } + }, + "ja": { + "stringUnit": { + "state": "translated", + "value": "Markdown ビューア: 実際のサイズ" + } + }, + "km": { + "stringUnit": { + "state": "translated", + "value": "កម្មវិធីមើល Markdown: Actual Size" + } + }, + "ko": { + "stringUnit": { + "state": "translated", + "value": "Markdown 뷰어: 실제 크기" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Markdown-visning: Faktisk størrelse" + } + }, + "pl": { + "stringUnit": { + "state": "translated", + "value": "Przeglądarka Markdown: Rozmiar rzeczywisty" + } + }, + "pt-BR": { + "stringUnit": { + "state": "translated", + "value": "Visualizador de Markdown: Tamanho Real" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Просмотр Markdown: Фактический размер" + } + }, + "th": { + "stringUnit": { + "state": "translated", + "value": "ตัวแสดง Markdown: ขนาดจริง" + } + }, + "tr": { + "stringUnit": { + "state": "translated", + "value": "Markdown Görüntüleyici: Gerçek Boyut" + } + }, + "uk": { + "stringUnit": { + "state": "translated", + "value": "Переглядач Markdown: Фактичний розмір" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "Markdown 查看器: 实际大小" + } + }, + "zh-Hant": { + "stringUnit": { + "state": "translated", + "value": "Markdown 檢視器: 實際大小" + } + } + } + }, "shortcut.saveFilePreview.label": { "extractionState": "manual", "localizations": { @@ -144419,6 +144919,256 @@ } } }, + "settings.app.markdownViewerFontSize": { + "extractionState": "manual", + "localizations": { + "ar": { + "stringUnit": { + "state": "translated", + "value": "حجم خط عارض Markdown" + } + }, + "bs": { + "stringUnit": { + "state": "translated", + "value": "Veličina fonta Markdown preglednika" + } + }, + "da": { + "stringUnit": { + "state": "translated", + "value": "Skriftstørrelse for Markdown-visning" + } + }, + "de": { + "stringUnit": { + "state": "translated", + "value": "Schriftgröße des Markdown-Viewers" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Markdown Viewer Font Size" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Tamaño de fuente del visor de Markdown" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Taille de police de la visionneuse Markdown" + } + }, + "it": { + "stringUnit": { + "state": "translated", + "value": "Dimensione font del visualizzatore Markdown" + } + }, + "ja": { + "stringUnit": { + "state": "translated", + "value": "Markdown ビューアのフォントサイズ" + } + }, + "km": { + "stringUnit": { + "state": "translated", + "value": "ទំហំអក្សររបស់កម្មវិធីមើល Markdown" + } + }, + "ko": { + "stringUnit": { + "state": "translated", + "value": "Markdown 뷰어 글꼴 크기" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Skriftstørrelse for Markdown-visning" + } + }, + "pl": { + "stringUnit": { + "state": "translated", + "value": "Rozmiar czcionki przeglądarki Markdown" + } + }, + "pt-BR": { + "stringUnit": { + "state": "translated", + "value": "Tamanho da fonte do visualizador de Markdown" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Размер шрифта просмотра Markdown" + } + }, + "th": { + "stringUnit": { + "state": "translated", + "value": "ขนาดฟอนต์ของตัวแสดง Markdown" + } + }, + "tr": { + "stringUnit": { + "state": "translated", + "value": "Markdown Görüntüleyici Yazı Tipi Boyutu" + } + }, + "uk": { + "stringUnit": { + "state": "translated", + "value": "Розмір шрифту переглядача Markdown" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "Markdown 查看器字体大小" + } + }, + "zh-Hant": { + "stringUnit": { + "state": "translated", + "value": "Markdown 檢視器字體大小" + } + } + } + }, + "settings.app.markdownViewerFontSize.subtitle": { + "extractionState": "manual", + "localizations": { + "ar": { + "stringUnit": { + "state": "translated", + "value": "الحجم الافتراضي لعرض Markdown في لوحات العارض الجديدة. اختصارات تكبير اللوحة تتجاوز هذا الإعداد لكل لوحة حتى إعادة التعيين." + } + }, + "bs": { + "stringUnit": { + "state": "translated", + "value": "Zadana veličina renderovanog Markdowna za nove panele preglednika. Prečice za zumiranje panela nadjačavaju ovo po panelu do resetovanja." + } + }, + "da": { + "stringUnit": { + "state": "translated", + "value": "Standardstørrelse for gengivet Markdown i nye visningspaneler. Panelets zoomgenveje tilsidesætter dette pr. panel indtil nulstilling." + } + }, + "de": { + "stringUnit": { + "state": "translated", + "value": "Standardgröße für gerendertes Markdown in neuen Viewer-Panels. Panel-Zoomkurzbefehle überschreiben sie pro Panel bis zum Zurücksetzen." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Default rendered markdown size for new viewer panels. Panel zoom shortcuts override this per panel until reset." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Tamaño predeterminado del Markdown renderizado para nuevos paneles del visor. Los atajos de zoom del panel lo reemplazan por panel hasta restablecerlo." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Taille par défaut du Markdown rendu pour les nouveaux panneaux de visionneuse. Les raccourcis de zoom du panneau la remplacent par panneau jusqu’à réinitialisation." + } + }, + "it": { + "stringUnit": { + "state": "translated", + "value": "Dimensione predefinita del Markdown renderizzato per i nuovi pannelli del visualizzatore. Le scorciatoie di zoom del pannello la sostituiscono per pannello fino al ripristino." + } + }, + "ja": { + "stringUnit": { + "state": "translated", + "value": "新しいビューアパネルでレンダリングする Markdown の既定サイズです。パネルのズームショートカットは、リセットするまでそのパネルだけを上書きします。" + } + }, + "km": { + "stringUnit": { + "state": "translated", + "value": "ទំហំលំនាំដើមសម្រាប់ Markdown ដែលបានបង្ហាញក្នុងផ្ទាំងមើលថ្មី។ ផ្លូវកាត់ពង្រីកបង្រួមរបស់ផ្ទាំងនឹងជំនួសតាមផ្ទាំងរហូតដល់កំណត់ឡើងវិញ។" + } + }, + "ko": { + "stringUnit": { + "state": "translated", + "value": "새 뷰어 패널에서 렌더링되는 Markdown의 기본 크기입니다. 패널 줌 단축키는 재설정할 때까지 패널별로 이 값을 재정의합니다." + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Standardstørrelse for gjengitt Markdown i nye visningspaneler. Panelets zoomsnarveier overstyrer dette per panel til tilbakestilling." + } + }, + "pl": { + "stringUnit": { + "state": "translated", + "value": "Domyślny rozmiar renderowanego Markdownu w nowych panelach przeglądarki. Skróty powiększenia panelu nadpisują go dla danego panelu do czasu resetu." + } + }, + "pt-BR": { + "stringUnit": { + "state": "translated", + "value": "Tamanho padrão do Markdown renderizado para novos painéis do visualizador. Atalhos de zoom do painel substituem isso por painel até redefinir." + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Размер отрисованного Markdown по умолчанию для новых панелей просмотра. Сочетания масштаба панели переопределяют его для панели до сброса." + } + }, + "th": { + "stringUnit": { + "state": "translated", + "value": "ขนาดเริ่มต้นของ Markdown ที่เรนเดอร์สำหรับแผงตัวแสดงใหม่ ทางลัดซูมของแผงจะแทนที่ค่านี้เฉพาะแผงจนกว่าจะรีเซ็ต" + } + }, + "tr": { + "stringUnit": { + "state": "translated", + "value": "Yeni görüntüleyici panelleri için varsayılan işlenmiş Markdown boyutu. Panel yakınlaştırma kısayolları sıfırlanana kadar bunu panel bazında geçersiz kılar." + } + }, + "uk": { + "stringUnit": { + "state": "translated", + "value": "Типовий розмір відтвореного Markdown для нових панелей перегляду. Комбінації масштабування панелі перевизначають його для панелі до скидання." + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "新查看器面板中渲染 Markdown 的默认大小。面板缩放快捷键会按面板覆盖此设置,直到重置。" + } + }, + "zh-Hant": { + "stringUnit": { + "state": "translated", + "value": "新檢視器面板中轉譯 Markdown 的預設大小。面板縮放快捷鍵會依面板覆蓋此設定,直到重設。" + } + } + } + }, "rightSidebar.close.accessibilityLabel": { "extractionState": "manual", "localizations": { diff --git a/Resources/markdown-viewer/shell.html b/Resources/markdown-viewer/shell.html index 5d5006267888..84d2a998dc14 100644 --- a/Resources/markdown-viewer/shell.html +++ b/Resources/markdown-viewer/shell.html @@ -13,7 +13,12 @@ {{highlightDarkCSS}}