Skip to content

Commit f676e8e

Browse files
aixierclaude
andcommitted
fix: 改进终端文本复制功能 - 支持无选中时复制最后内容
改进内容: - 复制功能现在使用 xterm.js buffer API - 如果没有鼠标选中文本,自动复制最后20行内容 - 添加 selectAll() 方法支持 Ctrl+A 全选 - 改进快捷键处理,添加日志反馈 快捷键列表: Ctrl+A - 全选终端内容 Ctrl+L - 清屏 Ctrl+Shift+C - 复制选中(或最后20行) Ctrl+Shift+V - 粘贴内容 Ctrl+Shift+R - 刷新光标 技术实现: - 使用 terminal.buffer.active 和 translateToString() - 使用 terminal.select() 实现全选 - 使用 navigator.clipboard API 前端构建:✅ 成功 代码语法:✅ 检查通过 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
1 parent c6ba061 commit f676e8e

2 files changed

Lines changed: 63 additions & 6 deletions

File tree

terminal-ui/src/components/TerminalBest.vue

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,14 @@ function setupKeyboardShortcuts() {
175175
if (!terminalContainer.value) return
176176
177177
terminalContainer.value.addEventListener('keydown', async (e) => {
178+
// Ctrl+A: 全选
179+
if (e.ctrlKey && e.key === 'a') {
180+
e.preventDefault()
181+
const selected = terminalEngine.value?.selectAll()
182+
if (selected) {
183+
console.log('[TerminalBest] All content selected')
184+
}
185+
}
178186
// Ctrl+L: 清屏
179187
if (e.ctrlKey && e.key === 'l') {
180188
e.preventDefault()
@@ -185,20 +193,24 @@ function setupKeyboardShortcuts() {
185193
e.preventDefault()
186194
refreshCursor()
187195
}
188-
// Ctrl+Shift+C: 复制选中文本
196+
// Ctrl+Shift+C: 复制选中文本(如果没有选中,复制最后20行)
189197
if (e.ctrlKey && e.shiftKey && e.key === 'C') {
190198
e.preventDefault()
191199
const copied = await terminalEngine.value?.copySelection()
192200
if (copied) {
193-
console.log('[TerminalBest] Text copied successfully')
201+
console.log('[TerminalBest] Text copied to clipboard')
202+
} else {
203+
console.warn('[TerminalBest] Failed to copy text')
194204
}
195205
}
196206
// Ctrl+Shift+V: 粘贴剪贴板内容
197207
if (e.ctrlKey && e.shiftKey && e.key === 'V') {
198208
e.preventDefault()
199209
const pasted = await terminalEngine.value?.pasteFromClipboard()
200210
if (pasted) {
201-
console.log('[TerminalBest] Text pasted successfully')
211+
console.log('[TerminalBest] Text pasted from clipboard')
212+
} else {
213+
console.warn('[TerminalBest] Failed to paste text')
202214
}
203215
}
204216
})
@@ -226,6 +238,7 @@ defineExpose({
226238
reconnect,
227239
refreshCursor,
228240
reinitializeTerminal,
241+
selectAll: () => terminalEngine.value?.selectAll(),
229242
copySelection: () => terminalEngine.value?.copySelection(),
230243
pasteFromClipboard: () => terminalEngine.value?.pasteFromClipboard()
231244
})

terminal-ui/src/core/terminal-engine/xterm-engine.js

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -377,19 +377,63 @@ export class XTermEngine {
377377
// 复制选中的文本到剪贴板
378378
async copySelection() {
379379
try {
380-
const selection = this.terminal.getSelection()
381-
if (selection) {
380+
// 方式1:尝试获取当前选中的文本(鼠标选择)
381+
let selection = this.terminal.getSelection()
382+
383+
// 方式2:如果没有选中内容,复制最后一行
384+
if (!selection || selection.trim().length === 0) {
385+
// 从 buffer 获取最后N行的内容
386+
const buffer = this.terminal.buffer.active
387+
if (!buffer || buffer.length === 0) {
388+
console.warn('[XTerm] No content to copy')
389+
return false
390+
}
391+
392+
// 获取最后显示的20行内容
393+
const lines = []
394+
const startLine = Math.max(0, buffer.length - 20)
395+
for (let i = startLine; i < buffer.length; i++) {
396+
const line = buffer.getLine(i)
397+
if (line) {
398+
lines.push(line.translateToString())
399+
}
400+
}
401+
selection = lines.join('\n')
402+
}
403+
404+
if (selection && selection.trim().length > 0) {
382405
await navigator.clipboard.writeText(selection)
383-
console.log('[XTerm] Text copied to clipboard')
406+
console.log('[XTerm] Text copied to clipboard:', selection.length, 'chars')
384407
return true
385408
}
409+
410+
console.warn('[XTerm] No text to copy')
386411
return false
387412
} catch (error) {
388413
console.error('[XTerm] Failed to copy:', error)
389414
return false
390415
}
391416
}
392417

418+
// 全选终端中的所有文本
419+
selectAll() {
420+
try {
421+
const buffer = this.terminal.buffer.active
422+
if (!buffer || buffer.length === 0) {
423+
console.warn('[XTerm] No content to select')
424+
return false
425+
}
426+
427+
// 选择从第一行到最后一行的所有内容
428+
this.terminal.select(0, 0, buffer.length, buffer.getLine(buffer.length - 1)?.length || 0)
429+
console.log('[XTerm] All content selected')
430+
return true
431+
} catch (error) {
432+
console.error('[XTerm] Failed to select all:', error)
433+
return false
434+
}
435+
}
436+
393437
// 粘贴剪贴板内容
394438
async pasteFromClipboard() {
395439
try {

0 commit comments

Comments
 (0)