Refactor project opening and add status bar tooltips

This commit is contained in:
Mohamed Boudra
2026-03-19 19:04:01 +07:00
parent efb3df8233
commit 3d4ac57bd0
15 changed files with 420 additions and 167 deletions
+4 -3
View File
@@ -6,6 +6,7 @@ import { keyboardActionDispatcher } from "@/keyboard/keyboard-action-dispatcher"
import { useHosts } from "@/runtime/host-runtime";
import { useAllAgentsList } from "@/hooks/use-all-agents-list";
import type { AggregatedAgent } from "@/hooks/use-aggregated-agents";
import { useOpenProjectPicker } from "@/hooks/use-open-project-picker";
import {
clearCommandCenterFocusRestoreElement,
takeCommandCenterFocusRestoreElement,
@@ -217,18 +218,18 @@ export function useCommandCenter() {
[pathname, setOpen]
);
const setProjectPickerOpen = useKeyboardShortcutsStore((s) => s.setProjectPickerOpen);
const openProjectPicker = useOpenProjectPicker(activeServerId);
const handleSelectAction = useCallback((action: CommandCenterActionItem) => {
clearCommandCenterFocusRestoreElement();
setOpen(false);
if (action.id === "new-agent") {
setProjectPickerOpen(true);
void openProjectPicker();
return;
}
didNavigateRef.current = true;
router.push(action.route);
}, [setOpen, setProjectPickerOpen]);
}, [openProjectPicker, setOpen]);
const handleSelectItem = useCallback(
(item: CommandCenterItem) => {
@@ -0,0 +1,40 @@
import { useQuery } from '@tanstack/react-query'
import { getManagedDaemonStatus } from '@/desktop/managed-runtime/managed-runtime'
import { isTauriEnvironment } from '@/utils/tauri'
const MANAGED_DAEMON_SERVER_ID_QUERY_KEY = ['managed-daemon-server-id'] as const
interface ManagedDaemonServerIdResult {
serverId: string | null
}
async function loadManagedDaemonServerId(): Promise<ManagedDaemonServerIdResult> {
const status = await getManagedDaemonStatus()
const serverId = status.serverId.trim()
return {
serverId: serverId.length > 0 ? serverId : null,
}
}
export function useIsLocalDaemon(serverId: string): boolean {
const normalizedServerId = serverId.trim()
const isDesktop = isTauriEnvironment()
const query = useQuery({
queryKey: MANAGED_DAEMON_SERVER_ID_QUERY_KEY,
queryFn: loadManagedDaemonServerId,
enabled: isDesktop,
staleTime: Infinity,
gcTime: Infinity,
refetchOnMount: false,
refetchOnReconnect: false,
refetchOnWindowFocus: false,
retry: false,
})
if (!isDesktop || normalizedServerId.length === 0) {
return false
}
return query.data?.serverId === normalizedServerId
}
@@ -2,10 +2,12 @@ import { useEffect } from "react";
import { Platform } from "react-native";
import { usePathname } from "expo-router";
import { getIsTauri } from "@/constants/layout";
import { useHosts } from "@/runtime/host-runtime";
import { useKeyboardShortcutsStore } from "@/stores/keyboard-shortcuts-store";
import { setCommandCenterFocusRestoreElement } from "@/utils/command-center-focus-restore";
import {
parseHostAgentRouteFromPathname,
parseServerIdFromPathname,
parseHostWorkspaceRouteFromPathname,
} from "@/utils/host-routes";
import { navigateToWorkspace } from "@/hooks/use-workspace-navigation";
@@ -18,6 +20,7 @@ import { keyboardActionDispatcher } from "@/keyboard/keyboard-action-dispatcher"
import { resolveKeyboardShortcut } from "@/keyboard/keyboard-shortcuts";
import { resolveKeyboardFocusScope } from "@/keyboard/focus-scope";
import { getShortcutOs } from "@/utils/shortcut-platform";
import { useOpenProjectPicker } from "@/hooks/use-open-project-picker";
export function useKeyboardShortcuts({
enabled,
@@ -33,7 +36,14 @@ export function useKeyboardShortcuts({
toggleFileExplorer?: () => void;
}) {
const pathname = usePathname();
const hosts = useHosts();
const resetModifiers = useKeyboardShortcutsStore((s) => s.resetModifiers);
const activeServerIdFromPath = parseServerIdFromPathname(pathname);
const activeServerId =
hosts.find((host) => host.serverId === activeServerIdFromPath)?.serverId ??
hosts[0]?.serverId ??
null;
const openProjectPickerAction = useOpenProjectPicker(activeServerId);
useEffect(() => {
if (!enabled) return;
@@ -92,7 +102,7 @@ export function useKeyboardShortcuts({
};
const openProjectPicker = (): boolean => {
useKeyboardShortcutsStore.getState().setProjectPickerOpen(true);
void openProjectPickerAction();
return true;
};
@@ -326,6 +336,7 @@ export function useKeyboardShortcuts({
}, [
enabled,
isMobile,
openProjectPickerAction,
pathname,
resetModifiers,
selectedAgentId,
@@ -0,0 +1,32 @@
import { useCallback } from "react";
import { pickDirectory } from "@/desktop/pick-directory";
import { useKeyboardShortcutsStore } from "@/stores/keyboard-shortcuts-store";
import { useIsLocalDaemon } from "./use-is-local-daemon";
import { useOpenProject } from "./use-open-project";
export function useOpenProjectPicker(serverId: string | null): () => Promise<void> {
const normalizedServerId = serverId?.trim() ?? "";
const isLocalDaemon = useIsLocalDaemon(normalizedServerId);
const setProjectPickerOpen = useKeyboardShortcutsStore(
(state) => state.setProjectPickerOpen
);
const openProject = useOpenProject(serverId);
return useCallback(async () => {
if (!normalizedServerId) {
return;
}
if (!isLocalDaemon) {
setProjectPickerOpen(true);
return;
}
const path = await pickDirectory();
if (path === null) {
return;
}
await openProject(path);
}, [isLocalDaemon, normalizedServerId, openProject, setProjectPickerOpen]);
}
@@ -0,0 +1,62 @@
import { router } from "expo-router";
import { useCallback } from "react";
import { useToast } from "@/contexts/toast-context";
import { useHostRuntimeSession } from "@/runtime/host-runtime";
import {
normalizeWorkspaceDescriptor,
useSessionStore,
} from "@/stores/session-store";
import { buildHostWorkspaceRouteWithOpenIntent } from "@/utils/host-routes";
export function useOpenProject(
serverId: string | null
): (path: string) => Promise<boolean> {
const normalizedServerId = serverId?.trim() ?? "";
const toast = useToast();
const { client } = useHostRuntimeSession(normalizedServerId);
const mergeWorkspaces = useSessionStore((state) => state.mergeWorkspaces);
const setHasHydratedWorkspaces = useSessionStore(
(state) => state.setHasHydratedWorkspaces
);
return useCallback(
async (path: string) => {
const trimmedPath = path.trim();
if (!trimmedPath || !client || !normalizedServerId) {
return false;
}
try {
const payload = await client.openProject(trimmedPath);
if (payload.error || !payload.workspace) {
throw new Error(payload.error || "Failed to open project");
}
mergeWorkspaces(normalizedServerId, [
normalizeWorkspaceDescriptor(payload.workspace),
]);
setHasHydratedWorkspaces(normalizedServerId, true);
router.replace(
buildHostWorkspaceRouteWithOpenIntent(
normalizedServerId,
payload.workspace.id,
{ kind: "draft", draftId: "new" }
) as any
);
return true;
} catch (error) {
toast.error(
error instanceof Error ? error.message : "Failed to open project"
);
return false;
}
},
[
client,
mergeWorkspaces,
normalizedServerId,
setHasHydratedWorkspaces,
toast,
]
);
}