Handling early output better.
Browse files- package.json +1 -1
- src/app/boot-app.js +4 -1
- src/app/output-message.js +59 -1
package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
{
|
| 2 |
"name": "localm",
|
| 3 |
-
"version": "1.1.
|
| 4 |
"description": "Chat application",
|
| 5 |
"scripts": {
|
| 6 |
"build": "esbuild src/index.js --target=es6 --bundle --sourcemap --outfile=./index.js --format=iife --external:fs --external:path --external:child_process --external:ws --external:katex/dist/katex.min.css",
|
|
|
|
| 1 |
{
|
| 2 |
"name": "localm",
|
| 3 |
+
"version": "1.1.19",
|
| 4 |
"description": "Chat application",
|
| 5 |
"scripts": {
|
| 6 |
"build": "esbuild src/index.js --target=es6 --bundle --sourcemap --outfile=./index.js --format=iife --external:fs --external:path --external:child_process --external:ws --external:katex/dist/katex.min.css",
|
src/app/boot-app.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
| 3 |
import { makeEnterPlugins, setupCrepeEnterKey } from './enter-key';
|
| 4 |
import { initHTML } from './init-html';
|
| 5 |
import { initMilkdown } from './init-milkdown';
|
| 6 |
-
import { outputMessage } from './output-message';
|
| 7 |
import { workerConnection } from './worker-connection';
|
| 8 |
|
| 9 |
import { name, description, version } from '../../package.json';
|
|
@@ -49,6 +49,9 @@ export async function bootApp() {
|
|
| 49 |
chatLogEditor = chatLogEditorInstance;
|
| 50 |
chatInputEditor = chatInputEditorInstance;
|
| 51 |
|
|
|
|
|
|
|
|
|
|
| 52 |
// Setup Enter key handling for the Crepe input editor
|
| 53 |
setupCrepeEnterKey(crepeInput, worker);
|
| 54 |
document.title = name + ' v' + version;
|
|
|
|
| 3 |
import { makeEnterPlugins, setupCrepeEnterKey } from './enter-key';
|
| 4 |
import { initHTML } from './init-html';
|
| 5 |
import { initMilkdown } from './init-milkdown';
|
| 6 |
+
import { outputMessage, flushBufferedOutputs } from './output-message';
|
| 7 |
import { workerConnection } from './worker-connection';
|
| 8 |
|
| 9 |
import { name, description, version } from '../../package.json';
|
|
|
|
| 49 |
chatLogEditor = chatLogEditorInstance;
|
| 50 |
chatInputEditor = chatInputEditorInstance;
|
| 51 |
|
| 52 |
+
// Flush any outputs that were buffered before the editor was ready
|
| 53 |
+
flushBufferedOutputs();
|
| 54 |
+
|
| 55 |
// Setup Enter key handling for the Crepe input editor
|
| 56 |
setupCrepeEnterKey(crepeInput, worker);
|
| 57 |
document.title = name + ' v' + version;
|
src/app/output-message.js
CHANGED
|
@@ -4,15 +4,32 @@ import { defaultValueCtx, Editor, editorViewCtx, editorViewOptionsCtx, parserCtx
|
|
| 4 |
|
| 5 |
import { chatLogEditor } from './boot-app';
|
| 6 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
export function outputMessage(msg) {
|
| 8 |
if (!chatLogEditor) {
|
|
|
|
|
|
|
|
|
|
| 9 |
const elem = document.createElement('pre');
|
| 10 |
elem.textContent = msg;
|
| 11 |
elem.style.whiteSpace = 'pre-wrap';
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
return;
|
| 14 |
}
|
| 15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
chatLogEditor.action((ctx) => {
|
| 17 |
const view = ctx.get(editorViewCtx);
|
| 18 |
const parser = ctx.get(parserCtx);
|
|
@@ -36,3 +53,44 @@ export function outputMessage(msg) {
|
|
| 36 |
}
|
| 37 |
}
|
| 38 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
|
| 5 |
import { chatLogEditor } from './boot-app';
|
| 6 |
|
| 7 |
+
// Buffer early outputs when the chat log editor is not yet initialized
|
| 8 |
+
const earlyOutputs = [];
|
| 9 |
+
const earlyElements = [];
|
| 10 |
+
|
| 11 |
export function outputMessage(msg) {
|
| 12 |
if (!chatLogEditor) {
|
| 13 |
+
// Store message for later flushing
|
| 14 |
+
earlyOutputs.push(msg);
|
| 15 |
+
// Also create a temporary visible element so user sees progress
|
| 16 |
const elem = document.createElement('pre');
|
| 17 |
elem.textContent = msg;
|
| 18 |
elem.style.whiteSpace = 'pre-wrap';
|
| 19 |
+
elem.dataset.earlyOutput = '1';
|
| 20 |
+
// Prefer appending into .chat-log container if present
|
| 21 |
+
const container = document.querySelector('.chat-log') || document.body;
|
| 22 |
+
container.appendChild(elem);
|
| 23 |
+
earlyElements.push(elem);
|
| 24 |
return;
|
| 25 |
}
|
| 26 |
|
| 27 |
+
// If there are buffered early outputs, flush them first
|
| 28 |
+
if (earlyOutputs.length > 0) {
|
| 29 |
+
flushBufferedOutputs();
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
// Insert the new message into the Milkdown editor
|
| 33 |
chatLogEditor.action((ctx) => {
|
| 34 |
const view = ctx.get(editorViewCtx);
|
| 35 |
const parser = ctx.get(parserCtx);
|
|
|
|
| 53 |
}
|
| 54 |
}
|
| 55 |
}
|
| 56 |
+
|
| 57 |
+
/**
|
| 58 |
+
* Move any early buffered outputs into the initialized chat log editor and remove temporary DOM nodes
|
| 59 |
+
*/
|
| 60 |
+
export function flushBufferedOutputs() {
|
| 61 |
+
if (!chatLogEditor) return;
|
| 62 |
+
if (earlyOutputs.length === 0) return;
|
| 63 |
+
|
| 64 |
+
// Combine buffered messages into one block separated by newlines
|
| 65 |
+
const combined = earlyOutputs.join('\n');
|
| 66 |
+
|
| 67 |
+
chatLogEditor.action((ctx) => {
|
| 68 |
+
const view = ctx.get(editorViewCtx);
|
| 69 |
+
const parser = ctx.get(parserCtx);
|
| 70 |
+
const serializer = ctx.get(serializerCtx);
|
| 71 |
+
const state = view.state;
|
| 72 |
+
|
| 73 |
+
const currentMarkdown = serializer(state.doc);
|
| 74 |
+
const newMarkdown = currentMarkdown ? (currentMarkdown + '\n' + combined) : combined;
|
| 75 |
+
const doc = parser(newMarkdown);
|
| 76 |
+
const tr = state.tr.replaceWith(0, state.doc.content.size, doc.content);
|
| 77 |
+
view.dispatch(tr);
|
| 78 |
+
});
|
| 79 |
+
|
| 80 |
+
// Remove temporary DOM elements that showed early outputs
|
| 81 |
+
for (const el of earlyElements) {
|
| 82 |
+
if (el && el.parentNode) el.parentNode.removeChild(el);
|
| 83 |
+
}
|
| 84 |
+
earlyElements.length = 0;
|
| 85 |
+
earlyOutputs.length = 0;
|
| 86 |
+
|
| 87 |
+
// Scroll chat log to bottom after flushing
|
| 88 |
+
const chatLogElem = document.querySelector('.chat-log .milkdown .ProseMirror');
|
| 89 |
+
if (chatLogElem) {
|
| 90 |
+
if (typeof chatLogElem.scrollTo === 'function') {
|
| 91 |
+
chatLogElem.scrollTo({ top: chatLogElem.scrollHeight, behavior: 'smooth' });
|
| 92 |
+
} else {
|
| 93 |
+
chatLogElem.scrollTop = chatLogElem.scrollHeight;
|
| 94 |
+
}
|
| 95 |
+
}
|
| 96 |
+
}
|