diff --git a/packages/app/src/context/global-sync.tsx b/packages/app/src/context/global-sync.tsx index 96f8c63eab2..6300f51bcc0 100644 --- a/packages/app/src/context/global-sync.tsx +++ b/packages/app/src/context/global-sync.tsx @@ -74,6 +74,7 @@ type State = { lsp: LspStatus[] vcs: VcsInfo | undefined limit: number + new?: boolean message: { [sessionID: string]: Message[] } @@ -110,8 +111,9 @@ function createGlobalSync() { }) const children: Record, SetStoreFunction]> = {} + const bootstrapPromises: Record> = {} - function child(directory: string) { + function child(directory: string, options?: { new?: boolean }) { if (!directory) console.error("No directory provided") if (!children[directory]) { const cache = runWithOwner(owner, () => @@ -145,8 +147,11 @@ function createGlobalSync() { limit: 5, message: {}, part: {}, + new: options?.new, + }) + bootstrapPromises[directory] = bootstrapInstance(directory).finally(() => { + delete bootstrapPromises[directory] }) - bootstrapInstance(directory) } runWithOwner(owner, init) @@ -156,6 +161,21 @@ function createGlobalSync() { return childStore } + function reactive(directory: () => string) { + return { + get store() { + return child(directory())[0] + }, + get setStore() { + return child(directory())[1] + }, + } + } + + function wait(directory: string) { + return bootstrapPromises[directory] ?? Promise.resolve() + } + async function loadSessions(directory: string) { const [store, setStore] = child(directory) const limit = store.limit @@ -312,6 +332,7 @@ function createGlobalSync() { }), ]).then(() => { setStore("status", "complete") + setStore("new", false) }) }) .catch((e) => setGlobalStore("error", e)) @@ -348,7 +369,9 @@ function createGlobalSync() { const [store, setStore] = child(directory) switch (event.type) { case "server.instance.disposed": { - bootstrapInstance(directory) + bootstrapPromises[directory] = bootstrapInstance(directory).finally(() => { + delete bootstrapPromises[directory] + }) break } case "session.created": { @@ -638,6 +661,8 @@ function createGlobalSync() { return globalStore.error }, child, + reactive, + wait, bootstrap, project: { loadSessions, diff --git a/packages/app/src/context/sdk.tsx b/packages/app/src/context/sdk.tsx index aa4820c4945..445558570f1 100644 --- a/packages/app/src/context/sdk.tsx +++ b/packages/app/src/context/sdk.tsx @@ -1,31 +1,49 @@ import { createOpencodeClient, type Event } from "@opencode-ai/sdk/v2/client" import { createSimpleContext } from "@opencode-ai/ui/context" import { createGlobalEmitter } from "@solid-primitives/event-bus" -import { onCleanup } from "solid-js" +import { createEffect, createMemo, on, onCleanup, type Accessor } from "solid-js" import { useGlobalSDK } from "./global-sdk" import { usePlatform } from "./platform" export const { use: useSDK, provider: SDKProvider } = createSimpleContext({ name: "SDK", - init: (props: { directory: string }) => { + init: (props: { directory: Accessor }) => { const platform = usePlatform() const globalSDK = useGlobalSDK() - const sdk = createOpencodeClient({ - baseUrl: globalSDK.url, - fetch: platform.fetch, - directory: props.directory, - throwOnError: true, - }) + + const client = createMemo(() => + createOpencodeClient({ + baseUrl: globalSDK.url, + fetch: platform.fetch, + directory: props.directory(), + throwOnError: true, + }), + ) const emitter = createGlobalEmitter<{ [key in Event["type"]]: Extract }>() - const unsub = globalSDK.event.on(props.directory, (event) => { - emitter.emit(event.type, event) - }) - onCleanup(unsub) + let unsub: VoidFunction | undefined + createEffect( + on(props.directory, (directory) => { + unsub?.() + unsub = globalSDK.event.on(directory, (event) => { + emitter.emit(event.type, event) + }) + }), + ) + onCleanup(() => unsub?.()) - return { directory: props.directory, client: sdk, event: emitter, url: globalSDK.url } + return { + get directory() { + return props.directory() + }, + get client() { + return client() + }, + event: emitter, + url: globalSDK.url, + } }, }) diff --git a/packages/app/src/context/sync.tsx b/packages/app/src/context/sync.tsx index 33129e1b475..7bdc9542e4c 100644 --- a/packages/app/src/context/sync.tsx +++ b/packages/app/src/context/sync.tsx @@ -12,7 +12,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ init: () => { const globalSync = useGlobalSync() const sdk = useSDK() - const [store, setStore] = globalSync.child(sdk.directory) + const { store, setStore } = globalSync.reactive(() => sdk.directory) const absolute = (path: string) => (store.path.directory + "/" + path).replace("//", "/") const chunk = 400 const inflight = new Map>() diff --git a/packages/app/src/pages/directory-layout.tsx b/packages/app/src/pages/directory-layout.tsx index dca02489a8a..894ca47127f 100644 --- a/packages/app/src/pages/directory-layout.tsx +++ b/packages/app/src/pages/directory-layout.tsx @@ -16,8 +16,8 @@ export default function Layout(props: ParentProps) { return base64Decode(params.dir!) }) return ( - - + + {iife(() => { const sync = useSync() @@ -40,7 +40,7 @@ export default function Layout(props: ParentProps) { return ( store.workspaceExpanded[props.directory] ?? true) - const loading = createMemo(() => open() && workspaceStore.status !== "complete" && sessions().length === 0) + const loading = createMemo( + () => open() && !workspaceStore.new && workspaceStore.status !== "complete" && sessions().length === 0, + ) const hasMore = createMemo(() => local() && workspaceStore.sessionTotal > workspaceStore.session.length) const loadMore = async () => { if (!local()) return @@ -1804,7 +1806,9 @@ export default function Layout(props: ParentProps) { .filter((session) => !session.parentID && !session.time?.archived) .toSorted(sortSessions), ) - const loading = createMemo(() => workspaceStore.status !== "complete" && sessions().length === 0) + const loading = createMemo( + () => !workspaceStore.new && workspaceStore.status !== "complete" && sessions().length === 0, + ) const hasMore = createMemo(() => workspaceStore.sessionTotal > workspaceStore.session.length) const loadMore = async () => { setWorkspaceStore("limit", (limit) => limit + 5) @@ -1876,7 +1880,8 @@ export default function Layout(props: ParentProps) { if (!created?.directory) return - globalSync.child(created.directory) + globalSync.child(created.directory, { new: true }) + await globalSync.wait(created.directory) navigate(`/${base64Encode(created.directory)}/session`) }