]> acesimba.cloud Git - codebuddy-web.git/commitdiff
修复粘贴框关闭与终端复制
authorCodebuddy <codebuddy@localhost>
Thu, 18 Jun 2026 08:46:35 +0000 (16:46 +0800)
committerCodebuddy <codebuddy@localhost>
Thu, 18 Jun 2026 08:46:35 +0000 (16:46 +0800)
问题1: 粘贴框无法关闭
- ESC 监听提升到 document 级(避免焦点不在 pasteInput 时失效)
- 点击遮罩层(modal 外部)关闭 modal
- 移除 pasteInput 上的重复 ESC 处理

问题2: 终端选区无法复制
- 加 attachCustomKeyEventHandler: 有选区时 Ctrl+C 复制到剪贴板(不发中断)
- 优先用 navigator.clipboard.writeText,HTTP 下用临时 textarea + execCommand 兜底
- 无选区时 Ctrl+C 正常发中断信号

frontend/index.html

index 9f30e5c202387a6cb7c94d740743cd61ea71d585..285e8ba0dd2920fedbb39b333c7f0b961553245c 100644 (file)
@@ -435,6 +435,18 @@ html { font-size: 15px; } html, body { height: 100%; background: #1a1a2e; color:
   }
   document.getElementById('btn-paste').onclick = openPasteModal;
 
+  // ESC 关闭 modal(监听 document,避免焦点不在 pasteInput 时失效)
+  document.addEventListener('keydown', (e) => {
+    if (e.key === 'Escape' && pasteModal.style.display === 'flex') {
+      e.preventDefault();
+      closePasteModal();
+    }
+  });
+  // 点击遮罩层(modal 外部)关闭
+  pasteModal.addEventListener('click', (e) => {
+    if (e.target === pasteModal) closePasteModal();
+  });
+
   // textarea 的 paste 事件:文字直接插入,图片上传后加 chip
   pasteInput.addEventListener('paste', async (e) => {
     if (!e.clipboardData) return;
@@ -516,10 +528,8 @@ html { font-size: 15px; } html, body { height: 100%; background: #1a1a2e; color:
     if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
       e.preventDefault();
       sendPasteContent();
-    } else if (e.key === 'Escape') {
-      e.preventDefault();
-      closePasteModal();
     }
+    // ESC 关闭由 document 级监听器统一处理
   });
 
   function initTerminal() {
@@ -544,6 +554,30 @@ html { font-size: 15px; } html, body { height: 100%; background: #1a1a2e; color:
     term.open(termContainer);
     fitAddon.fit();
 
+    // 复制支持:有选区时 Ctrl+C 复制到剪贴板(不发中断信号)
+    // Ctrl+Shift+C 也复制(xterm 默认行为)
+    term.attachCustomKeyEventHandler((e) => {
+      // 复制:Ctrl+C 且有选区
+      if (e.type === 'keydown' && e.ctrlKey && !e.shiftKey && !e.altKey && e.key === 'c') {
+        const sel = term.getSelection();
+        if (sel) {
+          // 写入剪贴板
+          if (navigator.clipboard && navigator.clipboard.writeText) {
+            navigator.clipboard.writeText(sel).catch(() => {});
+          } else {
+            // HTTP 下 navigator.clipboard 可能不可用,用临时 textarea 兜底
+            const ta = document.createElement('textarea');
+            ta.value = sel; ta.style.position = 'fixed'; ta.style.opacity = '0';
+            document.body.appendChild(ta); ta.select();
+            try { document.execCommand('copy'); } catch(_) {}
+            document.body.removeChild(ta);
+          }
+          return false;  // 不传给终端
+        }
+      }
+      return true;
+    });
+
     term.onData(data => sendInput(data));
     term.onResize(({ cols, rows }) => {
       if (ws && ws.readyState === WebSocket.OPEN && pid) ws.send(JSON.stringify({ type: 'resize', cols, rows }));