mirror of
https://github.com/anomalyco/opencode.git
synced 2026-07-10 04:38:31 +00:00
fix(tui): restore reverted prompt in v2
This commit is contained in:
parent
8f4b62eb49
commit
cdb8fc9960
3 changed files with 114 additions and 4 deletions
|
|
@ -72,6 +72,7 @@ import { usePathFormatter } from "../../context/path-format"
|
|||
import { LocationProvider } from "../../context/location"
|
||||
import { createSessionRows, type PartRef, type SessionRow } from "./rows"
|
||||
import { switchLabel } from "../../util/model"
|
||||
import { revertedPrompt } from "../../util/revert-prompt"
|
||||
|
||||
addDefaultParsers(parsers.parsers)
|
||||
|
||||
|
|
@ -398,17 +399,21 @@ export function Session() {
|
|||
title: "Undo previous message",
|
||||
value: "session.undo",
|
||||
category: "Session",
|
||||
slash: { name: "undo" },
|
||||
slash: { name: "undo", aliases: ["revert"] },
|
||||
run: () => {
|
||||
void (async () => {
|
||||
const current = prompt?.current
|
||||
if (current && current.parts.length === 0 && ["/undo", "/revert"].includes(current.input.trim())) {
|
||||
prompt?.reset()
|
||||
}
|
||||
const boundary = session()?.revert?.messageID
|
||||
const list = messages()
|
||||
let target: string | undefined
|
||||
let target: SessionMessageUser | undefined
|
||||
for (let i = list.length - 1; i >= 0; i--) {
|
||||
const message = list[i]
|
||||
if (message.type !== "user" || !message.text.trim()) continue
|
||||
if (boundary && message.id >= boundary) continue
|
||||
target = message.id
|
||||
target = message
|
||||
break
|
||||
}
|
||||
if (!target) {
|
||||
|
|
@ -416,11 +421,15 @@ export function Session() {
|
|||
dialog.clear()
|
||||
return
|
||||
}
|
||||
const error = await sdk.api.session.revertStage({ sessionID: route.sessionID, messageID: target }).then(
|
||||
const error = await sdk.api.session.revertStage({ sessionID: route.sessionID, messageID: target.id }).then(
|
||||
() => undefined,
|
||||
(error) => error,
|
||||
)
|
||||
if (error) toast.show({ message: errorMessage(error), variant: "error", duration: 5000 })
|
||||
if (!error && prompt) {
|
||||
const restored = revertedPrompt(prompt.current, target)
|
||||
if (restored) prompt.set(restored)
|
||||
}
|
||||
dialog.clear()
|
||||
})()
|
||||
},
|
||||
|
|
|
|||
40
packages/tui/src/util/revert-prompt.ts
Normal file
40
packages/tui/src/util/revert-prompt.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import type { SessionMessageUser } from "@opencode-ai/sdk/v2"
|
||||
import type { PromptInfo } from "../prompt/history"
|
||||
|
||||
export function revertedPrompt(current: PromptInfo, message: SessionMessageUser): PromptInfo | undefined {
|
||||
if (current.input || current.parts.length) return
|
||||
|
||||
return {
|
||||
input: message.text,
|
||||
parts: [
|
||||
...(message.files ?? []).map((file) => ({
|
||||
type: "file" as const,
|
||||
mime: file.mime,
|
||||
filename: file.name,
|
||||
url: file.uri,
|
||||
source: file.source
|
||||
? {
|
||||
type: "file" as const,
|
||||
path: file.name ?? file.uri,
|
||||
text: {
|
||||
start: file.source.start,
|
||||
end: file.source.end,
|
||||
value: file.source.text,
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
})),
|
||||
...(message.agents ?? []).map((agent) => ({
|
||||
type: "agent" as const,
|
||||
name: agent.name,
|
||||
source: agent.source
|
||||
? {
|
||||
start: agent.source.start,
|
||||
end: agent.source.end,
|
||||
value: agent.source.text,
|
||||
}
|
||||
: undefined,
|
||||
})),
|
||||
],
|
||||
}
|
||||
}
|
||||
61
packages/tui/test/util/revert-prompt.test.ts
Normal file
61
packages/tui/test/util/revert-prompt.test.ts
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import { describe, expect, test } from "bun:test"
|
||||
import type { SessionMessageUser } from "@opencode-ai/sdk/v2"
|
||||
import { revertedPrompt } from "../../src/util/revert-prompt"
|
||||
|
||||
const message: SessionMessageUser = {
|
||||
id: "message-1",
|
||||
type: "user",
|
||||
text: "Fix the tests",
|
||||
time: { created: 1 },
|
||||
files: [
|
||||
{
|
||||
uri: "file:///repo/test.ts",
|
||||
mime: "text/typescript",
|
||||
name: "test.ts",
|
||||
source: { start: 0, end: 8, text: "@test.ts" },
|
||||
},
|
||||
],
|
||||
agents: [{ name: "review", source: { start: 9, end: 16, text: "@review" } }],
|
||||
}
|
||||
|
||||
describe("reverted prompt", () => {
|
||||
test("restores the reverted user message into an empty prompt", () => {
|
||||
expect(revertedPrompt({ input: "", parts: [] }, message)).toEqual({
|
||||
input: "Fix the tests",
|
||||
parts: [
|
||||
{
|
||||
type: "file",
|
||||
mime: "text/typescript",
|
||||
filename: "test.ts",
|
||||
url: "file:///repo/test.ts",
|
||||
source: {
|
||||
type: "file",
|
||||
path: "test.ts",
|
||||
text: { start: 0, end: 8, value: "@test.ts" },
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "agent",
|
||||
name: "review",
|
||||
source: { start: 9, end: 16, value: "@review" },
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
test("preserves an existing text draft", () => {
|
||||
expect(revertedPrompt({ input: "Keep this", parts: [] }, message)).toBeUndefined()
|
||||
})
|
||||
|
||||
test("preserves an existing attachment draft", () => {
|
||||
expect(
|
||||
revertedPrompt(
|
||||
{
|
||||
input: "",
|
||||
parts: [{ type: "agent", name: "build" }],
|
||||
},
|
||||
message,
|
||||
),
|
||||
).toBeUndefined()
|
||||
})
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue