62 lines
2.7 KiB
JavaScript
62 lines
2.7 KiB
JavaScript
/**
|
|
* @file Ersetzt eine Antwort während der Neugenerierung in ihrer Nachrichtenkarte und stellt sie bei Fehlern wieder her.
|
|
*/
|
|
import {stream} from "./api.js";
|
|
import {updateReasoning, finishReasoning} from "./reasoning.js";
|
|
import {appendSources} from "./webSearch.js";
|
|
import {createStreamRenderer} from "./streamRenderer.js";
|
|
|
|
// Keep the original nodes (including action handlers) until a committed answer arrives.
|
|
export async function regenerateInline({card, scroller, endpoint, payload, renderContent, complete}) {
|
|
const original = [...card.childNodes];
|
|
const status = document.createElement("div");
|
|
status.className = "loading-shell";
|
|
status.setAttribute("role", "status");
|
|
status.textContent = "Generating...";
|
|
const body = document.createElement("div");
|
|
body.className = "msg-content streaming-content";
|
|
let committed = false;
|
|
const update = operation => {
|
|
const bottom = card.getBoundingClientRect().bottom;
|
|
const viewport = scroller.getBoundingClientRect();
|
|
const follow = bottom >= viewport.top && Math.abs(bottom - viewport.bottom) < 100;
|
|
operation();
|
|
if (follow) scroller.scrollTop += card.getBoundingClientRect().bottom - bottom;
|
|
};
|
|
// Anchor to this response, including when a long old answer collapses.
|
|
update(() => card.replaceChildren(original[0].cloneNode(true), status, body));
|
|
card.setAttribute("aria-busy", "true");
|
|
const rendering = createStreamRenderer({
|
|
onThinking: text => update(() => updateReasoning(card, text, true)),
|
|
onReply: text => update(() => {
|
|
status.hidden = Boolean(text);
|
|
if (text) finishReasoning(card);
|
|
renderContent(body, text);
|
|
})
|
|
});
|
|
try {
|
|
await stream(endpoint, payload, {
|
|
...rendering,
|
|
onStatus: data => update(() => {
|
|
status.textContent = data.status === "searching" ? "Searching the web..."
|
|
: data.status === "analyzing" ? "Analyzing attachments..." : "Generating...";
|
|
if ("sources" in data) {
|
|
card.querySelector(".msg-sources")?.remove();
|
|
appendSources(card, data.sources);
|
|
}
|
|
}),
|
|
onDone: ({message}) => {
|
|
if (!message) throw new Error("Missing regenerated response.");
|
|
rendering.dispose();
|
|
update(() => complete(message));
|
|
committed = true;
|
|
}
|
|
});
|
|
} finally {
|
|
rendering.dispose();
|
|
// Bei Fehler oder Abbruch die ursprünglichen Knoten samt Ereignishandlern wieder einsetzen.
|
|
if (!committed) update(() => card.replaceChildren(...original));
|
|
card.removeAttribute("aria-busy");
|
|
}
|
|
}
|