import { currentId, _openGen, historyLoad } from "../../stores/session"; import { historyT as t } from "./copy"; import { recoverConversation } from "./open"; import { useEffect, useLayoutEffect, useRef } from "preact/hooks"; import { bindStreamingPre, type StreamingPreHandle } from "./delta"; import { bindMessageScroll, down, unbindMessageScroll } from "./scroll"; export type StreamingPreProps = { className?: string; handleRef?: { current: StreamingPreHandle | null }; }; /** * Tool-output `
`. The text node is held on a ref; chunks call
 * `handle.append(delta)` → `textNode.appendData`. Do not set `textContent`
 * from the parent on every chunk.
 */
export function StreamingPre(props: StreamingPreProps) {
  const preRef = useRef(null);
  useLayoutEffect(() => {
    const pre = preRef.current;
    if (!pre) return;
    const textNode = document.createTextNode("");
    pre.appendChild(textNode);
    const handle = bindStreamingPre(textNode, "");
    if (props.handleRef) props.handleRef.current = handle;
    return () => {
      if (props.handleRef) props.handleRef.current = null;
    };
  }, [props.handleRef]);
  return 
;
}

/** Read feedback lives outside the transcript replaced by imperative history renders. */
export function HistoryLoadStatus() {
  const state = historyLoad.value;
  if (!state || state.fid !== currentId.value || state.generation !== _openGen.value || state.status === "loaded") return null;
  return (
    
{t("history." + state.status)} {Object.entries(state.errors).map(([part, message]) =>

{t("history.part." + part)}: {message}

)} {state.status !== "loading" && }
); } /** * Message column. `#messages` and `#jump-pill` keep the frozen DOM contract. * Live streaming is imperative (feed / flushRender) inside this host. */ export function MessageList() { const hostRef = useRef(null); useEffect(() => { bindMessageScroll(hostRef.current); return () => unbindMessageScroll(); }, []); return (
); }