feat(chat): persist more composer input history with global or session scope; recallable with up/down arrow keys (#3035)
* feat(chat): persist input history * feat(settings): configure input history scope * fix(web): keep input history validation packaged * fix(chat): preserve input history across tabs * fix(settings): restore prompt history limit * fix(settings): keep history deletion warning visible * fix(i18n): restore Turkish Git empty state translations --------- Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
This commit is contained in:
committed by
GitHub
co-authored by
Bohdan Triapitsyn
parent
29480383cc
commit
1d6b15bc04
@@ -1665,6 +1665,138 @@ describe("optimisticSend target directory", () => {
|
||||
expect(targetStore.getState().part[revertedMessage.id]).toEqual([revertedPart])
|
||||
})
|
||||
|
||||
test("runs appendSubmissions before revert cleanup and optimistic insertion", async () => {
|
||||
const revertedMessage = { id: "msg_000000000000Reverted", role: "user", sessionID: "session-reverted", time: { created: 2 } } as Message
|
||||
const targetStore = createStore({}, {
|
||||
session: [{ id: "session-reverted", revert: { messageID: revertedMessage.id } } as Session],
|
||||
message: { "session-reverted": [revertedMessage] },
|
||||
part: { [revertedMessage.id]: [{ id: "part_2", type: "text", text: "old branch" } as Part] },
|
||||
})
|
||||
const childStores = createChildStores([["/target/project", targetStore]])
|
||||
const callOrder: string[] = []
|
||||
|
||||
const { optimisticSend, setActionRefs, setOptimisticRefs } = await import("./session-actions")
|
||||
setActionRefs(mockSdk as unknown as OpencodeClient, childStores, () => "/target/project")
|
||||
setOptimisticRefs(
|
||||
() => {
|
||||
callOrder.push("optimistic-add")
|
||||
},
|
||||
() => {},
|
||||
() => {
|
||||
callOrder.push("revert-confirm")
|
||||
},
|
||||
)
|
||||
|
||||
await optimisticSend({
|
||||
sessionId: "session-reverted",
|
||||
directory: "/target/project",
|
||||
content: "new branch",
|
||||
providerID: "provider",
|
||||
modelID: "model",
|
||||
appendSubmissions: () => {
|
||||
callOrder.push("append")
|
||||
},
|
||||
send: async () => {},
|
||||
})
|
||||
|
||||
expect(callOrder).toEqual(["append", "revert-confirm", "optimistic-add"])
|
||||
})
|
||||
|
||||
test("runs appendSubmissions once for a definite rejection", async () => {
|
||||
const targetStore = createStore({})
|
||||
const childStores = createChildStores([["/target/project", targetStore]])
|
||||
let appendCalls = 0
|
||||
|
||||
const { optimisticSend, setActionRefs, setOptimisticRefs } = await import("./session-actions")
|
||||
setActionRefs(mockSdk as unknown as OpencodeClient, childStores, () => "/target/project")
|
||||
setOptimisticRefs(
|
||||
() => {},
|
||||
() => {},
|
||||
)
|
||||
|
||||
await expect(optimisticSend({
|
||||
sessionId: "session-rejected",
|
||||
directory: "/target/project",
|
||||
content: "hello",
|
||||
providerID: "provider",
|
||||
modelID: "model",
|
||||
appendSubmissions: () => {
|
||||
appendCalls += 1
|
||||
},
|
||||
send: async () => { throw new Error("rejected") },
|
||||
})).rejects.toThrow("rejected")
|
||||
|
||||
expect(appendCalls).toBe(1)
|
||||
})
|
||||
|
||||
test("runs appendSubmissions once for an ambiguous confirmation", async () => {
|
||||
const targetStore = createStore({})
|
||||
const childStores = createChildStores([["/target/project", targetStore]])
|
||||
let appendCalls = 0
|
||||
|
||||
const { optimisticSend, setActionRefs, setOptimisticRefs } = await import("./session-actions")
|
||||
setActionRefs(mockSdk as unknown as OpencodeClient, childStores, () => "/target/project")
|
||||
setOptimisticRefs(
|
||||
() => {},
|
||||
() => {},
|
||||
() => {},
|
||||
)
|
||||
|
||||
await optimisticSend({
|
||||
sessionId: "session-confirmed",
|
||||
directory: "/target/project",
|
||||
content: "hello",
|
||||
providerID: "provider",
|
||||
modelID: "model",
|
||||
appendSubmissions: () => {
|
||||
appendCalls += 1
|
||||
},
|
||||
send: async (messageID) => {
|
||||
sessionMessagesResult = {
|
||||
data: [{
|
||||
info: { id: messageID, role: "user", sessionID: "session-confirmed", time: { created: 1 } } as Message,
|
||||
parts: [{ id: "server-part", type: "text", text: "hello" } as Part],
|
||||
}],
|
||||
}
|
||||
const error = new Error("Failed to send message (504): gateway timeout") as Error & { status?: number }
|
||||
error.status = 504
|
||||
throw error
|
||||
},
|
||||
})
|
||||
|
||||
expect(appendCalls).toBe(1)
|
||||
})
|
||||
|
||||
test("does not run appendSubmissions when the runtime changes before dispatch", async () => {
|
||||
const targetStore = createStore({})
|
||||
const childStores = createChildStores([["/target/project", targetStore]])
|
||||
let appendCalls = 0
|
||||
const { switchRuntimeEndpoint } = await import("../lib/runtime-switch")
|
||||
switchRuntimeEndpoint({ apiBaseUrl: "http://runtime-b.test", runtimeKey: "runtime-b" })
|
||||
|
||||
const { optimisticSend, setActionRefs, setOptimisticRefs } = await import("./session-actions")
|
||||
setActionRefs(mockSdk as unknown as OpencodeClient, childStores, () => "/target/project")
|
||||
setOptimisticRefs(
|
||||
() => {},
|
||||
() => {},
|
||||
)
|
||||
|
||||
await expect(optimisticSend({
|
||||
sessionId: "session-race",
|
||||
directory: "/target/project",
|
||||
runtimeKey: "runtime-a",
|
||||
content: "hello",
|
||||
providerID: "provider",
|
||||
modelID: "model",
|
||||
appendSubmissions: () => {
|
||||
appendCalls += 1
|
||||
},
|
||||
send: async () => {},
|
||||
})).rejects.toThrow("runtime changed")
|
||||
|
||||
expect(appendCalls).toBe(0)
|
||||
})
|
||||
|
||||
test("rolls back a captured send when the runtime changes after optimistic insert", async () => {
|
||||
const targetStore = createStore({})
|
||||
const childStores = createChildStores([["/target/project", targetStore]])
|
||||
|
||||
Reference in New Issue
Block a user