Add keyboard shortcuts (Ctrl+Shift chord style)
| Ctrl+K | palette | | Ctrl+Shift+E | split active pane right | | Ctrl+Shift+O | split active pane down | | Ctrl+Shift+W | close active pane | | Ctrl+Shift+B | toggle broadcast on active | | Ctrl+Shift+Alt+B | toggle broadcast on ALL panes | | Ctrl+Shift+Arrow | focus neighbour pane in that direction | The handler attaches at capture phase on window so it wins against xterm.js. It bails when a non-terminal <input>/<textarea> is focused so label edits and the palette input keep working normally. Spatial neighbour-finding lives in tree.ts as findNeighborInDirection — picks the leaf whose centre is most aligned in the perpendicular axis, breaking ties by primary-axis distance. Tooltips on toolbar/titlebar buttons now mention their shortcuts; README has a key-binding table. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9aa0c5a828
commit
a4cd82440b
4 changed files with 188 additions and 21 deletions
|
|
@ -356,6 +356,69 @@ export function updateSplitRatio(root: TreeNode, splitId: NodeId, ratio: number)
|
|||
});
|
||||
}
|
||||
|
||||
export type Direction = "left" | "right" | "up" | "down";
|
||||
|
||||
/** Spatial pane navigation: given an active leaf, find the nearest neighbor
|
||||
* in the requested direction. Used for Ctrl+Shift+Arrow shortcuts. */
|
||||
export function findNeighborInDirection(
|
||||
leaves: LeafSlot[],
|
||||
fromLeafId: NodeId,
|
||||
direction: Direction,
|
||||
): NodeId | null {
|
||||
const from = leaves.find((s) => s.leaf.id === fromLeafId);
|
||||
if (!from) return null;
|
||||
const fromCenter = {
|
||||
x: from.box.left + from.box.width / 2,
|
||||
y: from.box.top + from.box.height / 2,
|
||||
};
|
||||
const EPS = 1e-3;
|
||||
|
||||
let best: { id: NodeId; perpDist: number; primaryDist: number } | null = null;
|
||||
|
||||
for (const slot of leaves) {
|
||||
if (slot.leaf.id === fromLeafId) continue;
|
||||
const center = {
|
||||
x: slot.box.left + slot.box.width / 2,
|
||||
y: slot.box.top + slot.box.height / 2,
|
||||
};
|
||||
let primary: number;
|
||||
let perp: number;
|
||||
|
||||
switch (direction) {
|
||||
case "right":
|
||||
if (slot.box.left < from.box.left + from.box.width - EPS) continue;
|
||||
primary = center.x - fromCenter.x;
|
||||
perp = Math.abs(center.y - fromCenter.y);
|
||||
break;
|
||||
case "left":
|
||||
if (slot.box.left + slot.box.width > from.box.left + EPS) continue;
|
||||
primary = fromCenter.x - center.x;
|
||||
perp = Math.abs(center.y - fromCenter.y);
|
||||
break;
|
||||
case "down":
|
||||
if (slot.box.top < from.box.top + from.box.height - EPS) continue;
|
||||
primary = center.y - fromCenter.y;
|
||||
perp = Math.abs(center.x - fromCenter.x);
|
||||
break;
|
||||
case "up":
|
||||
if (slot.box.top + slot.box.height > from.box.top + EPS) continue;
|
||||
primary = fromCenter.y - center.y;
|
||||
perp = Math.abs(center.x - fromCenter.x);
|
||||
break;
|
||||
}
|
||||
|
||||
if (
|
||||
best === null ||
|
||||
perp < best.perpDist ||
|
||||
(perp === best.perpDist && primary < best.primaryDist)
|
||||
) {
|
||||
best = { id: slot.leaf.id, perpDist: perp, primaryDist: primary };
|
||||
}
|
||||
}
|
||||
|
||||
return best?.id ?? null;
|
||||
}
|
||||
|
||||
/** Swap two leaves' tree positions. Each leaf carries its own data
|
||||
* (id, distro, cwd, label, broadcast) into the other's slot. PTYs stay
|
||||
* alive because React keys on leaf.id and our renderer is flat. */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue