mirror of
https://github.com/anomalyco/opencode.git
synced 2026-07-10 04:38:31 +00:00
fix(app): close missing session tabs (#33785)
This commit is contained in:
parent
9c0d813b8e
commit
bdee94ca40
4 changed files with 73 additions and 22 deletions
|
|
@ -58,6 +58,7 @@ import {
|
|||
selectSessionLineage,
|
||||
sessionHref,
|
||||
} from "./utils/session-route"
|
||||
import { isSessionNotFoundError } from "./utils/server-errors"
|
||||
|
||||
import Session from "@/pages/session"
|
||||
import { NewHome, LegacyHome } from "@/pages/home"
|
||||
|
|
@ -129,9 +130,13 @@ function ResolvedTargetSessionRoute() {
|
|||
const [resolved] = createResource(
|
||||
() => {
|
||||
if (cached()) return
|
||||
return { id: params.id, sync: sync() }
|
||||
return { id: params.id, server: serverKey(), sync: sync() }
|
||||
},
|
||||
({ id, sync }) => sync.session.lineage.resolve(id),
|
||||
({ id, server, sync }) =>
|
||||
sync.session.lineage.resolve(id).catch((error) => {
|
||||
if (isSessionNotFoundError(error, id)) tabs.removeSessionTab({ server, sessionId: id })
|
||||
throw error
|
||||
}),
|
||||
)
|
||||
const current = createMemo(() => selectSessionLineage(params.id, cached(), resolved()))
|
||||
const directory = createMemo(() => current()?.session.directory)
|
||||
|
|
|
|||
|
|
@ -112,6 +112,27 @@ export const { use: useTabs, provider: TabsProvider } = createSimpleContext({
|
|||
navigate(href)
|
||||
}
|
||||
|
||||
const removeTab = (index: number) => {
|
||||
const tab = store[index]
|
||||
if (!tab) return
|
||||
const key = tabKey(tab)
|
||||
const draftID = tab.type === "draft" ? tab.draftID : undefined
|
||||
const nextTab = store[index + 1] ?? store[index - 1]
|
||||
closing.add(key)
|
||||
void startTransition(() => {
|
||||
setStore(
|
||||
produce((tabs) => {
|
||||
tabs.splice(index, 1)
|
||||
}),
|
||||
)
|
||||
if (recent.key === key) setRecentKey(nextTab && tabKey(nextTab))
|
||||
if (nextTab) navigateTab(nextTab)
|
||||
else navigate("/")
|
||||
}).finally(() => closing.delete(key))
|
||||
memory.remove(key)
|
||||
if (draftID) removeDraftPersisted(draftID)
|
||||
}
|
||||
|
||||
const actions = {
|
||||
addSessionTab: (tab: Omit<SessionTab, "type">) => {
|
||||
const next = { type: "session" as const, ...tab }
|
||||
|
|
@ -175,25 +196,12 @@ export const { use: useTabs, provider: TabsProvider } = createSimpleContext({
|
|||
memory.remove(`draft:${draftID}`)
|
||||
removeDraftPersisted(draftID)
|
||||
},
|
||||
removeTab: (index: number) => {
|
||||
const tab = store[index]
|
||||
if (!tab) return
|
||||
const key = tabKey(tab)
|
||||
const draftID = tab.type === "draft" ? tab.draftID : undefined
|
||||
const nextTab = store[index + 1] ?? store[index - 1]
|
||||
closing.add(key)
|
||||
void startTransition(() => {
|
||||
setStore(
|
||||
produce((tabs) => {
|
||||
tabs.splice(index, 1)
|
||||
}),
|
||||
)
|
||||
if (recent.key === key) setRecentKey(nextTab && tabKey(nextTab))
|
||||
if (nextTab) navigateTab(nextTab)
|
||||
else navigate("/")
|
||||
}).finally(() => closing.delete(key))
|
||||
memory.remove(key)
|
||||
if (draftID) removeDraftPersisted(draftID)
|
||||
removeTab,
|
||||
removeSessionTab(input: Omit<SessionTab, "type">) {
|
||||
const index = store.findIndex(
|
||||
(tab) => tab.type === "session" && tab.server === input.server && tab.sessionId === input.sessionId,
|
||||
)
|
||||
if (index !== -1) removeTab(index)
|
||||
},
|
||||
removeServer(key: ServerConnection.Key) {
|
||||
const drafts = store.flatMap((tab) => (tab.type === "draft" && tab.server === key ? [tab.draftID] : []))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { describe, expect, test } from "bun:test"
|
||||
import type { SessionNotFoundError } from "@opencode-ai/sdk/v2/client"
|
||||
import type { ConfigInvalidError, ProviderModelNotFoundError } from "./server-errors"
|
||||
import { formatServerError, parseReadableConfigInvalidError } from "./server-errors"
|
||||
import { formatServerError, isSessionNotFoundError, parseReadableConfigInvalidError } from "./server-errors"
|
||||
|
||||
function fill(text: string, vars?: Record<string, string | number>) {
|
||||
if (!vars) return text
|
||||
|
|
@ -142,3 +143,33 @@ describe("formatServerError", () => {
|
|||
expect(formatServerError(wrapped, language.t)).toBe("Arquivo de config em config invalido: Missing host")
|
||||
})
|
||||
})
|
||||
|
||||
describe("isSessionNotFoundError", () => {
|
||||
test("matches an SDK-wrapped error for the requested session", () => {
|
||||
const body = {
|
||||
_tag: "SessionNotFoundError",
|
||||
sessionID: "ses_missing",
|
||||
message: "Session not found",
|
||||
} satisfies SessionNotFoundError
|
||||
|
||||
expect(isSessionNotFoundError(new Error(body.message, { cause: { body, status: 404 } }), body.sessionID)).toBe(true)
|
||||
})
|
||||
|
||||
test("rejects errors for other sessions and other 404 responses", () => {
|
||||
const body = {
|
||||
_tag: "SessionNotFoundError",
|
||||
sessionID: "ses_parent",
|
||||
message: "Session not found",
|
||||
} satisfies SessionNotFoundError
|
||||
|
||||
expect(isSessionNotFoundError(new Error(body.message, { cause: { body, status: 404 } }), "ses_tab")).toBe(false)
|
||||
expect(
|
||||
isSessionNotFoundError(
|
||||
new Error("Provider not found", {
|
||||
cause: { body: { _tag: "ProviderNotFoundError", providerID: "missing" }, status: 404 },
|
||||
}),
|
||||
"ses_tab",
|
||||
),
|
||||
).toBe(false)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -42,6 +42,13 @@ function unwrapNamedError(error: unknown): unknown {
|
|||
return error
|
||||
}
|
||||
|
||||
export function isSessionNotFoundError(error: unknown, sessionID: string) {
|
||||
const unwrapped = unwrapNamedError(error)
|
||||
if (typeof unwrapped !== "object" || unwrapped === null) return false
|
||||
const value = unwrapped as Record<string, unknown>
|
||||
return value._tag === "SessionNotFoundError" && value.sessionID === sessionID
|
||||
}
|
||||
|
||||
function isConfigInvalidErrorLike(error: unknown): error is ConfigInvalidError {
|
||||
if (typeof error !== "object" || error === null) return false
|
||||
const o = error as Record<string, unknown>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue