diff options
| author | Adam <[email protected]> | 2026-02-12 09:49:14 -0600 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-02-12 09:49:14 -0600 |
| commit | ff4414bb152acfddb5c0eb073c38bedc1df4ae14 (patch) | |
| tree | 78381c67d21ef6f089647f6b19e7aa2976840dbc /packages/app/src/context/sync.tsx | |
| parent | 56ad2db02055955f926fda0e4a89055b22ead6f9 (diff) | |
| download | opencode-ff4414bb152acfddb5c0eb073c38bedc1df4ae14.tar.gz opencode-ff4414bb152acfddb5c0eb073c38bedc1df4ae14.zip | |
chore: refactor packages/app files (#13236)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: Frank <[email protected]>
Diffstat (limited to 'packages/app/src/context/sync.tsx')
| -rw-r--r-- | packages/app/src/context/sync.tsx | 169 |
1 files changed, 88 insertions, 81 deletions
diff --git a/packages/app/src/context/sync.tsx b/packages/app/src/context/sync.tsx index 66c53dc80..e5916598b 100644 --- a/packages/app/src/context/sync.tsx +++ b/packages/app/src/context/sync.tsx @@ -7,6 +7,20 @@ import { useGlobalSync } from "./global-sync" import { useSDK } from "./sdk" import type { Message, Part } from "@opencode-ai/sdk/v2/client" +function sortParts(parts: Part[]) { + return parts.filter((part) => !!part?.id).sort((a, b) => cmp(a.id, b.id)) +} + +function runInflight(map: Map<string, Promise<void>>, key: string, task: () => Promise<void>) { + const pending = map.get(key) + if (pending) return pending + const promise = task().finally(() => { + map.delete(key) + }) + map.set(key, promise) + return promise +} + const keyFor = (directory: string, id: string) => `${directory}\n${id}` const cmp = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0) @@ -36,7 +50,7 @@ export function applyOptimisticAdd(draft: OptimisticStore, input: OptimisticAddI const result = Binary.search(messages, input.message.id, (m) => m.id) messages.splice(result.index, 0, input.message) } - draft.part[input.message.id] = input.parts.filter((part) => !!part?.id).sort((a, b) => cmp(a.id, b.id)) + draft.part[input.message.id] = sortParts(input.parts) } export function applyOptimisticRemove(draft: OptimisticStore, input: OptimisticRemoveInput) { @@ -48,6 +62,34 @@ export function applyOptimisticRemove(draft: OptimisticStore, input: OptimisticR delete draft.part[input.messageID] } +function setOptimisticAdd(setStore: (...args: unknown[]) => void, input: OptimisticAddInput) { + setStore("message", input.sessionID, (messages: Message[] | undefined) => { + if (!messages) return [input.message] + const result = Binary.search(messages, input.message.id, (m) => m.id) + const next = [...messages] + next.splice(result.index, 0, input.message) + return next + }) + setStore("part", input.message.id, sortParts(input.parts)) +} + +function setOptimisticRemove(setStore: (...args: unknown[]) => void, input: OptimisticRemoveInput) { + setStore("message", input.sessionID, (messages: Message[] | undefined) => { + if (!messages) return messages + const result = Binary.search(messages, input.messageID, (m) => m.id) + if (!result.found) return messages + const next = [...messages] + next.splice(result.index, 1) + return next + }) + setStore("part", (part: Record<string, Part[] | undefined>) => { + if (!(input.messageID in part)) return part + const next = { ...part } + delete next[input.messageID] + return next + }) +} + export const { use: useSync, provider: SyncProvider } = createSimpleContext({ name: "Sync", init: () => { @@ -63,7 +105,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ return globalSync.child(directory) } const absolute = (path: string) => (current()[0].path.directory + "/" + path).replace("//", "/") - const chunk = 400 + const messagePageSize = 400 const inflight = new Map<string, Promise<void>>() const inflightDiff = new Map<string, Promise<void>>() const inflightTodo = new Map<string, Promise<void>>() @@ -81,8 +123,25 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ } const limitFor = (count: number) => { - if (count <= chunk) return chunk - return Math.ceil(count / chunk) * chunk + if (count <= messagePageSize) return messagePageSize + return Math.ceil(count / messagePageSize) * messagePageSize + } + + const fetchMessages = async (input: { client: typeof sdk.client; sessionID: string; limit: number }) => { + const messages = await retry(() => + input.client.session.messages({ sessionID: input.sessionID, limit: input.limit }), + ) + const items = (messages.data ?? []).filter((x) => !!x?.info?.id) + const session = items + .map((x) => x.info) + .filter((m) => !!m?.id) + .sort((a, b) => cmp(a.id, b.id)) + const part = items.map((message) => ({ id: message.info.id, part: sortParts(message.parts) })) + return { + session, + part, + complete: session.length < input.limit, + } } const loadMessages = async (input: { @@ -96,30 +155,15 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ if (meta.loading[key]) return setMeta("loading", key, true) - await retry(() => input.client.session.messages({ sessionID: input.sessionID, limit: input.limit })) - .then((messages) => { - const items = (messages.data ?? []).filter((x) => !!x?.info?.id) - const next = items - .map((x) => x.info) - .filter((m) => !!m?.id) - .sort((a, b) => cmp(a.id, b.id)) - + await fetchMessages(input) + .then((next) => { batch(() => { - input.setStore("message", input.sessionID, reconcile(next, { key: "id" })) - - for (const message of items) { - input.setStore( - "part", - message.info.id, - reconcile( - message.parts.filter((p) => !!p?.id).sort((a, b) => cmp(a.id, b.id)), - { key: "id" }, - ), - ) + input.setStore("message", input.sessionID, reconcile(next.session, { key: "id" })) + for (const message of next.part) { + input.setStore("part", message.id, reconcile(message.part, { key: "id" })) } - setMeta("limit", key, input.limit) - setMeta("complete", key, next.length < input.limit) + setMeta("complete", key, next.complete) }) }) .finally(() => { @@ -151,19 +195,11 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ optimistic: { add(input: { directory?: string; sessionID: string; message: Message; parts: Part[] }) { const [, setStore] = target(input.directory) - setStore( - produce((draft) => { - applyOptimisticAdd(draft as OptimisticStore, input) - }), - ) + setOptimisticAdd(setStore as (...args: unknown[]) => void, input) }, remove(input: { directory?: string; sessionID: string; messageID: string }) { const [, setStore] = target(input.directory) - setStore( - produce((draft) => { - applyOptimisticRemove(draft as OptimisticStore, input) - }), - ) + setOptimisticRemove(setStore as (...args: unknown[]) => void, input) }, }, addOptimisticMessage(input: { @@ -182,15 +218,11 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ model: input.model, } const [, setStore] = target() - setStore( - produce((draft) => { - applyOptimisticAdd(draft as OptimisticStore, { - sessionID: input.sessionID, - message, - parts: input.parts, - }) - }), - ) + setOptimisticAdd(setStore as (...args: unknown[]) => void, { + sessionID: input.sessionID, + message, + parts: input.parts, + }) }, async sync(sessionID: string) { const directory = sdk.directory @@ -205,11 +237,9 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ const hasMessages = store.message[sessionID] !== undefined const hydrated = meta.limit[key] !== undefined if (hasSession && hasMessages && hydrated) return - const pending = inflight.get(key) - if (pending) return pending const count = store.message[sessionID]?.length ?? 0 - const limit = hydrated ? (meta.limit[key] ?? chunk) : limitFor(count) + const limit = hydrated ? (meta.limit[key] ?? messagePageSize) : limitFor(count) const sessionReq = hasSession ? Promise.resolve() @@ -240,14 +270,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ limit, }) - const promise = Promise.all([sessionReq, messagesReq]) - .then(() => {}) - .finally(() => { - inflight.delete(key) - }) - - inflight.set(key, promise) - return promise + return runInflight(inflight, key, () => Promise.all([sessionReq, messagesReq]).then(() => {})) }, async diff(sessionID: string) { const directory = sdk.directory @@ -256,19 +279,11 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ if (store.session_diff[sessionID] !== undefined) return const key = keyFor(directory, sessionID) - const pending = inflightDiff.get(key) - if (pending) return pending - - const promise = retry(() => client.session.diff({ sessionID })) - .then((diff) => { + return runInflight(inflightDiff, key, () => + retry(() => client.session.diff({ sessionID })).then((diff) => { setStore("session_diff", sessionID, reconcile(diff.data ?? [], { key: "file" })) - }) - .finally(() => { - inflightDiff.delete(key) - }) - - inflightDiff.set(key, promise) - return promise + }), + ) }, async todo(sessionID: string) { const directory = sdk.directory @@ -277,19 +292,11 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ if (store.todo[sessionID] !== undefined) return const key = keyFor(directory, sessionID) - const pending = inflightTodo.get(key) - if (pending) return pending - - const promise = retry(() => client.session.todo({ sessionID })) - .then((todo) => { + return runInflight(inflightTodo, key, () => + retry(() => client.session.todo({ sessionID })).then((todo) => { setStore("todo", sessionID, reconcile(todo.data ?? [], { key: "id" })) - }) - .finally(() => { - inflightTodo.delete(key) - }) - - inflightTodo.set(key, promise) - return promise + }), + ) }, history: { more(sessionID: string) { @@ -304,7 +311,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ const key = keyFor(sdk.directory, sessionID) return meta.loading[key] ?? false }, - async loadMore(sessionID: string, count = chunk) { + async loadMore(sessionID: string, count = messagePageSize) { const directory = sdk.directory const client = sdk.client const [, setStore] = globalSync.child(directory) @@ -312,7 +319,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ if (meta.loading[key]) return if (meta.complete[key]) return - const currentLimit = meta.limit[key] ?? chunk + const currentLimit = meta.limit[key] ?? messagePageSize await loadMessages({ directory, client, |
