diff --git a/packages/app/src/components/prompt-input/submit.ts b/packages/app/src/components/prompt-input/submit.ts index 7614bb5ea..024bdd7ae 100644 --- a/packages/app/src/components/prompt-input/submit.ts +++ b/packages/app/src/components/prompt-input/submit.ts @@ -547,10 +547,12 @@ export function createPromptSubmit(input: PromptSubmitInput) { }, timeoutMs) }) - const result = await Promise.race([WorktreeState.wait(sdk.scope, sessionDirectory), abortWait, timeout]).finally(() => { - if (timer.id === undefined) return - clearTimeout(timer.id) - }) + const result = await Promise.race([WorktreeState.wait(sdk.scope, sessionDirectory), abortWait, timeout]).finally( + () => { + if (timer.id === undefined) return + clearTimeout(timer.id) + }, + ) pending.delete(pendingKey(session.id)) if (controller.signal.aborted) return false if (result.status === "failed") throw new Error(result.message) diff --git a/packages/app/src/context/comments.tsx b/packages/app/src/context/comments.tsx index 64539e6d9..32946da7c 100644 --- a/packages/app/src/context/comments.tsx +++ b/packages/app/src/context/comments.tsx @@ -207,7 +207,11 @@ export const { use: useComments, provider: CommentsProvider } = createSimpleCont (key) => { const decoded = decodeSessionKey(key) return createRoot((dispose) => ({ - value: createCommentSession(serverSDK.scope, decoded.dir, decoded.id === WORKSPACE_KEY ? undefined : decoded.id), + value: createCommentSession( + serverSDK.scope, + decoded.dir, + decoded.id === WORKSPACE_KEY ? undefined : decoded.id, + ), dispose, })) }, diff --git a/packages/app/src/context/file.tsx b/packages/app/src/context/file.tsx index a4088810a..fcfd750c2 100644 --- a/packages/app/src/context/file.tsx +++ b/packages/app/src/context/file.tsx @@ -64,7 +64,9 @@ export const { use: useFile, provider: FileProvider } = createSimpleContext({ const scope = createMemo(() => sdk.directory) const path = createPathHelpers(scope) - const tabs = layout.tabs(() => SessionStateKey.from(serverSDK.scope, SessionRouteKey.fromRoute(params.dir, params.id))) + const tabs = layout.tabs(() => + SessionStateKey.from(serverSDK.scope, SessionRouteKey.fromRoute(params.dir, params.id)), + ) const inflight = new Map>() const [store, setStore] = createStore<{ diff --git a/packages/app/src/context/global-sync/bootstrap.test.ts b/packages/app/src/context/global-sync/bootstrap.test.ts index 48e49cc07..8393b0474 100644 --- a/packages/app/src/context/global-sync/bootstrap.test.ts +++ b/packages/app/src/context/global-sync/bootstrap.test.ts @@ -99,6 +99,10 @@ describe("query keys", () => { expect([...loadPathQuery(ServerScope.local, "/repo", client).queryKey]).toEqual(["local", "/repo", "path"]) expect([...loadPathQuery(remote, "/repo", client).queryKey]).toEqual(["https://debian.example", "/repo", "path"]) - expect([...loadProvidersQuery(remote, null, client).queryKey]).toEqual(["https://debian.example", null, "providers"]) + expect([...loadProvidersQuery(remote, null, client).queryKey]).toEqual([ + "https://debian.example", + null, + "providers", + ]) }) }) diff --git a/packages/app/src/context/global-sync/session-prefetch.test.ts b/packages/app/src/context/global-sync/session-prefetch.test.ts index 89ba0a9a1..cf8ee77b4 100644 --- a/packages/app/src/context/global-sync/session-prefetch.test.ts +++ b/packages/app/src/context/global-sync/session-prefetch.test.ts @@ -26,7 +26,12 @@ describe("session prefetch", () => { at: 123, }) - expect(getSessionPrefetch(scope, "/tmp/a", "ses_1")).toEqual({ limit: 200, cursor: "abc", complete: false, at: 123 }) + expect(getSessionPrefetch(scope, "/tmp/a", "ses_1")).toEqual({ + limit: 200, + cursor: "abc", + complete: false, + at: 123, + }) expect(getSessionPrefetch(scope, "/tmp/b", "ses_1")).toBeUndefined() clearSessionPrefetch(scope, "/tmp/a", ["ses_1"]) @@ -57,9 +62,33 @@ describe("session prefetch", () => { }) test("clears a whole directory", () => { - setSessionPrefetch({ scope, directory: "/tmp/d", sessionID: "ses_1", limit: 10, cursor: "a", complete: true, at: 1 }) - setSessionPrefetch({ scope, directory: "/tmp/d", sessionID: "ses_2", limit: 20, cursor: "b", complete: false, at: 2 }) - setSessionPrefetch({ scope, directory: "/tmp/e", sessionID: "ses_1", limit: 30, cursor: "c", complete: true, at: 3 }) + setSessionPrefetch({ + scope, + directory: "/tmp/d", + sessionID: "ses_1", + limit: 10, + cursor: "a", + complete: true, + at: 1, + }) + setSessionPrefetch({ + scope, + directory: "/tmp/d", + sessionID: "ses_2", + limit: 20, + cursor: "b", + complete: false, + at: 2, + }) + setSessionPrefetch({ + scope, + directory: "/tmp/e", + sessionID: "ses_1", + limit: 30, + cursor: "c", + complete: true, + at: 3, + }) clearSessionPrefetchDirectory(scope, "/tmp/d") diff --git a/packages/app/src/context/server-sync.tsx b/packages/app/src/context/server-sync.tsx index e05cdc6cb..9a61c4641 100644 --- a/packages/app/src/context/server-sync.tsx +++ b/packages/app/src/context/server-sync.tsx @@ -63,7 +63,11 @@ export const loadLspQuery = (scope: ServerScope, directory: string, sdk: Opencod queryFn: () => sdk.lsp.status().then((r) => r.data ?? []), }) -function makeQueryOptionsApi(scope: ServerScope, serverSDK: () => OpencodeClient, sdkFor: (dir: PathKey) => OpencodeClient) { +function makeQueryOptionsApi( + scope: ServerScope, + serverSDK: () => OpencodeClient, + sdkFor: (dir: PathKey) => OpencodeClient, +) { return { globalConfig: () => loadGlobalConfigQuery(scope, serverSDK()), projects: () => loadProjectsQuery(scope, serverSDK()), @@ -447,7 +451,9 @@ export function createServerSyncContextInner(_serverSDK?: ServerSDK) { // Invalidate all provider queries so newly configured custom providers // appear immediately in the available provider list across all directories. queryClient.invalidateQueries({ queryKey: [serverSDK.scope, null, "providers"] }) - queryClient.invalidateQueries({ predicate: (query) => query.queryKey[0] === serverSDK.scope && query.queryKey[2] === "providers" }) + queryClient.invalidateQueries({ + predicate: (query) => query.queryKey[0] === serverSDK.scope && query.queryKey[2] === "providers", + }) }, })) diff --git a/packages/app/src/context/server.tsx b/packages/app/src/context/server.tsx index 9e3b892d4..cc8e30b53 100644 --- a/packages/app/src/context/server.tsx +++ b/packages/app/src/context/server.tsx @@ -80,7 +80,11 @@ export function createServerProjects(input: { setStore("projects", scope, [{ worktree: directory, expanded: true }, ...current()]) }, close(directory: string) { - setStore("projects", input.scope(), current().filter((project) => project.worktree !== directory)) + setStore( + "projects", + input.scope(), + current().filter((project) => project.worktree !== directory), + ) }, expand(directory: string) { const index = current().findIndex((project) => project.worktree === directory) diff --git a/packages/app/src/pages/home.tsx b/packages/app/src/pages/home.tsx index 2ecaa3979..b15e528a1 100644 --- a/packages/app/src/pages/home.tsx +++ b/packages/app/src/pages/home.tsx @@ -118,14 +118,23 @@ function createHomeSessionStatus(input: { const notification = useNotification() const permission = usePermission() const sessionStore = createMemo(() => input.sync().child(input.record().session.directory, { bootstrap: false })[0]) - const unseenCount = createMemo(() => (input.activeServer() ? notification.session.unseenCount(input.record().session.id) : 0)) - const hasError = createMemo(() => input.activeServer() && notification.session.unseenHasError(input.record().session.id)) + const unseenCount = createMemo(() => + input.activeServer() ? notification.session.unseenCount(input.record().session.id) : 0, + ) + const hasError = createMemo( + () => input.activeServer() && notification.session.unseenHasError(input.record().session.id), + ) const hasPermissions = createMemo( () => input.activeServer() && - !!sessionPermissionRequest(sessionStore().session, sessionStore().permission, input.record().session.id, (item) => { - return !permission.autoResponds(item, input.record().session.directory) - }), + !!sessionPermissionRequest( + sessionStore().session, + sessionStore().permission, + input.record().session.id, + (item) => { + return !permission.autoResponds(item, input.record().session.directory) + }, + ), ) const serverStatus = createMemo(() => homeSessionServerStatus(input.activeServer(), () => ({ @@ -278,7 +287,13 @@ function HomeDesign() { function selectProject(conn: ServerConnection.Any, directory: string) { const key = ServerConnection.key(conn) - if (!global.createServerCtx(conn).projects.list().some((project) => project.worktree === directory)) return + if ( + !global + .createServerCtx(conn) + .projects.list() + .some((project) => project.worktree === directory) + ) + return setSelection(toggleHomeProjectSelection(state.selection, key, directory)) } @@ -534,11 +549,7 @@ function HomeProjectColumn(props: {
- +
) @@ -586,7 +597,10 @@ function HomeProjectList(props: { props.editProject(props.server, props.project)}> {props.language.t("common.edit")} - props.clearNotifications(props.server, props.project)}> + props.clearNotifications(props.server, props.project)} + > {props.language.t("sidebar.project.clearNotifications")} @@ -902,7 +919,11 @@ function HomeSessionSearchResultRow(props: { onHighlight: () => void onSelect: (session: Session) => void }) { - const status = createHomeSessionStatus({ record: () => props.record, sync: () => props.sync, activeServer: () => props.activeServer }) + const status = createHomeSessionStatus({ + record: () => props.record, + sync: () => props.sync, + activeServer: () => props.activeServer, + }) const title = createMemo(() => sessionTitle(props.record.session.title) || props.record.session.id) const key = () => homeSessionSearchKey(props.record) @@ -993,7 +1014,11 @@ function HomeSessionRow(props: { activeServer: boolean openSession: (session: Session) => void }) { - const status = createHomeSessionStatus({ record: () => props.record, sync: () => props.sync, activeServer: () => props.activeServer }) + const status = createHomeSessionStatus({ + record: () => props.record, + sync: () => props.sync, + activeServer: () => props.activeServer, + }) const title = createMemo(() => sessionTitle(props.record.session.title) || props.record.session.id) return ( diff --git a/packages/app/src/pages/layout/helpers.test.ts b/packages/app/src/pages/layout/helpers.test.ts index 9353bce4a..df87ddfec 100644 --- a/packages/app/src/pages/layout/helpers.test.ts +++ b/packages/app/src/pages/layout/helpers.test.ts @@ -227,7 +227,9 @@ describe("layout workspace helpers", () => { }) test("scopes home project selection by server", () => { - expect(toggleHomeProjectSelection(undefined, serverKey("https://debian.example"), "/home/luke/repos/amazon")).toEqual({ + expect( + toggleHomeProjectSelection(undefined, serverKey("https://debian.example"), "/home/luke/repos/amazon"), + ).toEqual({ server: serverKey("https://debian.example"), directory: "/home/luke/repos/amazon", }) @@ -270,11 +272,19 @@ describe("layout workspace helpers", () => { }) test("defers home project navigation until its server is active", () => { - expect(homeProjectNavigation(serverKey("sidecar"), serverKey("https://debian.example"), "/YW1hem9u/session")).toEqual({ + expect( + homeProjectNavigation(serverKey("sidecar"), serverKey("https://debian.example"), "/YW1hem9u/session"), + ).toEqual({ server: serverKey("https://debian.example"), href: "/YW1hem9u/session", }) - expect(homeProjectNavigation(serverKey("https://debian.example"), serverKey("https://debian.example"), "/YW1hem9u/session")).toEqual({ + expect( + homeProjectNavigation( + serverKey("https://debian.example"), + serverKey("https://debian.example"), + "/YW1hem9u/session", + ), + ).toEqual({ href: "/YW1hem9u/session", }) }) diff --git a/packages/app/src/pages/layout/project-avatar-state.ts b/packages/app/src/pages/layout/project-avatar-state.ts index 443dbab57..e98922ef5 100644 --- a/packages/app/src/pages/layout/project-avatar-state.ts +++ b/packages/app/src/pages/layout/project-avatar-state.ts @@ -4,7 +4,11 @@ import { useNotification } from "@/context/notification" import { usePermission } from "@/context/permission" import { sessionPermissionRequest } from "@/pages/session/composer/session-request-tree" -export function useSessionTabAvatarState(directory: Accessor, sessionId: Accessor, active: Accessor = () => true) { +export function useSessionTabAvatarState( + directory: Accessor, + sessionId: Accessor, + active: Accessor = () => true, +) { const globalSync = useServerSync() const notification = useNotification() const permission = usePermission() diff --git a/packages/app/src/utils/server-scope.test.ts b/packages/app/src/utils/server-scope.test.ts index 87d579a2f..67d43b7ab 100644 --- a/packages/app/src/utils/server-scope.test.ts +++ b/packages/app/src/utils/server-scope.test.ts @@ -3,13 +3,15 @@ import { ScopedKey, ServerScope, SessionRouteKey, SessionStateKey, migrateLegacy describe("ServerScope", () => { test("uses a stable local scope for the canonical sidecar", () => { - expect(String(ServerScope.fromServerKey("sidecar" as Parameters[0]))).toBe("local") + expect(String(ServerScope.fromServerKey("sidecar" as Parameters[0]))).toBe( + "local", + ) }) test("keeps configured loopback servers distinct from the canonical sidecar", () => { - expect(String(ServerScope.fromServerKey("http://localhost:4096" as Parameters[0]))).toBe( - "http://localhost:4096", - ) + expect( + String(ServerScope.fromServerKey("http://localhost:4096" as Parameters[0])), + ).toBe("http://localhost:4096") }) test("uses a stable local scope for an explicit canonical web server", () => { @@ -52,6 +54,8 @@ describe("migrateLegacySessionStateKeys", () => { }) test("rejects invalid identity fragments", () => { - expect(() => ScopedKey.from(ServerScope.local, "bad\0directory")).toThrow("Scoped key part cannot contain null bytes") + expect(() => ScopedKey.from(ServerScope.local, "bad\0directory")).toThrow( + "Scoped key part cannot contain null bytes", + ) }) }) diff --git a/packages/app/src/utils/server-scope.ts b/packages/app/src/utils/server-scope.ts index 095f0d88d..9a4e941d3 100644 --- a/packages/app/src/utils/server-scope.ts +++ b/packages/app/src/utils/server-scope.ts @@ -19,7 +19,10 @@ function compose(scope: ServerScope, parts: string[]) { export const ServerScope = { local: "local" as ServerScope, fromServerKey(key: ServerConnection.Key, canonicalLocalServer?: ServerConnection.Key) { - return fragment("Server scope", key === "sidecar" || key === canonicalLocalServer ? ServerScope.local : key) as ServerScope + return fragment( + "Server scope", + key === "sidecar" || key === canonicalLocalServer ? ServerScope.local : key, + ) as ServerScope }, }