fix: restore messages after redo
Refetches session messages before applying redo Aligns undo/redo navigation with OpenCode behavior Keeps restored messages visible after unrevert
This commit is contained in:
@@ -33,7 +33,7 @@
|
|||||||
"@ibm/plex": "^6.4.1",
|
"@ibm/plex": "^6.4.1",
|
||||||
"@lezer/highlight": "^1.2.3",
|
"@lezer/highlight": "^1.2.3",
|
||||||
"@octokit/rest": "^22.0.1",
|
"@octokit/rest": "^22.0.1",
|
||||||
"@opencode-ai/sdk": "^1.4.39",
|
"@opencode-ai/sdk": "^1.4.41",
|
||||||
"@radix-ui/react-collapsible": "^1.1.12",
|
"@radix-ui/react-collapsible": "^1.1.12",
|
||||||
"@radix-ui/react-dialog": "^1.1.15",
|
"@radix-ui/react-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||||
@@ -95,7 +95,7 @@
|
|||||||
},
|
},
|
||||||
"packages/desktop": {
|
"packages/desktop": {
|
||||||
"name": "@openchamber/desktop",
|
"name": "@openchamber/desktop",
|
||||||
"version": "1.10.0",
|
"version": "1.10.2",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tauri-apps/cli": "^2",
|
"@tauri-apps/cli": "^2",
|
||||||
"@types/node": "^24.3.1",
|
"@types/node": "^24.3.1",
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
},
|
},
|
||||||
"packages/electron": {
|
"packages/electron": {
|
||||||
"name": "@openchamber/electron",
|
"name": "@openchamber/electron",
|
||||||
"version": "1.10.0",
|
"version": "1.10.2",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@openchamber/web": "workspace:*",
|
"@openchamber/web": "workspace:*",
|
||||||
"electron-context-menu": "^4.1.2",
|
"electron-context-menu": "^4.1.2",
|
||||||
@@ -119,7 +119,7 @@
|
|||||||
},
|
},
|
||||||
"packages/ui": {
|
"packages/ui": {
|
||||||
"name": "@openchamber/ui",
|
"name": "@openchamber/ui",
|
||||||
"version": "1.10.0",
|
"version": "1.10.2",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@base-ui/react": "^1.4.0",
|
"@base-ui/react": "^1.4.0",
|
||||||
"@codemirror/autocomplete": "^6.20.0",
|
"@codemirror/autocomplete": "^6.20.0",
|
||||||
@@ -150,7 +150,7 @@
|
|||||||
"@fontsource/ibm-plex-sans": "^5.1.1",
|
"@fontsource/ibm-plex-sans": "^5.1.1",
|
||||||
"@ibm/plex": "^6.4.1",
|
"@ibm/plex": "^6.4.1",
|
||||||
"@lezer/highlight": "^1.2.3",
|
"@lezer/highlight": "^1.2.3",
|
||||||
"@opencode-ai/sdk": "^1.4.39",
|
"@opencode-ai/sdk": "^1.4.41",
|
||||||
"@pierre/diffs": "1.1.0-beta.13",
|
"@pierre/diffs": "1.1.0-beta.13",
|
||||||
"@remixicon/react": "^4.7.0",
|
"@remixicon/react": "^4.7.0",
|
||||||
"@simplewebauthn/browser": "13.3.0",
|
"@simplewebauthn/browser": "13.3.0",
|
||||||
@@ -218,10 +218,10 @@
|
|||||||
},
|
},
|
||||||
"packages/vscode": {
|
"packages/vscode": {
|
||||||
"name": "openchamber",
|
"name": "openchamber",
|
||||||
"version": "1.10.0",
|
"version": "1.10.2",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@openchamber/ui": "workspace:*",
|
"@openchamber/ui": "workspace:*",
|
||||||
"@opencode-ai/sdk": "^1.4.39",
|
"@opencode-ai/sdk": "^1.4.41",
|
||||||
"adm-zip": "^0.5.16",
|
"adm-zip": "^0.5.16",
|
||||||
"jsonc-parser": "^3.3.1",
|
"jsonc-parser": "^3.3.1",
|
||||||
"react": "^19.1.1",
|
"react": "^19.1.1",
|
||||||
@@ -241,14 +241,14 @@
|
|||||||
},
|
},
|
||||||
"packages/web": {
|
"packages/web": {
|
||||||
"name": "@openchamber/web",
|
"name": "@openchamber/web",
|
||||||
"version": "1.10.0",
|
"version": "1.10.2",
|
||||||
"bin": {
|
"bin": {
|
||||||
"openchamber": "./bin/cli.js",
|
"openchamber": "./bin/cli.js",
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@clack/prompts": "^1.1.0",
|
"@clack/prompts": "^1.1.0",
|
||||||
"@octokit/rest": "^22.0.1",
|
"@octokit/rest": "^22.0.1",
|
||||||
"@opencode-ai/sdk": "^1.4.39",
|
"@opencode-ai/sdk": "^1.4.41",
|
||||||
"@simplewebauthn/server": "13.3.0",
|
"@simplewebauthn/server": "13.3.0",
|
||||||
"adm-zip": "^0.5.16",
|
"adm-zip": "^0.5.16",
|
||||||
"better-sqlite3": "^11.7.0",
|
"better-sqlite3": "^11.7.0",
|
||||||
|
|||||||
+1
-1
@@ -94,7 +94,7 @@
|
|||||||
"@ibm/plex": "^6.4.1",
|
"@ibm/plex": "^6.4.1",
|
||||||
"@lezer/highlight": "^1.2.3",
|
"@lezer/highlight": "^1.2.3",
|
||||||
"@octokit/rest": "^22.0.1",
|
"@octokit/rest": "^22.0.1",
|
||||||
"@opencode-ai/sdk": "^1.4.39",
|
"@opencode-ai/sdk": "^1.4.41",
|
||||||
"@base-ui/react": "^1.4.0",
|
"@base-ui/react": "^1.4.0",
|
||||||
"@radix-ui/react-collapsible": "^1.1.12",
|
"@radix-ui/react-collapsible": "^1.1.12",
|
||||||
"@radix-ui/react-dialog": "^1.1.15",
|
"@radix-ui/react-dialog": "^1.1.15",
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
"@fontsource/ibm-plex-sans": "^5.1.1",
|
"@fontsource/ibm-plex-sans": "^5.1.1",
|
||||||
"@ibm/plex": "^6.4.1",
|
"@ibm/plex": "^6.4.1",
|
||||||
"@lezer/highlight": "^1.2.3",
|
"@lezer/highlight": "^1.2.3",
|
||||||
"@opencode-ai/sdk": "^1.4.39",
|
"@opencode-ai/sdk": "^1.4.41",
|
||||||
"@pierre/diffs": "1.1.0-beta.13",
|
"@pierre/diffs": "1.1.0-beta.13",
|
||||||
"@base-ui/react": "^1.4.0",
|
"@base-ui/react": "^1.4.0",
|
||||||
"@remixicon/react": "^4.7.0",
|
"@remixicon/react": "^4.7.0",
|
||||||
|
|||||||
@@ -13,6 +13,11 @@ import { useGlobalSessionsStore } from "@/stores/useGlobalSessionsStore"
|
|||||||
import { useConfigStore } from "@/stores/useConfigStore"
|
import { useConfigStore } from "@/stores/useConfigStore"
|
||||||
import { registerSessionDirectory } from "./sync-refs"
|
import { registerSessionDirectory } from "./sync-refs"
|
||||||
import { isSyntheticPart } from "@/lib/messages/synthetic"
|
import { isSyntheticPart } from "@/lib/messages/synthetic"
|
||||||
|
import { materializeSessionSnapshots } from "./materialization"
|
||||||
|
import { stripMessageDiffSnapshots } from "./sanitize"
|
||||||
|
|
||||||
|
const MESSAGE_REFETCH_LIMIT = 200
|
||||||
|
const MESSAGE_REFETCH_SKIP_PARTS = new Set(["patch", "step-start", "step-finish"])
|
||||||
|
|
||||||
// Reference set by SyncProvider — allows actions to access SDK and stores
|
// Reference set by SyncProvider — allows actions to access SDK and stores
|
||||||
let _sdk: OpencodeClient | null = null
|
let _sdk: OpencodeClient | null = null
|
||||||
@@ -647,6 +652,26 @@ export async function revertToMessage(sessionId: string, messageId: string): Pro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function refetchSessionMessages(sessionId: string): Promise<void> {
|
||||||
|
const store = dirStore()
|
||||||
|
const result = await sdk().session.messages({ sessionID: sessionId, directory: dir(), limit: MESSAGE_REFETCH_LIMIT })
|
||||||
|
const records = (result.data ?? []).filter((record: { info?: { id?: string } }) => !!record?.info?.id)
|
||||||
|
if (records.length === 0) return
|
||||||
|
|
||||||
|
store.setState((state) => {
|
||||||
|
const materialized = materializeSessionSnapshots(
|
||||||
|
state,
|
||||||
|
sessionId,
|
||||||
|
records.map((record: { info: Message; parts?: Part[] }) => ({
|
||||||
|
info: stripMessageDiffSnapshots(record.info),
|
||||||
|
parts: record.parts ?? [],
|
||||||
|
})),
|
||||||
|
{ skipPartTypes: MESSAGE_REFETCH_SKIP_PARTS },
|
||||||
|
)
|
||||||
|
return { message: materialized.message, part: materialized.part }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unrevert — restore all previously reverted messages.
|
* Unrevert — restore all previously reverted messages.
|
||||||
* Restore all previously reverted messages. Aborts if busy, merges result.
|
* Restore all previously reverted messages. Aborts if busy, merges result.
|
||||||
@@ -675,6 +700,7 @@ export async function unrevertSession(sessionId: string): Promise<void> {
|
|||||||
store.setState({ session: sessions })
|
store.setState({ session: sessions })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
await refetchSessionMessages(sessionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ import {
|
|||||||
shareSession as shareSessionAction,
|
shareSession as shareSessionAction,
|
||||||
unshareSession as unshareSessionAction,
|
unshareSession as unshareSessionAction,
|
||||||
optimisticSend,
|
optimisticSend,
|
||||||
|
refetchSessionMessages,
|
||||||
} from "./session-actions"
|
} from "./session-actions"
|
||||||
import { useInputStore, type SyntheticContextPart } from "./input-store"
|
import { useInputStore, type SyntheticContextPart } from "./input-store"
|
||||||
import { useSelectionStore } from "./selection-store"
|
import { useSelectionStore } from "./selection-store"
|
||||||
@@ -967,8 +968,7 @@ export const useSessionUIStore = create<SessionUIState>()((set, get) => ({
|
|||||||
const revertToId = currentSession?.revert?.messageID
|
const revertToId = currentSession?.revert?.messageID
|
||||||
let targetMessage: typeof messages[number] | undefined
|
let targetMessage: typeof messages[number] | undefined
|
||||||
if (revertToId) {
|
if (revertToId) {
|
||||||
const revertIndex = userMessages.findIndex((m) => m.id === revertToId)
|
targetMessage = [...userMessages].reverse().find((m) => m.id < revertToId)
|
||||||
targetMessage = userMessages[revertIndex + 1]
|
|
||||||
} else {
|
} else {
|
||||||
targetMessage = userMessages[userMessages.length - 1]
|
targetMessage = userMessages[userMessages.length - 1]
|
||||||
}
|
}
|
||||||
@@ -996,10 +996,11 @@ export const useSessionUIStore = create<SessionUIState>()((set, get) => ({
|
|||||||
const revertToId = currentSession?.revert?.messageID
|
const revertToId = currentSession?.revert?.messageID
|
||||||
if (!revertToId) return
|
if (!revertToId) return
|
||||||
|
|
||||||
|
await refetchSessionMessages(sessionId)
|
||||||
|
|
||||||
const messages = getSyncMessages(sessionId)
|
const messages = getSyncMessages(sessionId)
|
||||||
const userMessages = messages.filter((m) => m.role === "user")
|
const userMessages = messages.filter((m) => m.role === "user")
|
||||||
const revertIndex = userMessages.findIndex((m) => m.id === revertToId)
|
const targetMessage = userMessages.find((m) => m.id > revertToId)
|
||||||
const targetMessage = userMessages[revertIndex - 1]
|
|
||||||
|
|
||||||
if (targetMessage) {
|
if (targetMessage) {
|
||||||
const targetParts = getSyncParts(targetMessage.id)
|
const targetParts = getSyncParts(targetMessage.id)
|
||||||
|
|||||||
@@ -243,7 +243,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@openchamber/ui": "workspace:*",
|
"@openchamber/ui": "workspace:*",
|
||||||
"@opencode-ai/sdk": "^1.4.39",
|
"@opencode-ai/sdk": "^1.4.41",
|
||||||
"adm-zip": "^0.5.16",
|
"adm-zip": "^0.5.16",
|
||||||
"jsonc-parser": "^3.3.1",
|
"jsonc-parser": "^3.3.1",
|
||||||
"react": "^19.1.1",
|
"react": "^19.1.1",
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@clack/prompts": "^1.1.0",
|
"@clack/prompts": "^1.1.0",
|
||||||
"@octokit/rest": "^22.0.1",
|
"@octokit/rest": "^22.0.1",
|
||||||
"@opencode-ai/sdk": "^1.4.39",
|
"@opencode-ai/sdk": "^1.4.41",
|
||||||
"@simplewebauthn/server": "13.3.0",
|
"@simplewebauthn/server": "13.3.0",
|
||||||
"adm-zip": "^0.5.16",
|
"adm-zip": "^0.5.16",
|
||||||
"better-sqlite3": "^11.7.0",
|
"better-sqlite3": "^11.7.0",
|
||||||
|
|||||||
Reference in New Issue
Block a user