diff options
| author | Brendan Allan <[email protected]> | 2026-04-20 13:17:37 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-04-20 05:17:37 +0000 |
| commit | 687b7588820df02dfe7397a399f213f394aa6b09 (patch) | |
| tree | 0497e08000939606f41b9a7577924d9376ecba7c /packages/app/src/context/global-sync | |
| parent | 84e322b0fdb178ad420f3f6507a22c0da590f524 (diff) | |
| download | opencode-687b7588820df02dfe7397a399f213f394aa6b09.tar.gz opencode-687b7588820df02dfe7397a399f213f394aa6b09.zip | |
app: better loading (#23489)
Diffstat (limited to 'packages/app/src/context/global-sync')
| -rw-r--r-- | packages/app/src/context/global-sync/bootstrap.ts | 144 | ||||
| -rw-r--r-- | packages/app/src/context/global-sync/child-store.ts | 10 |
2 files changed, 88 insertions, 66 deletions
diff --git a/packages/app/src/context/global-sync/bootstrap.ts b/packages/app/src/context/global-sync/bootstrap.ts index 17fe726f9..be789a5e5 100644 --- a/packages/app/src/context/global-sync/bootstrap.ts +++ b/packages/app/src/context/global-sync/bootstrap.ts @@ -19,7 +19,6 @@ import type { State, VcsCache } from "./types" import { cmp, normalizeAgentList, normalizeProviderList } from "./utils" import { formatServerError } from "@/utils/server-errors" import { QueryClient, queryOptions, skipToken } from "@tanstack/solid-query" -import { loadSessionsQuery } from "../global-sync" type GlobalStore = { ready: boolean @@ -82,6 +81,9 @@ export async function bootstrapGlobal(input: { input.setGlobalStore("config", x.data!) }), ), + ] + + const slow = [ () => input.queryClient.fetchQuery({ ...loadProvidersQuery(null), @@ -93,9 +95,6 @@ export async function bootstrapGlobal(input: { }), ), }), - ] - - const slow = [ () => retry(() => input.globalSDK.path.get().then((x) => { @@ -183,8 +182,43 @@ function warmSessions(input: { export const loadProvidersQuery = (directory: string | null) => queryOptions<null>({ queryKey: [directory, "providers"], queryFn: skipToken }) -export const loadAgentsQuery = (directory: string | null) => - queryOptions<null>({ queryKey: [directory, "agents"], queryFn: skipToken }) +export const loadAgentsQuery = ( + directory: string | null, + sdk?: OpencodeClient, + transform?: (x: Awaited<ReturnType<OpencodeClient["app"]["agents"]>>) => void, +) => + queryOptions<null>({ + queryKey: [directory, "agents"], + queryFn: + sdk && transform + ? () => + retry(() => + sdk.app + .agents() + .then(transform) + .then(() => null), + ) + : skipToken, + }) + +export const loadPathQuery = ( + directory: string | null, + sdk?: OpencodeClient, + transform?: (x: Awaited<ReturnType<OpencodeClient["path"]["get"]>>) => void, +) => + queryOptions<Path>({ + queryKey: [directory, "path"], + queryFn: + sdk && transform + ? () => + retry(() => + sdk.path.get().then(async (x) => { + transform(x) + return x.data! + }), + ) + : skipToken, + }) export async function bootstrapDirectory(input: { directory: string @@ -222,45 +256,27 @@ export async function bootstrapDirectory(input: { input.setStore("lsp", []) if (loading) input.setStore("status", "partial") - const fast = [() => Promise.resolve(input.loadSessions(input.directory))] - - const errs = errors(await runAll(fast)) - if (errs.length > 0) { - console.error("Failed to bootstrap instance", errs[0]) - const project = getFilename(input.directory) - showToast({ - variant: "error", - title: input.translate("toast.project.reloadFailed.title", { project }), - description: formatServerError(errs[0], input.translate), - }) - } - + const rev = (providerRev.get(input.directory) ?? 0) + 1 + providerRev.set(input.directory, rev) ;(async () => { const slow = [ + () => Promise.resolve(input.loadSessions(input.directory)), () => - input.queryClient.ensureQueryData({ - ...loadAgentsQuery(input.directory), - queryFn: () => - retry(() => input.sdk.app.agents().then((x) => input.setStore("agent", normalizeAgentList(x.data)))).then( - () => null, - ), - }), + input.queryClient.ensureQueryData( + loadAgentsQuery(input.directory, input.sdk, (x) => input.setStore("agent", normalizeAgentList(x.data))), + ), () => retry(() => input.sdk.config.get().then((x) => input.setStore("config", x.data!))), () => retry(() => input.sdk.session.status().then((x) => input.setStore("session_status", x.data!))), - () => - seededProject - ? Promise.resolve() - : retry(() => input.sdk.project.current()).then((x) => input.setStore("project", x.data!.id)), - () => - seededPath - ? Promise.resolve() - : retry(() => - input.sdk.path.get().then((x) => { - input.setStore("path", x.data!) - const next = projectID(x.data?.directory ?? input.directory, input.global.project) - if (next) input.setStore("project", next) - }), - ), + !seededProject && + (() => retry(() => input.sdk.project.current()).then((x) => input.setStore("project", x.data!.id))), + !seededPath && + (() => + input.queryClient.ensureQueryData( + loadPathQuery(input.directory, input.sdk, (x) => { + const next = projectID(x.data?.directory ?? input.directory, input.global.project) + if (next) input.setStore("project", next) + }), + )), () => retry(() => input.sdk.vcs.get().then((x) => { @@ -330,7 +346,28 @@ export async function bootstrapDirectory(input: { input.setStore("mcp_ready", true) }), ), - ] + () => + input.queryClient.ensureQueryData({ + ...loadProvidersQuery(input.directory), + queryFn: () => + retry(() => input.sdk.provider.list()) + .then((x) => { + if (providerRev.get(input.directory) !== rev) return + input.setStore("provider", normalizeProviderList(x.data!)) + input.setStore("provider_ready", true) + }) + .catch((err) => { + if (providerRev.get(input.directory) !== rev) console.error("Failed to refresh provider list", err) + const project = getFilename(input.directory) + showToast({ + variant: "error", + title: input.translate("toast.project.reloadFailed.title", { project }), + description: formatServerError(err, input.translate), + }) + }) + .then(() => null), + }), + ].filter(Boolean) as (() => Promise<any>)[] await waitForPaint() const slowErrs = errors(await runAll(slow)) @@ -344,29 +381,6 @@ export async function bootstrapDirectory(input: { }) } - if (loading && errs.length === 0 && slowErrs.length === 0) input.setStore("status", "complete") - - const rev = (providerRev.get(input.directory) ?? 0) + 1 - providerRev.set(input.directory, rev) - void input.queryClient.ensureQueryData({ - ...loadSessionsQuery(input.directory), - queryFn: () => - retry(() => input.sdk.provider.list()) - .then((x) => { - if (providerRev.get(input.directory) !== rev) return - input.setStore("provider", normalizeProviderList(x.data!)) - input.setStore("provider_ready", true) - }) - .catch((err) => { - if (providerRev.get(input.directory) !== rev) console.error("Failed to refresh provider list", err) - const project = getFilename(input.directory) - showToast({ - variant: "error", - title: input.translate("toast.project.reloadFailed.title", { project }), - description: formatServerError(err, input.translate), - }) - }) - .then(() => null), - }) + if (loading && slowErrs.length === 0) input.setStore("status", "complete") })() } diff --git a/packages/app/src/context/global-sync/child-store.ts b/packages/app/src/context/global-sync/child-store.ts index 3fe67e4fb..c92d2ae57 100644 --- a/packages/app/src/context/global-sync/child-store.ts +++ b/packages/app/src/context/global-sync/child-store.ts @@ -14,6 +14,8 @@ import { type VcsCache, } from "./types" import { canDisposeDirectory, pickDirectoriesToEvict } from "./eviction" +import { useQuery } from "@tanstack/solid-query" +import { loadPathQuery } from "./bootstrap" export function createChildStoreManager(input: { owner: Owner @@ -156,6 +158,8 @@ export function createChildStoreManager(input: { createRoot((dispose) => { const initialMeta = meta[0].value const initialIcon = icon[0].value + + const pathQuery = useQuery(() => loadPathQuery(directory)) const child = createStore<State>({ project: "", projectMeta: initialMeta, @@ -163,7 +167,11 @@ export function createChildStoreManager(input: { provider_ready: false, provider: { all: [], connected: [], default: {} }, config: {}, - path: { state: "", config: "", worktree: "", directory: "", home: "" }, + get path() { + if (pathQuery.isLoading || !pathQuery.data) + return { state: "", config: "", worktree: "", directory: "", home: "" } + return pathQuery.data + }, status: "loading" as const, agent: [], command: [], |
