llm-py-web/client/response.tsx

92 lines
2.5 KiB
TypeScript

import hljs from "deps/hljs.ts";
export class ChatResponse {
element = (<article className="assistant" />);
currentLine: Text | undefined;
codeBlockContext: Element | undefined;
thinkingContext: HTMLDetailsElement | undefined;
currentContext(): Element {
if (this.codeBlockContext) return this.codeBlockContext;
if (this.thinkingContext) return this.thinkingContext;
return this.element;
}
finalizeLine() {
if (!this.currentLine?.textContent) return;
const line = this.currentLine.textContent;
if (!this.thinkingContext && line === "<think>") {
this.thinkingContext = (
<details open>
<summary>{`<think />`}</summary>
</details>
) as HTMLDetailsElement;
this.thinkingContext.dataset.inProgress = "";
this.element.append(this.thinkingContext);
this.currentLine.remove();
} else if (this.thinkingContext && line === "</think>") {
this.currentLine.remove();
this.thinkingContext.open = false;
delete this.thinkingContext.dataset.inProgress;
this.thinkingContext = undefined;
}
if (line.startsWith("```")) {
const remainder = line.substring(3).trimStart();
this.currentLine.remove();
if (!this.codeBlockContext) {
const pre = (
<pre>
<code className={`language-${remainder}`} />
</pre>
);
this.currentContext().append(pre);
this.codeBlockContext = pre.querySelector("code")!;
} else {
hljs.highlightElement(this.codeBlockContext as HTMLElement);
this.codeBlockContext = undefined;
this.currentContext().append(remainder);
}
}
}
append(text: string) {
const tokens = text
.split(/(\s+)/)
.flatMap(s => s.split(/(\n)/))
.filter(s => s);
for (const token of tokens) {
if (token === "\n") {
if (!this.currentLine) continue;
this.finalizeLine();
this.currentLine.appendData(token);
this.currentLine = undefined;
continue;
}
if (this.currentLine === undefined) {
this.currentLine = document.createTextNode(token);
this.currentContext().append(this.currentLine);
} else {
this.currentLine.appendData(token);
}
}
if (this.thinkingContext) {
this.thinkingContext.scrollTop =
this.thinkingContext.scrollHeight - this.thinkingContext.clientHeight;
}
}
finalize() {
this.finalizeLine();
// TODO: check if we have a dangling think section or code block and lift it back out
}
}