Update files
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { useQueries, useQueryClient } from "@tanstack/react-query";
|
||||
import { useShallow } from "zustand/shallow";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { useDaemonConnections } from "@/contexts/daemon-connections-context";
|
||||
import { useSessionStore, type Agent } from "@/stores/session-store";
|
||||
import type { AggregatedAgent, AggregatedAgentsResult } from "@/hooks/use-aggregated-agents";
|
||||
@@ -31,106 +30,69 @@ function toAggregatedAgent(params: {
|
||||
};
|
||||
}
|
||||
|
||||
export function useAllAgentsList(): AggregatedAgentsResult {
|
||||
export function useAllAgentsList(options?: {
|
||||
serverId?: string | null;
|
||||
}): AggregatedAgentsResult {
|
||||
const { connectionStates } = useDaemonConnections();
|
||||
const queryClient = useQueryClient();
|
||||
const serverId = useMemo(() => {
|
||||
const value = options?.serverId;
|
||||
return typeof value === "string" && value.trim().length > 0
|
||||
? value.trim()
|
||||
: null;
|
||||
}, [options?.serverId]);
|
||||
|
||||
const sessionClients = useSessionStore(
|
||||
useShallow((state) => {
|
||||
const result: Record<
|
||||
string,
|
||||
NonNullable<typeof state.sessions[string]["client"]> | null
|
||||
> = {};
|
||||
for (const [serverId, session] of Object.entries(state.sessions)) {
|
||||
result[serverId] = session.client ?? null;
|
||||
const session = useSessionStore((state) =>
|
||||
serverId ? state.sessions[serverId] : undefined
|
||||
);
|
||||
const client = session?.client ?? null;
|
||||
const isConnected = session?.connection.isConnected ?? false;
|
||||
const liveAgents = session?.agents ?? null;
|
||||
const canFetch = Boolean(serverId && client && isConnected);
|
||||
|
||||
const agentsQuery = useQuery({
|
||||
queryKey: ["allAgents", serverId] as const,
|
||||
queryFn: async () => {
|
||||
if (!client) {
|
||||
throw new Error("Daemon client not available");
|
||||
}
|
||||
return result;
|
||||
})
|
||||
);
|
||||
|
||||
const sessionConnections = useSessionStore(
|
||||
useShallow((state) => {
|
||||
const result: Record<string, boolean> = {};
|
||||
for (const [serverId, session] of Object.entries(state.sessions)) {
|
||||
result[serverId] = session.connection.isConnected;
|
||||
}
|
||||
return result;
|
||||
})
|
||||
);
|
||||
|
||||
const liveAgents = useSessionStore(
|
||||
useShallow((state) => {
|
||||
const result: Record<string, Map<string, Agent> | undefined> = {};
|
||||
for (const [serverId, session] of Object.entries(state.sessions)) {
|
||||
result[serverId] = session.agents;
|
||||
}
|
||||
return result;
|
||||
})
|
||||
);
|
||||
|
||||
const serverEntries = useMemo(
|
||||
() =>
|
||||
Object.keys(sessionClients).map((serverId) => ({
|
||||
serverId,
|
||||
client: sessionClients[serverId] ?? null,
|
||||
isConnected: sessionConnections[serverId] ?? false,
|
||||
})),
|
||||
[sessionClients, sessionConnections]
|
||||
);
|
||||
|
||||
const queries = useQueries({
|
||||
queries: serverEntries.map(({ serverId, client, isConnected }) => ({
|
||||
queryKey: ["allAgents", serverId] as const,
|
||||
queryFn: async () => {
|
||||
if (!client) {
|
||||
throw new Error("Daemon client not available");
|
||||
}
|
||||
return await client.fetchAgents();
|
||||
},
|
||||
enabled: Boolean(client) && isConnected,
|
||||
staleTime: ALL_AGENTS_STALE_TIME,
|
||||
refetchOnMount: "always" as const,
|
||||
})),
|
||||
return await client.fetchAgents();
|
||||
},
|
||||
enabled: canFetch,
|
||||
staleTime: ALL_AGENTS_STALE_TIME,
|
||||
refetchOnMount: "always" as const,
|
||||
});
|
||||
|
||||
const refreshAll = useCallback(() => {
|
||||
for (const { serverId } of serverEntries) {
|
||||
void queryClient.invalidateQueries({
|
||||
queryKey: ["allAgents", serverId],
|
||||
});
|
||||
if (!serverId) {
|
||||
return;
|
||||
}
|
||||
}, [queryClient, serverEntries]);
|
||||
void queryClient.invalidateQueries({
|
||||
queryKey: ["allAgents", serverId],
|
||||
});
|
||||
}, [queryClient, serverId]);
|
||||
|
||||
const agents = useMemo(() => {
|
||||
const all: AggregatedAgent[] = [];
|
||||
if (!serverId) {
|
||||
return [];
|
||||
}
|
||||
const data = agentsQuery.data ?? [];
|
||||
const serverLabel = connectionStates.get(serverId)?.daemon.label ?? serverId;
|
||||
const list: AggregatedAgent[] = [];
|
||||
|
||||
for (let idx = 0; idx < serverEntries.length; idx++) {
|
||||
const entry = serverEntries[idx];
|
||||
if (!entry) {
|
||||
continue;
|
||||
}
|
||||
const data = queries[idx]?.data;
|
||||
if (!data) {
|
||||
continue;
|
||||
}
|
||||
const serverLabel =
|
||||
connectionStates.get(entry.serverId)?.daemon.label ?? entry.serverId;
|
||||
const liveById = liveAgents[entry.serverId];
|
||||
|
||||
for (const snapshot of data) {
|
||||
const normalized = normalizeAgentSnapshot(snapshot, entry.serverId);
|
||||
const live = liveById?.get(snapshot.id);
|
||||
all.push(
|
||||
toAggregatedAgent({
|
||||
source: live ?? normalized,
|
||||
serverId: entry.serverId,
|
||||
serverLabel,
|
||||
})
|
||||
);
|
||||
}
|
||||
for (const snapshot of data) {
|
||||
const normalized = normalizeAgentSnapshot(snapshot, serverId);
|
||||
const live = liveAgents?.get(snapshot.id);
|
||||
list.push(
|
||||
toAggregatedAgent({
|
||||
source: live ?? normalized,
|
||||
serverId,
|
||||
serverLabel,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
all.sort((left, right) => {
|
||||
list.sort((left, right) => {
|
||||
const leftRunning = left.status === "running";
|
||||
const rightRunning = right.status === "running";
|
||||
if (leftRunning && !rightRunning) {
|
||||
@@ -142,10 +104,11 @@ export function useAllAgentsList(): AggregatedAgentsResult {
|
||||
return right.lastActivityAt.getTime() - left.lastActivityAt.getTime();
|
||||
});
|
||||
|
||||
return all;
|
||||
}, [serverEntries, queries, connectionStates, liveAgents]);
|
||||
return list;
|
||||
}, [agentsQuery.data, connectionStates, liveAgents, serverId]);
|
||||
|
||||
const isFetching = queries.some((query) => query.isPending || query.isFetching);
|
||||
const isFetching =
|
||||
canFetch && (agentsQuery.isPending || agentsQuery.isFetching);
|
||||
const isInitialLoad = isFetching && agents.length === 0;
|
||||
const isRevalidating = isFetching && agents.length > 0;
|
||||
|
||||
|
||||
@@ -17,6 +17,12 @@ import {
|
||||
buildNewAgentRoute,
|
||||
resolveNewAgentWorkingDir,
|
||||
} from "@/utils/new-agent-routing";
|
||||
import {
|
||||
buildHostAgentDetailRoute,
|
||||
buildHostSettingsRoute,
|
||||
parseHostAgentRouteFromPathname,
|
||||
parseServerIdFromPathname,
|
||||
} from "@/utils/host-routes";
|
||||
import type { ShortcutKey } from "@/utils/format-shortcut";
|
||||
import { focusWithRetries } from "@/utils/web-focus";
|
||||
|
||||
@@ -46,19 +52,17 @@ function sortAgents(left: AggregatedAgent, right: AggregatedAgent): number {
|
||||
}
|
||||
|
||||
function parseAgentKeyFromPathname(pathname: string): string | null {
|
||||
const match = pathname.match(/^\/agent\/([^/]+)\/([^/]+)/);
|
||||
if (!match) return null;
|
||||
return `${match[1]}:${match[2]}`;
|
||||
const match = parseHostAgentRouteFromPathname(pathname);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
return `${match.serverId}:${match.agentId}`;
|
||||
}
|
||||
|
||||
function parseAgentRouteFromPathname(
|
||||
pathname: string
|
||||
): { serverId: string; agentId: string } | null {
|
||||
const match = pathname.match(/^\/agent\/([^/]+)\/([^/]+)/);
|
||||
if (!match) return null;
|
||||
const [, serverId, agentId] = match;
|
||||
if (!serverId || !agentId) return null;
|
||||
return { serverId, agentId };
|
||||
return parseHostAgentRouteFromPathname(pathname);
|
||||
}
|
||||
|
||||
type CommandCenterActionDefinition = {
|
||||
@@ -67,7 +71,7 @@ type CommandCenterActionDefinition = {
|
||||
icon?: "plus" | "settings";
|
||||
shortcutKeys?: ShortcutKey[];
|
||||
keywords: string[];
|
||||
buildRoute: (params: { newAgentRoute: string }) => string;
|
||||
buildRoute: (params: { newAgentRoute: string; settingsRoute: string }) => string;
|
||||
};
|
||||
|
||||
const COMMAND_CENTER_ACTIONS: readonly CommandCenterActionDefinition[] = [
|
||||
@@ -84,7 +88,7 @@ const COMMAND_CENTER_ACTIONS: readonly CommandCenterActionDefinition[] = [
|
||||
title: "Settings",
|
||||
icon: "settings",
|
||||
keywords: ["settings", "preferences", "config", "configuration"],
|
||||
buildRoute: () => "/settings",
|
||||
buildRoute: ({ settingsRoute }) => settingsRoute,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -137,22 +141,26 @@ export function useCommandCenter() {
|
||||
return filtered;
|
||||
}, [agents, query]);
|
||||
|
||||
const fallbackServerId = agents[0]?.serverId ?? null;
|
||||
|
||||
const agentKeyFromPathname = useMemo(
|
||||
() => parseAgentKeyFromPathname(pathname),
|
||||
[pathname]
|
||||
);
|
||||
|
||||
const newAgentRoute = useMemo(() => {
|
||||
const serverIdFromPath =
|
||||
parseServerIdFromPathname(pathname) ?? fallbackServerId;
|
||||
const routeAgent = parseAgentRouteFromPathname(pathname);
|
||||
if (!routeAgent) {
|
||||
return "/agent";
|
||||
return serverIdFromPath ? buildNewAgentRoute(serverIdFromPath) : "/";
|
||||
}
|
||||
|
||||
const { serverId, agentId } = routeAgent;
|
||||
const currentAgent = useSessionStore.getState().sessions[serverId]?.agents?.get(agentId);
|
||||
const cwd = currentAgent?.cwd?.trim();
|
||||
if (!cwd) {
|
||||
return "/agent";
|
||||
return buildNewAgentRoute(serverId);
|
||||
}
|
||||
|
||||
const checkout =
|
||||
@@ -160,8 +168,14 @@ export function useCommandCenter() {
|
||||
checkoutStatusQueryKey(serverId, cwd)
|
||||
) ?? null;
|
||||
const workingDir = resolveNewAgentWorkingDir(cwd, checkout);
|
||||
return buildNewAgentRoute(workingDir);
|
||||
}, [pathname]);
|
||||
return buildNewAgentRoute(serverId, workingDir);
|
||||
}, [fallbackServerId, pathname]);
|
||||
|
||||
const settingsRoute = useMemo(() => {
|
||||
const serverIdFromPath =
|
||||
parseServerIdFromPathname(pathname) ?? fallbackServerId;
|
||||
return serverIdFromPath ? buildHostSettingsRoute(serverIdFromPath) : "/";
|
||||
}, [fallbackServerId, pathname]);
|
||||
|
||||
const actionItems = useMemo(() => {
|
||||
return COMMAND_CENTER_ACTIONS.filter((action) =>
|
||||
@@ -171,10 +185,10 @@ export function useCommandCenter() {
|
||||
id: action.id,
|
||||
title: action.title,
|
||||
icon: action.icon,
|
||||
route: action.buildRoute({ newAgentRoute }),
|
||||
route: action.buildRoute({ newAgentRoute, settingsRoute }),
|
||||
shortcutKeys: action.shortcutKeys,
|
||||
}));
|
||||
}, [newAgentRoute, query]);
|
||||
}, [newAgentRoute, query, settingsRoute]);
|
||||
|
||||
const items = useMemo(() => {
|
||||
const next: CommandCenterItem[] = [];
|
||||
@@ -203,14 +217,14 @@ export function useCommandCenter() {
|
||||
const session = useSessionStore.getState().sessions[agent.serverId];
|
||||
session?.client?.clearAgentAttention(agent.id);
|
||||
|
||||
const shouldReplace = pathname.startsWith("/agent/");
|
||||
const shouldReplace = Boolean(parseHostAgentRouteFromPathname(pathname));
|
||||
const navigate = shouldReplace ? router.replace : router.push;
|
||||
|
||||
requestFocusChatInput(agentKey(agent));
|
||||
// Don't restore focus back to the prior element after we navigate.
|
||||
clearCommandCenterFocusRestoreElement();
|
||||
setOpen(false);
|
||||
navigate(`/agent/${agent.serverId}/${agent.id}` as any);
|
||||
navigate(buildHostAgentDetailRoute(agent.serverId, agent.id) as any);
|
||||
},
|
||||
[pathname, requestFocusChatInput, setOpen]
|
||||
);
|
||||
|
||||
@@ -15,6 +15,11 @@ import {
|
||||
buildNewAgentRoute,
|
||||
resolveNewAgentWorkingDir,
|
||||
} from "@/utils/new-agent-routing";
|
||||
import {
|
||||
buildHostAgentDetailRoute,
|
||||
parseHostAgentRouteFromPathname,
|
||||
parseServerIdFromPathname,
|
||||
} from "@/utils/host-routes";
|
||||
|
||||
export function useGlobalKeyboardNav({
|
||||
enabled,
|
||||
@@ -86,18 +91,20 @@ export function useGlobalKeyboardNav({
|
||||
}
|
||||
const { serverId, agentId } = parsed;
|
||||
|
||||
const shouldReplace = pathname.startsWith("/agent/");
|
||||
const shouldReplace = Boolean(parseHostAgentRouteFromPathname(pathname));
|
||||
const navigate = shouldReplace ? router.replace : router.push;
|
||||
navigate(`/agent/${serverId}/${agentId}` as any);
|
||||
navigate(buildHostAgentDetailRoute(serverId, agentId) as any);
|
||||
};
|
||||
|
||||
const navigateToNewAgent = () => {
|
||||
let target = "/agent";
|
||||
let targetServerId = parseServerIdFromPathname(pathname);
|
||||
let targetWorkingDir: string | null = null;
|
||||
if (selectedAgentId) {
|
||||
const separatorIndex = selectedAgentId.indexOf(":");
|
||||
if (separatorIndex > 0) {
|
||||
const serverId = selectedAgentId.slice(0, separatorIndex);
|
||||
const agentId = selectedAgentId.slice(separatorIndex + 1);
|
||||
targetServerId = serverId;
|
||||
const agent = useSessionStore.getState().sessions[serverId]?.agents?.get(agentId);
|
||||
const cwd = agent?.cwd?.trim();
|
||||
if (cwd) {
|
||||
@@ -105,13 +112,21 @@ export function useGlobalKeyboardNav({
|
||||
queryClient.getQueryData<CheckoutStatusPayload>(
|
||||
checkoutStatusQueryKey(serverId, cwd)
|
||||
) ?? null;
|
||||
const workingDir = resolveNewAgentWorkingDir(cwd, checkout);
|
||||
target = buildNewAgentRoute(workingDir);
|
||||
targetWorkingDir = resolveNewAgentWorkingDir(cwd, checkout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
router.push(target as any);
|
||||
if (!targetServerId) {
|
||||
const sessionServerIds = Object.keys(useSessionStore.getState().sessions);
|
||||
targetServerId = sessionServerIds[0] ?? null;
|
||||
}
|
||||
|
||||
if (!targetServerId) {
|
||||
return;
|
||||
}
|
||||
|
||||
router.push(buildNewAgentRoute(targetServerId, targetWorkingDir) as any);
|
||||
};
|
||||
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useCallback, useEffect, useMemo } from "react";
|
||||
import { useQueries, useQueryClient } from "@tanstack/react-query";
|
||||
import { useShallow } from "zustand/shallow";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { useDaemonConnections } from "@/contexts/daemon-connections-context";
|
||||
import { useSessionStore, type Agent } from "@/stores/session-store";
|
||||
import type { AggregatedAgent } from "@/hooks/use-aggregated-agents";
|
||||
@@ -15,7 +14,8 @@ const SIDEBAR_GROUPS_STALE_TIME = 15_000;
|
||||
const SIDEBAR_GROUPS_REFETCH_INTERVAL = 10_000;
|
||||
const MAX_AGENTS_PER_PROJECT = 5;
|
||||
|
||||
type SidebarGroupsPayload = FetchAgentsGroupedByProjectResponseMessage["payload"];
|
||||
type SidebarGroupsPayload =
|
||||
FetchAgentsGroupedByProjectResponseMessage["payload"];
|
||||
export type SidebarCheckoutLite =
|
||||
SidebarGroupsPayload["groups"][number]["agents"][number]["checkout"];
|
||||
type MutableSidebarGroup = {
|
||||
@@ -68,95 +68,63 @@ function toAggregatedAgent(params: {
|
||||
|
||||
export function useSidebarAgentsGrouped(options?: {
|
||||
isOpen?: boolean;
|
||||
serverId?: string | null;
|
||||
}): SidebarAgentsGroupedResult {
|
||||
const { connectionStates } = useDaemonConnections();
|
||||
const queryClient = useQueryClient();
|
||||
const isOpen = options?.isOpen ?? true;
|
||||
const serverId = useMemo(() => {
|
||||
const value = options?.serverId;
|
||||
return typeof value === "string" && value.trim().length > 0
|
||||
? value.trim()
|
||||
: null;
|
||||
}, [options?.serverId]);
|
||||
|
||||
const sessionClients = useSessionStore(
|
||||
useShallow((state) => {
|
||||
const result: Record<
|
||||
string,
|
||||
NonNullable<typeof state.sessions[string]["client"]> | null
|
||||
> = {};
|
||||
for (const [serverId, session] of Object.entries(state.sessions)) {
|
||||
result[serverId] = session.client ?? null;
|
||||
const session = useSessionStore((state) =>
|
||||
serverId ? state.sessions[serverId] : undefined
|
||||
);
|
||||
const client = session?.client ?? null;
|
||||
const liveAgents = session?.agents ?? null;
|
||||
const isConnected = session?.connection.isConnected ?? false;
|
||||
const canFetch = Boolean(serverId && client && isConnected);
|
||||
|
||||
const groupedQuery = useQuery({
|
||||
queryKey: ["sidebarAgentsGrouped", serverId] as const,
|
||||
queryFn: async () => {
|
||||
if (!client) {
|
||||
throw new Error("Daemon client not available");
|
||||
}
|
||||
return result;
|
||||
})
|
||||
);
|
||||
|
||||
const sessionAgents = useSessionStore(
|
||||
useShallow((state) => {
|
||||
const result: Record<string, Map<string, Agent> | undefined> = {};
|
||||
for (const [serverId, session] of Object.entries(state.sessions)) {
|
||||
result[serverId] = session.agents;
|
||||
}
|
||||
return result;
|
||||
})
|
||||
);
|
||||
|
||||
const sessionConnections = useSessionStore(
|
||||
useShallow((state) => {
|
||||
const result: Record<string, boolean> = {};
|
||||
for (const [serverId, session] of Object.entries(state.sessions)) {
|
||||
result[serverId] = session.connection.isConnected;
|
||||
}
|
||||
return result;
|
||||
})
|
||||
);
|
||||
|
||||
const serverEntries = useMemo(
|
||||
() =>
|
||||
Object.keys(sessionClients).map((serverId) => ({
|
||||
serverId,
|
||||
client: sessionClients[serverId] ?? null,
|
||||
isConnected: sessionConnections[serverId] ?? false,
|
||||
})),
|
||||
[sessionClients, sessionConnections]
|
||||
);
|
||||
|
||||
const groupedQueries = useQueries({
|
||||
queries: serverEntries.map(({ serverId, client, isConnected }) => ({
|
||||
queryKey: ["sidebarAgentsGrouped", serverId] as const,
|
||||
queryFn: async () => {
|
||||
if (!client) {
|
||||
throw new Error("Daemon client not available");
|
||||
}
|
||||
return await client.fetchAgentsGroupedByProject({
|
||||
filter: { labels: { ui: "true" } },
|
||||
});
|
||||
},
|
||||
enabled: Boolean(client) && isConnected,
|
||||
staleTime: SIDEBAR_GROUPS_STALE_TIME,
|
||||
refetchInterval: isOpen ? SIDEBAR_GROUPS_REFETCH_INTERVAL : false,
|
||||
refetchIntervalInBackground: isOpen,
|
||||
refetchOnMount: "always" as const,
|
||||
})),
|
||||
return await client.fetchAgentsGroupedByProject({
|
||||
filter: { labels: { ui: "true" } },
|
||||
});
|
||||
},
|
||||
enabled: canFetch,
|
||||
staleTime: SIDEBAR_GROUPS_STALE_TIME,
|
||||
refetchInterval: isOpen ? SIDEBAR_GROUPS_REFETCH_INTERVAL : false,
|
||||
refetchIntervalInBackground: isOpen,
|
||||
refetchOnMount: "always" as const,
|
||||
});
|
||||
|
||||
const projectOrder = useSectionOrderStore((state) => state.projectOrder);
|
||||
const setProjectOrder = useSectionOrderStore((state) => state.setProjectOrder);
|
||||
|
||||
const { sections, checkoutByAgentKey, hasAnyData } = useMemo(() => {
|
||||
if (!serverId) {
|
||||
return {
|
||||
sections: [] as SidebarSectionData[],
|
||||
checkoutByAgentKey: new Map<string, SidebarCheckoutLite>(),
|
||||
hasAnyData: false,
|
||||
};
|
||||
}
|
||||
|
||||
const groupsByKey = new Map<string, MutableSidebarGroup>();
|
||||
const checkoutLookup = new Map<string, SidebarCheckoutLite>();
|
||||
const seenAgentKeys = new Set<string>();
|
||||
const groupedFetchReadyByServer = new Map<string, boolean>();
|
||||
|
||||
for (let idx = 0; idx < serverEntries.length; idx++) {
|
||||
const { serverId } = serverEntries[idx] ?? {};
|
||||
if (!serverId) {
|
||||
continue;
|
||||
}
|
||||
groupedFetchReadyByServer.set(serverId, groupedQueries[idx]?.isFetched ?? false);
|
||||
const payload = groupedQueries[idx]?.data as SidebarGroupsPayload | undefined;
|
||||
if (!payload) {
|
||||
continue;
|
||||
}
|
||||
const serverLabel = connectionStates.get(serverId)?.daemon.label ?? serverId;
|
||||
const liveAgents = sessionAgents[serverId];
|
||||
const payload = groupedQuery.data as SidebarGroupsPayload | undefined;
|
||||
const groupedFetchReady = groupedQuery.isFetched;
|
||||
const serverLabel = connectionStates.get(serverId)?.daemon.label ?? serverId;
|
||||
|
||||
if (payload) {
|
||||
for (const group of payload.groups) {
|
||||
const existing: MutableSidebarGroup =
|
||||
groupsByKey.get(group.projectKey) ??
|
||||
@@ -180,7 +148,10 @@ export function useSidebarAgentsGrouped(options?: {
|
||||
|
||||
const agentKey = `${serverId}:${entry.agent.id}`;
|
||||
seenAgentKeys.add(agentKey);
|
||||
checkoutLookup.set(agentKey, live?.projectPlacement?.checkout ?? entry.checkout);
|
||||
checkoutLookup.set(
|
||||
agentKey,
|
||||
live?.projectPlacement?.checkout ?? entry.checkout
|
||||
);
|
||||
existing.agents.push(nextAgent);
|
||||
}
|
||||
|
||||
@@ -188,16 +159,7 @@ export function useSidebarAgentsGrouped(options?: {
|
||||
}
|
||||
}
|
||||
|
||||
for (const { serverId } of serverEntries) {
|
||||
if (!groupedFetchReadyByServer.get(serverId)) {
|
||||
continue;
|
||||
}
|
||||
const serverLabel = connectionStates.get(serverId)?.daemon.label ?? serverId;
|
||||
const liveAgents = sessionAgents[serverId];
|
||||
if (!liveAgents) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (groupedFetchReady && liveAgents) {
|
||||
for (const live of liveAgents.values()) {
|
||||
if (live.archivedAt || live.labels.ui !== "true") {
|
||||
continue;
|
||||
@@ -270,7 +232,14 @@ export function useSidebarAgentsGrouped(options?: {
|
||||
checkoutByAgentKey: checkoutLookup,
|
||||
hasAnyData: nextSections.length > 0,
|
||||
};
|
||||
}, [serverEntries, groupedQueries, connectionStates, sessionAgents, projectOrder]);
|
||||
}, [
|
||||
connectionStates,
|
||||
groupedQuery.data,
|
||||
groupedQuery.isFetched,
|
||||
liveAgents,
|
||||
projectOrder,
|
||||
serverId,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
const currentKeys = sections.map((section) => section.projectKey);
|
||||
@@ -282,16 +251,16 @@ export function useSidebarAgentsGrouped(options?: {
|
||||
}, [sections, projectOrder, setProjectOrder]);
|
||||
|
||||
const refreshAll = useCallback(() => {
|
||||
for (const { serverId } of serverEntries) {
|
||||
void queryClient.invalidateQueries({
|
||||
queryKey: ["sidebarAgentsGrouped", serverId],
|
||||
});
|
||||
if (!serverId) {
|
||||
return;
|
||||
}
|
||||
}, [queryClient, serverEntries]);
|
||||
void queryClient.invalidateQueries({
|
||||
queryKey: ["sidebarAgentsGrouped", serverId],
|
||||
});
|
||||
}, [queryClient, serverId]);
|
||||
|
||||
const isFetching = groupedQueries.some(
|
||||
(query) => query.isPending || query.isFetching
|
||||
);
|
||||
const isFetching =
|
||||
canFetch && (groupedQuery.isPending || groupedQuery.isFetching);
|
||||
const isInitialLoad = isFetching && !hasAnyData;
|
||||
const isRevalidating = isFetching && hasAnyData;
|
||||
|
||||
@@ -304,3 +273,4 @@ export function useSidebarAgentsGrouped(options?: {
|
||||
refreshAll,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user