Skip to content
Open
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,12 @@ enum CmuxEmbeddedConfigSchema {
"default": false,
"description": "When true, a paste that Ghostty's clipboard-paste-protection flags as unsafe (for example, text with a line break going to a program that has not enabled bracketed paste) waits for confirmation in a sheet attached to the terminal's window. When false, cmux pastes it without asking. OSC 52 clipboard reads are not affected."
},
"agentImagePasteSendsCtrlV": {
"type": "boolean",
"default": false,
"descriptionKey": "schemaDescriptions.terminal.agentImagePasteSendsCtrlV",
"description": "When true, Cmd+V with only an image on the clipboard sends Ctrl+V to Claude Code or Codex running in a local terminal pane, so the agent attaches the image from the clipboard itself. When false, or in any other pane (no running agent, cmux ssh, detected SSH, or Cloud), the image is saved to a temporary file and its path is pasted or uploaded as before. Clipboards that also carry text, rich text, URLs, or files always use the regular paste."
},
"textEditingGestures": {
"type": "boolean",
"default": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,16 @@ public struct TerminalCatalogSection: SettingCatalogSection {
userDefaultsKey: "terminal.confirmUnsafePaste"
)

/// Whether Cmd+V with only an image on the clipboard sends Ctrl+V to a
/// running Claude Code or Codex in a local pane, so the agent reads the
/// image from the clipboard itself, instead of pasting a temporary file
/// path. Off by default; other panes always get the file path.
public let agentImagePasteSendsCtrlV = DefaultsKey<Bool>(
id: "terminal.agentImagePasteSendsCtrlV",
defaultValue: false,
userDefaultsKey: "terminal.agentImagePasteSendsCtrlV"
)

/// Whether macOS text-editing gestures are replayed as their line-editor
/// equivalents: Command and Option arrow motion, and the Command and Option
/// deletion chords. Off by default, because the mode claims chords the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ struct SettingCatalogTests {
#expect(!SettingCatalog().terminal.confirmUnsafePaste.defaultValue)
}

@Test func agentImagePasteSendsCtrlVDefaultsOff() {
#expect(!SettingCatalog().terminal.agentImagePasteSendsCtrlV.defaultValue)
}

@Test func keyIdsMatchTheirSectionPrefix() {
// Each key's dotted id must start with its section's prefix; this is
// the convention that lets the JSON store use `id` as the JSON path.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,14 @@ extension Array where Element == CuratedSettingEntry {
paths: ["terminal.confirmUnsafePaste"],
synonyms: "terminal.confirmUnsafePaste confirm unsafe paste protection warning multi-line newline clipboard sheet"
),
.init(
section: .terminal,
id: "agent-image-paste-ctrl-v",
title: String(localized: "settings.terminal.agentImagePasteSendsCtrlV", defaultValue: "Paste Images into Agents with Ctrl+V"),
detailText: String(localized: "settings.terminal.agentImagePasteSendsCtrlV.subtitleOff", defaultValue: "Pasting an image saves it to a temporary file and pastes the file path."),
paths: ["terminal.agentImagePasteSendsCtrlV"],
synonyms: "terminal.agentImagePasteSendsCtrlV agent image paste ctrl+v control v clipboard screenshot picture claude code codex attach"
),
.init(
section: .terminal,
id: "password-input-indicator",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public struct TerminalSection: View {
@State private var copyOnSelect: DefaultsValueModel<Bool>
@State private var reflowHardWrapOnCopy: DefaultsValueModel<Bool>
@State private var confirmUnsafePaste: DefaultsValueModel<Bool>
@State private var agentImagePasteSendsCtrlV: DefaultsValueModel<Bool>
@State private var textEditingGestures: DefaultsValueModel<Bool>
@State private var textEditingCommandMovesByWord: DefaultsValueModel<Bool>
@State private var textEditingGesturesInFullScreenApps: DefaultsValueModel<Bool>
Expand Down Expand Up @@ -60,6 +61,7 @@ public struct TerminalSection: View {
_copyOnSelect = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.terminal.copyOnSelect))
_reflowHardWrapOnCopy = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.terminal.reflowHardWrapOnCopy))
_confirmUnsafePaste = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.terminal.confirmUnsafePaste))
_agentImagePasteSendsCtrlV = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.terminal.agentImagePasteSendsCtrlV))
_textEditingGestures = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.terminal.textEditingGestures))
_textEditingCommandMovesByWord = State(
initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.terminal.textEditingCommandMovesByWord)
Expand Down Expand Up @@ -108,6 +110,7 @@ public struct TerminalSection: View {
copyOnSelect,
reflowHardWrapOnCopy,
confirmUnsafePaste,
agentImagePasteSendsCtrlV,
textEditingGestures,
textEditingCommandMovesByWord,
textEditingGesturesInFullScreenApps,
Expand Down Expand Up @@ -437,6 +440,19 @@ public struct TerminalSection: View {
.accessibilityIdentifier("SettingsTerminalConfirmUnsafePasteToggle")
}
SettingsCardDivider()
SettingsCardRow(
configurationReview: .json("terminal.agentImagePasteSendsCtrlV"),
String(localized: "settings.terminal.agentImagePasteSendsCtrlV", defaultValue: "Paste Images into Agents with Ctrl+V"),
subtitle: agentImagePasteSendsCtrlV.current
? String(localized: "settings.terminal.agentImagePasteSendsCtrlV.subtitleOn", defaultValue: "When Claude Code or Codex is running in a local pane, pasting an image sends Ctrl+V so the agent attaches it from the clipboard. Other panes still get a file path.")
: String(localized: "settings.terminal.agentImagePasteSendsCtrlV.subtitleOff", defaultValue: "Pasting an image saves it to a temporary file and pastes the file path.")
) {
Toggle("", isOn: Binding(get: { agentImagePasteSendsCtrlV.current }, set: { agentImagePasteSendsCtrlV.set($0) }))
.labelsHidden()
.controlSize(.small)
.accessibilityIdentifier("SettingsTerminalAgentImagePasteSendsCtrlVToggle")
}
SettingsCardDivider()
SettingsCardRow(
configurationReview: .json("terminal.textEditingGestures"),
String(localized: "settings.terminal.textEditingGestures", defaultValue: "Text Editing Gestures"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ struct SettingsRowAnchorResolutionTests {
"terminal.copyOnSelect",
"terminal.reflowHardWrapOnCopy",
"terminal.confirmUnsafePaste",
"terminal.agentImagePasteSendsCtrlV",
"terminal.showPasswordInputIndicator",
"terminal.showPasswordInputDots",
"terminal.predictiveLocalEcho",
Expand Down
236 changes: 236 additions & 0 deletions Resources/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -558260,6 +558260,242 @@
}
}
},
"settings.terminal.agentImagePasteSendsCtrlV": {
"extractionState": "manual",
"localizations": {
"ar": {
"stringUnit": {
"state": "translated",
"value": "لصق الصور في الوكلاء باستخدام Ctrl+V"
}
},
"de": {
"stringUnit": {
"state": "translated",
"value": "Bilder mit Ctrl+V in Agenten einfügen"
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Paste Images into Agents with Ctrl+V"
}
},
"es": {
"stringUnit": {
"state": "translated",
"value": "Pegar imágenes en agentes con Ctrl+V"
}
},
"fr": {
"stringUnit": {
"state": "translated",
"value": "Coller les images dans les agents avec Ctrl+V"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "Ctrl+V でエージェントに画像を貼り付け"
}
},
"ko": {
"stringUnit": {
"state": "translated",
"value": "Ctrl+V로 에이전트에 이미지 붙여넣기"
}
},
"zh-Hans": {
"stringUnit": {
"state": "translated",
"value": "使用 Ctrl+V 将图片粘贴到代理"
}
},
"zh-Hant": {
"stringUnit": {
"state": "translated",
"value": "使用 Ctrl+V 將圖片貼到代理"
}
}
}
},
"settings.terminal.agentImagePasteSendsCtrlV.subtitleOn": {
"extractionState": "manual",
"localizations": {
"ar": {
"stringUnit": {
"state": "translated",
"value": "عندما يعمل Claude Code أو Codex في جزء محلي، يؤدي لصق صورة إلى إرسال Ctrl+V ليرفقها الوكيل من الحافظة. تظل الأجزاء الأخرى تتلقى مسار ملف."
}
},
"de": {
"stringUnit": {
"state": "translated",
"value": "Läuft Claude Code oder Codex in einem lokalen Bereich, sendet das Einfügen eines Bildes Ctrl+V, damit der Agent es aus der Zwischenablage anhängt. Andere Bereiche erhalten weiterhin einen Dateipfad."
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "When Claude Code or Codex is running in a local pane, pasting an image sends Ctrl+V so the agent attaches it from the clipboard. Other panes still get a file path."
}
},
"es": {
"stringUnit": {
"state": "translated",
"value": "Cuando Claude Code o Codex se ejecuta en un panel local, pegar una imagen envía Ctrl+V para que el agente la adjunte desde el portapapeles. Los demás paneles siguen recibiendo una ruta de archivo."
}
},
"fr": {
"stringUnit": {
"state": "translated",
"value": "Quand Claude Code ou Codex s’exécute dans un panneau local, coller une image envoie Ctrl+V pour que l’agent la joigne depuis le presse-papiers. Les autres panneaux reçoivent toujours un chemin de fichier."
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "ローカルのペインで Claude Code または Codex が実行中の場合、画像を貼り付けると Ctrl+V を送信し、エージェントがクリップボードから画像を添付します。その他のペインには引き続きファイルパスが貼り付けられます。"
}
},
"ko": {
"stringUnit": {
"state": "translated",
"value": "로컬 분할 창에서 Claude Code 또는 Codex가 실행 중이면 이미지를 붙여넣을 때 Ctrl+V를 보내 에이전트가 클립보드에서 이미지를 첨부합니다. 다른 분할 창에는 여전히 파일 경로가 붙여넣어집니다."
}
},
"zh-Hans": {
"stringUnit": {
"state": "translated",
"value": "当 Claude Code 或 Codex 在本地窗格中运行时,粘贴图片会发送 Ctrl+V,让代理从剪贴板附加图片。其他窗格仍会收到文件路径。"
}
},
"zh-Hant": {
"stringUnit": {
"state": "translated",
"value": "當 Claude Code 或 Codex 在本機窗格中執行時,貼上圖片會送出 Ctrl+V,讓代理從剪貼簿附加圖片。其他窗格仍會收到檔案路徑。"
}
}
}
},
"settings.terminal.agentImagePasteSendsCtrlV.subtitleOff": {
"extractionState": "manual",
"localizations": {
"ar": {
"stringUnit": {
"state": "translated",
"value": "يؤدي لصق صورة إلى حفظها في ملف مؤقت ولصق مسار الملف."
}
},
"de": {
"stringUnit": {
"state": "translated",
"value": "Beim Einfügen eines Bildes wird es in einer temporären Datei gespeichert und der Dateipfad eingefügt."
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Pasting an image saves it to a temporary file and pastes the file path."
}
},
"es": {
"stringUnit": {
"state": "translated",
"value": "Al pegar una imagen, se guarda en un archivo temporal y se pega la ruta del archivo."
}
},
"fr": {
"stringUnit": {
"state": "translated",
"value": "Coller une image l’enregistre dans un fichier temporaire et colle le chemin du fichier."
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "画像を貼り付けると一時ファイルに保存し、そのファイルパスを貼り付けます。"
}
},
"ko": {
"stringUnit": {
"state": "translated",
"value": "이미지를 붙여넣으면 임시 파일로 저장하고 파일 경로를 붙여넣습니다."
}
},
"zh-Hans": {
"stringUnit": {
"state": "translated",
"value": "粘贴图片时会将其保存为临时文件,并粘贴文件路径。"
}
},
"zh-Hant": {
"stringUnit": {
"state": "translated",
"value": "貼上圖片時會將其儲存為暫存檔,並貼上檔案路徑。"
}
}
}
},
"settings.search.alias.setting.terminal.agent-image-paste-ctrl-v": {
"extractionState": "manual",
"localizations": {
"ar": {
"stringUnit": {
"state": "translated",
"value": "terminal.agentImagePasteSendsCtrlV agent image paste ctrl+v control v clipboard screenshot picture claude code codex attach وكيل صورة لصق حافظة لقطة شاشة إرفاق"
}
},
"de": {
"stringUnit": {
"state": "translated",
"value": "terminal.agentImagePasteSendsCtrlV agent image paste ctrl+v control v clipboard screenshot picture claude code codex attach Agent Bild einfügen Zwischenablage Bildschirmfoto anhängen"
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "terminal.agentImagePasteSendsCtrlV agent image paste ctrl+v control v clipboard screenshot picture claude code codex attach"
}
},
"es": {
"stringUnit": {
"state": "translated",
"value": "terminal.agentImagePasteSendsCtrlV agent image paste ctrl+v control v clipboard screenshot picture claude code codex attach agente imagen pegar portapapeles captura de pantalla adjuntar"
}
},
"fr": {
"stringUnit": {
"state": "translated",
"value": "terminal.agentImagePasteSendsCtrlV agent image paste ctrl+v control v clipboard screenshot picture claude code codex attach agent image coller presse-papiers capture d’écran joindre"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "terminal.agentImagePasteSendsCtrlV agent image paste ctrl+v control v clipboard screenshot picture claude code codex attach エージェント 画像 貼り付け クリップボード スクリーンショット 添付"
}
},
"ko": {
"stringUnit": {
"state": "translated",
"value": "terminal.agentImagePasteSendsCtrlV agent image paste ctrl+v control v clipboard screenshot picture claude code codex attach 에이전트 이미지 붙여넣기 클립보드 스크린샷 첨부"
}
},
"zh-Hans": {
"stringUnit": {
"state": "translated",
"value": "terminal.agentImagePasteSendsCtrlV agent image paste ctrl+v control v clipboard screenshot picture claude code codex attach 代理 图片 粘贴 剪贴板 截图 附加"
}
},
"zh-Hant": {
"stringUnit": {
"state": "translated",
"value": "terminal.agentImagePasteSendsCtrlV agent image paste ctrl+v control v clipboard screenshot picture claude code codex attach 代理 圖片 貼上 剪貼簿 截圖 附加"
}
}
}
},
"appChannelSwitch.action.nightly": {
"extractionState": "manual",
"localizations": {
Expand Down
1 change: 1 addition & 0 deletions Sources/CmuxSettingsFileStore+SupportedPaths.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ extension CmuxSettingsFileStore {
"terminal.copyOnSelect",
"terminal.reflowHardWrapOnCopy",
"terminal.confirmUnsafePaste",
"terminal.agentImagePasteSendsCtrlV",
"terminal.textEditingGestures",
"terminal.textEditingCommandMovesByWord",
"terminal.textEditingGesturesInFullScreenApps",
Expand Down
5 changes: 5 additions & 0 deletions Sources/CmuxSettingsJSONPathSupport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,11 @@ enum TerminalSettingsFileMapping {
defaultsKey: terminal.confirmUnsafePaste.userDefaultsKey,
invalidPath: terminal.confirmUnsafePaste.id
),
.init(
jsonKey: "agentImagePasteSendsCtrlV",
defaultsKey: terminal.agentImagePasteSendsCtrlV.userDefaultsKey,
invalidPath: terminal.agentImagePasteSendsCtrlV.id
),
.init(
jsonKey: "showPasswordInputIndicator",
defaultsKey: terminal.showPasswordInputIndicator.userDefaultsKey,
Expand Down
Loading
Loading