refactor(tui): remove legacy sync context

This commit is contained in:
Dax Raad
2026-07-13 15:46:25 -04:00
parent af8480b2e2
commit 90a87b2a0c
6 changed files with 24 additions and 218 deletions
-1
View File
@@ -23,7 +23,6 @@
"./context/project": "./src/context/project.tsx", "./context/project": "./src/context/project.tsx",
"./context/runtime": "./src/context/runtime.tsx", "./context/runtime": "./src/context/runtime.tsx",
"./context/sdk": "./src/context/sdk.tsx", "./context/sdk": "./src/context/sdk.tsx",
"./context/sync": "./src/context/sync.tsx",
"./context/theme": "./src/context/theme.tsx", "./context/theme": "./src/context/theme.tsx",
"./context/editor": "./src/context/editor.ts", "./context/editor": "./src/context/editor.ts",
"./context/clipboard": "./src/context/clipboard.tsx", "./context/clipboard": "./src/context/clipboard.tsx",
+1 -5
View File
@@ -44,7 +44,6 @@ import { useEvent } from "./context/event"
import { SDKProvider, useSDK } from "./context/sdk" import { SDKProvider, useSDK } from "./context/sdk"
import { StartupLoading } from "./component/startup-loading" import { StartupLoading } from "./component/startup-loading"
import { Reconnecting } from "./component/reconnecting" import { Reconnecting } from "./component/reconnecting"
import { SyncProvider, useSync } from "./context/sync"
import { DataProvider, useData } from "./context/data" import { DataProvider, useData } from "./context/data"
import { LocationProvider } from "./context/location" import { LocationProvider } from "./context/location"
import { LocalProvider, useLocal } from "./context/local" import { LocalProvider, useLocal } from "./context/local"
@@ -345,7 +344,6 @@ export const run = Effect.fn("Tui.run")(function* (input: TuiInput) {
> >
<PermissionProvider> <PermissionProvider>
<ProjectProvider> <ProjectProvider>
<SyncProvider>
<DataProvider> <DataProvider>
<ThemeProvider mode={mode}> <ThemeProvider mode={mode}>
<LocalProvider> <LocalProvider>
@@ -377,7 +375,6 @@ export const run = Effect.fn("Tui.run")(function* (input: TuiInput) {
</LocalProvider> </LocalProvider>
</ThemeProvider> </ThemeProvider>
</DataProvider> </DataProvider>
</SyncProvider>
</ProjectProvider> </ProjectProvider>
</PermissionProvider> </PermissionProvider>
</SDKProvider> </SDKProvider>
@@ -430,7 +427,6 @@ function App(props: {
const keymap = useOpencodeKeymap() const keymap = useOpencodeKeymap()
const event = useEvent() const event = useEvent()
const sdk = useSDK() const sdk = useSDK()
const sync = useSync()
const toast = useToast() const toast = useToast()
const themeState = useTheme() const themeState = useTheme()
const { theme, mode, setMode, locked, lock, unlock } = themeState const { theme, mode, setMode, locked, lock, unlock } = themeState
@@ -480,7 +476,7 @@ function App(props: {
routes: pluginRuntime.routes, routes: pluginRuntime.routes,
event, event,
sdk, sdk,
sync, project,
data, data,
theme: themeState, theme: themeState,
toast, toast,
-94
View File
@@ -1,94 +0,0 @@
import type {
Agent,
Command,
Config,
FormatterStatus,
LspStatus,
McpResource,
McpStatus,
Message,
Part,
PermissionRequest,
Provider,
QuestionRequest,
Session,
FileDiffInfo,
VcsInfo,
} from "@opencode-ai/sdk/v2"
import { createStore } from "solid-js/store"
import { createSimpleContext } from "./helper"
import { useProject } from "./project"
export const {
context: SyncContext,
use: useSync,
provider: SyncProvider,
} = createSimpleContext({
name: "Sync",
init: () => {
const project = useProject()
const [store, setStore] = createStore<{
status: "loading" | "partial" | "complete"
provider: Provider[]
agent: Agent[]
command: Command[]
permission: Record<string, PermissionRequest[]>
question: Record<string, QuestionRequest[]>
config: Config
session: Session[]
session_diff: Record<string, FileDiffInfo[]>
message: Record<string, Message[]>
part: Record<string, Part[]>
lsp: LspStatus[]
mcp: Record<string, McpStatus>
mcp_resource: Record<string, McpResource>
formatter: FormatterStatus[]
vcs: VcsInfo | undefined
}>({
status: "complete",
provider: [],
agent: [],
command: [],
permission: {},
question: {},
config: {},
session: [],
session_diff: {},
message: {},
part: {},
lsp: [],
mcp: {},
mcp_resource: {},
formatter: [],
vcs: undefined,
})
return {
data: store,
set: setStore,
get status() {
return store.status
},
get ready() {
return true
},
get path() {
return project.instance.path()
},
session: {
get(_sessionID: string) {
return undefined as Session | undefined
},
query() {
return {} as { scope?: "project"; path?: string }
},
async refresh() {},
status(_sessionID: string) {
return "idle" as const
},
async sync(_sessionID: string) {},
},
async bootstrap(_input: { fatal?: boolean } = {}) {},
}
},
})
+21 -27
View File
@@ -3,8 +3,8 @@ import type { Config } from "../config"
import type { useEvent } from "../context/event" import type { useEvent } from "../context/event"
import type { useRoute } from "../context/route" import type { useRoute } from "../context/route"
import type { useSDK } from "../context/sdk" import type { useSDK } from "../context/sdk"
import type { useSync } from "../context/sync"
import type { useData } from "../context/data" import type { useData } from "../context/data"
import type { useProject } from "../context/project"
import type { useTheme } from "../context/theme" import type { useTheme } from "../context/theme"
import { Dialog as DialogUI, type useDialog } from "../ui/dialog" import { Dialog as DialogUI, type useDialog } from "../ui/dialog"
import type { useOpencodeKeymap } from "../keymap" import type { useOpencodeKeymap } from "../keymap"
@@ -29,7 +29,7 @@ type Input = {
routes: PluginRoutes routes: PluginRoutes
event: ReturnType<typeof useEvent> event: ReturnType<typeof useEvent>
sdk: ReturnType<typeof useSDK> sdk: ReturnType<typeof useSDK>
sync: ReturnType<typeof useSync> project: ReturnType<typeof useProject>
data: ReturnType<typeof useData> data: ReturnType<typeof useData>
theme: ReturnType<typeof useTheme> theme: ReturnType<typeof useTheme>
toast: ReturnType<typeof useToast> toast: ReturnType<typeof useToast>
@@ -95,57 +95,51 @@ function mapOptionCb<Value>(cb?: (item: TuiDialogSelectOption<Value>) => void) {
return (item: SelectOption<Value>) => cb(pickOption(item)) return (item: SelectOption<Value>) => cb(pickOption(item))
} }
function stateApi(sync: ReturnType<typeof useSync>, data: ReturnType<typeof useData>): TuiPluginApi["state"] { function stateApi(project: ReturnType<typeof useProject>, data: ReturnType<typeof useData>): TuiPluginApi["state"] {
return { return {
get ready() { get ready() {
return true return true
}, },
get config() { get config() {
return sync.data.config return {}
}, },
get provider() { get provider() {
return sync.data.provider return []
}, },
get path() { get path() {
return sync.path return project.instance.path()
}, },
get vcs() { get vcs() {
if (!sync.data.vcs) return return undefined
return {
branch: sync.data.vcs.branch,
default_branch: sync.data.vcs.default_branch,
}
}, },
session: { session: {
count() { count() {
return data.session.list().length return data.session.list().length
}, },
get(sessionID) { get(_sessionID) {
return sync.session.get(sessionID) return undefined
}, },
diff(sessionID) { diff(_sessionID) {
return (sync.data.session_diff[sessionID] ?? []).flatMap((item) => return []
item.file === undefined ? [] : [{ ...item, file: item.file }],
)
}, },
messages(sessionID) { messages(_sessionID) {
return sync.data.message[sessionID] ?? [] return []
}, },
status(sessionID) { status(sessionID) {
return data.session.status(sessionID) === "running" ? { type: "busy" } : { type: "idle" } return data.session.status(sessionID) === "running" ? { type: "busy" } : { type: "idle" }
}, },
permission(sessionID) { permission(_sessionID) {
return sync.data.permission[sessionID] ?? [] return []
}, },
question(sessionID) { question(_sessionID) {
return sync.data.question[sessionID] ?? [] return []
}, },
}, },
part(messageID) { part(_messageID) {
return sync.data.part[messageID] ?? [] return []
}, },
lsp() { lsp() {
return sync.data.lsp.map((item) => ({ id: item.id, root: item.root, status: item.status })) return []
}, },
mcp() { mcp() {
return (data.location.mcp.server.list() ?? []) return (data.location.mcp.server.list() ?? [])
@@ -297,7 +291,7 @@ export function createTuiApiAdapters(input: Input): Omit<TuiPluginApi, "lifecycl
set() {}, set() {},
ready: true, ready: true,
}, },
state: stateApi(input.sync, input.data), state: stateApi(input.project, input.data),
get client() { get client() {
return input.sdk.client return input.sdk.client
}, },
@@ -1,65 +0,0 @@
/** @jsxImportSource @opentui/solid */
import { testRender } from "@opentui/solid"
import { onMount } from "solid-js"
import { ArgsProvider } from "../../../../src/context/args"
import { ProjectProvider, useProject } from "../../../../src/context/project"
import { SDKProvider } from "../../../../src/context/sdk"
import { SyncProvider, useSync } from "../../../../src/context/sync"
import { PermissionProvider } from "../../../../src/context/permission"
import { ExitProvider } from "../../../../src/context/exit"
import { createApi, createClient, createEventStream, createFetch, type FetchHandler } from "../../../fixture/tui-sdk"
import { TestTuiContexts } from "../../../fixture/tui-environment"
export { createEventStream, createFetch, directory, json, worktree } from "../../../fixture/tui-sdk"
export async function wait(fn: () => boolean, timeout = 2000) {
const start = Date.now()
while (!fn()) {
if (Date.now() - start > timeout) throw new Error("timed out waiting for condition")
await Bun.sleep(10)
}
}
type Ctx = { project: ReturnType<typeof useProject>; sync: ReturnType<typeof useSync> }
export async function mount(override?: FetchHandler, state?: string) {
const events = createEventStream()
const calls = createFetch(override, events)
let sync!: ReturnType<typeof useSync>
let project!: ReturnType<typeof useProject>
let done!: () => void
const ready = new Promise<void>((resolve) => {
done = resolve
})
function Probe() {
const ctx: Ctx = { project: useProject(), sync: useSync() }
onMount(() => {
sync = ctx.sync
project = ctx.project
done()
})
return <box />
}
const app = await testRender(() => (
<TestTuiContexts paths={state ? { state } : undefined}>
<ArgsProvider>
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
<PermissionProvider>
<ProjectProvider>
<ExitProvider exit={() => {}}>
<SyncProvider>
<Probe />
</SyncProvider>
</ExitProvider>
</ProjectProvider>
</PermissionProvider>
</SDKProvider>
</ArgsProvider>
</TestTuiContexts>
))
await ready
await wait(() => sync.status === "complete")
return { app, emit: events.emit, project, sync, session: calls.session }
}
@@ -1,24 +0,0 @@
/** @jsxImportSource @opentui/solid */
import { expect, test } from "bun:test"
import { mount } from "./sync-fixture"
test("legacy sync is an inert compatibility context", async () => {
const { app, session, sync } = await mount()
try {
expect(sync.status).toBe("complete")
expect(sync.ready).toBe(true)
expect(sync.data.session).toEqual([])
expect(sync.data.message).toEqual({})
expect(sync.data.provider).toEqual([])
expect(sync.session.get("ses_test")).toBeUndefined()
await sync.bootstrap()
await sync.session.refresh()
await sync.session.sync("ses_test")
expect(session).toEqual([])
} finally {
app.renderer.destroy()
}
})