/** * Notebook dock: kernel chips / REPL header rendered apart from the cell list. * CellList is keyed by producing_cell_id; live chunks append a text node; * completed cells are memoized. Replaces app.js:10333-10630 `innerHTML=""` rebuild. */ import { render } from "preact"; import type { ComponentChildren } from "preact"; import { memo } from "preact/compat"; import { useLayoutEffect, useRef, useState } from "preact/hooks"; import { isReady } from "../../compat/stub"; import { t } from "../../i18n/runtime"; import { _kc, _replDrafts, _replLanguage, execSources, kernelFilter, pendingReplIdentity, } from "../../stores/notebook"; import { currentId } from "../../stores/session"; import { running } from "../../stores/stream"; import { executionQueue } from "../../stores/timeline"; import { publicText } from "../scrub/scrub"; import { appendTextNodeDelta, cellOutput, nbCellKey, notebookDisplayEntries } from "./cells"; import { notebookArtifactState, el, highlightCellSource, highlightTraceback, looksBinary, notebookExportLink, renderTableInto, stripAnsi, } from "./chrome"; import { branchCapability, copyNotebookCell, executeNotebookCode, forkNotebookCell, identityForOwner, interruptRepl, kernelCtl, kernelEpoch, kernelLabel, kernelStatusOf, nbPopulateEnvSelect, nbSwitchEnv, promoteNotebookCell, refreshKernelState, replEnabledNow, runtimeSummary, } from "./kernel"; import { bindNotebookScroll, followLiveOutput, measureNotebookFollow, nbRender, setNotebookRenderImpl, } from "./scroll"; import { filesT } from "../artifacts/copy"; import { applyArtifactDeepLink } from "../artifacts/ui"; import type { NotebookCell, NotebookOutputArtifact, ScrollBox } from "./types"; function notebookCellState(cell: NotebookCell): { key: string; cls: string; reasons?: unknown[]; } { if (cell.draft) return { key: "drafting", cls: "drafting" }; if (String(cell.replay_policy || "").toLowerCase() === "never") { return { key: "nonReplayable", cls: "non-replayable" }; } if (cell._historicalRevision) return { key: "historical", cls: "historical" }; if (cell.stale === true) { return { key: "stale", cls: "stale", reasons: Array.isArray(cell.stale_reasons) ? cell.stale_reasons : [], }; } return { key: "current", cls: "current" }; } function StreamingOutput({ text, isError }: { text: string; isError: boolean }) { const preRef = useRef(null); const seen = useRef(0); useLayoutEffect(() => { const pre = preRef.current; if (!pre) return; if (!pre.firstChild) pre.appendChild(document.createTextNode("")); const node = pre.firstChild as Text; seen.current = appendTextNodeDelta(node, seen.current, text); }, [text]); if (!text) return null; if (looksBinary(text)) { return
; } return (
output
    
); } function StaticOutput({ text, isError }: { text: string; isError: boolean }) { if (!text) return null; if (looksBinary(text)) { return
; } return (
output
{text}
); } function LiveStdout({ cellKey }: { cellKey: string }) { return ; } function LiveStderr({ cellKey }: { cellKey: string }) { return ; } function CodeBlock(opts: { cacheKey: string; source: string; lang: string; langLabel: string; status: string; env?: string; }) { const html = highlightCellSource(opts.cacheKey, opts.source || "", opts.lang); return (
{opts.langLabel}
{opts.status ? {opts.status} : null} {opts.env ? ( env {opts.env} ) : null}
        
      
); } function ErrorBlock({ raw }: { raw: string }) { const txt = stripAnsi(raw).replace(/\s+$/, ""); const nonEmpty = txt.split("\n").filter((l) => l.trim()); const summary = nonEmpty.length ? (nonEmpty[nonEmpty.length - 1] as string).trim() : t("nb.error.default"); const m = summary.match( /^([A-Za-z_][\w.]*(?:Error|Exception|Warning|Interrupt|Exit|Fault))\b:?\s*([\s\S]*)$/, ); return (
1 ? " clickable" : "")}> {m ? ( <> {m[1]} {m[2] ? {m[2]} : null} ) : ( {summary} )}
{nonEmpty.length > 1 ? (
      ) : null}
    
); } function OutputLinks({ artifact }: { artifact: NotebookOutputArtifact }) { return
{filesT("nb.artifact.download")}
; } function ConfirmedFigure({ artifact }: { artifact: NotebookOutputArtifact }) { const [failed, setFailed] = useState(false); const [attempt, setAttempt] = useState(0); return
{failed ?
{filesT("nb.artifact.failed")}
: {artifact.filename} setFailed(true)} />}
; } function CellFigures({ cell, names }: { cell: NotebookCell; names: string[] }) { return <>{names.map((filename) => { const result = notebookArtifactState(cell, filename); return result.artifact ? :
{filename + " · " + filesT("nb.artifact." + result.state)}
; })}; } function CellIo({ cell }: { cell: NotebookCell }) { const written = cell.files_written || []; const read = cell.files_read || []; if (!written.length && !read.length) return null; return (
{written.map((f) => { const result = notebookArtifactState(cell, f); return {result.artifact ? {f} : {f + " · " + filesT("nb.artifact." + result.state)}} ; })} {read.map((f) => ( {f} ))}
); } function CellActions({ cell }: { cell: NotebookCell }) { const st = kernelStatusOf(_kc.value.st); const replEnabled = !!st.repl_enabled; const appendable = replEnabled && !cell.live && !!String(cell.source || "").trim(); const canFork = !cell.live && branchCapability("fork_from_cell") && !!publicText(cell.fork_checkpoint_id, 96); return (
{canFork ? ( ) : null}
); } function CellShell({ cell, children, }: { cell: NotebookCell; children: ComponentChildren; }) { const k = cell.kernel_id || "python"; const cellState = notebookCellState(cell); return (
{(cell._revisions || []).length ? (
{t("nb.revisions.summary", (cell._revisions || []).length + 1, (cell._revisions || []).length)}
{(cell._revisions || []).map((rev) => ( ))}
) : null}
publicText(r, 240)).filter(Boolean).join("\n")} > {t("nb.cell." + cellState.key)} {cell.state_revision != null ? {"S" + cell.state_revision} : null}
{children}
); } function LiveCode({ cell }: { cell: NotebookCell }) { const rec = cellOutput(nbCellKey(cell)); const source = rec.source.value; const status = rec.status.value || "running"; const k = cell.kernel_id || "python"; const idx = cell.cell_index != null ? cell.cell_index : "…"; return ( ); } function LiveFigures({ cell }: { cell: NotebookCell }) { return ; } function LiveCell({ cell }: { cell: NotebookCell }) { const key = nbCellKey(cell); return ( {cell.error ? : null} ); } function CompletedCell({ cell }: { cell: NotebookCell }) { const k = cell.kernel_id || "python"; const st = cell.status || "ok"; const idx = cell.cell_index != null ? cell.cell_index : "…"; const csvs = (cell.files_written || []).filter((f) => /\.(csv|tsv)$/i.test(f)).slice(0, 4); return ( {cell.error ? : null} {csvs.map((f) => ( ))} {cell.draft ? null : } ); } function TableMount({ fname, cell }: { fname: string; cell: NotebookCell }) { const ref = useRef(null); const result = notebookArtifactState(cell, fname); const artifact = result.artifact; useLayoutEffect(() => { const host = ref.current; if (!host) return; host.replaceChildren(); if (artifact) return renderTableInto(host, fname, artifact.url); }, [fname, artifact?.url]); return
{fname}
{artifact ? :
{filesT("nb.artifact." + result.state)}
}
; } const MemoCompletedCell = memo(CompletedCell); function CellList() { const entries = notebookDisplayEntries(); const filter = kernelFilter.value; const shown = filter ? entries.filter((e) => (e.kernel_id || "python") === filter) : entries; if (!shown.length) return
{t("nb.empty")}
; return ( <> {shown.map((cell) => { const key = nbCellKey(cell); if (cell.live || cell.draft) return ; return ; })} ); } function toggleExecutedCodeLocal(): void { const fn = (globalThis as unknown as { toggleExecutedCode?: unknown }).toggleExecutedCode; if (isReady(fn)) { (fn as () => void)(); return; } type ExecSt = { open: boolean; data: unknown; selected: unknown; cells: unknown; loading: boolean; error: string; request: number; }; let st = execSources.value as ExecSt | null; if (!st) { st = { open: false, data: null, selected: null, cells: {}, loading: false, error: "", request: 0, }; execSources.value = st; } st.open = !st.open; execSources.value = st; nbRender(); } function KernelChips() { kernelEpoch.value; const entries = notebookDisplayEntries(); const kernels: string[] = []; entries.forEach((e) => { const k = e.kernel_id || "python"; if (!kernels.includes(k)) kernels.push(k); }); const filter = kernelFilter.value; const kc = _kc.value; const cachedRunning = !!(running.value || (kc.id === currentId.value && kernelStatusOf(kc.st).turn_running)); const cachedReady = !cachedRunning && !!(kc.id === currentId.value && kernelStatusOf(kc.st).alive); const runtimeMode = runtimeSummary().status; const badgeMode = runtimeMode || (cachedRunning ? "busy" : cachedReady ? "live" : "ended"); const execOpen = !!(execSources.value && (execSources.value as { open?: boolean }).open); return (
{kernels.map((k) => ( ))}
{t("runtime.status." + badgeMode)}
{currentId.value ? : null} {currentId.value ? ( ) : null}
); } function ExportMount({ frameId }: { frameId: string }) { const ref = useRef(null); useLayoutEffect(() => { const host = ref.current; if (!host) return; host.replaceChildren(); host.appendChild(notebookExportLink(frameId)); }, [frameId]); return ; } function OwnerChips() { const kinds = ["agent", "user_repl", "repair", "review_scratch"]; return (
{kinds.map((kind) => { const active = identityForOwner(executionQueue.value, kind); return ( {t("nb.owner." + kind)} ); })}
); } function StatusStrip() { kernelEpoch.value; const lineRef = useRef(null); useLayoutEffect(() => { const line = lineRef.current; void refreshKernelState({ strip: { line: line || undefined } }); }); return (
…
{t("nb.status.hint")}
); } function ReplPanel() { kernelEpoch.value; const envRef = useRef(null); const inputRef = useRef(null); const runRef = useRef(null); const stopRef = useRef(null); const titleRef = useRef(null); const stateRef = useRef(null); const reviveRef = useRef(null); useLayoutEffect(() => { void refreshKernelState({ state: stateRef.current || undefined, title: titleRef.current || undefined, revive: reviveRef.current || undefined, }); void nbPopulateEnvSelect(envRef.current); }); const pending = pendingReplIdentity.value && (pendingReplIdentity.value as { frame_id?: string }).frame_id === currentId.value ? pendingReplIdentity.value : null; const replIdentity = pending || identityForOwner(executionQueue.value, "user_repl"); const replBusy = !!replIdentity; const lang = _replLanguage.value === "r" ? "r" : "python"; const drafts = _replDrafts.value || { python: "", r: "" }; return (
kernel
…
{t("nb.repl.multilineHint")}