Skip to content

Commit 919f5e4

Browse files
IM.codesclaude
andcommitted
fix: @ picker shows all agents (main + sub), marks self, files first
- AtPicker: show all sessions passed in (no mainSession prefix filter) - AtPicker: isSelf flag renders "(You)" badge on current session - AtPicker: agents include sub-sessions (deck_sub_*) - AtPicker: show labels when available, fall back to short name - SessionControls: merge main sessions + sub-sessions into agent list - SubSessionWindow: pass subSessions through to SessionControls Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent f98767c commit 919f5e4

File tree

4 files changed

+42
-19
lines changed

4 files changed

+42
-19
lines changed

web/src/app.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1280,7 +1280,7 @@ export function App() {
12801280
</div>
12811281
);
12821282
})()}
1283-
<SessionControls ws={wsRef.current} activeSession={activeSessionInfo} inputRef={inputRef} onAfterAction={focusTerminal} onSend={(_name, text) => { addOptimisticUserMessage(text); scrollActiveToBottom(); }} onStopProject={handleStopProject} onRenameSession={() => activeSession && setRenameRequest(activeSession)} sessionDisplayName={activeSessionInfo?.project ?? null} quickData={quickData} detectedModel={activeSession ? detectedModels.get(activeSession) : undefined} hideShortcuts={false} activeThinking={!!activeThinkingTs} mobileFileBrowserOpen={showMobileFileBrowser} onMobileFileBrowserClose={() => setShowMobileFileBrowser(false)} sessions={sessions} />
1283+
<SessionControls ws={wsRef.current} activeSession={activeSessionInfo} inputRef={inputRef} onAfterAction={focusTerminal} onSend={(_name, text) => { addOptimisticUserMessage(text); scrollActiveToBottom(); }} onStopProject={handleStopProject} onRenameSession={() => activeSession && setRenameRequest(activeSession)} sessionDisplayName={activeSessionInfo?.project ?? null} quickData={quickData} detectedModel={activeSession ? detectedModels.get(activeSession) : undefined} hideShortcuts={false} activeThinking={!!activeThinkingTs} mobileFileBrowserOpen={showMobileFileBrowser} onMobileFileBrowserClose={() => setShowMobileFileBrowser(false)} sessions={sessions} subSessions={subSessions.map(s => ({ sessionName: s.sessionName, type: s.type, label: s.label, state: s.state, parentSession: s.parentSession }))} />
12841284

12851285
{/* Sub-session bar */}
12861286
{selectedServerId && (
@@ -1345,6 +1345,7 @@ export function App() {
13451345
zIndex={subZIndexes.get(sub.id) ?? 1000}
13461346
onFocus={() => bringSubToFront(sub.id)}
13471347
sessions={sessions}
1348+
subSessions={subSessions.map(s => ({ sessionName: s.sessionName, type: s.type, label: s.label, state: s.state, parentSession: s.parentSession }))}
13481349
/>
13491350
))}
13501351

web/src/components/AtPicker.tsx

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ interface SessionEntry {
1111
name: string;
1212
agentType: string;
1313
state: string;
14+
label?: string | null;
15+
isSelf?: boolean;
1416
}
1517

1618
interface AtPickerProps {
@@ -27,7 +29,7 @@ interface AtPickerProps {
2729

2830
type PickerItem =
2931
| { kind: 'file'; path: string; basename: string; dir: string }
30-
| { kind: 'agent'; session: string; shortName: string; agentType: string; busy: boolean };
32+
| { kind: 'agent'; session: string; shortName: string; agentType: string; busy: boolean; isSelf: boolean };
3133

3234
const MODES = ['audit', 'review', 'brainstorm', 'discuss'] as const;
3335

@@ -118,7 +120,6 @@ const modeBtnHoverStyle: Record<string, string | number> = {
118120
export function AtPicker({
119121
query,
120122
sessions,
121-
mainSession,
122123
wsClient,
123124
projectDir,
124125
onSelectFile,
@@ -135,24 +136,23 @@ export function AtPicker({
135136
const requestIdRef = useRef<string | null>(null);
136137
const containerRef = useRef<HTMLDivElement>(null);
137138

138-
// Filter agents by mainSession prefix
139+
// Show all sessions as agents — label or short name, mark self
139140
const agents = useMemo(() => {
140-
const prefix = mainSession + '_';
141141
return sessions
142-
.filter((s) => s.name.startsWith(prefix))
143142
.map((s) => {
144143
const parts = s.name.split('_');
145-
const shortName = parts[parts.length - 1];
144+
const shortName = s.label || parts[parts.length - 1] || s.name;
146145
return {
147146
kind: 'agent' as const,
148147
session: s.name,
149148
shortName,
150149
agentType: s.agentType,
151150
busy: s.state !== 'idle',
151+
isSelf: !!s.isSelf,
152152
};
153153
})
154-
.filter((a) => !query || a.shortName.toLowerCase().includes(query.toLowerCase()));
155-
}, [sessions, mainSession, query]);
154+
.filter((a) => !query || a.shortName.toLowerCase().includes(query.toLowerCase()) || a.session.toLowerCase().includes(query.toLowerCase()));
155+
}, [sessions, query]);
156156

157157
// Build flat item list for keyboard nav
158158
const items = useMemo<PickerItem[]>(() => {
@@ -316,7 +316,12 @@ export function AtPicker({
316316
);
317317
}
318318

319-
if (items.length === 0 && !query) return null;
319+
if (items.length === 0 && query) return (
320+
<div ref={containerRef} style={containerStyle}>
321+
<div style={{ ...itemStyle, color: '#64748b', justifyContent: 'center' }}>No results</div>
322+
</div>
323+
);
324+
if (items.length === 0) return null;
320325

321326
let flatIdx = 0;
322327

@@ -365,19 +370,15 @@ export function AtPicker({
365370
>
366371
<span style={{ fontWeight: 500 }}>{a.shortName}</span>
367372
<span style={dimStyle}>{a.agentType}</span>
373+
{a.isSelf && <span style={{ ...dimStyle, color: '#60a5fa', fontSize: 10, marginLeft: 4 }}>(You)</span>}
368374
{a.busy && <span style={busyDotStyle} title="Busy" />}
369375
</div>
370376
);
371377
})}
372378
</>
373379
)}
374380

375-
{/* Empty state */}
376-
{items.length === 0 && query && (
377-
<div style={{ ...itemStyle, color: '#64748b', justifyContent: 'center' }}>
378-
No results
379-
</div>
380-
)}
381+
{/* Empty state handled above */}
381382
</div>
382383
);
383384
}

web/src/components/SessionControls.tsx

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ interface Props {
4242
onMobileFileBrowserClose?: () => void;
4343
/** All sessions — for @ picker agent list. */
4444
sessions?: SessionInfo[];
45+
/** Sub-sessions — for @ picker agent list (includes deck_sub_*). */
46+
subSessions?: Array<{ sessionName: string; type: string; label?: string | null; state: string; parentSession?: string | null }>;
4547
}
4648

4749
type MenuAction = 'restart' | 'new' | 'stop';
@@ -81,7 +83,7 @@ function loadCodexModel(): CodexModelChoice | null {
8183
return null;
8284
}
8385

84-
export function SessionControls({ ws, activeSession, inputRef, onAfterAction, onStopProject, onRenameSession, sessionDisplayName, quickData, detectedModel, hideShortcuts, onSend, onSubRestart, onSubNew, onSubStop, activeThinking, mobileFileBrowserOpen, onMobileFileBrowserClose, sessions }: Props) {
86+
export function SessionControls({ ws, activeSession, inputRef, onAfterAction, onStopProject, onRenameSession, sessionDisplayName, quickData, detectedModel, hideShortcuts, onSend, onSubRestart, onSubNew, onSubStop, activeThinking, mobileFileBrowserOpen, onMobileFileBrowserClose, sessions, subSessions }: Props) {
8587
const { t } = useTranslation();
8688
const swipeBackRef = useSwipeBack(onMobileFileBrowserClose);
8789
const [hasText, setHasText] = useState(false);
@@ -482,7 +484,24 @@ export function SessionControls({ ws, activeSession, inputRef, onAfterAction, on
482484
{atPickerOpen && ws && activeSession && (
483485
<AtPicker
484486
query={atQuery}
485-
sessions={(sessions ?? []).map(s => ({ name: s.name, agentType: s.agentType, state: s.state }))}
487+
sessions={[
488+
// Main sessions
489+
...(sessions ?? []).map(s => ({
490+
name: s.name,
491+
agentType: s.agentType,
492+
state: s.state,
493+
label: s.label ?? null,
494+
isSelf: s.name === activeSession.name,
495+
})),
496+
// Sub-sessions
497+
...(subSessions ?? []).map(s => ({
498+
name: s.sessionName,
499+
agentType: s.type,
500+
state: s.state,
501+
label: s.label ?? null,
502+
isSelf: s.sessionName === activeSession.name,
503+
})),
504+
]}
486505
mainSession={activeSession.project ? `deck_${activeSession.project}` : activeSession.name.replace(/_[^_]+$/, '')}
487506
wsClient={ws}
488507
projectDir={activeSession.projectDir ?? ''}

web/src/components/SubSessionWindow.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ interface Props {
3232
zIndex: number;
3333
onFocus: () => void;
3434
sessions?: SessionInfo[];
35+
subSessions?: Array<{ sessionName: string; type: string; label?: string | null; state: string; parentSession?: string | null }>;
3536
}
3637

3738
type ViewMode = 'terminal' | 'chat';
@@ -59,7 +60,7 @@ function saveLocal(id: string, geom: WindowGeometry, viewMode: ViewMode) {
5960
}
6061

6162
export function SubSessionWindow({
62-
sub, ws, connected, onDiff, onHistory, onMinimize, onClose, onRestart, onRename, zIndex, onFocus, sessions,
63+
sub, ws, connected, onDiff, onHistory, onMinimize, onClose, onRestart, onRename, zIndex, onFocus, sessions, subSessions,
6364
}: Props) {
6465
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
6566
const swipeBackRef = useSwipeBack(isMobile ? onMinimize : null);
@@ -359,6 +360,7 @@ export function SubSessionWindow({
359360
sessionDisplayName={sub.label ?? agentTag}
360361
activeThinking={!!activeThinkingTs}
361362
sessions={sessions}
363+
subSessions={subSessions}
362364
/>
363365
</div>
364366
</div>

0 commit comments

Comments
 (0)