From 976b4997072513d93f78112c663495e677886eca Mon Sep 17 00:00:00 2001 From: Raing5Days Date: Sat, 7 Nov 2026 19:11:53 +0800 Subject: [PATCH] perf(nav-panel): optimize workspace session hierarchy indexing and partitioning --- .../sections/sessions/sessionNavHierarchy.ts | 49 ++++++++++++++----- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/src/web-ui/src/app/components/NavPanel/sections/sessions/sessionNavHierarchy.ts b/src/web-ui/src/app/components/NavPanel/sections/sessions/sessionNavHierarchy.ts index dd5604c043..e141b717e6 100644 --- a/src/web-ui/src/app/components/NavPanel/sections/sessions/sessionNavHierarchy.ts +++ b/src/web-ui/src/app/components/NavPanel/sections/sessions/sessionNavHierarchy.ts @@ -1,27 +1,50 @@ import type { Session } from '@/flow_chat/types/flow-chat'; import { resolveSessionRelationship } from '@/flow_chat/utils/sessionMetadata'; -import { sessionBelongsToWorkspaceNavRow } from '@/flow_chat/utils/sessionOrdering'; +import { sessionOwningWorkspaceId } from '@/flow_chat/utils/sessionOrdering'; + +/** Fast parent-id resolver: avoid full relationship normalization for ordinary normal sessions. */ +function resolveParentSessionId(session: Session): string | undefined { + if (session.sessionKind === 'normal' && !session.parentSessionId && !session.btwOrigin) { + return undefined; + } + return resolveSessionRelationship(session).parentSessionId; +} /** Build the same workspace-owned hierarchy for both rows and their count. */ export function getWorkspaceSessionHierarchy( sessions: Iterable, workspaceIds: readonly string[], ): { topLevelSessions: Session[]; childrenByParent: Map } { - const scoped = Array.from(sessions).filter(session => { - if (session.isTransient || session.sessionKind === 'subagent') return false; - return workspaceIds.length - ? workspaceIds.some(workspaceId => sessionBelongsToWorkspaceNavRow(session, workspaceId)) - : !session.workspaceId; - }); - const knownIds = new Set(scoped.map(session => session.sessionId)); + const hasWorkspaceScope = workspaceIds.length > 0; + const workspaceIdSet = hasWorkspaceScope ? new Set(workspaceIds) : null; + const scoped: Session[] = []; + const knownIds = new Set(); + + for (const session of sessions) { + if (session.isTransient || session.sessionKind === 'subagent') continue; + const ownerId = sessionOwningWorkspaceId(session); + const inScope = hasWorkspaceScope + ? (ownerId ? workspaceIdSet!.has(ownerId) : false) + : !ownerId; + if (!inScope) continue; + scoped.push(session); + knownIds.add(session.sessionId); + } + const topLevelSessions: Session[] = []; const childrenByParent = new Map(); - for (const session of scoped) { - const parentId = resolveSessionRelationship(session).parentSessionId; + // Resolve each parent link once, then bucket children and top-level rows in a + // single pass without intermediate arrays. + for (let i = 0; i < scoped.length; i++) { + const session = scoped[i]; + const parentId = resolveParentSessionId(session); if (parentId && knownIds.has(parentId)) { - const children = childrenByParent.get(parentId) ?? []; - children.push(session); - childrenByParent.set(parentId, children); + const children = childrenByParent.get(parentId); + if (children) { + children.push(session); + } else { + childrenByParent.set(parentId, [session]); + } } else { topLevelSessions.push(session); }