Skip to content

Commit 57624e4

Browse files
committed
feat(terminal): add per-tab split layouts with keyboard shortcuts
- Add per-tab split layout tracking in TerminalsContext using Map<string, TerminalGroup> - Add getTabSplitLayout, setTabSplitLayout, removeTabSplitLayout, closeTerminalTab - Add tabId option to useTerminalSplits for tab-scoped split state - Register keyboard shortcuts: - Ctrl+Shift+5 / Cmd+Shift+5: vertical split - Ctrl+Shift+": horizontal split - Ctrl+Shift+W: close active split pane - Alt+Arrow: navigate between splits - Add 7 keybindings in defaultBindings2.ts with terminalFocus context - Add 7 command palette entries in CommandContext.tsx - Add 5 event constants in events.ts - Add event handlers and listeners in TerminalsContext - Clean up split layouts when terminals are closed
1 parent ca2beab commit 57624e4

File tree

5 files changed

+337
-24
lines changed

5 files changed

+337
-24
lines changed

src/components/terminal/useTerminalSplits.ts

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ export interface UseTerminalSplitsOptions {
4747
enableKeyboardShortcuts?: boolean;
4848
/** Storage key for persistence */
4949
storageKey?: string;
50+
/** Tab ID to scope split state to a specific terminal tab */
51+
tabId?: string;
5052
}
5153

5254
export interface UseTerminalSplitsReturn {
@@ -122,9 +124,11 @@ export function useTerminalSplits(options: UseTerminalSplitsOptions): UseTermina
122124
onActiveChange,
123125
enableKeyboardShortcuts = true,
124126
storageKey = "default",
127+
tabId,
125128
} = options;
126129

127-
const fullStorageKey = `${STORAGE_KEY_PREFIX}${storageKey}`;
130+
const effectiveStorageKey = tabId ? `${STORAGE_KEY_PREFIX}tab_${tabId}` : `${STORAGE_KEY_PREFIX}${storageKey}`;
131+
const fullStorageKey = effectiveStorageKey;
128132

129133
// Initialize state from storage or empty
130134
const initialState = loadFromStorage(fullStorageKey) || {
@@ -366,38 +370,32 @@ export function useTerminalSplits(options: UseTerminalSplitsOptions): UseTermina
366370
if (!enableKeyboardShortcuts) return;
367371

368372
const handleKeyDown = (e: KeyboardEvent) => {
369-
const { key, ctrlKey, shiftKey, altKey } = e;
373+
const { key, ctrlKey, shiftKey, altKey, metaKey } = e;
374+
const mod = ctrlKey || metaKey;
370375

371-
// Ctrl+Shift+5: Split terminal right (horizontal layout)
372-
if (ctrlKey && shiftKey && key === "5") {
376+
// Ctrl+Shift+5 / Cmd+Shift+5: Split terminal vertically
377+
if (mod && shiftKey && key === "5") {
373378
e.preventDefault();
374-
const activeId = activeTerminalId();
375-
if (activeId) {
376-
window.dispatchEvent(
377-
new CustomEvent("terminal:split", {
378-
detail: { terminalId: activeId, direction: "horizontal" },
379-
})
380-
);
381-
}
379+
window.dispatchEvent(new CustomEvent("terminal:split-vertical"));
382380
return;
383381
}
384382

385-
// Ctrl+Shift+": Split terminal down (vertical layout)
386-
if (ctrlKey && shiftKey && (key === '"' || key === "'")) {
383+
// Ctrl+Shift+" : Split terminal horizontally
384+
if (mod && shiftKey && (key === '"' || key === "'")) {
387385
e.preventDefault();
388-
const activeId = activeTerminalId();
389-
if (activeId) {
390-
window.dispatchEvent(
391-
new CustomEvent("terminal:split", {
392-
detail: { terminalId: activeId, direction: "vertical" },
393-
})
394-
);
395-
}
386+
window.dispatchEvent(new CustomEvent("terminal:split-horizontal"));
387+
return;
388+
}
389+
390+
// Ctrl+Shift+W: Close active split pane
391+
if (mod && shiftKey && key === "W") {
392+
e.preventDefault();
393+
window.dispatchEvent(new CustomEvent("terminal:close-split-pane"));
396394
return;
397395
}
398396

399-
// Ctrl+Alt+Arrow: Navigate between splits
400-
if (ctrlKey && altKey && !shiftKey) {
397+
// Alt+Arrow: Navigate between splits
398+
if (altKey && !ctrlKey && !metaKey && !shiftKey) {
401399
const activeId = activeTerminalId();
402400
if (!activeId) return;
403401

src/context/CommandContext.tsx

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1727,6 +1727,69 @@ export function CommandProvider(props: { children: JSX.Element }) {
17271727
window.dispatchEvent(new CustomEvent("terminal:split"));
17281728
},
17291729
},
1730+
{
1731+
id: "terminal.splitVertical",
1732+
label: "Split Terminal Vertically",
1733+
shortcut: "Ctrl+Shift+5",
1734+
category: "Terminal",
1735+
action: () => {
1736+
window.dispatchEvent(new CustomEvent("terminal:split-vertical"));
1737+
},
1738+
},
1739+
{
1740+
id: "terminal.splitHorizontal",
1741+
label: "Split Terminal Horizontally",
1742+
shortcut: 'Ctrl+Shift+"',
1743+
category: "Terminal",
1744+
action: () => {
1745+
window.dispatchEvent(new CustomEvent("terminal:split-horizontal"));
1746+
},
1747+
},
1748+
{
1749+
id: "terminal.closeSplitPane",
1750+
label: "Close Active Split Pane",
1751+
shortcut: "Ctrl+Shift+W",
1752+
category: "Terminal",
1753+
action: () => {
1754+
window.dispatchEvent(new CustomEvent("terminal:close-split-pane"));
1755+
},
1756+
},
1757+
{
1758+
id: "terminal.navigateSplitLeft",
1759+
label: "Navigate Split Left",
1760+
shortcut: "Alt+Left",
1761+
category: "Terminal",
1762+
action: () => {
1763+
window.dispatchEvent(new CustomEvent("terminal:navigate-split", { detail: { direction: "left" } }));
1764+
},
1765+
},
1766+
{
1767+
id: "terminal.navigateSplitRight",
1768+
label: "Navigate Split Right",
1769+
shortcut: "Alt+Right",
1770+
category: "Terminal",
1771+
action: () => {
1772+
window.dispatchEvent(new CustomEvent("terminal:navigate-split", { detail: { direction: "right" } }));
1773+
},
1774+
},
1775+
{
1776+
id: "terminal.navigateSplitUp",
1777+
label: "Navigate Split Up",
1778+
shortcut: "Alt+Up",
1779+
category: "Terminal",
1780+
action: () => {
1781+
window.dispatchEvent(new CustomEvent("terminal:navigate-split", { detail: { direction: "up" } }));
1782+
},
1783+
},
1784+
{
1785+
id: "terminal.navigateSplitDown",
1786+
label: "Navigate Split Down",
1787+
shortcut: "Alt+Down",
1788+
category: "Terminal",
1789+
action: () => {
1790+
window.dispatchEvent(new CustomEvent("terminal:navigate-split", { detail: { direction: "down" } }));
1791+
},
1792+
},
17301793
{
17311794
id: "terminal.focus",
17321795
label: "Focus Terminal",

0 commit comments

Comments
 (0)