refactor(app): reshape session store reads around narrow hook surface
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).
This commit is contained in:
@@ -1,9 +1,14 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
vi.hoisted(() => {
|
||||
(globalThis as unknown as { __DEV__: boolean }).__DEV__ = false;
|
||||
});
|
||||
|
||||
import {
|
||||
resolveWorkspaceHeader,
|
||||
shouldRenderMissingWorkspaceDescriptor,
|
||||
} from "./workspace-header-source";
|
||||
import { buildSidebarProjectsFromWorkspaces } from "@/hooks/use-sidebar-workspaces-list";
|
||||
import { createSidebarWorkspaceEntry } from "@/hooks/use-sidebar-workspaces-list";
|
||||
import type { WorkspaceDescriptor } from "@/stores/session-store";
|
||||
|
||||
describe("workspace source of truth consumption", () => {
|
||||
@@ -23,17 +28,15 @@ describe("workspace source of truth consumption", () => {
|
||||
};
|
||||
|
||||
const header = resolveWorkspaceHeader({ workspace });
|
||||
const sidebarProjects = buildSidebarProjectsFromWorkspaces({
|
||||
const sidebarWorkspace = createSidebarWorkspaceEntry({
|
||||
serverId: "srv",
|
||||
workspaces: [workspace],
|
||||
projectOrder: [],
|
||||
workspaceOrderByScope: {},
|
||||
workspace,
|
||||
});
|
||||
|
||||
expect(header.title).toBe("feat/workspace-sot");
|
||||
expect(header.subtitle).toBe("getpaseo/paseo");
|
||||
expect(sidebarProjects[0]?.workspaces[0]?.name).toBe(header.title);
|
||||
expect(sidebarProjects[0]?.workspaces[0]?.statusBucket).toBe("running");
|
||||
expect(sidebarWorkspace.name).toBe(header.title);
|
||||
expect(sidebarWorkspace.statusBucket).toBe("running");
|
||||
});
|
||||
|
||||
it("renders explicit missing state only after workspace hydration", () => {
|
||||
|
||||
Reference in New Issue
Block a user