2e4a4b0a by shady

:bug: 修复输入框聚焦时 Cmd+V 粘贴图片失效:按剪贴板内容分流

根因:⌘V 在 QML 里有两条互斥路径。焦点不在输入框时走顶层
Shortcut{StandardKey.Paste} 粘贴图片;焦点在 promptArea 里时,
编辑控件通过 Qt 的 ShortcutOverride 吞掉 ⌘V 走内置文本粘贴,
那个全局 Shortcut 永远拿不到事件 —— 而剪贴板里是图片、文本粘贴
贴不出东西,表现为"粘贴无效"。:clipboard: 按钮直接调 pasteFromClipboardAction
绕开按键路由,所以一直有效。

修复(按剪贴板内容分流,而非靠焦点状态):
- bridges/imagegen.py 新增 clipboardHasImage():只读 formats()/urls(),
  无副作用、不写临时文件、macOS 不碰会崩的 imageData()。
- promptArea 加 Keys.onPressed:matches(StandardKey.Paste) 且剪贴板有图
  → 粘图 + event.accepted;否则放行给 TextArea 做文本粘贴。

文本粘贴行为不变(Never break userspace)。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent f2d92337
...@@ -172,6 +172,32 @@ class ImageGenBridge(QObject): ...@@ -172,6 +172,32 @@ class ImageGenBridge(QObject):
172 self._tqm.cancel_task(task_id) 172 self._tqm.cancel_task(task_id)
173 self.busyChanged.emit() 173 self.busyChanged.emit()
174 174
175 @Slot(result=bool)
176 def clipboardHasImage(self) -> bool:
177 """剪贴板里是否有图片 —— 轻量、无副作用、macOS 安全。
178
179 只读 mimeData.formats() / urls(),不做反序列化、不写临时文件,
180 所以即便在 macOS 上也不会触发 application/x-qt-image → NSImage 的
181 native crash(那个风险只在 imageData()/loadFromData 时出现,见
182 _extract_clipboard_image)。给焦点在输入框时的 Ctrl/Cmd+V 路由用:
183 有图就走图片粘贴,否则放行给文本粘贴。
184 """
185 clipboard = QGuiApplication.clipboard()
186 if clipboard is None:
187 return False
188 mime_data = clipboard.mimeData()
189 if mime_data is None:
190 return False
191 # 路径 A:文件 URL 指向图片文件
192 if mime_data.hasUrls():
193 for url in mime_data.urls():
194 if url.isLocalFile() and Path(url.toLocalFile()).suffix.lower() in _PASTE_VALID_EXT:
195 return True
196 # 路径 B/C:剪贴板里挂着图像 MIME(仅列 formats,不读字节)
197 image_mimes = {"image/png", "image/jpeg", "image/bmp", "image/tiff",
198 "image/gif", "image/webp", "application/x-qt-image"}
199 return any(fmt in image_mimes for fmt in mime_data.formats())
200
175 @Slot(result="QVariantList") 201 @Slot(result="QVariantList")
176 def pasteFromClipboard(self) -> list: 202 def pasteFromClipboard(self) -> list:
177 """从系统剪贴板拿图片,返回本地路径列表(正斜杠风格)。失败返回空列表。 203 """从系统剪贴板拿图片,返回本地路径列表(正斜杠风格)。失败返回空列表。
......
...@@ -122,8 +122,9 @@ Item { ...@@ -122,8 +122,9 @@ Item {
122 } 122 }
123 } 123 }
124 124
125 // 全局 Ctrl+V 粘贴图片(仅在图片生成 tab 激活时生效; 125 // 全局 Ctrl/Cmd+V 粘贴图片:覆盖焦点不在输入框的情况(按钮/下拉/空白处)。
126 // 焦点在 TextArea/TextField 里 Ctrl+V 优先走文本粘贴,不会触发这个 Shortcut) 126 // 焦点在 promptArea 里时编辑控件会吞掉 Ctrl/Cmd+V,这个 Shortcut 拿不到,
127 // 那种情况由 promptArea 的 Keys.onPressed 按剪贴板内容分流(见下方 TextArea)。
127 Shortcut { 128 Shortcut {
128 sequences: [StandardKey.Paste] 129 sequences: [StandardKey.Paste]
129 enabled: appState.currentTab === 0 130 enabled: appState.currentTab === 0
...@@ -635,6 +636,17 @@ Item { ...@@ -635,6 +636,17 @@ Item {
635 wrapMode: TextArea.Wrap 636 wrapMode: TextArea.Wrap
636 selectionColor: App.Theme.accent 637 selectionColor: App.Theme.accent
637 selectedTextColor: App.Theme.textOnAccent 638 selectedTextColor: App.Theme.textOnAccent
639
640 // 焦点在输入框时,编辑控件会吞掉 Ctrl/Cmd+V 走内置文本粘贴,
641 // 顶层那个 StandardKey.Paste Shortcut 拿不到 = 粘贴图片失效。
642 // 这里按"剪贴板内容"分流:有图就粘图(吃掉事件),
643 // 没图就放行,让 TextArea 自己做文本粘贴。
644 Keys.onPressed: function(event) {
645 if (event.matches(StandardKey.Paste) && imageGen.clipboardHasImage()) {
646 tab.pasteFromClipboardAction()
647 event.accepted = true
648 }
649 }
638 background: Rectangle { 650 background: Rectangle {
639 color: App.Theme.bgSubtle 651 color: App.Theme.bgSubtle
640 radius: App.Theme.radiusMd 652 radius: App.Theme.radiusMd
......