Skip to content

Commit c941a8f

Browse files
authored
Merge pull request #212 from yu-zhy/contribute
✨ feat: 鼠标右键粘贴+F2切换穿透模式,支持终端原生选择
2 parents b53ec91 + 39e0b67 commit c941a8f

2 files changed

Lines changed: 39 additions & 5 deletions

File tree

tui/model.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"charm.land/glamour/v2"
2929
"charm.land/glamour/v2/styles"
3030
"charm.land/lipgloss/v2"
31+
"github.com/atotto/clipboard"
3132
"github.com/charmbracelet/x/ansi"
3233
)
3334

@@ -209,6 +210,10 @@ type model struct {
209210
plan *planState
210211
planKind string // 当前 plan 来源:"todo"(Todo)/ "createplan"(CreatePlan),右栏分段显示用
211212

213+
// mousePassthrough 为 true 时,在 View() 中关掉鼠标捕获(MouseModeNone),
214+
// 让终端原生处理右键粘贴与文字选择。用 F2 切换。
215+
mousePassthrough bool
216+
212217
// 鼠标 chat 矩形选区。selecting=true 表示左键在 chat 区按下后还没松开;
213218
// selAnchor / selEnd 是选区两端 (cellPos: 显示列 + wrapped 行号)。
214219
// 松开左键时:抠出选区内文本写到系统剪贴板,然后 selecting=false 高亮自动消失。
@@ -1460,6 +1465,16 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
14601465
return m, nil
14611466
}
14621467
if msg.Button != tea.MouseLeft {
1468+
// 右键点击输入区 → 读取剪贴板文本并粘贴
1469+
if msg.Button == tea.MouseRight {
1470+
leftW, vpH := m.layout()
1471+
inInput := msg.Y >= vpH && msg.Y < m.height && msg.X >= 0 && msg.X < leftW
1472+
if inInput {
1473+
if text, err := clipboard.ReadAll(); err == nil && text != "" {
1474+
m.input.InsertString(text)
1475+
}
1476+
}
1477+
}
14631478
return m, nil
14641479
}
14651480
leftW, vpH := m.layout()
@@ -2477,6 +2492,17 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
24772492
// 显示/隐藏右侧状态栏(chat 铺满整宽);记忆到 meta。
24782493
m.toggleStatusPanel()
24792494
return m, nil
2495+
case "f2":
2496+
// 切换鼠标穿透模式:关掉 MouseMode,让终端原生处理右键粘贴和文字选择。
2497+
m.mousePassthrough = !m.mousePassthrough
2498+
hint := "鼠标"
2499+
if m.mousePassthrough {
2500+
hint += "穿透已开启(终端原生选择/粘贴)"
2501+
} else {
2502+
hint += "捕获已恢复(deepx 选区/复制)"
2503+
}
2504+
m.appendChat("System", hint)
2505+
return m, nil
24802506
case "ctrl+v":
24812507
// 剪贴板有图就落盘并插入到输入框;没图则下落到 textinput 走文本粘贴。
24822508
if data, err := readClipboardImage(); err == nil {

tui/view.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,9 @@ func (m model) wrapView(content string) tea.View {
2626
v := tea.NewView(content)
2727
v.AltScreen = true
2828
v.MouseMode = tea.MouseModeCellMotion
29-
// 文本输入类弹窗(填 API key / MCP / skill / web 配置)打开时关掉鼠标捕获,
30-
// 让终端恢复原生右键粘贴(WSL2 / Windows Terminal 等)。否则鼠标模式开着时,
31-
// 右键被当成鼠标事件吞掉,用户只能 Ctrl+V(见 issue #40)。这些弹窗内不需要拖拽/滚动。
32-
if m.showSetup || m.showMcpAdd || m.showSkillAdd || m.showWebConfig {
29+
// 鼠标穿透模式:关掉鼠标捕获,让终端原生处理右键粘贴与文字选择。
30+
// 用户可按 F2 在 TUI 内切换,无需退出 deepx。
31+
if m.mousePassthrough || m.showSetup || m.showMcpAdd || m.showSkillAdd || m.showWebConfig {
3332
v.MouseMode = tea.MouseModeNone
3433
}
3534
// 换行键统一为 ctrl+j(LF,终端原生、不依赖 Kitty 协议、三平台一致,见 issue #124),
@@ -531,9 +530,15 @@ func (m model) statusFooterLine(_ int) string {
531530
if m.turnToolCalls > 0 {
532531
s += dim(" · " + strconv.Itoa(m.turnToolCalls) + " " + T("done.tools"))
533532
}
533+
if m.mousePassthrough {
534+
s += dim(" · 🖱穿透")
535+
}
534536
return s
535537
}
536-
// 还没跑过任何一轮:留空。活动行的价值在"运行↔完成"的对比,启动时常挂个"就绪"只是噪音。
538+
// 还没跑过任何一轮:检查鼠标穿透指示。
539+
if m.mousePassthrough {
540+
return dim("🖱 鼠标穿透已开启(F2切换)")
541+
}
537542
return ""
538543
}
539544
head := statusIcon(m.status)
@@ -549,6 +554,9 @@ func (m model) statusFooterLine(_ int) string {
549554
if m.retryNotice != "" {
550555
left += dim(" · ") + lipgloss.NewStyle().Foreground(lipgloss.Color("11")).Bold(true).Render("⟳ "+m.retryNotice)
551556
}
557+
if m.mousePassthrough {
558+
left += dim(" · 🖱穿透")
559+
}
552560
// 不再右贴 "Esc 中断" —— 输入框 placeholder(misc.input_placeholder)已含,避免重复。
553561
return left
554562
}

0 commit comments

Comments
 (0)