feat(tui): undo recent prompt with double escape
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
This commit is contained in:
parent
34e5809059
commit
f32acba470
3 changed files with 68 additions and 1 deletions
|
|
@ -57,6 +57,8 @@ import { usePromptWorkspace } from "./workspace"
|
|||
import { usePromptMove } from "./move"
|
||||
import { readLocalAttachment } from "./local-attachment"
|
||||
import { useLocation } from "../../context/location"
|
||||
import { Identifier } from "@opencode-ai/core/id/id"
|
||||
import { createQuickUndo } from "./quick-undo"
|
||||
|
||||
registerOpencodeSpinner()
|
||||
|
||||
|
|
@ -296,6 +298,7 @@ export function Prompt(props: PromptProps) {
|
|||
extmarkToPartIndex: new Map(),
|
||||
interrupt: 0,
|
||||
})
|
||||
const quickUndo = createQuickUndo<PromptInfo>()
|
||||
|
||||
createEffect(
|
||||
on(
|
||||
|
|
@ -394,7 +397,7 @@ export function Prompt(props: PromptProps) {
|
|||
category: "Session",
|
||||
hidden: true,
|
||||
enabled: status().type !== "idle",
|
||||
run: () => {
|
||||
run: async () => {
|
||||
if (auto()?.visible) return
|
||||
if (!input.focused) return
|
||||
// TODO: this should be its own command
|
||||
|
|
@ -404,6 +407,18 @@ export function Prompt(props: PromptProps) {
|
|||
}
|
||||
if (!props.sessionID) return
|
||||
|
||||
const recent = quickUndo.escape()
|
||||
if (recent) {
|
||||
await sdk.client.session.abort({ sessionID: props.sessionID }).catch(() => {})
|
||||
await sdk.client.session.revert({ sessionID: props.sessionID, messageID: recent.messageID })
|
||||
input.setText(recent.value.input)
|
||||
setStore("prompt", recent.value)
|
||||
restoreExtmarksFromParts(recent.value.parts)
|
||||
input.gotoBufferEnd()
|
||||
input.focus()
|
||||
return
|
||||
}
|
||||
|
||||
setStore("interrupt", store.interrupt + 1)
|
||||
|
||||
setTimeout(() => {
|
||||
|
|
@ -1089,11 +1104,17 @@ export function Prompt(props: PromptProps) {
|
|||
parts: nonTextParts.filter((x) => x.type === "file"),
|
||||
})
|
||||
} else {
|
||||
const messageID = Identifier.ascending("message")
|
||||
quickUndo.submitted(messageID, {
|
||||
input: store.prompt.input,
|
||||
parts: [...unwrap(store.prompt.parts)],
|
||||
})
|
||||
move.startSubmit()
|
||||
sdk.client.session
|
||||
.prompt(
|
||||
{
|
||||
sessionID,
|
||||
messageID,
|
||||
...selectedModel,
|
||||
agent: agent.name,
|
||||
model: selectedModel,
|
||||
|
|
|
|||
21
packages/tui/src/component/prompt/quick-undo.test.ts
Normal file
21
packages/tui/src/component/prompt/quick-undo.test.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import { describe, expect, test } from "bun:test"
|
||||
import { createQuickUndo } from "./quick-undo"
|
||||
|
||||
describe("quick undo", () => {
|
||||
test("returns the submitted message on a second escape within two seconds", () => {
|
||||
const undo = createQuickUndo<string>()
|
||||
undo.submitted("msg_1", "hello", 1_000)
|
||||
|
||||
expect(undo.escape(1_500)).toBeUndefined()
|
||||
expect(undo.escape(1_750)).toEqual({ messageID: "msg_1", value: "hello" })
|
||||
expect(undo.escape(1_800)).toBeUndefined()
|
||||
})
|
||||
|
||||
test("expires two seconds after submission", () => {
|
||||
const undo = createQuickUndo<string>()
|
||||
undo.submitted("msg_1", "hello", 1_000)
|
||||
|
||||
expect(undo.escape(2_500)).toBeUndefined()
|
||||
expect(undo.escape(3_001)).toBeUndefined()
|
||||
})
|
||||
})
|
||||
25
packages/tui/src/component/prompt/quick-undo.ts
Normal file
25
packages/tui/src/component/prompt/quick-undo.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
const WINDOW_MS = 2_000
|
||||
|
||||
export function createQuickUndo<T>() {
|
||||
let submitted: { messageID: string; value: T; time: number } | undefined
|
||||
let escapedAt: number | undefined
|
||||
|
||||
return {
|
||||
submitted(messageID: string, value: T, time = Date.now()) {
|
||||
submitted = { messageID, value, time }
|
||||
escapedAt = undefined
|
||||
},
|
||||
escape(time = Date.now()) {
|
||||
if (!submitted || time - submitted.time > WINDOW_MS) return
|
||||
if (escapedAt === undefined || time - escapedAt > WINDOW_MS) {
|
||||
escapedAt = time
|
||||
return
|
||||
}
|
||||
|
||||
const result = { messageID: submitted.messageID, value: submitted.value }
|
||||
submitted = undefined
|
||||
escapedAt = undefined
|
||||
return result
|
||||
},
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue