fix(tui): dedupe consecutive prompt history entries (#27816)
This commit is contained in:
@@ -27,6 +27,11 @@ export type PromptInfo = {
|
|||||||
|
|
||||||
const MAX_HISTORY_ENTRIES = 50
|
const MAX_HISTORY_ENTRIES = 50
|
||||||
|
|
||||||
|
export function isDuplicateEntry(previous: PromptInfo | undefined, next: PromptInfo): boolean {
|
||||||
|
if (!previous) return false
|
||||||
|
return JSON.stringify(previous) === JSON.stringify(next)
|
||||||
|
}
|
||||||
|
|
||||||
export const { use: usePromptHistory, provider: PromptHistoryProvider } = createSimpleContext({
|
export const { use: usePromptHistory, provider: PromptHistoryProvider } = createSimpleContext({
|
||||||
name: "PromptHistory",
|
name: "PromptHistory",
|
||||||
init: () => {
|
init: () => {
|
||||||
@@ -83,6 +88,10 @@ export const { use: usePromptHistory, provider: PromptHistoryProvider } = create
|
|||||||
},
|
},
|
||||||
append(item: PromptInfo) {
|
append(item: PromptInfo) {
|
||||||
const entry = structuredClone(unwrap(item))
|
const entry = structuredClone(unwrap(item))
|
||||||
|
if (isDuplicateEntry(store.history.at(-1), entry)) {
|
||||||
|
setStore("index", 0)
|
||||||
|
return
|
||||||
|
}
|
||||||
let trimmed = false
|
let trimmed = false
|
||||||
setStore(
|
setStore(
|
||||||
produce((draft) => {
|
produce((draft) => {
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { describe, expect, test } from "bun:test"
|
||||||
|
import { isDuplicateEntry, type PromptInfo } from "../../../../src/cli/cmd/tui/component/prompt/history"
|
||||||
|
|
||||||
|
const entry = (input: string, parts: PromptInfo["parts"] = []): PromptInfo => ({ input, parts })
|
||||||
|
|
||||||
|
describe("prompt history dedupe", () => {
|
||||||
|
test("returns false when there is no previous entry", () => {
|
||||||
|
expect(isDuplicateEntry(undefined, entry("hello"))).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("dedupes identical consecutive entries", () => {
|
||||||
|
const a = entry("hello world this is over twenty chars")
|
||||||
|
const b = entry("hello world this is over twenty chars")
|
||||||
|
expect(isDuplicateEntry(a, b)).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("does not dedupe when input text differs", () => {
|
||||||
|
expect(isDuplicateEntry(entry("foo"), entry("bar"))).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("does not dedupe when parts differ", () => {
|
||||||
|
const a = entry("describe this", [
|
||||||
|
{
|
||||||
|
type: "file",
|
||||||
|
mime: "image/png",
|
||||||
|
filename: "a.png",
|
||||||
|
url: "data:image/png;base64,AAA",
|
||||||
|
},
|
||||||
|
])
|
||||||
|
const b = entry("describe this", [
|
||||||
|
{
|
||||||
|
type: "file",
|
||||||
|
mime: "image/png",
|
||||||
|
filename: "b.png",
|
||||||
|
url: "data:image/png;base64,BBB",
|
||||||
|
},
|
||||||
|
])
|
||||||
|
expect(isDuplicateEntry(a, b)).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("does not dedupe when mode differs", () => {
|
||||||
|
expect(isDuplicateEntry({ ...entry("ls"), mode: "normal" }, { ...entry("ls"), mode: "shell" })).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user