fix(app): demand the host directory from the workspace route (#7918)

A workspace restored at launch with no open chat had nothing demanding its host's directory on a phone, so the deck waited on hydration that never started. The workspace route now keeps its host's directory demanded while mounted, through the same hook the agent route uses.
This commit is contained in:
Mohamed Boudra
2026-10-26 15:46:10 +07:00
committed by GitHub
parent e277656cbd
commit 428ef707ad
4 changed files with 63 additions and 5 deletions
@@ -5,6 +5,7 @@ import { useFetchQuery } from "@/data/query";
import { resolveAgentRoute, type AgentRouteLookup } from "@/navigation/agent-route-resolution";
import { AgentRouteResolutionView } from "@/navigation/agent-route-resolution-view";
import { useHasHydratedWorkspaces } from "@/stores/session-store-hooks";
import { useDirectoryDemand } from "@/hooks/use-directory-demand";
import { useWorkspaceLayoutStoreHydrated } from "@/stores/workspace-layout-store";
import { useSessionStore } from "@/stores/session-store";
import { getHostRuntimeStore, useHostRuntimeSnapshot, useHosts } from "@/runtime/host-runtime";
@@ -87,11 +88,7 @@ function HostAgentReadyRouteContent() {
]);
const hasHydratedWorkspaces = useHasHydratedWorkspaces(serverId);
const hasHydratedLayout = useWorkspaceLayoutStoreHydrated();
useEffect(() => {
if (!serverId) return;
// A compact app can open this route before its sidebar has demanded the directory.
return getHostRuntimeStore().acquireDirectoryDemand(serverId);
}, [serverId]);
useDirectoryDemand(serverId);
const resolution = resolveAgentRoute({
hasHydratedWorkspaces,
hasHydratedLayout,
@@ -9,6 +9,7 @@ import {
useActiveWorkspaceSelection,
} from "@/stores/navigation-active-workspace-store";
import { useHasHydratedWorkspaces, useWorkspaceExists } from "@/stores/session-store-hooks";
import { useDirectoryDemand } from "@/hooks/use-directory-demand";
import type { WorkspaceTabTarget } from "@/workspace-tabs/model";
import { WorkspaceScreen } from "@/screens/workspace/workspace-screen";
import { useWorkspaceLayoutStore } from "@/stores/workspace-layout-store";
@@ -110,6 +111,10 @@ function HostWorkspaceRouteContent() {
? (decodeWorkspaceIdFromPathSegment(workspaceValue) ?? "")
: "";
const openValue = getParamValue(params.open);
// A restored workspace with no open chat has nothing else demanding its host's directory on a
// phone, where the sidebar only demands while its panel is active. Without demand the deck
// waits on workspace hydration that never starts.
useDirectoryDemand(serverId);
useEffect(() => {
if (!serverId || !workspaceId) {
return;
@@ -0,0 +1,40 @@
/**
* @vitest-environment jsdom
*/
import { act } from "react";
import { createRoot, type Root } from "react-dom/client";
import { afterEach, describe, expect, it, vi } from "vitest";
import { getHostRuntimeStore } from "@/runtime/host-runtime";
import { useDirectoryDemand } from "./use-directory-demand";
function Probe({ serverId }: { serverId: string | null }) {
useDirectoryDemand(serverId);
return null;
}
describe("useDirectoryDemand", () => {
let root: Root | null = null;
afterEach(() => {
act(() => root?.unmount());
root = null;
vi.restoreAllMocks();
});
it("demands the host directory while mounted and releases it on unmount", () => {
const release = vi.fn();
const acquire = vi
.spyOn(getHostRuntimeStore(), "acquireDirectoryDemand")
.mockImplementation(() => release);
const container = document.createElement("div");
root = createRoot(container);
act(() => root?.render(<Probe serverId="srv_a" />));
expect(acquire).toHaveBeenCalledTimes(1);
expect(acquire).toHaveBeenCalledWith("srv_a");
expect(release).not.toHaveBeenCalled();
act(() => root?.render(<Probe serverId={null} />));
expect(release).toHaveBeenCalledTimes(1);
expect(acquire).toHaveBeenCalledTimes(1);
});
});
@@ -0,0 +1,16 @@
import { useEffect } from "react";
import { getHostRuntimeStore } from "@/runtime/host-runtime";
/**
* Keeps a host's directory demanded while the caller is mounted.
*
* The sidebar only demands the directory while its panel is active, so a compact app that
* restores straight into a workspace or agent route has nothing else asking the host for
* workspaces and agents. Without demand the route waits on hydration that never starts.
*/
export function useDirectoryDemand(serverId: string | null | undefined): void {
useEffect(() => {
if (!serverId) return;
return getHostRuntimeStore().acquireDirectoryDemand(serverId);
}, [serverId]);
}