Eliminate sidebar re-render cascades by fixing both sides of the subscription contract: - Session-store writes preserve identity: setWorkspaces, mergeWorkspaces, removeWorkspace, and patchWorkspaceScripts return the previous Map/entry reference when content is unchanged. - HostRuntimeController.updateSnapshot only notifies when a patched field actually changed; idle probe ticks no longer replace the snapshot. - New canonical hook surface in stores/session-store-hooks.ts (useWorkspace, useWorkspaceFields, useWorkspaceStructure, useWorkspaceKeys, useResolveWorkspaceIdByCwd, useWorkspaceStatusesForBadges, useWorkspaceExecutionAuthority, useRecommendedProjectPaths, useHasWorkspaces). These are the only place to subscribe to workspaces. - 15 greedy subscribers migrated to the hook surface. The sidebar is now driven by useWorkspaceStructure for identity/ordering; each row hydrates its own descriptor via useWorkspaceFields, so a workspace update re-renders only its row. Archive-redirect callbacks switched to event-time useSessionStore.getState(). - Deleted dead code: use-session-directory.ts, workspace-fetch-debug.ts, and the unused SidebarProjectEntry aggregates (activeCount, totalWorkspaces, aggregated statusBucket). No user-visible behavior change. Targeted tests green (79 tests across host-runtime, session-store, session-store-hooks, sidebar list, sidebar row model, sidebar shortcuts, workspace source of truth).
34 lines
928 B
TypeScript
34 lines
928 B
TypeScript
import equal from "fast-deep-equal";
|
|
import type { ScriptStatusUpdateMessage } from "@server/shared/messages";
|
|
import type { WorkspaceDescriptor } from "@/stores/session-store";
|
|
import { resolveWorkspaceMapKeyByIdentity } from "@/utils/workspace-execution";
|
|
|
|
export function patchWorkspaceScripts(
|
|
workspaces: Map<string, WorkspaceDescriptor>,
|
|
update: ScriptStatusUpdateMessage["payload"],
|
|
): Map<string, WorkspaceDescriptor> {
|
|
const workspaceKey = resolveWorkspaceMapKeyByIdentity({
|
|
workspaces,
|
|
workspaceId: update.workspaceId,
|
|
});
|
|
if (!workspaceKey) {
|
|
return workspaces;
|
|
}
|
|
|
|
const existing = workspaces.get(workspaceKey);
|
|
if (!existing) {
|
|
return workspaces;
|
|
}
|
|
|
|
if (equal(existing.scripts, update.scripts)) {
|
|
return workspaces;
|
|
}
|
|
|
|
const next = new Map(workspaces);
|
|
next.set(workspaceKey, {
|
|
...existing,
|
|
scripts: update.scripts.map((s) => ({ ...s })),
|
|
});
|
|
return next;
|
|
}
|