langchain_agentx_stream_ui 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1429 @@
1
+ import {
2
+ InteractionBusContext,
3
+ ToolCallShell,
4
+ formatToolTitle,
5
+ getNoopInteractionBus,
6
+ resolveToolBody,
7
+ useInteractionBus
8
+ } from "./chunk-WM6Y6APP.js";
9
+ import {
10
+ DEFAULT_SESSION_VIEW_OPTIONS,
11
+ MultiSessionStoreContext,
12
+ NodeRegistryContext,
13
+ SessionStoreContext,
14
+ SessionTimeline,
15
+ SessionViewOptionsContext,
16
+ ToolDisplayOptionsContext,
17
+ createEmptyTree,
18
+ reduceTree,
19
+ replayEvents,
20
+ resolveEffectiveToolBodyMode,
21
+ useInternalErrors,
22
+ useNodeTyped,
23
+ usePendingPermissionForTool,
24
+ useSessionStatus,
25
+ useSessionViewOptions,
26
+ useToolDisplayOptions
27
+ } from "./chunk-3B4WC67E.js";
28
+ import {
29
+ createDefaultToolRegistry
30
+ } from "./chunk-MHK53ZHC.js";
31
+ import {
32
+ MarkdownRendererContext
33
+ } from "./chunk-4RIOBLGB.js";
34
+
35
+ // src/view/markdown/mermaidTheme.ts
36
+ var MERMAID_THEME_CSS = `
37
+ .node rect, .node circle, .node ellipse, .node polygon, .node path {
38
+ fill: var(--lax-bg-surface, #f8f4e6);
39
+ stroke: var(--lax-border-color, #d7c4bb);
40
+ stroke-width: 1px;
41
+ }
42
+ .edgePath .path {
43
+ stroke: var(--lax-accent-primary, #9b7cb9);
44
+ stroke-width: 1.5px;
45
+ }
46
+ .edgeLabel {
47
+ background-color: transparent;
48
+ color: var(--lax-foreground, #333333);
49
+ }
50
+ .edgeLabel p {
51
+ background-color: transparent !important;
52
+ }
53
+ .label {
54
+ color: var(--lax-foreground, #333333);
55
+ }
56
+ .cluster rect {
57
+ fill: var(--lax-bg-surface, #f8f4e6);
58
+ stroke: var(--lax-border-color, #d7c4bb);
59
+ stroke-width: 1px;
60
+ }
61
+ .actor {
62
+ fill: var(--lax-bg-surface, #f8f4e6);
63
+ stroke: var(--lax-border-color, #d7c4bb);
64
+ stroke-width: 1px;
65
+ }
66
+ text.actor {
67
+ fill: var(--lax-foreground, #333333);
68
+ stroke: none;
69
+ }
70
+ .messageText {
71
+ fill: var(--lax-foreground, #333333);
72
+ stroke: none;
73
+ }
74
+ .messageLine0, .messageLine1 {
75
+ stroke: var(--lax-accent-primary, #9b7cb9);
76
+ }
77
+
78
+ [data-theme="dark"] .node rect,
79
+ [data-theme="dark"] .node circle,
80
+ [data-theme="dark"] .node ellipse,
81
+ [data-theme="dark"] .node polygon,
82
+ [data-theme="dark"] .node path {
83
+ fill: var(--lax-card-bg, #222222);
84
+ stroke: var(--lax-border-color, #5d4037);
85
+ }
86
+ [data-theme="dark"] .edgePath .path,
87
+ [data-theme="dark"] .flowchart-link {
88
+ stroke: var(--lax-accent-primary, #9370db);
89
+ }
90
+ [data-theme="dark"] .edgeLabel,
91
+ [data-theme="dark"] .label,
92
+ [data-theme="dark"] text.actor,
93
+ [data-theme="dark"] .messageText,
94
+ [data-theme="dark"] text.sequenceText,
95
+ [data-theme="dark"] .noteText {
96
+ fill: var(--lax-foreground, #f0f0f0);
97
+ color: var(--lax-foreground, #f0f0f0);
98
+ }
99
+ [data-theme="dark"] .cluster rect,
100
+ [data-theme="dark"] .actor {
101
+ fill: var(--lax-card-bg, #222222);
102
+ stroke: var(--lax-border-color, #5d4037);
103
+ }
104
+ [data-theme="dark"] .messageLine0,
105
+ [data-theme="dark"] .messageLine1 {
106
+ stroke: var(--lax-accent-primary, #9370db);
107
+ stroke-width: 1.5px;
108
+ }
109
+ `;
110
+ var DEFAULT_MERMAID_INIT = {
111
+ theme: "neutral",
112
+ maxTextSize: 1e5
113
+ };
114
+
115
+ // src/view/markdown/initMermaid.ts
116
+ var mermaidInitPromise = null;
117
+ var activeInitOptions = { ...DEFAULT_MERMAID_INIT };
118
+ function buildInitializeConfig(options) {
119
+ return {
120
+ startOnLoad: false,
121
+ theme: options.theme ?? DEFAULT_MERMAID_INIT.theme,
122
+ securityLevel: "strict",
123
+ suppressErrorRendering: true,
124
+ logLevel: "error",
125
+ maxTextSize: options.maxTextSize ?? DEFAULT_MERMAID_INIT.maxTextSize,
126
+ htmlLabels: true,
127
+ themeCSS: MERMAID_THEME_CSS,
128
+ flowchart: {
129
+ htmlLabels: true,
130
+ curve: "basis",
131
+ nodeSpacing: 60,
132
+ rankSpacing: 60,
133
+ padding: 20
134
+ }
135
+ };
136
+ }
137
+ function configureMermaid(options) {
138
+ activeInitOptions = { ...DEFAULT_MERMAID_INIT, ...options };
139
+ mermaidInitPromise = null;
140
+ }
141
+ async function loadMermaid() {
142
+ if (typeof window === "undefined") return null;
143
+ if (!mermaidInitPromise) {
144
+ mermaidInitPromise = import("mermaid").then((mod) => {
145
+ const mermaid = mod.default;
146
+ mermaid.initialize(buildInitializeConfig(activeInitOptions));
147
+ return mermaid;
148
+ }).catch(() => null);
149
+ }
150
+ return mermaidInitPromise;
151
+ }
152
+
153
+ // src/view/markdown/MermaidDiagram.tsx
154
+ import { useEffect as useEffect4, useId, useMemo, useRef as useRef3, useState as useState4 } from "react";
155
+
156
+ // src/view/markdown/MermaidFullscreenModal.tsx
157
+ import { useEffect, useRef, useState } from "react";
158
+ import { jsx, jsxs } from "react/jsx-runtime";
159
+ function MermaidFullscreenModal({
160
+ isOpen,
161
+ onClose,
162
+ svg,
163
+ title = "Diagram"
164
+ }) {
165
+ const panelRef = useRef(null);
166
+ const [zoom, setZoom] = useState(1);
167
+ useEffect(() => {
168
+ if (!isOpen) return;
169
+ const onKeyDown = (event) => {
170
+ if (event.key === "Escape") onClose();
171
+ };
172
+ document.addEventListener("keydown", onKeyDown);
173
+ return () => document.removeEventListener("keydown", onKeyDown);
174
+ }, [isOpen, onClose]);
175
+ useEffect(() => {
176
+ if (isOpen) setZoom(1);
177
+ }, [isOpen]);
178
+ if (!isOpen) return null;
179
+ const zoomOut = () => setZoom((z) => Math.max(0.5, z - 0.1));
180
+ const zoomIn = () => setZoom((z) => Math.min(2, z + 0.1));
181
+ return /* @__PURE__ */ jsx(
182
+ "div",
183
+ {
184
+ className: "lax-mermaid-modal",
185
+ role: "dialog",
186
+ "aria-modal": "true",
187
+ "aria-label": title,
188
+ onMouseDown: (event) => {
189
+ if (panelRef.current && !panelRef.current.contains(event.target)) {
190
+ onClose();
191
+ }
192
+ },
193
+ children: /* @__PURE__ */ jsxs("div", { ref: panelRef, className: "lax-mermaid-modal__panel", children: [
194
+ /* @__PURE__ */ jsxs("div", { className: "lax-mermaid-modal__header", children: [
195
+ /* @__PURE__ */ jsx("span", { className: "lax-mermaid-modal__title", children: title }),
196
+ /* @__PURE__ */ jsxs("div", { className: "lax-mermaid-modal__controls", children: [
197
+ /* @__PURE__ */ jsx(ZoomButton, { label: "Zoom out", onClick: zoomOut, children: "\u2212" }),
198
+ /* @__PURE__ */ jsxs("span", { className: "lax-mermaid-modal__zoom", children: [
199
+ Math.round(zoom * 100),
200
+ "%"
201
+ ] }),
202
+ /* @__PURE__ */ jsx(ZoomButton, { label: "Zoom in", onClick: zoomIn, children: "+" }),
203
+ /* @__PURE__ */ jsx(ZoomButton, { label: "Reset zoom", onClick: () => setZoom(1), children: "\u21BA" }),
204
+ /* @__PURE__ */ jsx(ZoomButton, { label: "Close", onClick: onClose, children: "\xD7" })
205
+ ] })
206
+ ] }),
207
+ /* @__PURE__ */ jsx("div", { className: "lax-mermaid-modal__body", children: /* @__PURE__ */ jsx(
208
+ "div",
209
+ {
210
+ className: "lax-mermaid-modal__canvas",
211
+ style: {
212
+ transform: `scale(${zoom})`,
213
+ transformOrigin: "center center"
214
+ },
215
+ dangerouslySetInnerHTML: { __html: svg }
216
+ }
217
+ ) })
218
+ ] })
219
+ }
220
+ );
221
+ }
222
+ function ZoomButton({
223
+ label,
224
+ onClick,
225
+ children
226
+ }) {
227
+ return /* @__PURE__ */ jsx("button", { type: "button", className: "lax-mermaid-modal__btn", "aria-label": label, onClick, children });
228
+ }
229
+
230
+ // src/view/markdown/mermaidDisplayUtils.ts
231
+ import { useEffect as useEffect2, useState as useState2 } from "react";
232
+ function isDarkMode() {
233
+ if (typeof document === "undefined") return false;
234
+ const root = document.documentElement;
235
+ const dataTheme = root.getAttribute("data-theme");
236
+ if (dataTheme === "dark") return true;
237
+ if (dataTheme === "light") return false;
238
+ if (root.classList.contains("dark")) return true;
239
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
240
+ return false;
241
+ }
242
+ return window.matchMedia("(prefers-color-scheme: dark)").matches;
243
+ }
244
+ function applyDarkThemeToSvg(svg, dark) {
245
+ if (!svg) return svg;
246
+ const withoutAttr = svg.replace(/\sdata-theme="dark"/g, "");
247
+ if (!dark) return withoutAttr;
248
+ if (withoutAttr.includes('data-theme="dark"')) return withoutAttr;
249
+ return withoutAttr.replace("<svg ", '<svg data-theme="dark" ');
250
+ }
251
+ function useDarkMode() {
252
+ const [dark, setDark] = useState2(false);
253
+ useEffect2(() => {
254
+ const update = () => setDark(isDarkMode());
255
+ update();
256
+ const mq = typeof window.matchMedia === "function" ? window.matchMedia("(prefers-color-scheme: dark)") : null;
257
+ mq?.addEventListener("change", update);
258
+ const observer = new MutationObserver(update);
259
+ observer.observe(document.documentElement, {
260
+ attributes: true,
261
+ attributeFilter: ["class", "data-theme"]
262
+ });
263
+ return () => {
264
+ mq?.removeEventListener("change", update);
265
+ observer.disconnect();
266
+ };
267
+ }, []);
268
+ return dark;
269
+ }
270
+
271
+ // src/view/markdown/useCopyText.ts
272
+ import { useCallback, useEffect as useEffect3, useRef as useRef2, useState as useState3 } from "react";
273
+
274
+ // src/view/markdown/copyToClipboard.ts
275
+ async function copyToClipboard(text) {
276
+ if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
277
+ try {
278
+ await navigator.clipboard.writeText(text);
279
+ return true;
280
+ } catch {
281
+ }
282
+ }
283
+ if (typeof document === "undefined") return false;
284
+ try {
285
+ const textArea = document.createElement("textarea");
286
+ textArea.value = text;
287
+ textArea.style.position = "fixed";
288
+ textArea.style.left = "-9999px";
289
+ document.body.appendChild(textArea);
290
+ textArea.select();
291
+ const ok = document.execCommand("copy");
292
+ document.body.removeChild(textArea);
293
+ return ok;
294
+ } catch {
295
+ return false;
296
+ }
297
+ }
298
+
299
+ // src/view/markdown/useCopyText.ts
300
+ function useCopyText(text, resetMs = 2e3) {
301
+ const [copied, setCopied] = useState3(false);
302
+ const timerRef = useRef2();
303
+ useEffect3(() => {
304
+ return () => {
305
+ if (timerRef.current !== void 0) {
306
+ window.clearTimeout(timerRef.current);
307
+ }
308
+ };
309
+ }, []);
310
+ const copy = useCallback(async () => {
311
+ const ok = await copyToClipboard(text);
312
+ if (!ok) return;
313
+ setCopied(true);
314
+ if (timerRef.current !== void 0) {
315
+ window.clearTimeout(timerRef.current);
316
+ }
317
+ timerRef.current = window.setTimeout(() => setCopied(false), resetMs);
318
+ }, [text, resetMs]);
319
+ return { copied, copy };
320
+ }
321
+
322
+ // src/view/markdown/MermaidDiagram.tsx
323
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
324
+ function MermaidDiagram({
325
+ chart,
326
+ className = "",
327
+ zoomingEnabled = true,
328
+ fullscreenEnabled = true
329
+ }) {
330
+ const [rawSvg, setRawSvg] = useState4("");
331
+ const [error, setError] = useState4(null);
332
+ const [isFullscreen, setIsFullscreen] = useState4(false);
333
+ const { copied, copy } = useCopyText(chart);
334
+ const containerRef = useRef3(null);
335
+ const reactId = useId().replace(/:/g, "");
336
+ const renderId = `lax-mermaid-${reactId}`;
337
+ const isDark = useDarkMode();
338
+ const svg = useMemo(() => applyDarkThemeToSvg(rawSvg, isDark), [rawSvg, isDark]);
339
+ useEffect4(() => {
340
+ if (!chart.trim()) return;
341
+ let cancelled = false;
342
+ const renderChart = async () => {
343
+ const mermaid = await loadMermaid();
344
+ if (cancelled) return;
345
+ if (!mermaid) {
346
+ setError("mermaid peer dependency is not installed");
347
+ return;
348
+ }
349
+ try {
350
+ setError(null);
351
+ setRawSvg("");
352
+ const { svg: renderedSvg } = await mermaid.render(renderId, chart.trim());
353
+ if (!cancelled) {
354
+ setRawSvg(renderedSvg);
355
+ }
356
+ } catch (err) {
357
+ if (!cancelled) {
358
+ const message = err instanceof Error ? err.message : String(err);
359
+ setError(message);
360
+ }
361
+ }
362
+ };
363
+ void renderChart();
364
+ return () => {
365
+ cancelled = true;
366
+ };
367
+ }, [chart, renderId]);
368
+ useEffect4(() => {
369
+ if (!svg || !zoomingEnabled || !containerRef.current) return;
370
+ let disposed = false;
371
+ const setupPanZoom = async () => {
372
+ const svgElement = containerRef.current?.querySelector("svg");
373
+ if (!svgElement || disposed) return;
374
+ svgElement.style.maxWidth = "none";
375
+ svgElement.style.width = "100%";
376
+ try {
377
+ const svgPanZoom = (await import("svg-pan-zoom")).default;
378
+ if (disposed) return;
379
+ svgPanZoom(svgElement, {
380
+ zoomEnabled: true,
381
+ controlIconsEnabled: true,
382
+ fit: true,
383
+ center: true,
384
+ minZoom: 0.1,
385
+ maxZoom: 10,
386
+ zoomScaleSensitivity: 0.3
387
+ });
388
+ } catch {
389
+ }
390
+ };
391
+ const timer = window.setTimeout(() => {
392
+ void setupPanZoom();
393
+ }, 50);
394
+ return () => {
395
+ disposed = true;
396
+ window.clearTimeout(timer);
397
+ };
398
+ }, [svg, zoomingEnabled]);
399
+ const handleCopy = () => void copy();
400
+ const openFullscreen = () => {
401
+ if (!error && svg) setIsFullscreen(true);
402
+ };
403
+ const toolbar = /* @__PURE__ */ jsxs2("div", { className: "lax-mermaid__toolbar", children: [
404
+ /* @__PURE__ */ jsx2("span", { className: "lax-mermaid__label", children: "Mermaid" }),
405
+ /* @__PURE__ */ jsxs2("div", { className: "lax-mermaid__actions", children: [
406
+ fullscreenEnabled && svg ? /* @__PURE__ */ jsx2(
407
+ "button",
408
+ {
409
+ type: "button",
410
+ className: "lax-mermaid__expand",
411
+ onClick: openFullscreen,
412
+ "aria-label": "View fullscreen",
413
+ children: "Expand"
414
+ }
415
+ ) : null,
416
+ /* @__PURE__ */ jsx2("button", { type: "button", className: "lax-mermaid__copy", onClick: handleCopy, children: copied ? "Copied" : "Copy" })
417
+ ] })
418
+ ] });
419
+ if (error) {
420
+ return /* @__PURE__ */ jsxs2("div", { className: `lax-mermaid lax-mermaid--error ${className}`.trim(), children: [
421
+ toolbar,
422
+ /* @__PURE__ */ jsx2("pre", { className: "lax-mermaid__fallback", children: chart }),
423
+ /* @__PURE__ */ jsx2("p", { className: "lax-mermaid__error-text", children: error })
424
+ ] });
425
+ }
426
+ if (!svg) {
427
+ return /* @__PURE__ */ jsxs2("div", { className: `lax-mermaid lax-mermaid--loading ${className}`.trim(), children: [
428
+ /* @__PURE__ */ jsxs2("span", { className: "lax-mermaid__loading-dots", "aria-hidden": "true", children: [
429
+ /* @__PURE__ */ jsx2("span", {}),
430
+ /* @__PURE__ */ jsx2("span", {}),
431
+ /* @__PURE__ */ jsx2("span", {})
432
+ ] }),
433
+ /* @__PURE__ */ jsx2("span", { className: "lax-mermaid__loading-text", children: "Rendering diagram\u2026" })
434
+ ] });
435
+ }
436
+ const canvasClickable = fullscreenEnabled && !zoomingEnabled;
437
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
438
+ /* @__PURE__ */ jsxs2("div", { className: `lax-mermaid ${className}`.trim(), children: [
439
+ toolbar,
440
+ /* @__PURE__ */ jsx2(
441
+ "div",
442
+ {
443
+ ref: containerRef,
444
+ className: [
445
+ "lax-mermaid__canvas",
446
+ zoomingEnabled ? "lax-mermaid__canvas--zoom" : "",
447
+ canvasClickable ? "lax-mermaid__canvas--clickable" : ""
448
+ ].filter(Boolean).join(" "),
449
+ dangerouslySetInnerHTML: { __html: svg },
450
+ onClick: canvasClickable ? openFullscreen : void 0,
451
+ onKeyDown: canvasClickable ? (event) => {
452
+ if (event.key === "Enter" || event.key === " ") {
453
+ event.preventDefault();
454
+ openFullscreen();
455
+ }
456
+ } : void 0,
457
+ role: canvasClickable ? "button" : void 0,
458
+ tabIndex: canvasClickable ? 0 : void 0,
459
+ "aria-label": canvasClickable ? "Click to view fullscreen" : void 0,
460
+ title: canvasClickable ? "Click to view fullscreen" : void 0
461
+ }
462
+ )
463
+ ] }),
464
+ fullscreenEnabled ? /* @__PURE__ */ jsx2(
465
+ MermaidFullscreenModal,
466
+ {
467
+ isOpen: isFullscreen,
468
+ onClose: () => setIsFullscreen(false),
469
+ svg
470
+ }
471
+ ) : null
472
+ ] });
473
+ }
474
+
475
+ // src/view/markdown/PlantUMLBlock.tsx
476
+ import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
477
+ import { oneDark } from "react-syntax-highlighter/dist/esm/styles/prism";
478
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
479
+ function PlantUMLBlock({ content, className = "" }) {
480
+ const { copied, copy } = useCopyText(content);
481
+ return /* @__PURE__ */ jsxs3("div", { className: `lax-plantuml lax-code-block lax-code-block--rich ${className}`.trim(), children: [
482
+ /* @__PURE__ */ jsxs3("div", { className: "lax-code-block__header", children: [
483
+ /* @__PURE__ */ jsx3("span", { className: "lax-code-block__lang", children: "PlantUML" }),
484
+ /* @__PURE__ */ jsx3(
485
+ "button",
486
+ {
487
+ type: "button",
488
+ className: `lax-code-block__copy ${copied ? "lax-code-block__copy--copied" : ""}`,
489
+ onClick: () => void copy(),
490
+ children: copied ? "Copied" : "Copy"
491
+ }
492
+ )
493
+ ] }),
494
+ /* @__PURE__ */ jsx3(
495
+ SyntaxHighlighter,
496
+ {
497
+ language: "text",
498
+ style: oneDark,
499
+ customStyle: { margin: 0, padding: "0.75rem 1rem", background: "transparent" },
500
+ showLineNumbers: true,
501
+ wrapLongLines: true,
502
+ children: content
503
+ }
504
+ )
505
+ ] });
506
+ }
507
+
508
+ // src/view/markdown/plantumlConfig.ts
509
+ var DEFAULT_PLANTUML_SERVER_URL = "https://kroki.io";
510
+ var plantumlServerUrl = DEFAULT_PLANTUML_SERVER_URL;
511
+ function configurePlantumlServer(url) {
512
+ plantumlServerUrl = url.replace(/\/$/, "");
513
+ }
514
+ function getPlantumlServerUrl() {
515
+ return plantumlServerUrl;
516
+ }
517
+
518
+ // src/view/markdown/PlantUMLDiagram.tsx
519
+ import { useEffect as useEffect5, useState as useState5 } from "react";
520
+
521
+ // src/view/markdown/plantumlRender.ts
522
+ function normalizePlantumlSource(source) {
523
+ const trimmed = source.trim();
524
+ if (!trimmed) return trimmed;
525
+ if (trimmed.includes("@startuml")) return trimmed;
526
+ return `@startuml
527
+ ${trimmed}
528
+ @enduml`;
529
+ }
530
+ async function fetchPlantumlSvg(source, serverUrl = getPlantumlServerUrl()) {
531
+ const base = serverUrl.replace(/\/$/, "");
532
+ const diagramSource = normalizePlantumlSource(source);
533
+ const res = await fetch(`${base}/`, {
534
+ method: "POST",
535
+ headers: {
536
+ "Content-Type": "application/json",
537
+ Accept: "image/svg+xml"
538
+ },
539
+ body: JSON.stringify({
540
+ diagram_source: diagramSource,
541
+ diagram_type: "plantuml",
542
+ output_format: "svg"
543
+ })
544
+ });
545
+ if (!res.ok) {
546
+ const detail = await res.text().catch(() => "");
547
+ const suffix = detail ? `: ${detail.slice(0, 200)}` : "";
548
+ throw new Error(`PlantUML render failed (${res.status})${suffix}`);
549
+ }
550
+ const svg = await res.text();
551
+ if (!svg.includes("<svg")) {
552
+ throw new Error("PlantUML render returned non-SVG response");
553
+ }
554
+ return svg;
555
+ }
556
+
557
+ // src/view/markdown/PlantUMLDiagram.tsx
558
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
559
+ function PlantUMLDiagram({
560
+ content,
561
+ className = "",
562
+ serverUrl,
563
+ fullscreenEnabled = true,
564
+ sourceOnly = false
565
+ }) {
566
+ const [svg, setSvg] = useState5("");
567
+ const [error, setError] = useState5(null);
568
+ const [isFullscreen, setIsFullscreen] = useState5(false);
569
+ const { copied, copy } = useCopyText(content);
570
+ useEffect5(() => {
571
+ if (!content.trim() || sourceOnly) return;
572
+ let cancelled = false;
573
+ const render = async () => {
574
+ if (typeof window === "undefined") return;
575
+ try {
576
+ setError(null);
577
+ setSvg("");
578
+ const rendered = await fetchPlantumlSvg(content, serverUrl);
579
+ if (!cancelled) setSvg(rendered);
580
+ } catch (err) {
581
+ if (!cancelled) {
582
+ const message = err instanceof Error ? err.message : String(err);
583
+ setError(message);
584
+ }
585
+ }
586
+ };
587
+ void render();
588
+ return () => {
589
+ cancelled = true;
590
+ };
591
+ }, [content, serverUrl, sourceOnly]);
592
+ if (sourceOnly) {
593
+ return /* @__PURE__ */ jsx4(PlantUMLBlock, { content, className });
594
+ }
595
+ const handleCopy = () => void copy();
596
+ const openFullscreen = () => {
597
+ if (svg) setIsFullscreen(true);
598
+ };
599
+ const toolbar = /* @__PURE__ */ jsxs4("div", { className: "lax-plantuml__toolbar", children: [
600
+ /* @__PURE__ */ jsx4("span", { className: "lax-plantuml__label", children: "PlantUML" }),
601
+ /* @__PURE__ */ jsxs4("div", { className: "lax-plantuml__actions", children: [
602
+ fullscreenEnabled && svg ? /* @__PURE__ */ jsx4(
603
+ "button",
604
+ {
605
+ type: "button",
606
+ className: "lax-plantuml__expand",
607
+ onClick: openFullscreen,
608
+ "aria-label": "View fullscreen",
609
+ children: "Expand"
610
+ }
611
+ ) : null,
612
+ /* @__PURE__ */ jsx4("button", { type: "button", className: "lax-plantuml__copy", onClick: handleCopy, children: copied ? "Copied" : "Copy" })
613
+ ] })
614
+ ] });
615
+ if (error) {
616
+ return /* @__PURE__ */ jsxs4("div", { className: `lax-plantuml lax-plantuml--error ${className}`.trim(), children: [
617
+ toolbar,
618
+ /* @__PURE__ */ jsx4("pre", { className: "lax-plantuml__fallback", children: content }),
619
+ /* @__PURE__ */ jsx4("p", { className: "lax-plantuml__error-text", children: error })
620
+ ] });
621
+ }
622
+ if (!svg) {
623
+ return /* @__PURE__ */ jsxs4("div", { className: `lax-plantuml lax-plantuml--loading ${className}`.trim(), children: [
624
+ /* @__PURE__ */ jsxs4("span", { className: "lax-plantuml__loading-dots", "aria-hidden": "true", children: [
625
+ /* @__PURE__ */ jsx4("span", {}),
626
+ /* @__PURE__ */ jsx4("span", {}),
627
+ /* @__PURE__ */ jsx4("span", {})
628
+ ] }),
629
+ /* @__PURE__ */ jsx4("span", { className: "lax-plantuml__loading-text", children: "Rendering diagram\u2026" })
630
+ ] });
631
+ }
632
+ return /* @__PURE__ */ jsxs4(Fragment2, { children: [
633
+ /* @__PURE__ */ jsxs4("div", { className: `lax-plantuml ${className}`.trim(), children: [
634
+ toolbar,
635
+ /* @__PURE__ */ jsx4(
636
+ "div",
637
+ {
638
+ className: "lax-plantuml__canvas lax-plantuml__canvas--clickable",
639
+ dangerouslySetInnerHTML: { __html: svg },
640
+ onClick: fullscreenEnabled ? openFullscreen : void 0,
641
+ onKeyDown: fullscreenEnabled ? (event) => {
642
+ if (event.key === "Enter" || event.key === " ") {
643
+ event.preventDefault();
644
+ openFullscreen();
645
+ }
646
+ } : void 0,
647
+ role: fullscreenEnabled ? "button" : void 0,
648
+ tabIndex: fullscreenEnabled ? 0 : void 0,
649
+ "aria-label": fullscreenEnabled ? "Click to view fullscreen" : void 0
650
+ }
651
+ )
652
+ ] }),
653
+ fullscreenEnabled ? /* @__PURE__ */ jsx4(
654
+ MermaidFullscreenModal,
655
+ {
656
+ isOpen: isFullscreen,
657
+ onClose: () => setIsFullscreen(false),
658
+ svg,
659
+ title: "PlantUML"
660
+ }
661
+ ) : null
662
+ ] });
663
+ }
664
+
665
+ // src/view/markdown/RichMarkdown.tsx
666
+ import { useMemo as useMemo2 } from "react";
667
+ import ReactMarkdown from "react-markdown";
668
+ import remarkGfm from "remark-gfm";
669
+
670
+ // src/view/markdown/markdownUtils.ts
671
+ var MD_SYNTAX_RE = /[#*`|[>\-_~]|\n\n|^\d+\. |\n\d+\. /;
672
+ function hasMarkdownSyntax(content) {
673
+ const sample = content.length > 500 ? content.slice(0, 500) : content;
674
+ return MD_SYNTAX_RE.test(sample);
675
+ }
676
+ function hashContent(content) {
677
+ let hash = 5381;
678
+ for (let i = 0; i < content.length; i++) {
679
+ hash = (hash << 5) + hash + content.charCodeAt(i) & 4294967295;
680
+ }
681
+ return hash.toString(36);
682
+ }
683
+ function collapseExcessiveBlankLines(text) {
684
+ return text.replace(/\n{3,}/g, "\n\n").trim();
685
+ }
686
+ function paragraphHasContent(children) {
687
+ if (children == null || children === false) return false;
688
+ if (typeof children === "string") return children.trim().length > 0;
689
+ if (typeof children === "number") return true;
690
+ if (Array.isArray(children)) return children.some(paragraphHasContent);
691
+ return true;
692
+ }
693
+
694
+ // src/view/markdown/richMarkdownComponents.tsx
695
+ import { Prism as SyntaxHighlighter2 } from "react-syntax-highlighter";
696
+ import { oneDark as oneDark2 } from "react-syntax-highlighter/dist/esm/styles/prism";
697
+
698
+ // src/view/markdown/headingIds.ts
699
+ var headingCounter = 0;
700
+ function generateHeadingId(text) {
701
+ const slug = text.trim().toLowerCase().replace(/[^\w\u4e00-\u9fff]+/g, "-").replace(/^-+|-+$/g, "");
702
+ if (slug) return slug;
703
+ headingCounter += 1;
704
+ return `heading-${headingCounter}`;
705
+ }
706
+
707
+ // src/view/markdown/richMarkdownComponents.tsx
708
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
709
+ function RichCodeBlock({ language, content }) {
710
+ const { copied, copy } = useCopyText(content);
711
+ return /* @__PURE__ */ jsxs5("div", { className: "lax-code-block lax-code-block--rich", children: [
712
+ /* @__PURE__ */ jsxs5("div", { className: "lax-code-block__header", children: [
713
+ /* @__PURE__ */ jsx5("span", { className: "lax-code-block__lang", children: language || "code" }),
714
+ /* @__PURE__ */ jsx5(
715
+ "button",
716
+ {
717
+ type: "button",
718
+ className: `lax-code-block__copy ${copied ? "lax-code-block__copy--copied" : ""}`,
719
+ onClick: () => void copy(),
720
+ children: copied ? "Copied" : "Copy"
721
+ }
722
+ )
723
+ ] }),
724
+ /* @__PURE__ */ jsx5(
725
+ SyntaxHighlighter2,
726
+ {
727
+ language: language || "text",
728
+ style: oneDark2,
729
+ customStyle: { margin: 0, padding: "0.75rem 1rem", background: "transparent" },
730
+ showLineNumbers: true,
731
+ wrapLongLines: true,
732
+ children: content
733
+ }
734
+ )
735
+ ] });
736
+ }
737
+ function heading(children, level, className) {
738
+ const text = children != null ? String(children) : "";
739
+ const id = generateHeadingId(text);
740
+ const Tag = level;
741
+ return /* @__PURE__ */ jsx5(Tag, { id, className: `lax-markdown__heading ${className}`, children });
742
+ }
743
+ var RICH_MARKDOWN_COMPONENTS = {
744
+ code(props) {
745
+ const { inline, className, children, ...rest } = props;
746
+ const match = /language-(\w+)/.exec(className || "");
747
+ const codeContent = children != null ? String(children).replace(/\n$/, "") : "";
748
+ if (!inline && match?.[1] === "mermaid" && codeContent.trim()) {
749
+ return /* @__PURE__ */ jsx5(MermaidDiagram, { chart: codeContent });
750
+ }
751
+ if (!inline && match?.[1] === "plantuml" && codeContent.trim()) {
752
+ return /* @__PURE__ */ jsx5(PlantUMLDiagram, { content: codeContent });
753
+ }
754
+ if (!inline && match) {
755
+ return /* @__PURE__ */ jsx5(RichCodeBlock, { language: match[1], content: codeContent });
756
+ }
757
+ return /* @__PURE__ */ jsx5("code", { className: "lax-markdown__inline-code", ...rest, children });
758
+ },
759
+ h1({ children }) {
760
+ return heading(children, "h1", "lax-markdown__h1");
761
+ },
762
+ h2({ children }) {
763
+ return heading(children, "h2", "lax-markdown__h2");
764
+ },
765
+ h3({ children }) {
766
+ return heading(children, "h3", "lax-markdown__h3");
767
+ },
768
+ h4({ children }) {
769
+ return heading(children, "h4", "lax-markdown__h4");
770
+ },
771
+ h5({ children }) {
772
+ return heading(children, "h5", "lax-markdown__h5");
773
+ },
774
+ h6({ children }) {
775
+ return heading(children, "h6", "lax-markdown__h6");
776
+ },
777
+ p({ children }) {
778
+ if (!paragraphHasContent(children)) return null;
779
+ return /* @__PURE__ */ jsx5("p", { className: "lax-markdown__paragraph", children });
780
+ },
781
+ ul({ children }) {
782
+ return /* @__PURE__ */ jsx5("ul", { className: "lax-markdown__list lax-markdown__ul", children });
783
+ },
784
+ ol({ children }) {
785
+ return /* @__PURE__ */ jsx5("ol", { className: "lax-markdown__list lax-markdown__ol", children });
786
+ },
787
+ li({ children }) {
788
+ return /* @__PURE__ */ jsx5("li", { className: "lax-markdown__li", children });
789
+ },
790
+ a({ children, href }) {
791
+ return /* @__PURE__ */ jsx5("a", { href, className: "lax-markdown__link", target: "_blank", rel: "noopener noreferrer", children });
792
+ },
793
+ blockquote({ children }) {
794
+ return /* @__PURE__ */ jsx5("blockquote", { className: "lax-markdown__blockquote", children });
795
+ },
796
+ table({ children }) {
797
+ return /* @__PURE__ */ jsx5("div", { className: "lax-markdown__table-wrapper", children: /* @__PURE__ */ jsx5("table", { className: "lax-markdown__table", children }) });
798
+ },
799
+ thead({ children }) {
800
+ return /* @__PURE__ */ jsx5("thead", { className: "lax-markdown__thead", children });
801
+ },
802
+ tbody({ children }) {
803
+ return /* @__PURE__ */ jsx5("tbody", { className: "lax-markdown__tbody", children });
804
+ },
805
+ tr({ children }) {
806
+ return /* @__PURE__ */ jsx5("tr", { className: "lax-markdown__tr", children });
807
+ },
808
+ th({ children }) {
809
+ return /* @__PURE__ */ jsx5("th", { className: "lax-markdown__th", children });
810
+ },
811
+ td({ children }) {
812
+ return /* @__PURE__ */ jsx5("td", { className: "lax-markdown__td", children });
813
+ },
814
+ hr() {
815
+ return /* @__PURE__ */ jsx5("hr", { className: "lax-markdown__hr" });
816
+ },
817
+ strong({ children }) {
818
+ return /* @__PURE__ */ jsx5("strong", { className: "lax-markdown__strong", children });
819
+ },
820
+ em({ children }) {
821
+ return /* @__PURE__ */ jsx5("em", { className: "lax-markdown__em", children });
822
+ }
823
+ };
824
+
825
+ // src/view/markdown/RichMarkdown.tsx
826
+ import { jsx as jsx6 } from "react/jsx-runtime";
827
+ function RichMarkdown({ content, className = "" }) {
828
+ const processed = useMemo2(() => collapseExcessiveBlankLines(content), [content]);
829
+ if (!processed) {
830
+ return null;
831
+ }
832
+ if (!hasMarkdownSyntax(processed)) {
833
+ return /* @__PURE__ */ jsx6("p", { className: `lax-markdown__paragraph ${className}`.trim(), children: processed });
834
+ }
835
+ return /* @__PURE__ */ jsx6("div", { className: className || void 0, children: /* @__PURE__ */ jsx6(ReactMarkdown, { remarkPlugins: [remarkGfm], components: RICH_MARKDOWN_COMPONENTS, children: processed }) });
836
+ }
837
+
838
+ // src/view/nodes/ReasoningNode.tsx
839
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
840
+ var REASONING_DOT = "\u25CF";
841
+ function ReasoningBubble({ nodeId }) {
842
+ const node = useNodeTyped(nodeId, "reasoning");
843
+ if (!node) return null;
844
+ const text = node.accumulated.trim();
845
+ if (!text) return null;
846
+ return /* @__PURE__ */ jsx7("div", { className: "lax-reasoning-bubble", "data-status": node.status, "data-round-id": node.roundId, children: /* @__PURE__ */ jsxs6("div", { className: "lax-reasoning-bubble__body", children: [
847
+ /* @__PURE__ */ jsx7("span", { className: "lax-reasoning-bubble__dot", "aria-hidden": "true", children: REASONING_DOT }),
848
+ /* @__PURE__ */ jsx7("span", { className: "lax-reasoning-bubble__content", children: node.accumulated })
849
+ ] }) });
850
+ }
851
+
852
+ // src/view/tools/presentation/StreamingMarkdown.tsx
853
+ import { useRef as useRef4 } from "react";
854
+ import { marked } from "marked";
855
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
856
+ var TOKEN_CACHE_MAX = 500;
857
+ var tokenCache = /* @__PURE__ */ new Map();
858
+ function cachedLexer(content) {
859
+ if (!hasMarkdownSyntax(content)) {
860
+ return [{
861
+ type: "paragraph",
862
+ raw: content,
863
+ text: content,
864
+ tokens: [{
865
+ type: "text",
866
+ raw: content,
867
+ text: content
868
+ }]
869
+ }];
870
+ }
871
+ const key = hashContent(content);
872
+ const hit = tokenCache.get(key);
873
+ if (hit) {
874
+ tokenCache.delete(key);
875
+ tokenCache.set(key, hit);
876
+ return hit;
877
+ }
878
+ const tokens = marked.lexer(content);
879
+ if (tokenCache.size >= TOKEN_CACHE_MAX) {
880
+ const first = tokenCache.keys().next().value;
881
+ if (first !== void 0) tokenCache.delete(first);
882
+ }
883
+ tokenCache.set(key, tokens);
884
+ return tokens;
885
+ }
886
+ function StreamingMarkdown({ children }) {
887
+ "use no memo";
888
+ const stablePrefixRef = useRef4("");
889
+ if (!children.startsWith(stablePrefixRef.current)) {
890
+ stablePrefixRef.current = "";
891
+ }
892
+ const boundary = stablePrefixRef.current.length;
893
+ const tokens = cachedLexer(children.substring(boundary));
894
+ let lastContentIdx = tokens.length - 1;
895
+ while (lastContentIdx >= 0 && tokens[lastContentIdx].type === "space") {
896
+ lastContentIdx--;
897
+ }
898
+ let advance = 0;
899
+ for (let i = 0; i < lastContentIdx; i++) {
900
+ advance += tokens[i].raw.length;
901
+ }
902
+ if (advance > 0) {
903
+ stablePrefixRef.current = children.substring(0, boundary + advance);
904
+ }
905
+ const stablePrefix = stablePrefixRef.current;
906
+ const unstableSuffix = children.substring(stablePrefix.length);
907
+ return /* @__PURE__ */ jsxs7("div", { className: "lax-markdown lax-markdown--rich", children: [
908
+ stablePrefix ? /* @__PURE__ */ jsx8(RichMarkdown, { content: stablePrefix }, `stable-${hashContent(stablePrefix)}`) : null,
909
+ unstableSuffix ? /* @__PURE__ */ jsx8(RichMarkdown, { content: unstableSuffix }, `unstable-${hashContent(unstableSuffix)}`) : null
910
+ ] });
911
+ }
912
+
913
+ // src/view/nodes/TextNode.tsx
914
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
915
+ function TextBubble({ nodeId }) {
916
+ const node = useNodeTyped(nodeId, "text");
917
+ if (!node) return null;
918
+ return /* @__PURE__ */ jsx9("div", { className: "lax-text-bubble", "data-status": node.status, children: /* @__PURE__ */ jsxs8("div", { className: "lax-text-bubble__body", children: [
919
+ /* @__PURE__ */ jsx9("span", { className: "lax-text-bubble__dot", "aria-hidden": "true", children: REASONING_DOT }),
920
+ /* @__PURE__ */ jsx9("div", { className: "lax-text-bubble__content lax-text-bubble__content--markdown", children: /* @__PURE__ */ jsx9(StreamingMarkdown, { children: node.accumulated }) })
921
+ ] }) });
922
+ }
923
+
924
+ // src/view/nodes/ToolCallNode.tsx
925
+ import { useEffect as useEffect6, useState as useState6 } from "react";
926
+ import { jsx as jsx10 } from "react/jsx-runtime";
927
+ function ToolCallCard({ nodeId, layout = "default" }) {
928
+ const node = useNodeTyped(nodeId, "tool_call");
929
+ const { registry, defaultBodyMode } = useToolDisplayOptions();
930
+ const viewOptions = useSessionViewOptions();
931
+ const pendingPermission = usePendingPermissionForTool(node?.toolName ?? "");
932
+ const effectiveDefault = resolveEffectiveToolBodyMode(viewOptions, defaultBodyMode);
933
+ const [bodyMode, setBodyMode] = useState6(effectiveDefault);
934
+ useEffect6(() => {
935
+ setBodyMode(resolveEffectiveToolBodyMode(viewOptions, defaultBodyMode));
936
+ }, [viewOptions.displayMode, viewOptions.verbose, defaultBodyMode]);
937
+ if (!node) return null;
938
+ if (node.hidden) return null;
939
+ const details = node.details;
940
+ const inputStream = details?.inputStream ?? node.inputDelta;
941
+ const result = node.result;
942
+ const displayBody = result?.display ?? null;
943
+ const hasSubagentProgress = (node.subagentProgress?.length ?? 0) > 0;
944
+ const showBody = hasSubagentProgress || !(node.status === "running" && result == null);
945
+ const BodyComponent = resolveToolBody(node.toolName, displayBody, registry);
946
+ const title = formatToolTitle(
947
+ node.toolName,
948
+ details?.input ?? node.input,
949
+ displayBody,
950
+ result?.summary,
951
+ result?.meta
952
+ );
953
+ const bodyProps = {
954
+ nodeId,
955
+ toolName: node.toolName,
956
+ status: node.status,
957
+ input: details?.input ?? node.input,
958
+ inputStream,
959
+ result,
960
+ progress: node.progress ?? void 0,
961
+ subagentProgress: node.subagentProgress,
962
+ subagentSessions: node.subagentSessions,
963
+ bodyMode,
964
+ onBodyModeChange: setBodyMode
965
+ };
966
+ const errorMessage = result?.error?.message ?? null;
967
+ const permissionWait = viewOptions.permissionUiMode !== "standalone" && node.status === "running" ? pendingPermission : void 0;
968
+ return /* @__PURE__ */ jsx10(
969
+ ToolCallShell,
970
+ {
971
+ status: node.status,
972
+ title,
973
+ progress: node.progress ?? void 0,
974
+ errorMessage,
975
+ layout,
976
+ permissionWait,
977
+ permissionUiMode: viewOptions.permissionUiMode,
978
+ children: showBody ? /* @__PURE__ */ jsx10("div", { className: "lax-tool-shell__body", children: /* @__PURE__ */ jsx10(BodyComponent, { ...bodyProps }) }) : null
979
+ }
980
+ );
981
+ }
982
+
983
+ // src/view/nodes/StepNode.tsx
984
+ import { jsx as jsx11 } from "react/jsx-runtime";
985
+ function StepCard({ nodeId }) {
986
+ const node = useNodeTyped(nodeId, "step");
987
+ if (!node) return null;
988
+ return /* @__PURE__ */ jsx11("details", { className: "lax-step-card", "data-status": node.status, children: /* @__PURE__ */ jsx11("summary", { className: "lax-step-card__summary", children: node.label }) });
989
+ }
990
+
991
+ // src/view/nodes/ErrorNode.tsx
992
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
993
+ function ErrorCard({ nodeId }) {
994
+ const node = useNodeTyped(nodeId, "error");
995
+ if (!node) return null;
996
+ return /* @__PURE__ */ jsxs9("div", { className: "lax-error-card", role: "alert", children: [
997
+ /* @__PURE__ */ jsx12("div", { className: "lax-error-card__message", children: node.message }),
998
+ node.errorType ? /* @__PURE__ */ jsx12("div", { className: "lax-error-card__type", children: node.errorType }) : null
999
+ ] });
1000
+ }
1001
+
1002
+ // src/view/nodes/UnknownNode.tsx
1003
+ import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
1004
+ function UnknownCard({ nodeId }) {
1005
+ const node = useNodeTyped(nodeId, "unknown");
1006
+ if (!node) return null;
1007
+ return /* @__PURE__ */ jsxs10("div", { className: "lax-unknown-card", children: [
1008
+ /* @__PURE__ */ jsxs10("div", { className: "lax-unknown-card__type", children: [
1009
+ "Unknown: ",
1010
+ node.eventType
1011
+ ] }),
1012
+ /* @__PURE__ */ jsx13("pre", { className: "lax-unknown-card__data", children: JSON.stringify(node.rawData, null, 2) })
1013
+ ] });
1014
+ }
1015
+
1016
+ // src/view/nodes/PermissionNode.tsx
1017
+ import { useState as useState7 } from "react";
1018
+ import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
1019
+ function PermissionCard({ nodeId }) {
1020
+ const node = useNodeTyped(nodeId, "permission");
1021
+ const bus = useInteractionBus();
1022
+ const [resolvedLocally, setResolvedLocally] = useState7(false);
1023
+ if (!node) return null;
1024
+ const pending = node.status === "pending" && !resolvedLocally;
1025
+ const onAllow = () => {
1026
+ void bus.resolvePermission({ requestId: node.requestId, allow: true });
1027
+ setResolvedLocally(true);
1028
+ };
1029
+ const onDeny = () => {
1030
+ void bus.resolvePermission({ requestId: node.requestId, allow: false });
1031
+ setResolvedLocally(true);
1032
+ };
1033
+ return /* @__PURE__ */ jsxs11(
1034
+ "div",
1035
+ {
1036
+ className: "lax-permission-card",
1037
+ "data-status": pending ? "pending" : "resolved",
1038
+ "data-request-id": node.requestId,
1039
+ children: [
1040
+ /* @__PURE__ */ jsxs11("div", { className: "lax-permission-card__header", children: [
1041
+ /* @__PURE__ */ jsx14("span", { className: "lax-permission-card__label", children: "Permission" }),
1042
+ /* @__PURE__ */ jsx14("span", { className: "lax-permission-card__tool", children: node.toolName })
1043
+ ] }),
1044
+ /* @__PURE__ */ jsx14("div", { className: "lax-permission-card__message", children: node.message }),
1045
+ node.askPrompt && node.askPrompt !== node.message ? /* @__PURE__ */ jsx14("div", { className: "lax-permission-card__prompt", children: node.askPrompt }) : null,
1046
+ pending ? /* @__PURE__ */ jsxs11("div", { className: "lax-permission-card__actions", children: [
1047
+ /* @__PURE__ */ jsx14("button", { type: "button", className: "lax-permission-card__allow", onClick: onAllow, children: "Allow" }),
1048
+ /* @__PURE__ */ jsx14("button", { type: "button", className: "lax-permission-card__deny", onClick: onDeny, children: "Deny" })
1049
+ ] }) : /* @__PURE__ */ jsx14("div", { className: "lax-permission-card__resolved", children: "Resolved" })
1050
+ ]
1051
+ }
1052
+ );
1053
+ }
1054
+
1055
+ // src/view/nodes/MemorySavedNode.tsx
1056
+ import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
1057
+ function MemorySavedRow({ nodeId }) {
1058
+ const node = useNodeTyped(nodeId, "memory_saved");
1059
+ if (!node) return null;
1060
+ const hasPaths = node.writtenPaths.length > 0;
1061
+ return /* @__PURE__ */ jsxs12(
1062
+ "details",
1063
+ {
1064
+ className: "lax-memory-saved",
1065
+ "data-testid": "lax-memory-saved",
1066
+ "data-node-id": nodeId,
1067
+ children: [
1068
+ /* @__PURE__ */ jsxs12("summary", { className: "lax-memory-saved__summary", children: [
1069
+ /* @__PURE__ */ jsx15("span", { className: "lax-memory-saved__icon", "aria-hidden": "true", children: "\u{1F4BE}" }),
1070
+ /* @__PURE__ */ jsx15("span", { className: "lax-memory-saved__hint", children: node.displayHint })
1071
+ ] }),
1072
+ hasPaths ? /* @__PURE__ */ jsx15("ul", { className: "lax-memory-saved__paths", children: node.writtenPaths.map((p) => /* @__PURE__ */ jsx15("li", { className: "lax-memory-saved__path", children: /* @__PURE__ */ jsx15("code", { children: p }) }, p)) }) : null
1073
+ ]
1074
+ }
1075
+ );
1076
+ }
1077
+
1078
+ // src/view/NodeRegistry.ts
1079
+ var DEFAULT_WIDGETS = /* @__PURE__ */ new Map([
1080
+ ["text", TextBubble],
1081
+ ["reasoning", ReasoningBubble],
1082
+ ["tool_call", ToolCallCard],
1083
+ ["step", StepCard],
1084
+ ["error", ErrorCard],
1085
+ ["unknown", UnknownCard],
1086
+ ["permission", PermissionCard],
1087
+ ["memory_saved", MemorySavedRow]
1088
+ ]);
1089
+ var NodeRegistry = class _NodeRegistry {
1090
+ widgets;
1091
+ constructor(widgets) {
1092
+ this.widgets = widgets;
1093
+ }
1094
+ static default() {
1095
+ return new _NodeRegistry(new Map(DEFAULT_WIDGETS));
1096
+ }
1097
+ override(kind, widget) {
1098
+ const next = new Map(this.widgets);
1099
+ next.set(kind, widget);
1100
+ return new _NodeRegistry(next);
1101
+ }
1102
+ register(kind, widget) {
1103
+ return this.override(kind, widget);
1104
+ }
1105
+ get(kind) {
1106
+ return this.widgets.get(kind);
1107
+ }
1108
+ has(kind) {
1109
+ return this.widgets.has(kind);
1110
+ }
1111
+ };
1112
+
1113
+ // src/view/defaultRegistry.ts
1114
+ function createDefaultRegistry() {
1115
+ return NodeRegistry.default();
1116
+ }
1117
+
1118
+ // src/core/sessionStore.ts
1119
+ import { createStore } from "zustand/vanilla";
1120
+
1121
+ // src/core/dedupe.ts
1122
+ function shouldSkipDuplicateEvent(seenEventIds, sseEventId) {
1123
+ if (!sseEventId) return false;
1124
+ return seenEventIds.has(sseEventId);
1125
+ }
1126
+ function markEventIdSeen(seenEventIds, sseEventId) {
1127
+ if (sseEventId) seenEventIds.add(sseEventId);
1128
+ }
1129
+
1130
+ // src/core/sessionStore.ts
1131
+ var DEFAULT_MAX_SESSIONS = 10;
1132
+ function touchOrder(order, sessionId) {
1133
+ return [...order.filter((id) => id !== sessionId), sessionId];
1134
+ }
1135
+ function safeReduce(tree, event, eventIndex, options) {
1136
+ try {
1137
+ return reduceTree(tree, event, eventIndex, options);
1138
+ } catch (err) {
1139
+ const message = err instanceof Error ? err.message : String(err);
1140
+ const stack = err instanceof Error ? err.stack : void 0;
1141
+ if (import.meta.env?.DEV) {
1142
+ console.error("[langchain_agentx_stream_ui] reducer error:", err);
1143
+ }
1144
+ const reducerError = {
1145
+ eventIndex,
1146
+ eventType: event.event_type,
1147
+ message,
1148
+ stack
1149
+ };
1150
+ return {
1151
+ ...tree,
1152
+ internalErrors: [...tree.internalErrors, reducerError]
1153
+ };
1154
+ }
1155
+ }
1156
+ function evictSessions(sessions, order, maxSessions, activeSessionId) {
1157
+ let nextSessions = { ...sessions };
1158
+ let nextOrder = [...order];
1159
+ while (nextOrder.length > maxSessions) {
1160
+ const candidate = nextOrder.find((id) => id !== activeSessionId) ?? nextOrder[0];
1161
+ if (!candidate) break;
1162
+ const { [candidate]: _removed, ...rest } = nextSessions;
1163
+ nextSessions = rest;
1164
+ nextOrder = nextOrder.filter((id) => id !== candidate);
1165
+ }
1166
+ return { sessions: nextSessions, order: nextOrder };
1167
+ }
1168
+ function createMultiSessionStore(options) {
1169
+ const maxSessions = options?.maxSessions ?? DEFAULT_MAX_SESSIONS;
1170
+ const reduceOpts = {
1171
+ tierOverrides: options?.tierOverrides,
1172
+ collectDebug: options?.debug === true
1173
+ };
1174
+ const replayOpts = {
1175
+ tierOverrides: options?.tierOverrides,
1176
+ collectDebug: options?.debug === true
1177
+ };
1178
+ const seenBySession = /* @__PURE__ */ new Map();
1179
+ function getSeenSet(sessionId) {
1180
+ let set = seenBySession.get(sessionId);
1181
+ if (!set) {
1182
+ set = /* @__PURE__ */ new Set();
1183
+ seenBySession.set(sessionId, set);
1184
+ }
1185
+ return set;
1186
+ }
1187
+ return createStore((set, get) => ({
1188
+ sessions: {},
1189
+ activeSessionId: options?.activeSessionId ?? null,
1190
+ order: [],
1191
+ getSessionTree(sessionId) {
1192
+ return get().sessions[sessionId]?.tree;
1193
+ },
1194
+ ensureSession(sessionId, initialEvents) {
1195
+ const state = get();
1196
+ if (state.sessions[sessionId]) return;
1197
+ const tree = initialEvents && initialEvents.length > 0 ? replayEvents(initialEvents, replayOpts) : createEmptyTree();
1198
+ const slice = {
1199
+ tree,
1200
+ eventCount: initialEvents?.length ?? 0
1201
+ };
1202
+ let order = touchOrder(state.order, sessionId);
1203
+ let sessions = { ...state.sessions, [sessionId]: slice };
1204
+ ({ sessions, order } = evictSessions(sessions, order, maxSessions, state.activeSessionId));
1205
+ set({
1206
+ sessions,
1207
+ order,
1208
+ activeSessionId: state.activeSessionId ?? sessionId
1209
+ });
1210
+ },
1211
+ applyEvent(sessionId, event, ctx) {
1212
+ const sseEventId = ctx?.sseEventId;
1213
+ const seen = getSeenSet(sessionId);
1214
+ if (shouldSkipDuplicateEvent(seen, sseEventId)) return;
1215
+ const state = get();
1216
+ let slice = state.sessions[sessionId];
1217
+ if (!slice) {
1218
+ get().ensureSession(sessionId);
1219
+ slice = get().sessions[sessionId];
1220
+ }
1221
+ const reduced = safeReduce(slice.tree, event, slice.eventCount, reduceOpts);
1222
+ const nextTree = sseEventId ? { ...reduced, meta: { ...reduced.meta, lastEventId: sseEventId } } : reduced;
1223
+ const nextSlice = {
1224
+ tree: nextTree,
1225
+ eventCount: slice.eventCount + 1
1226
+ };
1227
+ markEventIdSeen(seen, sseEventId);
1228
+ let sessions = { ...state.sessions, [sessionId]: nextSlice };
1229
+ let order = touchOrder(state.order, sessionId);
1230
+ ({ sessions, order } = evictSessions(sessions, order, maxSessions, state.activeSessionId));
1231
+ set({ sessions, order });
1232
+ },
1233
+ applyEvents(sessionId, events) {
1234
+ for (const event of events) {
1235
+ get().applyEvent(sessionId, event);
1236
+ }
1237
+ },
1238
+ setActiveSessionId(id) {
1239
+ set({ activeSessionId: id });
1240
+ },
1241
+ removeSession(sessionId) {
1242
+ const state = get();
1243
+ if (!state.sessions[sessionId]) return;
1244
+ seenBySession.delete(sessionId);
1245
+ const { [sessionId]: _removed, ...sessions } = state.sessions;
1246
+ const order = state.order.filter((id) => id !== sessionId);
1247
+ const activeSessionId = state.activeSessionId === sessionId ? order[order.length - 1] ?? null : state.activeSessionId;
1248
+ set({ sessions, order, activeSessionId });
1249
+ },
1250
+ replaySession(sessionId, events) {
1251
+ const tree = replayEvents(events, replayOpts);
1252
+ const slice = { tree, eventCount: events.length };
1253
+ const state = get();
1254
+ let order = touchOrder(state.order, sessionId);
1255
+ let sessions = { ...state.sessions, [sessionId]: slice };
1256
+ ({ sessions, order } = evictSessions(sessions, order, maxSessions, state.activeSessionId));
1257
+ set({ sessions, order });
1258
+ }
1259
+ }));
1260
+ }
1261
+ function createActiveSessionBridge(multiStore) {
1262
+ const bridge = createStore(() => ({
1263
+ tree: createEmptyTree(),
1264
+ eventCount: 0,
1265
+ applyEvent(event, ctx) {
1266
+ const id = multiStore.getState().activeSessionId;
1267
+ if (id) multiStore.getState().applyEvent(id, event, ctx);
1268
+ },
1269
+ applyEvents(events) {
1270
+ const id = multiStore.getState().activeSessionId;
1271
+ if (id) multiStore.getState().applyEvents(id, events);
1272
+ },
1273
+ reset() {
1274
+ const id = multiStore.getState().activeSessionId;
1275
+ if (id) multiStore.getState().replaySession(id, []);
1276
+ }
1277
+ }));
1278
+ const sync = () => {
1279
+ const { activeSessionId, sessions } = multiStore.getState();
1280
+ const slice = activeSessionId ? sessions[activeSessionId] : void 0;
1281
+ bridge.setState({
1282
+ tree: slice?.tree ?? createEmptyTree(),
1283
+ eventCount: slice?.eventCount ?? 0
1284
+ });
1285
+ };
1286
+ multiStore.subscribe(sync);
1287
+ sync();
1288
+ return bridge;
1289
+ }
1290
+
1291
+ // src/view/MultiAgentSession.tsx
1292
+ import { useEffect as useEffect7, useMemo as useMemo3, useRef as useRef5 } from "react";
1293
+ import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
1294
+ function DebugPanel() {
1295
+ const status = useSessionStatus();
1296
+ const errors = useInternalErrors();
1297
+ if (errors.length === 0) return null;
1298
+ return /* @__PURE__ */ jsxs13("div", { className: "lax-debug-panel", "data-testid": "lax-debug-panel", children: [
1299
+ /* @__PURE__ */ jsxs13("div", { children: [
1300
+ "status: ",
1301
+ status
1302
+ ] }),
1303
+ /* @__PURE__ */ jsx16("ul", { children: errors.map((err, i) => /* @__PURE__ */ jsxs13("li", { children: [
1304
+ "[",
1305
+ err.eventType,
1306
+ "] ",
1307
+ err.message
1308
+ ] }, `${err.eventIndex}-${i}`)) })
1309
+ ] });
1310
+ }
1311
+ function MultiAgentSession({
1312
+ sessions,
1313
+ activeSessionId: controlledActiveId,
1314
+ defaultActiveSessionId,
1315
+ onActiveSessionChange,
1316
+ maxSessions,
1317
+ tierOverrides,
1318
+ debug = false,
1319
+ registry,
1320
+ onError,
1321
+ virtualized = false,
1322
+ virtualizeThreshold,
1323
+ interactionBus,
1324
+ toolDisplayRegistry,
1325
+ defaultBodyMode = "preview",
1326
+ groupParallelTools = false,
1327
+ permissionUiMode = "standalone",
1328
+ markdownRenderer,
1329
+ children
1330
+ }) {
1331
+ const multiStoreRef = useRef5(null);
1332
+ const bridgeRef = useRef5(null);
1333
+ if (multiStoreRef.current === null) {
1334
+ const defaultId = defaultActiveSessionId ?? controlledActiveId ?? sessions[0]?.sessionId ?? null;
1335
+ multiStoreRef.current = createMultiSessionStore({
1336
+ maxSessions,
1337
+ tierOverrides,
1338
+ debug,
1339
+ activeSessionId: defaultId
1340
+ });
1341
+ bridgeRef.current = createActiveSessionBridge(multiStoreRef.current);
1342
+ for (const cfg of sessions) {
1343
+ multiStoreRef.current.getState().ensureSession(cfg.sessionId, cfg.initialEvents);
1344
+ }
1345
+ }
1346
+ const registryRef = useMemo3(() => registry ?? createDefaultRegistry(), [registry]);
1347
+ const busRef = useMemo3(
1348
+ () => interactionBus ?? getNoopInteractionBus(),
1349
+ [interactionBus]
1350
+ );
1351
+ const toolDisplayRef = useMemo3(
1352
+ () => ({
1353
+ registry: toolDisplayRegistry ?? createDefaultToolRegistry(),
1354
+ defaultBodyMode
1355
+ }),
1356
+ [toolDisplayRegistry, defaultBodyMode]
1357
+ );
1358
+ const sessionViewRef = useMemo3(
1359
+ () => ({
1360
+ ...DEFAULT_SESSION_VIEW_OPTIONS,
1361
+ groupParallelTools,
1362
+ permissionUiMode,
1363
+ verboseReasoning: debug
1364
+ }),
1365
+ [groupParallelTools, permissionUiMode, debug]
1366
+ );
1367
+ useEffect7(() => {
1368
+ if (controlledActiveId === void 0) return;
1369
+ multiStoreRef.current.getState().setActiveSessionId(controlledActiveId);
1370
+ }, [controlledActiveId]);
1371
+ useEffect7(() => {
1372
+ if (!onActiveSessionChange) return;
1373
+ return multiStoreRef.current.subscribe((state) => {
1374
+ onActiveSessionChange(state.activeSessionId);
1375
+ });
1376
+ }, [onActiveSessionChange]);
1377
+ useEffect7(() => {
1378
+ const multiStore = multiStoreRef.current;
1379
+ const controllers = sessions.map((cfg) => {
1380
+ const controller = new AbortController();
1381
+ multiStore.getState().ensureSession(cfg.sessionId, cfg.initialEvents);
1382
+ void cfg.source.start((event, ctx) => {
1383
+ multiStore.getState().applyEvent(cfg.sessionId, event, ctx);
1384
+ }, controller.signal).catch((err) => {
1385
+ const error = err instanceof Error ? err : new Error(String(err));
1386
+ onError?.(error, cfg.sessionId);
1387
+ });
1388
+ return controller;
1389
+ });
1390
+ return () => {
1391
+ for (const c of controllers) c.abort();
1392
+ };
1393
+ }, [sessions, onError]);
1394
+ return /* @__PURE__ */ jsx16(MultiSessionStoreContext.Provider, { value: multiStoreRef.current, children: /* @__PURE__ */ jsx16(SessionStoreContext.Provider, { value: bridgeRef.current, children: /* @__PURE__ */ jsx16(InteractionBusContext.Provider, { value: busRef, children: /* @__PURE__ */ jsx16(NodeRegistryContext.Provider, { value: registryRef, children: /* @__PURE__ */ jsx16(MarkdownRendererContext.Provider, { value: markdownRenderer, children: /* @__PURE__ */ jsx16(ToolDisplayOptionsContext.Provider, { value: toolDisplayRef, children: /* @__PURE__ */ jsx16(SessionViewOptionsContext.Provider, { value: sessionViewRef, children: /* @__PURE__ */ jsxs13("div", { className: "lax-agent-session lax-multi-agent-session", "data-testid": "lax-multi-agent-session", children: [
1395
+ children ?? /* @__PURE__ */ jsx16(
1396
+ SessionTimeline,
1397
+ {
1398
+ virtualized,
1399
+ virtualizeThreshold,
1400
+ groupParallelTools
1401
+ }
1402
+ ),
1403
+ debug ? /* @__PURE__ */ jsx16(DebugPanel, {}) : null
1404
+ ] }) }) }) }) }) }) }) });
1405
+ }
1406
+
1407
+ export {
1408
+ shouldSkipDuplicateEvent,
1409
+ markEventIdSeen,
1410
+ configureMermaid,
1411
+ MermaidDiagram,
1412
+ PlantUMLBlock,
1413
+ configurePlantumlServer,
1414
+ PlantUMLDiagram,
1415
+ RichMarkdown,
1416
+ ReasoningBubble,
1417
+ TextBubble,
1418
+ ToolCallCard,
1419
+ StepCard,
1420
+ ErrorCard,
1421
+ UnknownCard,
1422
+ PermissionCard,
1423
+ NodeRegistry,
1424
+ createDefaultRegistry,
1425
+ createMultiSessionStore,
1426
+ createActiveSessionBridge,
1427
+ MultiAgentSession
1428
+ };
1429
+ //# sourceMappingURL=chunk-A4W6B7C3.js.map