File size: 3,417 Bytes
811916b
 
 
 
 
 
cbf8fa6
 
 
 
811916b
 
cbf8fa6
 
 
811916b
 
 
cbf8fa6
 
 
 
 
811916b
 
 
cbf8fa6
 
 
 
 
 
811916b
 
 
 
 
 
 
 
 
 
 
 
 
 
93fa727
811916b
 
 
 
 
 
 
 
cbf8fa6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
// @ts-check

import { defaultValueCtx, Editor, editorViewCtx, editorViewOptionsCtx, parserCtx, rootCtx, serializerCtx } from '@milkdown/core';

import { chatLogEditor } from './boot-app';

// Buffer early outputs when the chat log editor is not yet initialized
const earlyOutputs = [];
const earlyElements = [];

export function outputMessage(msg) {
  if (!chatLogEditor) {
    // Store message for later flushing
    earlyOutputs.push(msg);
    // Also create a temporary visible element so user sees progress
    const elem = document.createElement('pre');
    elem.textContent = msg;
    elem.style.whiteSpace = 'pre-wrap';
    elem.dataset.earlyOutput = '1';
    // Prefer appending into .chat-log container if present
    const container = document.querySelector('.chat-log') || document.body;
    container.appendChild(elem);
    earlyElements.push(elem);
    return;
  }

  // If there are buffered early outputs, flush them first
  if (earlyOutputs.length > 0) {
    flushBufferedOutputs();
  }

  // Insert the new message into the Milkdown editor
  chatLogEditor.action((ctx) => {
    const view = ctx.get(editorViewCtx);
    const parser = ctx.get(parserCtx);
    const serializer = ctx.get(serializerCtx);
    const state = view.state;
    // Get current markdown, append new message, and parse
    const currentMarkdown = serializer(state.doc);
    const newMarkdown = currentMarkdown ? (currentMarkdown + '\n' + msg) : msg;
    const doc = parser(newMarkdown);
    // Use replaceWith and doc.content to avoid TransformError
    const tr = state.tr.replaceWith(0, state.doc.content.size, doc.content);
    view.dispatch(tr);
  });
  // Scroll chat log to bottom (smooth if possible)
  const chatLogElem = document.querySelector('.chat-log .milkdown .ProseMirror');
  if (chatLogElem) {
    if (typeof chatLogElem.scrollTo === 'function') {
      chatLogElem.scrollTo({ top: chatLogElem.scrollHeight, behavior: 'smooth' });
    } else {
      chatLogElem.scrollTop = chatLogElem.scrollHeight;
    }
  }
}

/**
 * Move any early buffered outputs into the initialized chat log editor and remove temporary DOM nodes
 */
export function flushBufferedOutputs() {
  if (!chatLogEditor) return;
  if (earlyOutputs.length === 0) return;

  // Combine buffered messages into one block separated by newlines
  const combined = earlyOutputs.join('\n');

  chatLogEditor.action((ctx) => {
    const view = ctx.get(editorViewCtx);
    const parser = ctx.get(parserCtx);
    const serializer = ctx.get(serializerCtx);
    const state = view.state;

    const currentMarkdown = serializer(state.doc);
    const newMarkdown = currentMarkdown ? (currentMarkdown + '\n' + combined) : combined;
    const doc = parser(newMarkdown);
    const tr = state.tr.replaceWith(0, state.doc.content.size, doc.content);
    view.dispatch(tr);
  });

  // Remove temporary DOM elements that showed early outputs
  for (const el of earlyElements) {
    if (el && el.parentNode) el.parentNode.removeChild(el);
  }
  earlyElements.length = 0;
  earlyOutputs.length = 0;

  // Scroll chat log to bottom after flushing
  const chatLogElem = document.querySelector('.chat-log .milkdown .ProseMirror');
  if (chatLogElem) {
    if (typeof chatLogElem.scrollTo === 'function') {
      chatLogElem.scrollTo({ top: chatLogElem.scrollHeight, behavior: 'smooth' });
    } else {
      chatLogElem.scrollTop = chatLogElem.scrollHeight;
    }
  }
}