mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-31 21:52:54 +00:00
Workspace creation landed on the setup tab because openTab implicitly focused the tab it opened, and the setup auto-open effect ran after the setup dialog's navigation, stomping on the agent tab's focus. Split the store API into explicit openTabFocused and openTabInBackground so every caller declares intent. The setup auto-open effect now opens in background and no longer steals focus; user-intent call sites (sidebar, kebab menus, file explorer, terminal creation, prepareWorkspaceTab) use openTabFocused and drop the redundant paired focusTab call.
86 lines
3.0 KiB
TypeScript
86 lines
3.0 KiB
TypeScript
import { useCallback } from "react";
|
|
import { router } from "expo-router";
|
|
import type { DaemonClient } from "@server/client/daemon-client";
|
|
import { useHostRuntimeClient, useHostRuntimeIsConnected } from "@/runtime/host-runtime";
|
|
import {
|
|
normalizeWorkspaceDescriptor,
|
|
type WorkspaceDescriptor,
|
|
useSessionStore,
|
|
} from "@/stores/session-store";
|
|
import {
|
|
buildWorkspaceTabPersistenceKey,
|
|
useWorkspaceLayoutStore,
|
|
} from "@/stores/workspace-layout-store";
|
|
import { generateDraftId } from "@/stores/draft-keys";
|
|
import { buildHostWorkspaceRoute } from "@/utils/host-routes";
|
|
|
|
interface OpenProjectDirectlyInput {
|
|
serverId: string;
|
|
projectPath: string;
|
|
isConnected: boolean;
|
|
client: Pick<DaemonClient, "openProject"> | null;
|
|
mergeWorkspaces: (serverId: string, workspaces: Iterable<WorkspaceDescriptor>) => void;
|
|
setHasHydratedWorkspaces: (serverId: string, hydrated: boolean) => void;
|
|
openDraftTab: (workspaceKey: string) => string | null;
|
|
replaceRoute: (route: string) => void;
|
|
}
|
|
|
|
export async function openProjectDirectly(input: OpenProjectDirectlyInput): Promise<boolean> {
|
|
const normalizedServerId = input.serverId.trim();
|
|
const trimmedPath = input.projectPath.trim();
|
|
if (!normalizedServerId || !trimmedPath || !input.client || !input.isConnected) {
|
|
return false;
|
|
}
|
|
|
|
const payload = await input.client.openProject(trimmedPath);
|
|
if (payload.error || !payload.workspace) {
|
|
return false;
|
|
}
|
|
|
|
const workspace = normalizeWorkspaceDescriptor(payload.workspace);
|
|
input.mergeWorkspaces(normalizedServerId, [workspace]);
|
|
input.setHasHydratedWorkspaces(normalizedServerId, true);
|
|
|
|
const workspaceKey = buildWorkspaceTabPersistenceKey({
|
|
serverId: normalizedServerId,
|
|
workspaceId: workspace.id,
|
|
});
|
|
if (!workspaceKey) {
|
|
return false;
|
|
}
|
|
|
|
input.openDraftTab(workspaceKey);
|
|
input.replaceRoute(buildHostWorkspaceRoute(normalizedServerId, workspace.id));
|
|
return true;
|
|
}
|
|
|
|
export function useOpenProject(serverId: string | null): (path: string) => Promise<boolean> {
|
|
const normalizedServerId = serverId?.trim() ?? "";
|
|
const client = useHostRuntimeClient(normalizedServerId);
|
|
const isConnected = useHostRuntimeIsConnected(normalizedServerId);
|
|
const mergeWorkspaces = useSessionStore((state) => state.mergeWorkspaces);
|
|
const setHasHydratedWorkspaces = useSessionStore((state) => state.setHasHydratedWorkspaces);
|
|
|
|
return useCallback(
|
|
async (path: string) => {
|
|
return openProjectDirectly({
|
|
serverId: normalizedServerId,
|
|
projectPath: path,
|
|
isConnected,
|
|
client,
|
|
mergeWorkspaces,
|
|
setHasHydratedWorkspaces,
|
|
openDraftTab: (workspaceKey: string) =>
|
|
useWorkspaceLayoutStore.getState().openTabFocused(workspaceKey, {
|
|
kind: "draft",
|
|
draftId: generateDraftId(),
|
|
}),
|
|
replaceRoute: (route) => {
|
|
router.replace(route as any);
|
|
},
|
|
});
|
|
},
|
|
[client, isConnected, mergeWorkspaces, normalizedServerId, setHasHydratedWorkspaces],
|
|
);
|
|
}
|