Skip to content

Commit af562da

Browse files
committed
Add session bulk close actions
1 parent 9e0d356 commit af562da

4 files changed

Lines changed: 40 additions & 2 deletions

File tree

extensions/builtin/sessions/renderer/GlobalSessionsSidebar.tsx

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ interface Props {
1919
onSelectSession(id: string): void;
2020
onOpenWorkspace(id: string): void;
2121
onCloseSession(id: string): void;
22+
onCloseSessions(ids: string[]): void;
2223
onRenameSession(id: string, name: string): void;
2324
onRestartSession(id: string): void;
2425
onDuplicateSession(id: string): void;
@@ -27,7 +28,7 @@ interface Props {
2728
onDetachSession(id: string): void;
2829
}
2930

30-
export function GlobalSessionsSidebar({ workspaces, activeWorkspaceId, activeSessionId, highlightedSessionIds, sessions, profiles, defaultProfileId, emptySessionsVisible, showSessionCwdForAll, onCreateSession, onSelectSession, onOpenWorkspace, onCloseSession, onRenameSession, onRestartSession, onDuplicateSession, onSetCwd, onSplitSession, onDetachSession }: Props) {
31+
export function GlobalSessionsSidebar({ workspaces, activeWorkspaceId, activeSessionId, highlightedSessionIds, sessions, profiles, defaultProfileId, emptySessionsVisible, showSessionCwdForAll, onCreateSession, onSelectSession, onOpenWorkspace, onCloseSession, onCloseSessions, onRenameSession, onRestartSession, onDuplicateSession, onSetCwd, onSplitSession, onDetachSession }: Props) {
3132
const [menuOpen, setMenuOpen] = useState(false);
3233
const [menuPosition, setMenuPosition] = useState<{ x: number; y: number; width: number } | null>(null);
3334
const [actionMenu, setActionMenu] = useState<{ session: WorkspaceTerminalSession; x: number; y: number } | null>(null);
@@ -123,7 +124,7 @@ export function GlobalSessionsSidebar({ workspaces, activeWorkspaceId, activeSes
123124

124125
function openActionMenu(session: WorkspaceTerminalSession, x: number, y: number) {
125126
const menuWidth = 190;
126-
const menuHeight = 250;
127+
const menuHeight = 340;
127128
setActionMenu({
128129
session,
129130
x: Math.min(Math.max(8, x), window.innerWidth - menuWidth - 8),
@@ -136,6 +137,27 @@ export function GlobalSessionsSidebar({ workspaces, activeWorkspaceId, activeSes
136137
action();
137138
}
138139

140+
function sessionGroupFor(session: WorkspaceTerminalSession) {
141+
return sessions.filter((item) => item.workspaceId === session.workspaceId);
142+
}
143+
144+
function closeAbove(session: WorkspaceTerminalSession) {
145+
const group = sessionGroupFor(session);
146+
const index = group.findIndex((item) => item.id === session.id);
147+
if (index > 0) onCloseSessions(group.slice(0, index).map((item) => item.id));
148+
}
149+
150+
function closeBelow(session: WorkspaceTerminalSession) {
151+
const group = sessionGroupFor(session);
152+
const index = group.findIndex((item) => item.id === session.id);
153+
if (index >= 0 && index < group.length - 1) onCloseSessions(group.slice(index + 1).map((item) => item.id));
154+
}
155+
156+
function closeOthers(session: WorkspaceTerminalSession) {
157+
const group = sessionGroupFor(session).filter((item) => item.id !== session.id);
158+
if (group.length) onCloseSessions(group.map((item) => item.id));
159+
}
160+
139161
function renderSession(session: WorkspaceTerminalSession, index: number) {
140162
const active = session.id === activeSessionId;
141163
const highlighted = highlightedSessionIds.includes(session.id);
@@ -185,6 +207,12 @@ export function GlobalSessionsSidebar({ workspaces, activeWorkspaceId, activeSes
185207
return group.map(renderSession);
186208
}
187209

210+
const actionMenuGroup = actionMenu ? sessionGroupFor(actionMenu.session) : [];
211+
const actionMenuIndex = actionMenu ? actionMenuGroup.findIndex((session) => session.id === actionMenu.session.id) : -1;
212+
const canCloseAbove = actionMenuIndex > 0;
213+
const canCloseBelow = actionMenuIndex >= 0 && actionMenuIndex < actionMenuGroup.length - 1;
214+
const canCloseOthers = actionMenuGroup.length >= 2;
215+
188216
const createMenu = menuOpen && menuPosition ? createPortal(
189217
<div ref={createMenuRef} className="new-session-menu session-create-menu" role="menu" style={{ left: menuPosition.x, top: menuPosition.y, width: menuPosition.width }}>
190218
{flatWorkspace ? (
@@ -242,6 +270,9 @@ export function GlobalSessionsSidebar({ workspaces, activeWorkspaceId, activeSes
242270
<button className="context-menu-item" onClick={() => runAction(() => onSplitSession(actionMenu.session.id, 'right'))}>Split Right</button>
243271
<button className="context-menu-item" onClick={() => runAction(() => onSplitSession(actionMenu.session.id, 'down'))}>Split Down</button>
244272
{actionMenu.session.splitGroupId ? <button className="context-menu-item" onClick={() => runAction(() => onDetachSession(actionMenu.session.id))}>Detach from Split</button> : null}
273+
{canCloseBelow ? <button className="context-menu-item danger" onClick={() => runAction(() => closeBelow(actionMenu.session))}>Close Below</button> : null}
274+
{canCloseAbove ? <button className="context-menu-item danger" onClick={() => runAction(() => closeAbove(actionMenu.session))}>Close Above</button> : null}
275+
{canCloseOthers ? <button className="context-menu-item danger" onClick={() => runAction(() => closeOthers(actionMenu.session))}>Close Others</button> : null}
245276
<button className="context-menu-item danger" onClick={() => runAction(() => onCloseSession(actionMenu.session.id))}>Close</button>
246277
</div>
247278
) : null}

extensions/builtin/sessions/renderer/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export const sessionsExtension: NativeExtension = {
2626
onSelectSession={ctx.actions.selectSession}
2727
onOpenWorkspace={ctx.sessionActions.openWorkspace}
2828
onCloseSession={ctx.sessionActions.close}
29+
onCloseSessions={ctx.sessionActions.closeMany}
2930
onRenameSession={ctx.sessionActions.rename}
3031
onRestartSession={ctx.sessionActions.restart}
3132
onDuplicateSession={ctx.sessionActions.duplicate}

src/components/workspace/WorkspaceShell.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1217,6 +1217,10 @@ export function WorkspaceShell({ workspace, onBack, onUpdateWorkspace, workspace
12171217
await sessionStore.closeSession(id);
12181218
}
12191219

1220+
async function closeTerminals(ids: string[]) {
1221+
for (const id of ids) await sessionStore.closeSession(id);
1222+
}
1223+
12201224
function showGitError(error: unknown) {
12211225
setGitError(error instanceof Error ? error.message : String(error));
12221226
}
@@ -1603,6 +1607,7 @@ export function WorkspaceShell({ workspace, onBack, onUpdateWorkspace, workspace
16031607
create: async (target, profileId) => { const setup = automation?.workspaces[target.id]; const profile = profiles.find((item) => item.id === profileId); const startupCommand = resolveTerminalStartupCommand({ profileStartupCommand: profile?.startupCommand, workspaceStartupCommand: setup?.newSessionCommand }); await sessionStore.createSession({ workspaceId: target.id, workspaceName: target.name, workspacePath: target.path, profileId, cwd: target.path, name: 'Terminal', startupCommand }); if (target.id !== workspace.id) await onOpenWorkspace(target.id); },
16041608
openWorkspace: (id) => void onOpenWorkspace(id),
16051609
close: (id) => void closeTerminal(id),
1610+
closeMany: (ids) => void closeTerminals(ids),
16061611
rename: renameTerminal,
16071612
restart: (id) => void restartTerminal(id),
16081613
duplicate: (id) => void duplicateTerminal(id),

src/extensions/extensionTypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ export interface WorkspaceExtensionContext {
7878
create(target: Workspace, profileId: string): Promise<void>;
7979
openWorkspace(id: string): void | Promise<void>;
8080
close(id: string): void | Promise<void>;
81+
closeMany(ids: string[]): void | Promise<void>;
8182
rename(id: string, name: string): void | Promise<void>;
8283
restart(id: string): void | Promise<void>;
8384
duplicate(id: string): void | Promise<void>;

0 commit comments

Comments
 (0)