abmux 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +188 -170
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -228,13 +228,6 @@ var detectStatusFromText = (paneText) => {
|
|
|
228
228
|
}
|
|
229
229
|
return SESSION_STATUS.idle;
|
|
230
230
|
};
|
|
231
|
-
var formatCwd = (cwd) => {
|
|
232
|
-
const home = process.env["HOME"] ?? "";
|
|
233
|
-
if (home && cwd.startsWith(home)) {
|
|
234
|
-
return `~${cwd.slice(home.length)}`;
|
|
235
|
-
}
|
|
236
|
-
return cwd;
|
|
237
|
-
};
|
|
238
231
|
var toUnifiedPane = (pane) => {
|
|
239
232
|
const kind = classifyPane(pane);
|
|
240
233
|
if (kind === PANE_KIND.claude) {
|
|
@@ -250,66 +243,44 @@ var toUnifiedPane = (pane) => {
|
|
|
250
243
|
var createSessionDetectionService = () => ({
|
|
251
244
|
groupBySession: ({ panes }) => {
|
|
252
245
|
const windowKey = (pane) => `${pane.sessionName}:${String(pane.windowIndex)}`;
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
const
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
if (
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
activePaneTitle: pane.isActive ? pane.title : "",
|
|
270
|
-
panes: [unified]
|
|
271
|
-
});
|
|
246
|
+
const panesByWindow = Map.groupBy(panes, windowKey);
|
|
247
|
+
const sortPanes = (items) => items.toSorted((a, b) => {
|
|
248
|
+
const kindOrder = { claude: 0, available: 1, busy: 2 };
|
|
249
|
+
const kindDiff = kindOrder[a.kind] - kindOrder[b.kind];
|
|
250
|
+
if (kindDiff !== 0) return kindDiff;
|
|
251
|
+
if (a.kind === "claude" && b.kind === "claude") {
|
|
252
|
+
const statusOrder = {
|
|
253
|
+
"waiting-confirm": 0,
|
|
254
|
+
"waiting-input": 1,
|
|
255
|
+
thinking: 2,
|
|
256
|
+
"tool-running": 3,
|
|
257
|
+
idle: 4
|
|
258
|
+
};
|
|
259
|
+
const sa = statusOrder[a.claudeStatus ?? "idle"] ?? 4;
|
|
260
|
+
const sb = statusOrder[b.claudeStatus ?? "idle"] ?? 4;
|
|
261
|
+
if (sa !== sb) return sa - sb;
|
|
272
262
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
}
|
|
294
|
-
return a.pane.paneIndex - b.pane.paneIndex;
|
|
295
|
-
})
|
|
263
|
+
return a.pane.paneIndex - b.pane.paneIndex;
|
|
264
|
+
});
|
|
265
|
+
const windowGroups = [...panesByWindow.entries()].toSorted(([a], [b]) => a.localeCompare(b)).map(([, group]) => {
|
|
266
|
+
const first = group[0];
|
|
267
|
+
const activePaneTitle = group.find((p) => p.isActive)?.title ?? "";
|
|
268
|
+
return {
|
|
269
|
+
windowIndex: first.windowIndex,
|
|
270
|
+
windowName: first.windowName || activePaneTitle || `Window ${String(first.windowIndex)}`,
|
|
271
|
+
sessionName: first.sessionName,
|
|
272
|
+
panes: sortPanes(group.map(toUnifiedPane))
|
|
273
|
+
};
|
|
274
|
+
});
|
|
275
|
+
const sessionGroups = Map.groupBy(windowGroups, (win) => win.sessionName);
|
|
276
|
+
return [...sessionGroups.entries()].map(([sessionName, wins]) => ({
|
|
277
|
+
sessionName,
|
|
278
|
+
tabs: wins.map(({ windowIndex, windowName, panes: p }) => ({
|
|
279
|
+
windowIndex,
|
|
280
|
+
windowName,
|
|
281
|
+
panes: p
|
|
282
|
+
}))
|
|
296
283
|
}));
|
|
297
|
-
const sessionMap = /* @__PURE__ */ new Map();
|
|
298
|
-
for (const win of windowGroups) {
|
|
299
|
-
const existing = sessionMap.get(win.sessionName);
|
|
300
|
-
if (existing) {
|
|
301
|
-
existing.push({
|
|
302
|
-
windowIndex: win.windowIndex,
|
|
303
|
-
windowName: win.windowName,
|
|
304
|
-
panes: win.panes
|
|
305
|
-
});
|
|
306
|
-
} else {
|
|
307
|
-
sessionMap.set(win.sessionName, [
|
|
308
|
-
{ windowIndex: win.windowIndex, windowName: win.windowName, panes: win.panes }
|
|
309
|
-
]);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
return [...sessionMap.entries()].map(([sessionName, tabs]) => ({ sessionName, tabs }));
|
|
313
284
|
},
|
|
314
285
|
detectStatusFromText
|
|
315
286
|
});
|
|
@@ -394,24 +365,15 @@ var findProjects = async (dir, depth) => {
|
|
|
394
365
|
const dirs = entries.filter(
|
|
395
366
|
(e) => e.isDirectory() && !e.name.startsWith(".") && !SKIP_DIRS.has(e.name)
|
|
396
367
|
);
|
|
397
|
-
const
|
|
398
|
-
const childScans = [];
|
|
399
|
-
for (const entry of dirs) {
|
|
368
|
+
const childScans = dirs.map((entry) => {
|
|
400
369
|
const fullPath = join2(dir, entry.name);
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
);
|
|
407
|
-
}
|
|
370
|
+
return exists(join2(fullPath, ".git")).then(async (isProject) => {
|
|
371
|
+
if (isProject) return [fullPath];
|
|
372
|
+
return await findProjects(fullPath, depth + 1);
|
|
373
|
+
});
|
|
374
|
+
});
|
|
408
375
|
const nested = await Promise.all(childScans);
|
|
409
|
-
|
|
410
|
-
for (const p of paths) {
|
|
411
|
-
results.push(p);
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
return results;
|
|
376
|
+
return nested.flat();
|
|
415
377
|
} catch {
|
|
416
378
|
return [];
|
|
417
379
|
}
|
|
@@ -505,7 +467,7 @@ var createUsecases = (context) => ({
|
|
|
505
467
|
// package.json
|
|
506
468
|
var package_default = {
|
|
507
469
|
name: "abmux",
|
|
508
|
-
version: "0.0.
|
|
470
|
+
version: "0.0.5",
|
|
509
471
|
repository: {
|
|
510
472
|
type: "git",
|
|
511
473
|
url: "https://github.com/cut0/abmux.git"
|
|
@@ -576,7 +538,7 @@ import { createElement } from "react";
|
|
|
576
538
|
// src/components/ManagerView.tsx
|
|
577
539
|
import { basename as basename2 } from "node:path";
|
|
578
540
|
import { Box as Box10, Text as Text10 } from "ink";
|
|
579
|
-
import { useCallback as useCallback3, useEffect as useEffect2, useMemo as useMemo5,
|
|
541
|
+
import { useCallback as useCallback3, useEffect as useEffect2, useMemo as useMemo5, useState as useState5 } from "react";
|
|
580
542
|
|
|
581
543
|
// src/components/shared/Header.tsx
|
|
582
544
|
import { Box, Text } from "ink";
|
|
@@ -616,15 +578,28 @@ var useScroll = (cursor, totalItems, availableRows) => {
|
|
|
616
578
|
}, [cursor, totalItems, availableRows]);
|
|
617
579
|
};
|
|
618
580
|
|
|
581
|
+
// src/utils/PathUtils.ts
|
|
582
|
+
var formatCwd = (cwd) => {
|
|
583
|
+
const home = process.env["HOME"] ?? "";
|
|
584
|
+
if (home && cwd.startsWith(home)) {
|
|
585
|
+
return `~${cwd.slice(home.length)}`;
|
|
586
|
+
}
|
|
587
|
+
return cwd;
|
|
588
|
+
};
|
|
589
|
+
var findMatchingDirectory = (path, directories) => directories.filter((dir) => path === dir || path.startsWith(dir + "/")).reduce(
|
|
590
|
+
(best, dir) => !best || dir.length > best.length ? dir : best,
|
|
591
|
+
void 0
|
|
592
|
+
);
|
|
593
|
+
|
|
619
594
|
// src/components/shared/DirectorySelect.tsx
|
|
620
595
|
import { jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
621
|
-
var
|
|
622
|
-
const current =
|
|
623
|
-
const rest =
|
|
596
|
+
var sortSessions = (sessions, currentSession) => {
|
|
597
|
+
const current = sessions.filter((s) => s.name === currentSession);
|
|
598
|
+
const rest = sessions.filter((s) => s.name !== currentSession);
|
|
624
599
|
return [...current, ...rest];
|
|
625
600
|
};
|
|
626
601
|
var SessionListPanel = ({
|
|
627
|
-
|
|
602
|
+
sessions,
|
|
628
603
|
currentSession,
|
|
629
604
|
isFocused,
|
|
630
605
|
availableRows,
|
|
@@ -635,30 +610,30 @@ var SessionListPanel = ({
|
|
|
635
610
|
}) => {
|
|
636
611
|
const { exit } = useApp();
|
|
637
612
|
const [cursor, setCursor] = useState(0);
|
|
638
|
-
const
|
|
639
|
-
() =>
|
|
640
|
-
[
|
|
613
|
+
const sortedSessions = useMemo2(
|
|
614
|
+
() => sortSessions(sessions, currentSession),
|
|
615
|
+
[sessions, currentSession]
|
|
641
616
|
);
|
|
642
|
-
const
|
|
643
|
-
const clampedCursor = cursor >=
|
|
617
|
+
const names = useMemo2(() => sortedSessions.map((s) => s.name), [sortedSessions]);
|
|
618
|
+
const clampedCursor = cursor >= names.length ? Math.max(0, names.length - 1) : cursor;
|
|
644
619
|
if (clampedCursor !== cursor) {
|
|
645
620
|
setCursor(clampedCursor);
|
|
646
621
|
}
|
|
647
622
|
const reservedLines = 1;
|
|
648
623
|
const { scrollOffset, visibleCount } = useScroll(
|
|
649
624
|
clampedCursor,
|
|
650
|
-
|
|
625
|
+
names.length,
|
|
651
626
|
availableRows - reservedLines
|
|
652
627
|
);
|
|
653
|
-
const visibleSessions =
|
|
628
|
+
const visibleSessions = sortedSessions.slice(scrollOffset, scrollOffset + visibleCount);
|
|
654
629
|
const moveCursor = useCallback(
|
|
655
630
|
(next) => {
|
|
656
|
-
const clamped = Math.max(0, Math.min(
|
|
631
|
+
const clamped = Math.max(0, Math.min(names.length - 1, next));
|
|
657
632
|
setCursor(clamped);
|
|
658
|
-
const name =
|
|
633
|
+
const name = names[clamped];
|
|
659
634
|
if (name) onCursorChange(name);
|
|
660
635
|
},
|
|
661
|
-
[
|
|
636
|
+
[names, onCursorChange]
|
|
662
637
|
);
|
|
663
638
|
useInput(
|
|
664
639
|
(input, key) => {
|
|
@@ -675,12 +650,12 @@ var SessionListPanel = ({
|
|
|
675
650
|
return;
|
|
676
651
|
}
|
|
677
652
|
if (key.return || key.rightArrow) {
|
|
678
|
-
const name =
|
|
653
|
+
const name = names[clampedCursor];
|
|
679
654
|
if (name) onSelect(name);
|
|
680
655
|
return;
|
|
681
656
|
}
|
|
682
657
|
if (input === "d" && onDeleteSession) {
|
|
683
|
-
const name =
|
|
658
|
+
const name = names[clampedCursor];
|
|
684
659
|
if (name) onDeleteSession(name);
|
|
685
660
|
return;
|
|
686
661
|
}
|
|
@@ -698,19 +673,19 @@ var SessionListPanel = ({
|
|
|
698
673
|
"(",
|
|
699
674
|
clampedCursor + 1,
|
|
700
675
|
"/",
|
|
701
|
-
|
|
676
|
+
names.length,
|
|
702
677
|
")"
|
|
703
678
|
] })
|
|
704
679
|
] }),
|
|
705
|
-
/* @__PURE__ */ jsx3(Box3, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children: visibleSessions.map((
|
|
680
|
+
/* @__PURE__ */ jsx3(Box3, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children: visibleSessions.map((session, i) => {
|
|
706
681
|
const globalIndex = scrollOffset + i;
|
|
707
682
|
const isHighlighted = globalIndex === clampedCursor;
|
|
708
|
-
const isCurrent = name === currentSession;
|
|
683
|
+
const isCurrent = session.name === currentSession;
|
|
709
684
|
return /* @__PURE__ */ jsxs(Box3, { paddingLeft: 1, gap: 1, children: [
|
|
710
685
|
/* @__PURE__ */ jsx3(Text3, { color: isHighlighted ? "green" : void 0, children: isHighlighted ? "\u25B6" : " " }),
|
|
711
|
-
/* @__PURE__ */ jsx3(Text3, { color: isHighlighted ? "green" : "cyan", bold: isHighlighted, wrap: "truncate", children: name }),
|
|
686
|
+
/* @__PURE__ */ jsx3(Text3, { color: isHighlighted ? "green" : "cyan", bold: isHighlighted, wrap: "truncate", children: session.path ? formatCwd(session.path) : session.name }),
|
|
712
687
|
isCurrent && /* @__PURE__ */ jsx3(Text3, { color: "yellow", children: "(cwd)" })
|
|
713
|
-
] }, name);
|
|
688
|
+
] }, session.name);
|
|
714
689
|
}) })
|
|
715
690
|
] });
|
|
716
691
|
};
|
|
@@ -1118,32 +1093,33 @@ var POLL_INTERVAL = 3e3;
|
|
|
1118
1093
|
var ManagerView = ({
|
|
1119
1094
|
actions,
|
|
1120
1095
|
currentSession,
|
|
1121
|
-
currentCwd,
|
|
1122
1096
|
directories,
|
|
1123
1097
|
restoredPrompt,
|
|
1124
|
-
restoredSession
|
|
1098
|
+
restoredSession,
|
|
1099
|
+
restoredCwd
|
|
1125
1100
|
}) => {
|
|
1126
1101
|
const { rows, columns } = useTerminalSize();
|
|
1127
|
-
const [
|
|
1102
|
+
const [sessionsState, setSessionsState] = useState5({
|
|
1103
|
+
sessions: [],
|
|
1104
|
+
isLoading: true
|
|
1105
|
+
});
|
|
1128
1106
|
const [mode, setMode] = useState5(restoredPrompt ? MODE.confirm : MODE.split);
|
|
1129
1107
|
const [focus, setFocus] = useState5(FOCUS.left);
|
|
1130
1108
|
const [selectedSession, setSelectedSession] = useState5(restoredSession);
|
|
1131
1109
|
const [pendingPrompt, setPendingPrompt] = useState5(restoredPrompt ?? "");
|
|
1132
1110
|
const [pendingDeleteSession, setPendingDeleteSession] = useState5(void 0);
|
|
1133
|
-
const sessionCwdMap = useRef2(/* @__PURE__ */ new Map());
|
|
1134
1111
|
const refresh = useCallback3(async () => {
|
|
1135
1112
|
try {
|
|
1136
|
-
const
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
}
|
|
1143
|
-
}
|
|
1144
|
-
setFetchState({ data: [...missing, ...groups], isLoading: false });
|
|
1113
|
+
const fetched = await actions.fetchSessions();
|
|
1114
|
+
setSessionsState((prev) => {
|
|
1115
|
+
const fetchedNames = new Set(fetched.map((s) => s.name));
|
|
1116
|
+
const userOnly = prev.sessions.filter(
|
|
1117
|
+
(s) => !fetchedNames.has(s.name) && s.groups.length === 0
|
|
1118
|
+
);
|
|
1119
|
+
return { sessions: [...userOnly, ...fetched], isLoading: false };
|
|
1120
|
+
});
|
|
1145
1121
|
} catch {
|
|
1146
|
-
|
|
1122
|
+
setSessionsState((prev) => ({ ...prev, isLoading: false }));
|
|
1147
1123
|
}
|
|
1148
1124
|
}, [actions]);
|
|
1149
1125
|
useEffect2(() => {
|
|
@@ -1155,30 +1131,34 @@ var ManagerView = ({
|
|
|
1155
1131
|
clearInterval(timer);
|
|
1156
1132
|
};
|
|
1157
1133
|
}, [refresh]);
|
|
1158
|
-
const resolvedSession = selectedSession ??
|
|
1159
|
-
const
|
|
1160
|
-
() =>
|
|
1161
|
-
[
|
|
1134
|
+
const resolvedSession = selectedSession ?? sessionsState.sessions[0]?.name;
|
|
1135
|
+
const selectedManagedSession = useMemo5(
|
|
1136
|
+
() => sessionsState.sessions.find((s) => s.name === resolvedSession),
|
|
1137
|
+
[sessionsState.sessions, resolvedSession]
|
|
1162
1138
|
);
|
|
1139
|
+
const selectedGroup = useMemo5(() => {
|
|
1140
|
+
if (!selectedManagedSession) return void 0;
|
|
1141
|
+
return {
|
|
1142
|
+
sessionName: selectedManagedSession.name,
|
|
1143
|
+
tabs: selectedManagedSession.groups.flatMap((g) => g.tabs)
|
|
1144
|
+
};
|
|
1145
|
+
}, [selectedManagedSession]);
|
|
1163
1146
|
const handleOpenAddSession = useCallback3(() => {
|
|
1164
1147
|
setMode(MODE.addSession);
|
|
1165
1148
|
}, []);
|
|
1166
|
-
const handleAddSessionSelect = useCallback3(
|
|
1167
|
-
(path)
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
},
|
|
1180
|
-
[fetchState.data]
|
|
1181
|
-
);
|
|
1149
|
+
const handleAddSessionSelect = useCallback3((path) => {
|
|
1150
|
+
const name = basename2(path);
|
|
1151
|
+
setSessionsState((prev) => {
|
|
1152
|
+
const exists2 = prev.sessions.some((s) => s.name === name);
|
|
1153
|
+
if (exists2) return prev;
|
|
1154
|
+
return {
|
|
1155
|
+
...prev,
|
|
1156
|
+
sessions: [{ name, path, groups: [] }, ...prev.sessions]
|
|
1157
|
+
};
|
|
1158
|
+
});
|
|
1159
|
+
setSelectedSession(name);
|
|
1160
|
+
setMode(MODE.split);
|
|
1161
|
+
}, []);
|
|
1182
1162
|
const handleCancelAddSession = useCallback3(() => {
|
|
1183
1163
|
setMode(MODE.split);
|
|
1184
1164
|
}, []);
|
|
@@ -1188,31 +1168,43 @@ var ManagerView = ({
|
|
|
1188
1168
|
}, []);
|
|
1189
1169
|
const handleConfirmDelete = useCallback3(() => {
|
|
1190
1170
|
if (!pendingDeleteSession) return;
|
|
1191
|
-
|
|
1171
|
+
const session = sessionsState.sessions.find((s) => s.name === pendingDeleteSession);
|
|
1172
|
+
setSessionsState((prev) => ({
|
|
1173
|
+
...prev,
|
|
1174
|
+
sessions: prev.sessions.filter((s) => s.name !== pendingDeleteSession)
|
|
1175
|
+
}));
|
|
1192
1176
|
if (resolvedSession === pendingDeleteSession) {
|
|
1193
1177
|
setSelectedSession(void 0);
|
|
1194
1178
|
}
|
|
1195
|
-
|
|
1179
|
+
if (session) {
|
|
1180
|
+
const killAll = Promise.all(
|
|
1181
|
+
session.groups.map((g) => swallow(() => actions.killSession(g.sessionName)))
|
|
1182
|
+
);
|
|
1183
|
+
void killAll.then(() => void refresh());
|
|
1184
|
+
}
|
|
1196
1185
|
setPendingDeleteSession(void 0);
|
|
1197
1186
|
setMode(MODE.split);
|
|
1198
|
-
}, [pendingDeleteSession, resolvedSession, actions, refresh]);
|
|
1187
|
+
}, [pendingDeleteSession, resolvedSession, sessionsState.sessions, actions, refresh]);
|
|
1199
1188
|
const handleCancelDelete = useCallback3(() => {
|
|
1200
1189
|
setPendingDeleteSession(void 0);
|
|
1201
1190
|
setMode(MODE.split);
|
|
1202
1191
|
}, []);
|
|
1203
1192
|
const handleNewSession = useCallback3(
|
|
1204
1193
|
(sessionName) => {
|
|
1205
|
-
|
|
1194
|
+
const cwd = selectedManagedSession?.path;
|
|
1195
|
+
if (!cwd) return;
|
|
1196
|
+
actions.openEditor(sessionName, cwd);
|
|
1206
1197
|
},
|
|
1207
|
-
[actions]
|
|
1198
|
+
[actions, selectedManagedSession]
|
|
1208
1199
|
);
|
|
1209
1200
|
const handleConfirmNew = useCallback3(() => {
|
|
1210
1201
|
if (!resolvedSession) return;
|
|
1211
|
-
const cwd =
|
|
1202
|
+
const cwd = restoredCwd ?? selectedManagedSession?.path;
|
|
1203
|
+
if (!cwd) return;
|
|
1212
1204
|
void actions.createSession(resolvedSession, cwd, pendingPrompt).then(() => void refresh());
|
|
1213
1205
|
setPendingPrompt("");
|
|
1214
1206
|
setMode(MODE.split);
|
|
1215
|
-
}, [resolvedSession,
|
|
1207
|
+
}, [resolvedSession, restoredCwd, selectedManagedSession, pendingPrompt, actions, refresh]);
|
|
1216
1208
|
const handleCancelConfirm = useCallback3(() => {
|
|
1217
1209
|
setPendingPrompt("");
|
|
1218
1210
|
setMode(MODE.split);
|
|
@@ -1226,7 +1218,7 @@ var ManagerView = ({
|
|
|
1226
1218
|
}, []);
|
|
1227
1219
|
const handleNavigate = useCallback3(
|
|
1228
1220
|
(up) => {
|
|
1229
|
-
actions.
|
|
1221
|
+
void actions.navigateToPane(up);
|
|
1230
1222
|
},
|
|
1231
1223
|
[actions]
|
|
1232
1224
|
);
|
|
@@ -1252,7 +1244,7 @@ var ManagerView = ({
|
|
|
1252
1244
|
},
|
|
1253
1245
|
[actions]
|
|
1254
1246
|
);
|
|
1255
|
-
if (
|
|
1247
|
+
if (sessionsState.isLoading) {
|
|
1256
1248
|
return /* @__PURE__ */ jsxs7(Box10, { flexDirection: "column", height: rows, children: [
|
|
1257
1249
|
/* @__PURE__ */ jsx10(Header, { title: `${APP_TITLE} v${APP_VERSION}` }),
|
|
1258
1250
|
/* @__PURE__ */ jsx10(StatusBar, { message: "Loading...", type: "info" })
|
|
@@ -1269,8 +1261,11 @@ var ManagerView = ({
|
|
|
1269
1261
|
);
|
|
1270
1262
|
}
|
|
1271
1263
|
if (mode === MODE.deleteSession && pendingDeleteSession) {
|
|
1272
|
-
const
|
|
1273
|
-
const paneCount =
|
|
1264
|
+
const deleteSession = sessionsState.sessions.find((s) => s.name === pendingDeleteSession);
|
|
1265
|
+
const paneCount = deleteSession?.groups.reduce(
|
|
1266
|
+
(sum, g) => sum + g.tabs.reduce((s, t) => s + t.panes.length, 0),
|
|
1267
|
+
0
|
|
1268
|
+
) ?? 0;
|
|
1274
1269
|
return /* @__PURE__ */ jsx10(
|
|
1275
1270
|
DeleteSessionView,
|
|
1276
1271
|
{
|
|
@@ -1308,7 +1303,7 @@ var ManagerView = ({
|
|
|
1308
1303
|
children: /* @__PURE__ */ jsx10(
|
|
1309
1304
|
SessionListPanel,
|
|
1310
1305
|
{
|
|
1311
|
-
|
|
1306
|
+
sessions: sessionsState.sessions,
|
|
1312
1307
|
currentSession,
|
|
1313
1308
|
isFocused: focus === FOCUS.left,
|
|
1314
1309
|
availableRows: panelHeight,
|
|
@@ -1355,21 +1350,35 @@ var createTuiCommand = ({ usecases, services, infra }) => async () => {
|
|
|
1355
1350
|
let instance;
|
|
1356
1351
|
let pendingPrompt;
|
|
1357
1352
|
let pendingSession;
|
|
1353
|
+
let pendingCwd;
|
|
1358
1354
|
const actions = {
|
|
1359
1355
|
fetchSessions: async () => {
|
|
1360
1356
|
const result = await usecases.manager.list();
|
|
1361
|
-
|
|
1362
|
-
result.sessionGroups.map(async (group) =>
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1357
|
+
const resolved = await Promise.all(
|
|
1358
|
+
result.sessionGroups.map(async (group) => {
|
|
1359
|
+
const enrichedGroup = {
|
|
1360
|
+
sessionName: group.sessionName,
|
|
1361
|
+
tabs: await Promise.all(
|
|
1362
|
+
group.tabs.map(async (tab) => ({
|
|
1363
|
+
windowIndex: tab.windowIndex,
|
|
1364
|
+
windowName: tab.windowName,
|
|
1365
|
+
panes: await Promise.all(
|
|
1366
|
+
tab.panes.map((up) => usecases.manager.enrichStatus(up))
|
|
1367
|
+
)
|
|
1368
|
+
}))
|
|
1369
|
+
)
|
|
1370
|
+
};
|
|
1371
|
+
const paneCwd = group.tabs[0]?.panes[0]?.pane.cwd ?? "";
|
|
1372
|
+
const path = findMatchingDirectory(paneCwd, directories) ?? paneCwd;
|
|
1373
|
+
return { path, group: enrichedGroup };
|
|
1374
|
+
})
|
|
1372
1375
|
);
|
|
1376
|
+
const grouped = Map.groupBy(resolved, (item) => item.path || item.group.sessionName);
|
|
1377
|
+
return [...grouped.entries()].map(([key, items]) => ({
|
|
1378
|
+
name: basename3(key) || items[0].group.sessionName,
|
|
1379
|
+
path: items[0].path,
|
|
1380
|
+
groups: items.map((item) => item.group)
|
|
1381
|
+
}));
|
|
1373
1382
|
},
|
|
1374
1383
|
createSession: async (sessionName, cwd, prompt) => {
|
|
1375
1384
|
await usecases.manager.createSession({ sessionName, cwd, prompt });
|
|
@@ -1386,33 +1395,41 @@ var createTuiCommand = ({ usecases, services, infra }) => async () => {
|
|
|
1386
1395
|
unhighlightWindow: async (up) => {
|
|
1387
1396
|
await usecases.manager.unhighlightWindow(up);
|
|
1388
1397
|
},
|
|
1389
|
-
openEditor: (sessionName) => {
|
|
1398
|
+
openEditor: (sessionName, cwd) => {
|
|
1390
1399
|
instance.unmount();
|
|
1391
1400
|
const prompt = infra.editor.open();
|
|
1392
1401
|
pendingPrompt = prompt;
|
|
1393
1402
|
pendingSession = sessionName;
|
|
1403
|
+
pendingCwd = cwd;
|
|
1394
1404
|
instance = renderApp();
|
|
1395
1405
|
return prompt;
|
|
1396
1406
|
},
|
|
1397
|
-
|
|
1407
|
+
navigateToPane: async (up) => {
|
|
1408
|
+
const target = `${up.pane.sessionName}:${String(up.pane.windowIndex)}`;
|
|
1409
|
+
await infra.tmuxCli.selectWindow(target);
|
|
1410
|
+
await infra.tmuxCli.selectPane(up.pane.paneId);
|
|
1398
1411
|
instance.unmount();
|
|
1399
|
-
|
|
1412
|
+
await infra.tmuxCli.attachSession(up.pane.sessionName);
|
|
1400
1413
|
instance = renderApp();
|
|
1401
1414
|
}
|
|
1402
1415
|
};
|
|
1403
1416
|
const renderApp = () => {
|
|
1404
1417
|
const prompt = pendingPrompt;
|
|
1405
1418
|
const session = pendingSession;
|
|
1419
|
+
const cwd = pendingCwd;
|
|
1406
1420
|
pendingPrompt = void 0;
|
|
1407
1421
|
pendingSession = void 0;
|
|
1422
|
+
pendingCwd = void 0;
|
|
1423
|
+
const rawCwd = process.cwd();
|
|
1424
|
+
const currentSession = basename3(findMatchingDirectory(rawCwd, directories) ?? rawCwd);
|
|
1408
1425
|
return render(
|
|
1409
1426
|
createElement(ManagerView, {
|
|
1410
1427
|
actions,
|
|
1411
|
-
currentSession
|
|
1412
|
-
currentCwd: process.cwd(),
|
|
1428
|
+
currentSession,
|
|
1413
1429
|
directories,
|
|
1414
1430
|
restoredPrompt: prompt,
|
|
1415
|
-
restoredSession: session
|
|
1431
|
+
restoredSession: session,
|
|
1432
|
+
restoredCwd: cwd
|
|
1416
1433
|
}),
|
|
1417
1434
|
{ concurrent: true }
|
|
1418
1435
|
);
|
|
@@ -1470,10 +1487,11 @@ var createListCommand = ({ usecases }) => async () => {
|
|
|
1470
1487
|
console.log("No sessions found.");
|
|
1471
1488
|
return;
|
|
1472
1489
|
}
|
|
1473
|
-
|
|
1490
|
+
const lines = result.sessionGroups.map((group) => {
|
|
1474
1491
|
const paneCount = group.tabs.reduce((sum, t) => sum + t.panes.length, 0);
|
|
1475
|
-
|
|
1476
|
-
}
|
|
1492
|
+
return `${group.sessionName} (${String(paneCount)} panes)`;
|
|
1493
|
+
});
|
|
1494
|
+
console.log(lines.join("\n"));
|
|
1477
1495
|
};
|
|
1478
1496
|
|
|
1479
1497
|
// src/cli/index.ts
|