Add support for message history pagination, preserve scroll in rendering, and improve draft/message workflows.
This commit is contained in:
@@ -74,7 +74,12 @@ export function createComposerController({
|
||||
const {signal} = sendController;
|
||||
const visible = () => state.activeChatId === chatId;
|
||||
let userCard, loadingMsg, rendering, completedReply = null;
|
||||
let completedThinking = "", completedSources = null;
|
||||
const userMessage = {role: "user", content: message, attachments};
|
||||
const clearSentDraft = () => {
|
||||
if (state.chatDrafts.get(chatId)?.trim() === message) state.chatDrafts.delete(chatId);
|
||||
attachmentComposer.clearSentDraft(chatId, attachments.map(item => item.id));
|
||||
};
|
||||
try {
|
||||
setLoadingState(true, false);
|
||||
if (retryMessage && !msgInput.value.trim()) {
|
||||
@@ -115,11 +120,11 @@ export function createComposerController({
|
||||
if (follow) scrollMessagesToBottom();
|
||||
};
|
||||
rendering = createStreamRenderer({
|
||||
onThinking: text => render(() => {
|
||||
onThinking: text => { completedThinking = text; render(() => {
|
||||
if (!reasoning) return;
|
||||
updateReasoning(loadingMsg, text, true);
|
||||
loadingShell?.classList.toggle("hidden", Boolean(text));
|
||||
}),
|
||||
}); },
|
||||
onReply: text => render(() => {
|
||||
if (text) finishReasoning(loadingMsg);
|
||||
loadingShell?.classList.toggle("hidden", Boolean(text));
|
||||
@@ -136,24 +141,36 @@ export function createComposerController({
|
||||
: searchState === "no_relevant_results" ? "No sufficiently relevant results. Generating response..."
|
||||
: sourceCount === 0 ? "No web results found. Generating response..." : "Generating response...";
|
||||
},
|
||||
onDone: ({reply}) => { completedReply = reply; }
|
||||
onDone: ({reply, sources}) => { completedReply = reply; completedSources = sources; }
|
||||
}, {signal});
|
||||
rendering.flush();
|
||||
rendering.dispose();
|
||||
loadingMsg?.remove();
|
||||
// Keep a completed answer in both state and the DOM until history replaces it.
|
||||
if (visible()) {
|
||||
const reply = {role: "bot", content: completedReply, modelName: model,
|
||||
...(completedThinking ? {thinking: completedThinking} : {}), ...(completedSources ? {sources: completedSources} : {})};
|
||||
state.currentMessages.push(reply);
|
||||
if (loadingMsg?.isConnected) loadingMsg.replaceWith(addMessage(reply, false, false, {detached: true}));
|
||||
else addMessage(reply);
|
||||
} else loadingMsg?.remove();
|
||||
webSearchToggle.reset(); reasoningToggle.reset();
|
||||
if (visible()) {
|
||||
const followOutput = messagesDiv.scrollHeight - messagesDiv.scrollTop - messagesDiv.clientHeight < 100;
|
||||
const previousScrollTop = messagesDiv.scrollTop;
|
||||
if (msgInput.value.trim() === message) { msgInput.value = ""; resizeComposerInput(); }
|
||||
const currentAttachments = attachmentComposer.attachments;
|
||||
if (currentAttachments.length === attachments.length && currentAttachments.every((item, index) => item.id === attachments[index].id)) attachmentComposer.clear();
|
||||
await setActiveChat(chatId);
|
||||
const refreshed = await setActiveChat(chatId);
|
||||
if (refreshed?.error) throw new Error(refreshed.error);
|
||||
if (visible()) {
|
||||
if (msgInput.value.trim() === message) { msgInput.value = ""; resizeComposerInput(); }
|
||||
if (msgInput.value) state.chatDrafts.set(chatId, msgInput.value);
|
||||
else state.chatDrafts.delete(chatId);
|
||||
const currentAttachments = attachmentComposer.attachments;
|
||||
if (currentAttachments.length === attachments.length && currentAttachments.every((item, index) => item.id === attachments[index].id)) attachmentComposer.clear();
|
||||
} else clearSentDraft();
|
||||
if (!followOutput && visible()) requestAnimationFrame(() => {
|
||||
if (visible()) messagesDiv.scrollTop = previousScrollTop;
|
||||
});
|
||||
} else {
|
||||
if (state.chatDrafts.get(chatId)?.trim() === message) state.chatDrafts.delete(chatId);
|
||||
attachmentComposer.clearSentDraft(chatId, attachments.map(item => item.id));
|
||||
clearSentDraft();
|
||||
}
|
||||
await loadSummary();
|
||||
markOnboardingCompleted(); maybeShowPersonaNudge();
|
||||
@@ -162,10 +179,6 @@ export function createComposerController({
|
||||
loadingMsg?.remove();
|
||||
const messageText = signal.aborted ? "Generation stopped." : error?.message || "Unable to send message.";
|
||||
if (completedReply !== null) {
|
||||
if (visible() && userCard?.isConnected) {
|
||||
state.currentMessages.push({role: "bot", content: completedReply});
|
||||
addMessage(completedReply);
|
||||
}
|
||||
setNotice(`Reply sent, but the conversation could not be refreshed: ${messageText}`, "error");
|
||||
} else {
|
||||
if (visible()) {
|
||||
|
||||
Reference in New Issue
Block a user