From a98f22202d792585d5947d8e79ee1548e8ce55f6 Mon Sep 17 00:00:00 2001 From: Hermes Date: Wed, 29 Jul 2026 07:12:52 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20add=20'=E5=A4=8D=E5=88=B6=E6=A0=87?= =?UTF-8?q?=E7=AD=BE'=20option=20to=20terminal=20tab=20right-click=20menu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User asked: '标签使用鼠标右键应该有复制标签的功能' In ui/terminal_tab_widget.py _on_tab_context_menu: - Added '📋 复制标签' action between '重命名' and '关闭标签' - New method _duplicate_tab(idx): creates a new terminal tab for the same host (reuses SSH connection, opens independent shell channel via paramiko invoke_shell) - New tab title = ' (副本)' with ⚪ state dot - If host not connected: duplicate creates a placeholder tab - If host connected: duplicate immediately opens a new shell Verified: 3 tabs after duplicating 'test-server' (欢迎 + ⚪ test-server + ⚪ test-server (副本)) All tests still pass. --- ui/terminal_tab_widget.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/ui/terminal_tab_widget.py b/ui/terminal_tab_widget.py index e054b4f..dbe63d8 100644 --- a/ui/terminal_tab_widget.py +++ b/ui/terminal_tab_widget.py @@ -231,6 +231,7 @@ class TerminalTabWidget(QWidget): menu = QMenu(self) panel = self._panel_at(idx) a_rename = menu.addAction("重命名") + a_duplicate = menu.addAction("📋 复制标签") a_close = menu.addAction("关闭标签") a_close_others = menu.addAction("关闭其他") if panel and panel._connected: @@ -242,6 +243,8 @@ class TerminalTabWidget(QWidget): chosen = menu.exec_(self.tabs.tabBar().mapToGlobal(pos)) if chosen == a_rename: self._on_tab_double_clicked(idx) + elif chosen == a_duplicate: + self._duplicate_tab(idx) elif chosen == a_close: self._close_tab(idx) elif chosen == a_close_others: @@ -250,6 +253,29 @@ class TerminalTabWidget(QWidget): if panel and panel.conn: panel._reopen_shell() + def _duplicate_tab(self, idx: int): + """复制标签:新建一个同主机的终端标签(独立 shell 通道)""" + src_panel = self._panel_at(idx) + if src_panel is None: + return + host_id = self._host_id_for_panel(src_panel) + # 决定新标签的初始连接 + conn = self.manager.get_connection(host_id) if host_id else None + # 新建标签(open_terminal 会 attach conn) + new_idx = self.open_terminal(host_id or "", conn=conn) + # 用源标签的当前标题做新标签名(去掉状态点前缀) + src_title = self.tabs.tabText(idx) + for prefix in ("⚪ ", "🟢 ", "🔴 ", "🟡 "): + if src_title.startswith(prefix): + src_title = src_title[len(prefix):] + break + # 加 " (副本)" 后缀 + self.tabs.setTabText(new_idx, f"⚪ {src_title} (副本)") + self.tabs.setCurrentIndex(new_idx) + # 如果已连接,给用户一个小提示:每个副本是独立 shell + if conn: + self.statusBar_msg = f"已创建副本(独立 shell)" if hasattr(self, "statusBar_msg") else None + def _close_tab(self, idx: int): panel = self._panel_at(idx) if panel and panel._connected: