fix: intercept Ctrl+B and app shortcuts in capture phase when terminal is focused
This commit is contained in:
+100
-86
@@ -217,97 +217,111 @@ class TerminalApp {
|
||||
}
|
||||
|
||||
initKeyboardShortcuts() {
|
||||
window.addEventListener('keydown', (e) => {
|
||||
// Ctrl+Shift+P 或 F1: 命令面板
|
||||
if ((e.ctrlKey && e.shiftKey && e.key.toLowerCase() === 'p') || e.key === 'F1') {
|
||||
e.preventDefault();
|
||||
this.commandPalette.toggle();
|
||||
return;
|
||||
}
|
||||
|
||||
// Ctrl+B: 切换左侧工作区侧边栏
|
||||
if (e.ctrlKey && e.key.toLowerCase() === 'b') {
|
||||
e.preventDefault();
|
||||
this.workspaceManager?.toggleSidebar();
|
||||
return;
|
||||
}
|
||||
|
||||
// Ctrl+Shift+T: 新建当前工作区标签页
|
||||
if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() === 't') {
|
||||
e.preventDefault();
|
||||
const activeWs = this.workspaceManager ? this.workspaceManager.getActiveWorkspace() : null;
|
||||
const spawnOpts = this.workspaceManager ? this.workspaceManager.getSpawnOptions(activeWs?.id) : {};
|
||||
this.tabManager.createTab({
|
||||
workspaceId: activeWs?.id,
|
||||
title: spawnOpts.title,
|
||||
cwd: spawnOpts.cwd,
|
||||
shell: spawnOpts.shell,
|
||||
args: spawnOpts.args,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Ctrl+Shift+W: 关闭窗格
|
||||
if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() === 'w') {
|
||||
e.preventDefault();
|
||||
const tab = this.tabManager.getActiveTab();
|
||||
if (tab && tab.splitTree.activeTerminal) {
|
||||
tab.splitTree.closeTerminal(tab.splitTree.activeTerminal.id);
|
||||
window.addEventListener(
|
||||
'keydown',
|
||||
(e) => {
|
||||
// Ctrl+Shift+P 或 F1: 命令面板
|
||||
if ((e.ctrlKey && e.shiftKey && e.key.toLowerCase() === 'p') || e.key === 'F1') {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.commandPalette.toggle();
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Ctrl+Shift+E: 水平分屏 (左右)
|
||||
if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() === 'e') {
|
||||
e.preventDefault();
|
||||
const tab = this.tabManager.getActiveTab();
|
||||
if (tab) tab.splitTree.splitCurrent('horizontal');
|
||||
return;
|
||||
}
|
||||
|
||||
// Ctrl+Shift+O: 垂直分屏 (上下)
|
||||
if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() === 'o') {
|
||||
e.preventDefault();
|
||||
const tab = this.tabManager.getActiveTab();
|
||||
if (tab) tab.splitTree.splitCurrent('vertical');
|
||||
return;
|
||||
}
|
||||
|
||||
// Ctrl+Shift+F: 搜索
|
||||
if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() === 'f') {
|
||||
e.preventDefault();
|
||||
this.toggleSearch();
|
||||
return;
|
||||
}
|
||||
|
||||
// Ctrl+,: 设置
|
||||
if (e.ctrlKey && e.key === ',') {
|
||||
e.preventDefault();
|
||||
this.settings.open();
|
||||
return;
|
||||
}
|
||||
|
||||
// Ctrl+Tab / Ctrl+Shift+Tab: 标签页切换
|
||||
if (e.ctrlKey && e.key === 'Tab') {
|
||||
e.preventDefault();
|
||||
if (e.shiftKey) {
|
||||
this.tabManager.prevTab();
|
||||
} else {
|
||||
this.tabManager.nextTab();
|
||||
// Ctrl+B: 切换左侧工作区侧边栏
|
||||
if (e.ctrlKey && !e.shiftKey && !e.altKey && e.key.toLowerCase() === 'b') {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.workspaceManager?.toggleSidebar();
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Alt+1 .. Alt+9: 快速直达标签页
|
||||
if (e.altKey && e.key >= '1' && e.key <= '9') {
|
||||
e.preventDefault();
|
||||
const index = parseInt(e.key, 10) - 1;
|
||||
const ids = Array.from(this.tabManager.tabs.keys());
|
||||
if (ids[index]) {
|
||||
this.tabManager.switchTab(ids[index]);
|
||||
// Ctrl+Shift+T: 新建当前工作区标签页
|
||||
if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() === 't') {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const activeWs = this.workspaceManager ? this.workspaceManager.getActiveWorkspace() : null;
|
||||
const spawnOpts = this.workspaceManager ? this.workspaceManager.getSpawnOptions(activeWs?.id) : {};
|
||||
this.tabManager.createTab({
|
||||
workspaceId: activeWs?.id,
|
||||
title: spawnOpts.title,
|
||||
cwd: spawnOpts.cwd,
|
||||
shell: spawnOpts.shell,
|
||||
args: spawnOpts.args,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Ctrl+Shift+W: 关闭窗格
|
||||
if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() === 'w') {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const tab = this.tabManager.getActiveTab();
|
||||
if (tab && tab.splitTree.activeTerminal) {
|
||||
tab.splitTree.closeTerminal(tab.splitTree.activeTerminal.id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Ctrl+Shift+E: 水平分屏 (左右)
|
||||
if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() === 'e') {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const tab = this.tabManager.getActiveTab();
|
||||
if (tab) tab.splitTree.splitCurrent('horizontal');
|
||||
return;
|
||||
}
|
||||
|
||||
// Ctrl+Shift+O: 垂直分屏 (上下)
|
||||
if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() === 'o') {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const tab = this.tabManager.getActiveTab();
|
||||
if (tab) tab.splitTree.splitCurrent('vertical');
|
||||
return;
|
||||
}
|
||||
|
||||
// Ctrl+Shift+F: 搜索
|
||||
if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() === 'f') {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.toggleSearch();
|
||||
return;
|
||||
}
|
||||
|
||||
// Ctrl+,: 设置
|
||||
if (e.ctrlKey && e.key === ',') {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.settings.open();
|
||||
return;
|
||||
}
|
||||
|
||||
// Ctrl+Tab / Ctrl+Shift+Tab: 标签页切换
|
||||
if (e.ctrlKey && e.key === 'Tab') {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (e.shiftKey) {
|
||||
this.tabManager.prevTab();
|
||||
} else {
|
||||
this.tabManager.nextTab();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Alt+1 .. Alt+9: 快速直达标签页
|
||||
if (e.altKey && e.key >= '1' && e.key <= '9') {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const index = parseInt(e.key, 10) - 1;
|
||||
const ids = Array.from(this.tabManager.tabs.keys());
|
||||
if (ids[index]) {
|
||||
this.tabManager.switchTab(ids[index]);
|
||||
}
|
||||
}
|
||||
},
|
||||
true // 启用捕获阶段,确保终端即使在聚焦/输入状态下应用级快捷键也能被优先拦截响应
|
||||
);
|
||||
}
|
||||
|
||||
async initStatusBar() {
|
||||
|
||||
Reference in New Issue
Block a user