Skip to content

Commit 1a0585f

Browse files
committed
support tmux switching operation in /mux command
1 parent fe08023 commit 1a0585f

3 files changed

Lines changed: 45 additions & 17 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ Fork your current session at any previous message, the original stays open.
3232

3333
### `/mux`
3434

35-
Lists every Pi running on your machine. Kill the ones you're done with.
35+
Lists every Pi running on your machine. Press Enter to jump to an open Pi or bring a backgrounded one forward; kill the ones you're done with.
3636

3737
Each row shows cwd, session name, and a tag:
3838

src/index.ts

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ type SessionManagerLike = {
1212
getBranch(): ReturnType<SessionManager["getBranch"]>;
1313
};
1414
import * as heartbeat from "./heartbeat.js";
15-
import { MuxMenu } from "./mux-menu.js";
15+
import { MuxMenu, type MuxMenuSelection } from "./mux-menu.js";
1616
import { spawnAndSwap } from "./swap.js";
1717

1818
const SELF = fileURLToPath(import.meta.url);
@@ -407,25 +407,38 @@ export default function (pi: ExtensionAPI) {
407407
});
408408

409409
pi.registerCommand("mux", {
410-
description: "List and manage backgrounded pi-mux sessions",
410+
description: "List, switch, and manage pi-mux sessions",
411411
handler: async (_args, ctx) => {
412412
if (!inTmux()) {
413413
ctx.ui.notify("not in tmux", "error");
414414
return;
415415
}
416416
const self = process.env.TMUX_PANE!;
417417
const cwd = ctx.cwd;
418-
await ctx.ui.custom<undefined>((tui, theme, _keybindings, done) => {
419-
const menu = new MuxMenu({
420-
tui,
421-
theme,
422-
currentPaneId: self,
423-
currentCwd: cwd,
424-
done,
425-
});
426-
tui.setFocus(menu);
427-
return menu;
428-
});
418+
const picked = await ctx.ui.custom<MuxMenuSelection | undefined>(
419+
(tui, theme, _keybindings, done) => {
420+
const menu = new MuxMenu({
421+
tui,
422+
theme,
423+
currentPaneId: self,
424+
currentCwd: cwd,
425+
done,
426+
});
427+
tui.setFocus(menu);
428+
return menu;
429+
},
430+
);
431+
432+
if (!picked || picked.paneId === self) return;
433+
try {
434+
if (picked.inPool) {
435+
execFileSync("tmux", ["swap-pane", "-s", picked.paneId, "-t", self]);
436+
} else {
437+
execFileSync("tmux", ["switch-client", "-t", picked.paneId]);
438+
}
439+
} catch {
440+
ctx.ui.notify("failed to switch session", "error");
441+
}
429442
},
430443
});
431444
}

src/mux-menu.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,17 @@ interface TreeRow {
2525
ancestorContinues: boolean[];
2626
}
2727

28+
export interface MuxMenuSelection {
29+
paneId: string;
30+
inPool: boolean;
31+
}
32+
2833
export interface MuxMenuOptions {
2934
tui: TUI;
3035
theme: Theme;
3136
currentPaneId: string;
3237
currentCwd: string;
33-
done: (result: undefined) => void;
38+
done: (result: MuxMenuSelection | undefined) => void;
3439
}
3540

3641
export class MuxMenu extends Container implements Focusable {
@@ -44,7 +49,7 @@ export class MuxMenu extends Container implements Focusable {
4449
private readonly theme: Theme;
4550
private readonly currentPaneId: string;
4651
private readonly currentCwd: string;
47-
private readonly done: (result: undefined) => void;
52+
private readonly done: (result: MuxMenuSelection | undefined) => void;
4853
private refreshTimer?: ReturnType<typeof setInterval>;
4954
private poolPanes = new Set<string>();
5055
private readonly list: MuxList;
@@ -135,7 +140,16 @@ export class MuxMenu extends Container implements Focusable {
135140
this.handleConfirmInput(data);
136141
return;
137142
}
138-
if (data === "\r" || data === "\n" || data === "\u001b" || data === "q") {
143+
if (data === "\r" || data === "\n") {
144+
const row = this.selectedRow();
145+
if (!row || row.paneId === this.currentPaneId) {
146+
this.done(undefined);
147+
return;
148+
}
149+
this.done({ paneId: row.paneId, inPool: this.poolPanes.has(row.paneId) });
150+
return;
151+
}
152+
if (data === "\u001b" || data === "q") {
139153
this.done(undefined);
140154
return;
141155
}
@@ -260,6 +274,7 @@ class MuxList implements Component {
260274
} else {
261275
const sep = theme.fg("muted", " · ");
262276
const hints = [
277+
rawKeyHint("enter", "switch"),
263278
rawKeyHint("d", "kill"),
264279
rawKeyHint("D", "kill all"),
265280
rawKeyHint("tab", "scope"),

0 commit comments

Comments
 (0)