Add copy-on-select preference (#2282)
This commit is contained in:
parent
94cc865e83
commit
35cb42fbc8
4 changed files with 178 additions and 14 deletions
|
|
@ -47815,6 +47815,57 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"settings.app.copyOnSelect": {
|
||||||
|
"extractionState": "manual",
|
||||||
|
"localizations": {
|
||||||
|
"en": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "Copy on Select"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ja": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "選択時にコピー"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings.app.copyOnSelect.subtitleOff": {
|
||||||
|
"extractionState": "manual",
|
||||||
|
"localizations": {
|
||||||
|
"en": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "Selecting terminal text does not copy it to the system clipboard."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ja": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "ターミナルテキストを選択してもシステムのクリップボードにはコピーしません。"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings.app.copyOnSelect.subtitleOn": {
|
||||||
|
"extractionState": "manual",
|
||||||
|
"localizations": {
|
||||||
|
"en": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "Automatically copy selected terminal text to the system clipboard."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ja": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "選択したターミナルテキストをシステムのクリップボードに自動でコピーします。"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"settings.app.paneFirstClickFocus": {
|
"settings.app.paneFirstClickFocus": {
|
||||||
"extractionState": "manual",
|
"extractionState": "manual",
|
||||||
"localizations": {
|
"localizations": {
|
||||||
|
|
|
||||||
|
|
@ -1349,11 +1349,45 @@ class GhosttyApp {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func loadInlineGhosttyConfig(
|
||||||
|
_ contents: String,
|
||||||
|
into config: ghostty_config_t,
|
||||||
|
prefix: String,
|
||||||
|
logLabel: String
|
||||||
|
) {
|
||||||
|
let trimmed = contents.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
guard !trimmed.isEmpty else { return }
|
||||||
|
|
||||||
|
let tmpURL = FileManager.default.temporaryDirectory
|
||||||
|
.appendingPathComponent("\(prefix)-\(UUID().uuidString).conf")
|
||||||
|
do {
|
||||||
|
try trimmed.write(to: tmpURL, atomically: true, encoding: .utf8)
|
||||||
|
defer { try? FileManager.default.removeItem(at: tmpURL) }
|
||||||
|
tmpURL.path.withCString { path in
|
||||||
|
ghostty_config_load_file(config, path)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
#if DEBUG
|
||||||
|
dlog("ghostty.config.inlineLoad.failed label=\(logLabel) error=\(error.localizedDescription)")
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func loadCopyOnSelectOverride(_ config: ghostty_config_t) {
|
||||||
|
loadInlineGhosttyConfig(
|
||||||
|
TerminalCopyOnSelectSettings.overrideConfigLine(),
|
||||||
|
into: config,
|
||||||
|
prefix: "cmux-copy-on-select",
|
||||||
|
logLabel: "copy-on-select override"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private func loadDefaultConfigFilesWithLegacyFallback(_ config: ghostty_config_t) {
|
private func loadDefaultConfigFilesWithLegacyFallback(_ config: ghostty_config_t) {
|
||||||
ghostty_config_load_default_files(config)
|
ghostty_config_load_default_files(config)
|
||||||
loadLegacyGhosttyConfigIfNeeded(config)
|
loadLegacyGhosttyConfigIfNeeded(config)
|
||||||
ghostty_config_load_recursive_files(config)
|
ghostty_config_load_recursive_files(config)
|
||||||
loadCmuxAppSupportGhosttyConfigIfNeeded(config)
|
loadCmuxAppSupportGhosttyConfigIfNeeded(config)
|
||||||
|
loadCopyOnSelectOverride(config)
|
||||||
loadCJKFontFallbackIfNeeded(config)
|
loadCJKFontFallbackIfNeeded(config)
|
||||||
ghostty_config_finalize(config)
|
ghostty_config_finalize(config)
|
||||||
}
|
}
|
||||||
|
|
@ -1376,20 +1410,12 @@ class GhosttyApp {
|
||||||
let lines = mappings.map { range, font in
|
let lines = mappings.map { range, font in
|
||||||
"font-codepoint-map = \(range)=\(font)"
|
"font-codepoint-map = \(range)=\(font)"
|
||||||
}.joined(separator: "\n")
|
}.joined(separator: "\n")
|
||||||
|
loadInlineGhosttyConfig(
|
||||||
let tmpURL = FileManager.default.temporaryDirectory
|
lines,
|
||||||
.appendingPathComponent("cmux-cjk-font-fallback-\(UUID().uuidString).conf")
|
into: config,
|
||||||
do {
|
prefix: "cmux-cjk-font-fallback",
|
||||||
try lines.write(to: tmpURL, atomically: true, encoding: .utf8)
|
logLabel: "CJK font fallback"
|
||||||
defer { try? FileManager.default.removeItem(at: tmpURL) }
|
)
|
||||||
tmpURL.path.withCString { path in
|
|
||||||
ghostty_config_load_file(config, path)
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
#if DEBUG
|
|
||||||
Self.initLog("failed to write CJK font fallback config: \(error)")
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unicode ranges shared by all CJK languages (Han ideographs, symbols, fullwidth forms).
|
/// Unicode ranges shared by all CJK languages (Han ideographs, symbols, fullwidth forms).
|
||||||
|
|
|
||||||
|
|
@ -3785,6 +3785,22 @@ enum CommandPaletteRenameSelectionSettings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum TerminalCopyOnSelectSettings {
|
||||||
|
static let enabledKey = "terminalCopyOnSelectEnabled"
|
||||||
|
static let defaultEnabled = false
|
||||||
|
|
||||||
|
static func isEnabled(defaults: UserDefaults = .standard) -> Bool {
|
||||||
|
if defaults.object(forKey: enabledKey) == nil {
|
||||||
|
return defaultEnabled
|
||||||
|
}
|
||||||
|
return defaults.bool(forKey: enabledKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func overrideConfigLine(defaults: UserDefaults = .standard) -> String {
|
||||||
|
isEnabled(defaults: defaults) ? "copy-on-select = clipboard" : "copy-on-select = false"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum CommandPaletteSwitcherSearchSettings {
|
enum CommandPaletteSwitcherSearchSettings {
|
||||||
static let searchAllSurfacesKey = "commandPalette.switcherSearchAllSurfaces"
|
static let searchAllSurfacesKey = "commandPalette.switcherSearchAllSurfaces"
|
||||||
static let defaultSearchAllSurfaces = false
|
static let defaultSearchAllSurfaces = false
|
||||||
|
|
@ -3869,6 +3885,8 @@ struct SettingsView: View {
|
||||||
@AppStorage(QuitWarningSettings.warnBeforeQuitKey) private var warnBeforeQuitShortcut = QuitWarningSettings.defaultWarnBeforeQuit
|
@AppStorage(QuitWarningSettings.warnBeforeQuitKey) private var warnBeforeQuitShortcut = QuitWarningSettings.defaultWarnBeforeQuit
|
||||||
@AppStorage(CommandPaletteRenameSelectionSettings.selectAllOnFocusKey)
|
@AppStorage(CommandPaletteRenameSelectionSettings.selectAllOnFocusKey)
|
||||||
private var commandPaletteRenameSelectAllOnFocus = CommandPaletteRenameSelectionSettings.defaultSelectAllOnFocus
|
private var commandPaletteRenameSelectAllOnFocus = CommandPaletteRenameSelectionSettings.defaultSelectAllOnFocus
|
||||||
|
@AppStorage(TerminalCopyOnSelectSettings.enabledKey)
|
||||||
|
private var terminalCopyOnSelectEnabled = TerminalCopyOnSelectSettings.defaultEnabled
|
||||||
@AppStorage(CommandPaletteSwitcherSearchSettings.searchAllSurfacesKey)
|
@AppStorage(CommandPaletteSwitcherSearchSettings.searchAllSurfacesKey)
|
||||||
private var commandPaletteSearchAllSurfaces = CommandPaletteSwitcherSearchSettings.defaultSearchAllSurfaces
|
private var commandPaletteSearchAllSurfaces = CommandPaletteSwitcherSearchSettings.defaultSearchAllSurfaces
|
||||||
@AppStorage(ShortcutHintDebugSettings.alwaysShowHintsKey)
|
@AppStorage(ShortcutHintDebugSettings.alwaysShowHintsKey)
|
||||||
|
|
@ -3990,6 +4008,19 @@ struct SettingsView: View {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var terminalCopyOnSelectSubtitle: String {
|
||||||
|
if terminalCopyOnSelectEnabled {
|
||||||
|
return String(
|
||||||
|
localized: "settings.app.copyOnSelect.subtitleOn",
|
||||||
|
defaultValue: "Automatically copy selected terminal text to the system clipboard."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return String(
|
||||||
|
localized: "settings.app.copyOnSelect.subtitleOff",
|
||||||
|
defaultValue: "Selecting terminal text does not copy it to the system clipboard."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private var selectedSidebarActiveTabIndicatorStyle: SidebarActiveTabIndicatorStyle {
|
private var selectedSidebarActiveTabIndicatorStyle: SidebarActiveTabIndicatorStyle {
|
||||||
SidebarActiveTabIndicatorSettings.resolvedStyle(rawValue: sidebarActiveTabIndicatorStyle)
|
SidebarActiveTabIndicatorSettings.resolvedStyle(rawValue: sidebarActiveTabIndicatorStyle)
|
||||||
}
|
}
|
||||||
|
|
@ -4511,6 +4542,20 @@ struct SettingsView: View {
|
||||||
|
|
||||||
SettingsCardDivider()
|
SettingsCardDivider()
|
||||||
|
|
||||||
|
SettingsCardRow(
|
||||||
|
String(localized: "settings.app.copyOnSelect", defaultValue: "Copy on Select"),
|
||||||
|
subtitle: terminalCopyOnSelectSubtitle
|
||||||
|
) {
|
||||||
|
Toggle("", isOn: $terminalCopyOnSelectEnabled)
|
||||||
|
.labelsHidden()
|
||||||
|
.controlSize(.small)
|
||||||
|
.accessibilityLabel(
|
||||||
|
String(localized: "settings.app.copyOnSelect", defaultValue: "Copy on Select")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsCardDivider()
|
||||||
|
|
||||||
SettingsCardRow(
|
SettingsCardRow(
|
||||||
String(localized: "settings.app.reorderOnNotification", defaultValue: "Reorder on Notification"),
|
String(localized: "settings.app.reorderOnNotification", defaultValue: "Reorder on Notification"),
|
||||||
subtitle: String(localized: "settings.app.reorderOnNotification.subtitle", defaultValue: "Move workspaces to the top when they receive a notification. Disable for stable shortcut positions.")
|
subtitle: String(localized: "settings.app.reorderOnNotification.subtitle", defaultValue: "Move workspaces to the top when they receive a notification. Disable for stable shortcut positions.")
|
||||||
|
|
@ -5654,6 +5699,9 @@ struct SettingsView: View {
|
||||||
.onChange(of: notificationSoundCustomFilePath) { _, _ in
|
.onChange(of: notificationSoundCustomFilePath) { _, _ in
|
||||||
refreshNotificationCustomSoundStatus()
|
refreshNotificationCustomSoundStatus()
|
||||||
}
|
}
|
||||||
|
.onChange(of: terminalCopyOnSelectEnabled) { _, _ in
|
||||||
|
GhosttyApp.shared.reloadConfiguration(source: "settings.copy_on_select")
|
||||||
|
}
|
||||||
.onChange(of: browserInsecureHTTPAllowlist) { oldValue, newValue in
|
.onChange(of: browserInsecureHTTPAllowlist) { oldValue, newValue in
|
||||||
// Keep draft in sync with external changes unless the user has local unsaved edits.
|
// Keep draft in sync with external changes unless the user has local unsaved edits.
|
||||||
if browserInsecureHTTPAllowlistDraft == oldValue {
|
if browserInsecureHTTPAllowlistDraft == oldValue {
|
||||||
|
|
@ -5777,6 +5825,7 @@ struct SettingsView: View {
|
||||||
showMenuBarExtra = MenuBarExtraSettings.defaultShowInMenuBar
|
showMenuBarExtra = MenuBarExtraSettings.defaultShowInMenuBar
|
||||||
warnBeforeQuitShortcut = QuitWarningSettings.defaultWarnBeforeQuit
|
warnBeforeQuitShortcut = QuitWarningSettings.defaultWarnBeforeQuit
|
||||||
commandPaletteRenameSelectAllOnFocus = CommandPaletteRenameSelectionSettings.defaultSelectAllOnFocus
|
commandPaletteRenameSelectAllOnFocus = CommandPaletteRenameSelectionSettings.defaultSelectAllOnFocus
|
||||||
|
terminalCopyOnSelectEnabled = TerminalCopyOnSelectSettings.defaultEnabled
|
||||||
commandPaletteSearchAllSurfaces = CommandPaletteSwitcherSearchSettings.defaultSearchAllSurfaces
|
commandPaletteSearchAllSurfaces = CommandPaletteSwitcherSearchSettings.defaultSearchAllSurfaces
|
||||||
ShortcutHintDebugSettings.resetVisibilityDefaults()
|
ShortcutHintDebugSettings.resetVisibilityDefaults()
|
||||||
alwaysShowShortcutHints = ShortcutHintDebugSettings.defaultAlwaysShowHints
|
alwaysShowShortcutHints = ShortcutHintDebugSettings.defaultAlwaysShowHints
|
||||||
|
|
|
||||||
|
|
@ -584,6 +584,44 @@ final class GhosttyConfigTests: XCTestCase {
|
||||||
XCTAssertFalse(TelemetrySettings.isEnabled(defaults: defaults))
|
XCTAssertFalse(TelemetrySettings.isEnabled(defaults: defaults))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testTerminalCopyOnSelectDefaultsToDisabledWhenUnset() {
|
||||||
|
let suiteName = "cmux.tests.copy-on-select.\(UUID().uuidString)"
|
||||||
|
guard let defaults = UserDefaults(suiteName: suiteName) else {
|
||||||
|
XCTFail("Failed to create isolated user defaults suite")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer {
|
||||||
|
defaults.removePersistentDomain(forName: suiteName)
|
||||||
|
}
|
||||||
|
|
||||||
|
defaults.removeObject(forKey: TerminalCopyOnSelectSettings.enabledKey)
|
||||||
|
|
||||||
|
XCTAssertFalse(TerminalCopyOnSelectSettings.isEnabled(defaults: defaults))
|
||||||
|
XCTAssertEqual(
|
||||||
|
TerminalCopyOnSelectSettings.overrideConfigLine(defaults: defaults),
|
||||||
|
"copy-on-select = false"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTerminalCopyOnSelectUsesClipboardOverrideWhenEnabled() {
|
||||||
|
let suiteName = "cmux.tests.copy-on-select.\(UUID().uuidString)"
|
||||||
|
guard let defaults = UserDefaults(suiteName: suiteName) else {
|
||||||
|
XCTFail("Failed to create isolated user defaults suite")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer {
|
||||||
|
defaults.removePersistentDomain(forName: suiteName)
|
||||||
|
}
|
||||||
|
|
||||||
|
defaults.set(true, forKey: TerminalCopyOnSelectSettings.enabledKey)
|
||||||
|
|
||||||
|
XCTAssertTrue(TerminalCopyOnSelectSettings.isEnabled(defaults: defaults))
|
||||||
|
XCTAssertEqual(
|
||||||
|
TerminalCopyOnSelectSettings.overrideConfigLine(defaults: defaults),
|
||||||
|
"copy-on-select = clipboard"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private func rgb255(_ color: NSColor) -> RGB {
|
private func rgb255(_ color: NSColor) -> RGB {
|
||||||
let srgb = color.usingColorSpace(.sRGB)!
|
let srgb = color.usingColorSpace(.sRGB)!
|
||||||
var red: CGFloat = 0
|
var red: CGFloat = 0
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue