Files
paseo/packages/app/src/hooks/use-status-mode-workspaces.ts
T
Mohamed BoudraandGitHub a743a22496 Keep large sidebars responsive (#1966)
* perf(app): keep large sidebars responsive

Sidebar rows rescanned every agent for each session update, multiplying work with both workspace and agent count. Build workspace activity once and share the derived sidebar model so updates scale with the underlying collections.

* test(app): use valid agent fixture for workspace restore

* refactor(app): keep sidebar model surface focused
2026-07-10 06:12:56 +08:00

101 lines
3.1 KiB
TypeScript

import { useMemo } from "react";
import { useStoreWithEqualityFn } from "zustand/traditional";
import { useCreateFlowStore } from "@/stores/create-flow-store";
import { useSessionStore, type SessionState } from "@/stores/session-store";
import {
buildSidebarStatusWorkspacePlacements,
type SidebarStatusWorkspacePlacement,
type SidebarWorkspacePlacement,
} from "./use-sidebar-workspaces-list";
const EMPTY_WORKSPACES: SidebarStatusWorkspacePlacement[] = [];
const EMPTY_STATUS_SESSIONS: StatusModeSession[] = [];
const EMPTY_PENDING_CREATE_ATTEMPTS: ReturnType<
typeof useCreateFlowStore.getState
>["pendingByDraftId"] = {};
interface StatusModeSessionSource {
workspaces: SessionState["workspaces"];
workspaceAgentActivity: SessionState["workspaceAgentActivity"];
}
export interface StatusModeSession {
serverId: string;
workspaces: SessionState["workspaces"];
workspaceAgentActivity: SessionState["workspaceAgentActivity"];
}
export function selectStatusModeSessions(
sessions: Record<string, StatusModeSessionSource | undefined>,
serverIds: readonly string[],
): StatusModeSession[] {
const statusSessions: StatusModeSession[] = [];
for (const serverId of serverIds) {
const session = sessions[serverId];
if (!session) {
continue;
}
statusSessions.push({
serverId,
workspaces: session.workspaces,
workspaceAgentActivity: session.workspaceAgentActivity,
});
}
return statusSessions;
}
export function areStatusModeSessionsEqual(
left: readonly StatusModeSession[],
right: readonly StatusModeSession[],
): boolean {
if (left.length !== right.length) {
return false;
}
for (let index = 0; index < left.length; index += 1) {
const leftSession = left[index];
const rightSession = right[index];
if (
!leftSession ||
!rightSession ||
leftSession.serverId !== rightSession.serverId ||
leftSession.workspaces !== rightSession.workspaces ||
leftSession.workspaceAgentActivity !== rightSession.workspaceAgentActivity
) {
return false;
}
}
return true;
}
export function useStatusModeWorkspacePlacements(input: {
placements: SidebarWorkspacePlacement[];
enabled?: boolean;
}): SidebarStatusWorkspacePlacement[] {
const isEnabled = input.enabled !== false && input.placements.length > 0;
const serverIds = useMemo(
() => Array.from(new Set(input.placements.map((placement) => placement.serverId))),
[input.placements],
);
const statusSessions = useStoreWithEqualityFn(
useSessionStore,
(state) =>
isEnabled ? selectStatusModeSessions(state.sessions, serverIds) : EMPTY_STATUS_SESSIONS,
areStatusModeSessionsEqual,
);
const pendingCreateAttempts = useCreateFlowStore((state) =>
isEnabled ? state.pendingByDraftId : EMPTY_PENDING_CREATE_ATTEMPTS,
);
return useMemo(() => {
if (!isEnabled) {
return EMPTY_WORKSPACES;
}
return buildSidebarStatusWorkspacePlacements({
placements: input.placements,
sessions: statusSessions,
pendingCreateAttempts,
});
}, [input.placements, isEnabled, pendingCreateAttempts, statusSessions]);
}