Add support for message history pagination, preserve scroll in rendering, and improve draft/message workflows.
CI / check (22) (push) Successful in 9m34s
CI / check (24) (push) Successful in 9m34s

This commit is contained in:
2026-10-06 00:32:18 +02:00
parent 1c0f06716e
commit 387c0ac2e7
40 changed files with 2374 additions and 1314 deletions
+26 -1
View File
@@ -24,7 +24,7 @@ function fixture(overrides = {}) {
getChatById: id => state.chatSessions.find(chat => chat.id === id),
updateWorkspaceCopy: noop, renderChatList: noop, updateChatActionState: noop, scrollMessagesToBottom: noop,
addMessage: () => ({isConnected: true, querySelector: () => element(), remove: noop, replaceWith: noop}),
setMessageContent: noop, setActiveChat: async id => calls.refresh.push(id), loadSummary: noop, markOnboardingCompleted: noop, maybeShowPersonaNudge: noop,
setMessageContent: noop, setActiveChat: async id => { calls.refresh.push(id); return overrides.refreshResult; }, loadSummary: noop, markOnboardingCompleted: noop, maybeShowPersonaNudge: noop,
api: {put: async () => overrides.rename ? await overrides.rename : {}, post: async () => assert.fail("Send cancellation must abort its own request"),
stream: async (_url, payload, handlers) => {
calls.streams.push(payload);
@@ -84,3 +84,28 @@ test("Stop during chat creation prevents a later stream and releases the send lo
assert.equal(s.state.isProcessing, false);
assert.equal(s.elements.msgInput.value, "Hello");
});
test("a completed reply and draft survive a failed history refresh without a false success notice", async () => {
const s = fixture({refreshResult: {error: "History unavailable", status: 503}});
await s.sendMessage();
assert.equal(s.state.currentMessages.at(-1).content, "Done");
assert.equal(s.state.currentMessages.filter(message => message.role === "bot").length, 1);
assert.equal(s.elements.msgInput.value, "Hello");
assert.match(s.calls.notices.at(-1), /could not be refreshed: History unavailable/);
assert.ok(!s.calls.notices.includes("Reply received."));
assert.equal(s.state.isProcessing, false);
});
test("navigation during refresh clears only the sent chat's saved draft", async () => {
const refresh = deferred();
const s = fixture({refreshResult: refresh.promise});
const sending = s.sendMessage();
await new Promise(resolve => setImmediate(resolve));
s.state.chatDrafts.set(1, "Hello");
s.state.activeChatId = 2;
s.elements.msgInput.value = "Other chat draft";
refresh.resolve({ok: true});
await sending;
assert.equal(s.state.chatDrafts.has(1), false);
assert.equal(s.elements.msgInput.value, "Other chat draft");
});