Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/renderer/components/QuickActionsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,13 @@ export const QuickActionsModal = memo(function QuickActionsModal(props: QuickAct
};

const handleMoveToGroup = (groupId: string) => {
const updatedSessions = sessions.map((s) => (s.id === activeSessionId ? { ...s, groupId } : s));
const normalizedGroupId = groupId || undefined;
const updatedSessions = sessions.map((s) => {
if (s.id === activeSessionId) return { ...s, groupId: normalizedGroupId };
// Also update worktree children to keep groupId in sync
if (s.parentSessionId === activeSessionId) return { ...s, groupId: normalizedGroupId };
return s;
});
setSessions(updatedSessions);
setQuickActionOpen(false);
};
Expand Down
8 changes: 7 additions & 1 deletion src/renderer/components/SessionList/SessionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -332,8 +332,14 @@ function SessionListInner(props: SessionListProps) {

const handleMoveToGroup = useCallback(
(sessionId: string, groupId: string) => {
const normalizedGroupId = groupId || undefined;
setSessions((prev) =>
prev.map((s) => (s.id === sessionId ? { ...s, groupId: groupId || undefined } : s))
prev.map((s) => {
if (s.id === sessionId) return { ...s, groupId: normalizedGroupId };
// Also update worktree children to keep groupId in sync
if (s.parentSessionId === sessionId) return { ...s, groupId: normalizedGroupId };
return s;
})
);
},
[setSessions]
Expand Down
10 changes: 8 additions & 2 deletions src/renderer/hooks/agent/useAgentListeners.ts
Original file line number Diff line number Diff line change
Expand Up @@ -414,8 +414,14 @@ export function useAgentListeners(deps: UseAgentListenersDeps): void {
const sessionSizeBytes = logs.reduce((sum, log) => sum + (log.text?.length || 0), 0);
const sessionSizeKB = (sessionSizeBytes / 1024).toFixed(1);

const sessionGroup = currentSession.groupId
? getGroups().find((g) => g.id === currentSession.groupId)
// Resolve group: worktree children may inherit group from parent
const effectiveGroupId =
currentSession.groupId ||
(currentSession.parentSessionId
? getSessions().find((s) => s.id === currentSession.parentSessionId)?.groupId
: undefined);
const sessionGroup = effectiveGroupId
? getGroups().find((g) => g.id === effectiveGroupId)
: null;
const groupName = sessionGroup?.name || 'Ungrouped';
const projectName =
Expand Down
31 changes: 21 additions & 10 deletions src/renderer/hooks/batch/useBatchHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,25 @@ import type { RightPanelHandle } from '../../components/RightPanel';
import type { AgentSpawnResult } from '../agent/useAgentExecution';
import * as Sentry from '@sentry/electron/renderer';

/**
* Resolve the effective group name for a session, falling back to the parent's group
* for worktree children whose groupId may not be in sync.
*/
function resolveGroupName(
sessionId: string,
sessions: { id: string; groupId?: string; parentSessionId?: string }[],
groups: { id: string; name: string }[]
): string {
const session = sessions.find((s) => s.id === sessionId);
const effectiveGroupId =
session?.groupId ||
(session?.parentSessionId
? sessions.find((s) => s.id === session.parentSessionId)?.groupId
: undefined);
const group = effectiveGroupId ? groups.find((g) => g.id === effectiveGroupId) : null;
return group?.name || 'Ungrouped';
}

/**
* Find the session that is actually paused on error.
* Prefer the active session when it is paused; otherwise pick the first errorPaused session.
Expand Down Expand Up @@ -213,12 +232,8 @@ export function useBatchHandlers(deps: UseBatchHandlersDeps): UseBatchHandlersRe
} = settingsState;
const isLbRegistered = selectIsLeaderboardRegistered(settingsState);

// Find group name for the session
const session = currentSessions.find((s) => s.id === info.sessionId);
const sessionGroup = session?.groupId
? currentGroups.find((g) => g.id === session.groupId)
: null;
const groupName = sessionGroup?.name || 'Ungrouped';
const groupName = resolveGroupName(info.sessionId, currentSessions, currentGroups);

// Determine toast type and message based on completion status
const toastType = info.wasStopped
Expand Down Expand Up @@ -508,11 +523,7 @@ export function useBatchHandlers(deps: UseBatchHandlersDeps): UseBatchHandlersRe
const currentSessions = useSessionStore.getState().sessions;
const currentGroups = useSessionStore.getState().groups;

const session = currentSessions.find((s) => s.id === info.sessionId);
const sessionGroup = session?.groupId
? currentGroups.find((g) => g.id === session.groupId)
: null;
const groupName = sessionGroup?.name || 'Ungrouped';
const groupName = resolveGroupName(info.sessionId, currentSessions, currentGroups);

if (info.success) {
notifyToast({
Expand Down
14 changes: 12 additions & 2 deletions src/renderer/hooks/session/useGroupManagement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,12 @@ export function useGroupManagement(deps: UseGroupManagementDeps): UseGroupManage
(groupId: string) => {
if (draggingSessionId) {
setSessions((prev) =>
prev.map((s) => (s.id === draggingSessionId ? { ...s, groupId } : s))
prev.map((s) => {
if (s.id === draggingSessionId) return { ...s, groupId };
// Also update worktree children to keep groupId in sync
if (s.parentSessionId === draggingSessionId) return { ...s, groupId };
return s;
})
);
setDraggingSessionId(null);
}
Expand All @@ -144,7 +149,12 @@ export function useGroupManagement(deps: UseGroupManagementDeps): UseGroupManage
const handleDropOnUngrouped = useCallback(() => {
if (draggingSessionId) {
setSessions((prev) =>
prev.map((s) => (s.id === draggingSessionId ? { ...s, groupId: undefined } : s))
prev.map((s) => {
if (s.id === draggingSessionId) return { ...s, groupId: undefined };
// Also update worktree children to keep groupId in sync
if (s.parentSessionId === draggingSessionId) return { ...s, groupId: undefined };
return s;
})
);
setDraggingSessionId(null);
}
Expand Down
7 changes: 6 additions & 1 deletion src/renderer/hooks/session/useSessionCrud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,12 @@ export function useSessionCrud(deps: UseSessionCrudDeps): UseSessionCrudReturn {
(groupId: string) => {
if (pendingMoveToGroupSessionId) {
setSessions((prev) =>
prev.map((s) => (s.id === pendingMoveToGroupSessionId ? { ...s, groupId } : s))
prev.map((s) => {
if (s.id === pendingMoveToGroupSessionId) return { ...s, groupId };
// Also update worktree children to keep groupId in sync
if (s.parentSessionId === pendingMoveToGroupSessionId) return { ...s, groupId };
return s;
})
);
setPendingMoveToGroupSessionId(null);
}
Expand Down
12 changes: 11 additions & 1 deletion src/renderer/hooks/wizard/useWizardHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,17 @@ export function useWizardHandlers(deps: UseWizardHandlersDeps): UseWizardHandler
}

const currentGroups = useSessionStore.getState().groups;
const group = currentGroups.find((g) => g.id === currentSession.groupId);
// Worktree children inherit group from parent
const effectiveGroupId =
currentSession.groupId ||
(currentSession.parentSessionId
? useSessionStore
.getState()
.sessions.find((s) => s.id === currentSession.parentSessionId)?.groupId
: undefined);
const group = effectiveGroupId
? currentGroups.find((g) => g.id === effectiveGroupId)
: null;
const groupName = group?.name || 'Ungrouped';

const elapsedTimeMs = activeTab.lastSynopsisTime
Expand Down