diff options
| author | Adam <[email protected]> | 2026-02-17 07:16:23 -0600 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-02-17 07:16:23 -0600 |
| commit | 10985671ad9553e7ac594ede30981166f69ba3c5 (patch) | |
| tree | 0f501ccba5dcf90e86beb18a0732826f7a3b1d0a /packages/app/src/context | |
| parent | 3dfbb7059345350fdcb3f45fe9a44697c08a040a (diff) | |
| download | opencode-10985671ad9553e7ac594ede30981166f69ba3c5.tar.gz opencode-10985671ad9553e7ac594ede30981166f69ba3c5.zip | |
feat(app): session timeline/turn rework (#13196)
Co-authored-by: David Hill <[email protected]>
Diffstat (limited to 'packages/app/src/context')
| -rw-r--r-- | packages/app/src/context/command.tsx | 2 | ||||
| -rw-r--r-- | packages/app/src/context/global-sync.tsx | 23 | ||||
| -rw-r--r-- | packages/app/src/context/global-sync/bootstrap.ts | 4 | ||||
| -rw-r--r-- | packages/app/src/context/global-sync/event-reducer.ts | 14 | ||||
| -rw-r--r-- | packages/app/src/context/sync.tsx | 17 |
5 files changed, 54 insertions, 6 deletions
diff --git a/packages/app/src/context/command.tsx b/packages/app/src/context/command.tsx index 03437c973..03bd6318d 100644 --- a/packages/app/src/context/command.tsx +++ b/packages/app/src/context/command.tsx @@ -11,7 +11,7 @@ const IS_MAC = typeof navigator === "object" && /(Mac|iPod|iPhone|iPad)/.test(na const PALETTE_ID = "command.palette" const DEFAULT_PALETTE_KEYBIND = "mod+shift+p" const SUGGESTED_PREFIX = "suggested." -const EDITABLE_KEYBIND_IDS = new Set(["terminal.toggle", "terminal.new"]) +const EDITABLE_KEYBIND_IDS = new Set(["terminal.toggle", "terminal.new", "file.attach"]) function actionId(id: string) { if (!id.startsWith(SUGGESTED_PREFIX)) return id diff --git a/packages/app/src/context/global-sync.tsx b/packages/app/src/context/global-sync.tsx index ec5efc675..9733b72af 100644 --- a/packages/app/src/context/global-sync.tsx +++ b/packages/app/src/context/global-sync.tsx @@ -4,6 +4,7 @@ import { type Project, type ProviderAuthResponse, type ProviderListResponse, + type Todo, createOpencodeClient, } from "@opencode-ai/sdk/v2/client" import { createStore, produce, reconcile } from "solid-js/store" @@ -41,6 +42,9 @@ type GlobalStore = { error?: InitError path: Path project: Project[] + session_todo: { + [sessionID: string]: Todo[] + } provider: ProviderListResponse provider_auth: ProviderAuthResponse config: Config @@ -87,12 +91,27 @@ function createGlobalSync() { ready: false, path: { state: "", config: "", worktree: "", directory: "", home: "" }, project: projectCache.value, + session_todo: {}, provider: { all: [], connected: [], default: {} }, provider_auth: {}, config: {}, reload: undefined, }) + const setSessionTodo = (sessionID: string, todos: Todo[] | undefined) => { + if (!sessionID) return + if (!todos) { + setGlobalStore( + "session_todo", + produce((draft) => { + delete draft[sessionID] + }), + ) + return + } + setGlobalStore("session_todo", sessionID, reconcile(todos, { key: "id" })) + } + const updateStats = (activeDirectoryStores: number) => { if (!import.meta.env.DEV) return setDevStats({ @@ -288,6 +307,7 @@ function createGlobalSync() { store, setStore, push: queue.push, + setSessionTodo, vcsCache: children.vcsCache.get(directory), loadLsp: () => { sdkFor(directory) @@ -358,6 +378,9 @@ function createGlobalSync() { bootstrap, updateConfig, project: projectApi, + todo: { + set: setSessionTodo, + }, } } diff --git a/packages/app/src/context/global-sync/bootstrap.ts b/packages/app/src/context/global-sync/bootstrap.ts index 2137a19a8..478bc02f5 100644 --- a/packages/app/src/context/global-sync/bootstrap.ts +++ b/packages/app/src/context/global-sync/bootstrap.ts @@ -6,6 +6,7 @@ import { type ProviderAuthResponse, type ProviderListResponse, type QuestionRequest, + type Todo, createOpencodeClient, } from "@opencode-ai/sdk/v2/client" import { batch } from "solid-js" @@ -20,6 +21,9 @@ type GlobalStore = { ready: boolean path: Path project: Project[] + session_todo: { + [sessionID: string]: Todo[] + } provider: ProviderListResponse provider_auth: ProviderAuthResponse config: Config diff --git a/packages/app/src/context/global-sync/event-reducer.ts b/packages/app/src/context/global-sync/event-reducer.ts index 48ac0fea1..241dfb14d 100644 --- a/packages/app/src/context/global-sync/event-reducer.ts +++ b/packages/app/src/context/global-sync/event-reducer.ts @@ -39,7 +39,12 @@ export function applyGlobalEvent(input: { }) } -function cleanupSessionCaches(store: Store<State>, setStore: SetStoreFunction<State>, sessionID: string) { +function cleanupSessionCaches( + store: Store<State>, + setStore: SetStoreFunction<State>, + sessionID: string, + setSessionTodo?: (sessionID: string, todos: Todo[] | undefined) => void, +) { if (!sessionID) return const hasAny = store.message[sessionID] !== undefined || @@ -48,6 +53,7 @@ function cleanupSessionCaches(store: Store<State>, setStore: SetStoreFunction<St store.permission[sessionID] !== undefined || store.question[sessionID] !== undefined || store.session_status[sessionID] !== undefined + setSessionTodo?.(sessionID, undefined) if (!hasAny) return setStore( produce((draft) => { @@ -77,6 +83,7 @@ export function applyDirectoryEvent(input: { directory: string loadLsp: () => void vcsCache?: VcsCache + setSessionTodo?: (sessionID: string, todos: Todo[] | undefined) => void }) { const event = input.event switch (event.type) { @@ -110,7 +117,7 @@ export function applyDirectoryEvent(input: { }), ) } - cleanupSessionCaches(input.store, input.setStore, info.id) + cleanupSessionCaches(input.store, input.setStore, info.id, input.setSessionTodo) if (info.parentID) break input.setStore("sessionTotal", (value) => Math.max(0, value - 1)) break @@ -136,7 +143,7 @@ export function applyDirectoryEvent(input: { }), ) } - cleanupSessionCaches(input.store, input.setStore, info.id) + cleanupSessionCaches(input.store, input.setStore, info.id, input.setSessionTodo) if (info.parentID) break input.setStore("sessionTotal", (value) => Math.max(0, value - 1)) break @@ -149,6 +156,7 @@ export function applyDirectoryEvent(input: { case "todo.updated": { const props = event.properties as { sessionID: string; todos: Todo[] } input.setStore("todo", props.sessionID, reconcile(props.todos, { key: "id" })) + input.setSessionTodo?.(props.sessionID, props.todos) break } case "session.status": { diff --git a/packages/app/src/context/sync.tsx b/packages/app/src/context/sync.tsx index e5916598b..60888b1a6 100644 --- a/packages/app/src/context/sync.tsx +++ b/packages/app/src/context/sync.tsx @@ -289,12 +289,25 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ const directory = sdk.directory const client = sdk.client const [store, setStore] = globalSync.child(directory) - if (store.todo[sessionID] !== undefined) return + const existing = store.todo[sessionID] + if (existing !== undefined) { + if (globalSync.data.session_todo[sessionID] === undefined) { + globalSync.todo.set(sessionID, existing) + } + return + } + + const cached = globalSync.data.session_todo[sessionID] + if (cached !== undefined) { + setStore("todo", sessionID, reconcile(cached, { key: "id" })) + } const key = keyFor(directory, sessionID) return runInflight(inflightTodo, key, () => retry(() => client.session.todo({ sessionID })).then((todo) => { - setStore("todo", sessionID, reconcile(todo.data ?? [], { key: "id" })) + const list = todo.data ?? [] + setStore("todo", sessionID, reconcile(list, { key: "id" })) + globalSync.todo.set(sessionID, list) }), ) }, |
