feat(app): persist workspace branch

This commit is contained in:
Adam
2026-01-15 14:46:26 -06:00
parent da3dea0429
commit 47d43aaf2d

View File

@@ -19,15 +19,27 @@ import {
type QuestionRequest, type QuestionRequest,
createOpencodeClient, createOpencodeClient,
} from "@opencode-ai/sdk/v2/client" } from "@opencode-ai/sdk/v2/client"
import { createStore, produce, reconcile } from "solid-js/store" import { createStore, produce, reconcile, type SetStoreFunction, type Store } from "solid-js/store"
import { Binary } from "@opencode-ai/util/binary" import { Binary } from "@opencode-ai/util/binary"
import { retry } from "@opencode-ai/util/retry" import { retry } from "@opencode-ai/util/retry"
import { useGlobalSDK } from "./global-sdk" import { useGlobalSDK } from "./global-sdk"
import { ErrorPage, type InitError } from "../pages/error" import { ErrorPage, type InitError } from "../pages/error"
import { batch, createContext, useContext, onCleanup, onMount, type ParentProps, Switch, Match } from "solid-js" import {
batch,
createContext,
createEffect,
useContext,
onCleanup,
onMount,
type Accessor,
type ParentProps,
Switch,
Match,
} from "solid-js"
import { showToast } from "@opencode-ai/ui/toast" import { showToast } from "@opencode-ai/ui/toast"
import { getFilename } from "@opencode-ai/util/path" import { getFilename } from "@opencode-ai/util/path"
import { usePlatform } from "./platform" import { usePlatform } from "./platform"
import { Persist, persisted } from "@/utils/persist"
type State = { type State = {
status: "loading" | "partial" | "complete" status: "loading" | "partial" | "complete"
@@ -68,9 +80,16 @@ type State = {
} }
} }
type VcsCache = {
store: Store<{ value: VcsInfo | undefined }>
setStore: SetStoreFunction<{ value: VcsInfo | undefined }>
ready: Accessor<boolean>
}
function createGlobalSync() { function createGlobalSync() {
const globalSDK = useGlobalSDK() const globalSDK = useGlobalSDK()
const platform = usePlatform() const platform = usePlatform()
const vcsCache = new Map<string, VcsCache>()
const [globalStore, setGlobalStore] = createStore<{ const [globalStore, setGlobalStore] = createStore<{
ready: boolean ready: boolean
error?: InitError error?: InitError
@@ -86,10 +105,16 @@ function createGlobalSync() {
provider_auth: {}, provider_auth: {},
}) })
const children: Record<string, ReturnType<typeof createStore<State>>> = {} const children: Record<string, [Store<State>, SetStoreFunction<State>]> = {}
function child(directory: string) { function child(directory: string) {
if (!directory) console.error("No directory provided") if (!directory) console.error("No directory provided")
if (!children[directory]) { if (!children[directory]) {
const cache = persisted(
Persist.workspace(directory, "vcs", ["vcs.v1"]),
createStore({ value: undefined as VcsInfo | undefined }),
)
vcsCache.set(directory, { store: cache[0], setStore: cache[1], ready: cache[3] })
children[directory] = createStore<State>({ children[directory] = createStore<State>({
project: "", project: "",
provider: { all: [], connected: [], default: {} }, provider: { all: [], connected: [], default: {} },
@@ -107,14 +132,16 @@ function createGlobalSync() {
question: {}, question: {},
mcp: {}, mcp: {},
lsp: [], lsp: [],
vcs: undefined, vcs: cache[0].value,
limit: 5, limit: 5,
message: {}, message: {},
part: {}, part: {},
}) })
bootstrapInstance(directory) bootstrapInstance(directory)
} }
return children[directory] const childStore = children[directory]
if (!childStore) throw new Error("Failed to create store")
return childStore
} }
async function loadSessions(directory: string) { async function loadSessions(directory: string) {
@@ -157,6 +184,8 @@ function createGlobalSync() {
async function bootstrapInstance(directory: string) { async function bootstrapInstance(directory: string) {
if (!directory) return if (!directory) return
const [store, setStore] = child(directory) const [store, setStore] = child(directory)
const cache = vcsCache.get(directory)
if (!cache) return
const sdk = createOpencodeClient({ const sdk = createOpencodeClient({
baseUrl: globalSDK.url, baseUrl: globalSDK.url,
fetch: platform.fetch, fetch: platform.fetch,
@@ -164,6 +193,13 @@ function createGlobalSync() {
throwOnError: true, throwOnError: true,
}) })
createEffect(() => {
if (!cache.ready()) return
const cached = cache.store.value
if (!cached?.branch) return
setStore("vcs", (value) => value ?? cached)
})
const blockingRequests = { const blockingRequests = {
project: () => sdk.project.current().then((x) => setStore("project", x.data!.id)), project: () => sdk.project.current().then((x) => setStore("project", x.data!.id)),
provider: () => provider: () =>
@@ -193,7 +229,11 @@ function createGlobalSync() {
loadSessions(directory), loadSessions(directory),
sdk.mcp.status().then((x) => setStore("mcp", x.data!)), sdk.mcp.status().then((x) => setStore("mcp", x.data!)),
sdk.lsp.status().then((x) => setStore("lsp", x.data!)), sdk.lsp.status().then((x) => setStore("lsp", x.data!)),
sdk.vcs.get().then((x) => setStore("vcs", x.data)), sdk.vcs.get().then((x) => {
const next = x.data ?? store.vcs
setStore("vcs", next)
if (next?.branch) cache.setStore("value", next)
}),
sdk.permission.list().then((x) => { sdk.permission.list().then((x) => {
const grouped: Record<string, PermissionRequest[]> = {} const grouped: Record<string, PermissionRequest[]> = {}
for (const perm of x.data ?? []) { for (const perm of x.data ?? []) {
@@ -406,7 +446,10 @@ function createGlobalSync() {
break break
} }
case "vcs.branch.updated": { case "vcs.branch.updated": {
setStore("vcs", { branch: event.properties.branch }) const next = { branch: event.properties.branch }
setStore("vcs", next)
const cache = vcsCache.get(directory)
if (cache) cache.setStore("value", next)
break break
} }
case "permission.asked": { case "permission.asked": {