fix(ui): collapse session lists and scope selection controls to panels

This commit is contained in:
Codex
2026-09-13 18:12:27 +02:00
parent 571a4bcaa2
commit cf90c1bd51
11 changed files with 119 additions and 47 deletions
+37 -35
View File
@@ -256,7 +256,7 @@ export function AppShell({ canLogout }: AppShellProps) {
const contextLocked = interactionBusy || creatingSession || administrationBusy
|| preprocessingQuery.data?.state === "running";
const [adminNavigationValue, setAdminNavigationValue] = useState<string[]>([]);
const [activeOpen, setActiveOpen] = useState(true);
const [activeOpen, setActiveOpen] = useState(false);
const [archiveOpen, setArchiveOpen] = useState(false);
const [renameTarget, setRenameTarget] = useState<SessionSummary | null>(null);
const [deleteTargets, setDeleteTargets] = useState<SessionSummary[]>([]);
@@ -326,8 +326,39 @@ export function AppShell({ canLogout }: AppShellProps) {
});
}
function toggleAllSessions(selected: boolean) {
setSelectedSessionIds(selected ? new Set(sessions.map((session) => session.id)) : new Set());
function sessionSelectionControls(list: SessionSummary[]) {
if (list.length === 0) return null;
const selected = list.filter((session) => selectedSessionIds.has(session.id));
return (
<div className="flex items-center justify-between gap-2 px-2 pb-2">
<label className="flex cursor-pointer items-center gap-2 text-xs font-medium text-muted-foreground hover:text-foreground">
<Checkbox
checked={selected.length === list.length}
indeterminate={selected.length > 0 && selected.length < list.length}
onCheckedChange={(checked) => setSelectedSessionIds((current) => {
const next = new Set(current);
for (const session of list) {
if (checked) next.add(session.id);
else next.delete(session.id);
}
return next;
})}
/>
<span>{t("Select all")}</span>
</label>
{selected.length > 0 && (
<Button
variant="destructive"
size="xs"
aria-label={t("Delete {count} selected sessions", { count: selected.length })}
onClick={() => requestDelete(selected)}
>
<Trash2 />
{t("Delete ({count})", { count: selected.length })}
</Button>
)}
</div>
);
}
function handleSessionScopeTabKeyDown(
@@ -1124,42 +1155,11 @@ export function AppShell({ canLogout }: AppShellProps) {
className="flex min-h-0 flex-1 flex-col"
>
{canReadAllSessions && showingAllSessions && (
<p className="mx-4 mb-2 mt-2 text-xs font-medium text-muted-foreground">
<p className="sr-only">
{t("Administrator view: all sessions")}
</p>
)}
{/* L1 — rail title */}
<div className="px-4 pb-1.5 pt-1">
<span className="text-sm font-semibold text-foreground">
{t("Sessions")}
</span>
</div>
<div className="flex items-center justify-between px-4 pb-2">
<label className="flex cursor-pointer items-center gap-2 text-xs font-medium text-muted-foreground hover:text-foreground">
<Checkbox
checked={allSessionsSelected}
aria-label={t("Select all sessions")}
disabled={sessions.length === 0}
onCheckedChange={(selected) => toggleAllSessions(selected === true)}
/>
<span>{t("Select all")}</span>
</label>
{selectedSessions.length > 0 && (
<Button
variant="destructive"
size="xs"
aria-label={t("Delete {count} selected sessions", { count: selectedSessions.length })}
onClick={() => {
requestDelete(selectedSessions);
}}
>
<Trash2 />
{t("Delete ({count})", { count: selectedSessions.length })}
</Button>
)}
</div>
<Accordion.Root multiple
value={[...(activeOpen ? ["active"] : []), ...(archiveOpen ? ["archive"] : [])]}
onValueChange={value => { setActiveOpen(value.includes("active")); setArchiveOpen(value.includes("archive")); }}
@@ -1172,6 +1172,7 @@ export function AppShell({ canLogout }: AppShellProps) {
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Panel className="thot-session-accordion__panel" tabIndex={0}>
{sessionSelectionControls(activeList)}
<div className="flex flex-col gap-3 pb-2">
{groups.map((g) => (
<div key={g}>
@@ -1233,6 +1234,7 @@ export function AppShell({ canLogout }: AppShellProps) {
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Panel className="thot-session-accordion__panel" tabIndex={0}>
{sessionSelectionControls(archivedList)}
<NavSessions
sessions={archivedList}
activeSessionId={activeSessionId}