@opengeni/react 7.0.0 → 7.1.0-canary.35855175602001

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.
Files changed (81) hide show
  1. package/README.md +34 -0
  2. package/dist/approvals.d.ts +2 -1
  3. package/dist/artifacts.js +17 -17
  4. package/dist/{browser-viewer-LGH4IP47.js → browser-viewer-MN7MOYQT.js} +5 -5
  5. package/dist/{chunk-YS24CFDI.js → chunk-2O2HXRGP.js} +7 -7
  6. package/dist/{chunk-IP4BUBKH.js → chunk-3LL47F4U.js} +28 -17
  7. package/dist/chunk-3LL47F4U.js.map +1 -0
  8. package/dist/{chunk-UXE5SSNN.js → chunk-4ACTWOOW.js} +8 -8
  9. package/dist/{chunk-IYUPN32E.js → chunk-DBPVUGQT.js} +6 -1
  10. package/dist/{chunk-IYUPN32E.js.map → chunk-DBPVUGQT.js.map} +1 -1
  11. package/dist/{chunk-5GAL23FF.js → chunk-IOLPHOF4.js} +2 -5
  12. package/dist/chunk-IOLPHOF4.js.map +1 -0
  13. package/dist/{chunk-JPHDWKZE.js → chunk-PFDNB3U7.js} +15 -10
  14. package/dist/chunk-PFDNB3U7.js.map +1 -0
  15. package/dist/{chunk-D7GAPJ3V.js → chunk-SHRCATV6.js} +67 -34
  16. package/dist/chunk-SHRCATV6.js.map +1 -0
  17. package/dist/{chunk-H5YGYNXS.js → chunk-TV5PP2PE.js} +11 -11
  18. package/dist/chunk-TV5PP2PE.js.map +1 -0
  19. package/dist/{chunk-PNAVEK2U.js → chunk-UQS7BGNQ.js} +12 -12
  20. package/dist/{chunk-JKLGJQO5.js → chunk-VI7HELBI.js} +40 -20
  21. package/dist/chunk-VI7HELBI.js.map +1 -0
  22. package/dist/{chunk-HJ5VJ4JB.js → chunk-WKFFRFJF.js} +73 -15
  23. package/dist/chunk-WKFFRFJF.js.map +1 -0
  24. package/dist/{chunk-D5NOQPLR.js → chunk-ZC2DK5SC.js} +75 -65
  25. package/dist/chunk-ZC2DK5SC.js.map +1 -0
  26. package/dist/components/markdown.d.ts +2 -0
  27. package/dist/components/model-policy-picker.d.ts +17 -1
  28. package/dist/components/session-conversation.d.ts +3 -0
  29. package/dist/composer.d.ts +1 -1
  30. package/dist/composer.js +7 -7
  31. package/dist/{computer-viewer-CTFCJVS7.js → computer-viewer-FX4WOZMX.js} +4 -4
  32. package/dist/connect.js +5 -5
  33. package/dist/hooks/use-session-events.d.ts +6 -1
  34. package/dist/index.d.ts +1 -1
  35. package/dist/index.js +112 -115
  36. package/dist/index.js.map +1 -1
  37. package/dist/interaction.js +5 -5
  38. package/dist/machines.js +2 -2
  39. package/dist/{model-policy-picker-menu-RVOQ4ZZG.js → model-policy-picker-menu-AGXXJZ2B.js} +4 -4
  40. package/dist/model-policy.js +1 -1
  41. package/dist/realtime.js +5 -5
  42. package/dist/session-ui.js +12 -12
  43. package/dist/session.js +3 -3
  44. package/dist/timeline/tool-display-name.d.ts +2 -1
  45. package/dist/timeline/types.d.ts +2 -1
  46. package/package.json +3 -3
  47. package/src/approvals.ts +8 -1
  48. package/src/components/approval-surface.tsx +12 -2
  49. package/src/components/machine-card.tsx +5 -5
  50. package/src/components/machine-dock-bar.tsx +1 -1
  51. package/src/components/machines/machine-detail.tsx +5 -2
  52. package/src/components/markdown.tsx +101 -55
  53. package/src/components/model-policy-picker-menu.tsx +131 -33
  54. package/src/components/model-policy-picker.tsx +61 -12
  55. package/src/components/session-commands-panel.tsx +8 -3
  56. package/src/components/session-conversation.tsx +5 -1
  57. package/src/composer.ts +1 -0
  58. package/src/hooks/use-last-started-turn-policy.ts +3 -7
  59. package/src/hooks/use-session-events.ts +32 -7
  60. package/src/index.ts +1 -0
  61. package/src/model-policy.ts +1 -4
  62. package/src/timeline/projection.ts +4 -0
  63. package/src/timeline/rolling-activity.tsx +4 -2
  64. package/src/timeline/tool-display-name.ts +8 -6
  65. package/src/timeline/tool-renderers.tsx +5 -3
  66. package/src/timeline/types.ts +2 -0
  67. package/styles/compiled.css +15 -0
  68. package/dist/chunk-5GAL23FF.js.map +0 -1
  69. package/dist/chunk-D5NOQPLR.js.map +0 -1
  70. package/dist/chunk-D7GAPJ3V.js.map +0 -1
  71. package/dist/chunk-H5YGYNXS.js.map +0 -1
  72. package/dist/chunk-HJ5VJ4JB.js.map +0 -1
  73. package/dist/chunk-IP4BUBKH.js.map +0 -1
  74. package/dist/chunk-JKLGJQO5.js.map +0 -1
  75. package/dist/chunk-JPHDWKZE.js.map +0 -1
  76. /package/dist/{browser-viewer-LGH4IP47.js.map → browser-viewer-MN7MOYQT.js.map} +0 -0
  77. /package/dist/{chunk-YS24CFDI.js.map → chunk-2O2HXRGP.js.map} +0 -0
  78. /package/dist/{chunk-UXE5SSNN.js.map → chunk-4ACTWOOW.js.map} +0 -0
  79. /package/dist/{chunk-PNAVEK2U.js.map → chunk-UQS7BGNQ.js.map} +0 -0
  80. /package/dist/{computer-viewer-CTFCJVS7.js.map → computer-viewer-FX4WOZMX.js.map} +0 -0
  81. /package/dist/{model-policy-picker-menu-RVOQ4ZZG.js.map → model-policy-picker-menu-AGXXJZ2B.js.map} +0 -0
@@ -3,10 +3,6 @@ import {
3
3
  rangeFromOffsets,
4
4
  visibleClientRect
5
5
  } from "./chunk-2VHARTBL.js";
6
- import {
7
- copyTextToClipboard,
8
- tableElementToTsv
9
- } from "./chunk-JVYQRRC4.js";
10
6
  import {
11
7
  Tooltip,
12
8
  TooltipContent,
@@ -20,6 +16,10 @@ import {
20
16
  import {
21
17
  cn
22
18
  } from "./chunk-22KP6LR5.js";
19
+ import {
20
+ copyTextToClipboard,
21
+ tableElementToTsv
22
+ } from "./chunk-JVYQRRC4.js";
23
23
 
24
24
  // src/timeline/disclosure-context.tsx
25
25
  import { createContext, useContext } from "react";
@@ -1135,39 +1135,68 @@ var baseComponents = {
1135
1135
  img: MarkdownImage
1136
1136
  };
1137
1137
  var MARKDOWN_LINK_CLASS = "break-words font-medium text-og-accent-strong underline-offset-2 hover:underline";
1138
- function markdownComponents(onSandboxFile) {
1139
- return {
1140
- ...baseComponents,
1141
- a: ({ children, href, ...props }) => {
1142
- const location = sandboxFileLocationFromHref(href);
1143
- if (location !== null) {
1144
- return /* @__PURE__ */ jsx5(SandboxMarkdownLink, { location, onSandboxFile, children });
1145
- }
1146
- if (isSandboxHref(href) || !href) {
1147
- return /* @__PURE__ */ jsx5(
1148
- "span",
1149
- {
1150
- className: "break-words font-medium text-og-fg-subtle underline decoration-dotted underline-offset-2",
1151
- "aria-disabled": "true",
1152
- title: isSandboxHref(href) ? "This sandbox file reference is invalid" : "This link is unavailable",
1153
- children
1154
- }
1155
- );
1156
- }
1157
- return /* @__PURE__ */ jsx5(
1138
+ var MarkdownLinkContext = createContext4(
1139
+ {}
1140
+ );
1141
+ var markdownComponents = {
1142
+ ...baseComponents,
1143
+ p: ({ children, node, ...props }) => {
1144
+ const containsArtifact = node?.children.some(
1145
+ (child) => child.type === "element" && child.tagName === "img" && typeof child.properties.src === "string" && retainedImageId(child.properties.src)
1146
+ );
1147
+ const Tag = containsArtifact ? "div" : "p";
1148
+ return /* @__PURE__ */ jsx5(Tag, { className: "my-2.5 leading-7 first:mt-0 last:mb-0", ...props, children });
1149
+ },
1150
+ a: ({ children, href, ...props }) => {
1151
+ const { onSandboxFile, artifactHref } = useContext4(MarkdownLinkContext);
1152
+ const artifactId = href ? retainedImageId(href) : null;
1153
+ if (artifactId) {
1154
+ const destination = artifactHref?.(artifactId);
1155
+ return destination ? /* @__PURE__ */ jsx5(
1158
1156
  "a",
1159
1157
  {
1160
1158
  className: MARKDOWN_LINK_CLASS,
1159
+ href: defaultUrlTransform(destination),
1161
1160
  target: "_blank",
1162
1161
  rel: "noreferrer noopener",
1163
- href,
1164
- ...props,
1165
1162
  children
1166
1163
  }
1164
+ ) : /* @__PURE__ */ jsxs4("span", { title: "This artifact requires a workspace-aware host", children: [
1165
+ children,
1166
+ " (artifact unavailable)"
1167
+ ] });
1168
+ }
1169
+ const location = sandboxFileLocationFromHref(href);
1170
+ if (location !== null) {
1171
+ return /* @__PURE__ */ jsx5(SandboxMarkdownLink, { location, onSandboxFile, children });
1172
+ }
1173
+ if (isSandboxHref(href) || !href) {
1174
+ return /* @__PURE__ */ jsxs4(
1175
+ "span",
1176
+ {
1177
+ className: "break-words text-og-fg-subtle",
1178
+ "aria-disabled": "true",
1179
+ title: isSandboxHref(href) ? "This sandbox file reference is invalid" : "This link is unavailable",
1180
+ children: [
1181
+ children,
1182
+ " (link unavailable)"
1183
+ ]
1184
+ }
1167
1185
  );
1168
1186
  }
1169
- };
1170
- }
1187
+ return /* @__PURE__ */ jsx5(
1188
+ "a",
1189
+ {
1190
+ className: MARKDOWN_LINK_CLASS,
1191
+ target: "_blank",
1192
+ rel: "noreferrer noopener",
1193
+ href,
1194
+ ...props,
1195
+ children
1196
+ }
1197
+ );
1198
+ }
1199
+ };
1171
1200
  function SandboxMarkdownLink({
1172
1201
  location,
1173
1202
  onSandboxFile,
@@ -1255,7 +1284,7 @@ function MarkdownImage({ src, alt }) {
1255
1284
  ] });
1256
1285
  return /* @__PURE__ */ jsx5("img", { src, alt: alt ?? "", loading: "lazy", className: "my-3 max-w-full rounded-og-md" });
1257
1286
  }
1258
- var markdownUrlTransform = (url, key, node) => key === "href" && node.tagName === "a" && isSandboxHref(url) || key === "src" && node.tagName === "img" && retainedImageId(url) ? url : defaultUrlTransform(url);
1287
+ var markdownUrlTransform = (url, key, node) => key === "href" && node.tagName === "a" && (isSandboxHref(url) || retainedImageId(url)) || key === "src" && node.tagName === "img" && retainedImageId(url) ? url : defaultUrlTransform(url);
1259
1288
  var REVEAL_LINGER_MS = 480 * MOTION_INSPECT_SCALE;
1260
1289
  var MARKDOWN_SETTLE_MS = 480 * MOTION_INSPECT_SCALE;
1261
1290
  function nodeText(node) {
@@ -1378,6 +1407,7 @@ function MarkdownTable({ children, className, ...props }) {
1378
1407
  }
1379
1408
  function MarkdownImpl({
1380
1409
  children,
1410
+ artifactHref,
1381
1411
  className,
1382
1412
  streaming = false,
1383
1413
  onSandboxFile,
@@ -1446,7 +1476,10 @@ function MarkdownImpl({
1446
1476
  return () => clearTimeout(timer);
1447
1477
  }, [streaming]);
1448
1478
  const parseText = streaming || revealActive ? softenStreamingMarkdown(children) : children;
1449
- const components = useMemo(() => markdownComponents(onSandboxFile), [onSandboxFile]);
1479
+ const linkContext = useMemo(
1480
+ () => ({ onSandboxFile, artifactHref }),
1481
+ [onSandboxFile, artifactHref]
1482
+ );
1450
1483
  const interactiveContext = useMemo(
1451
1484
  () => ({
1452
1485
  source: children,
@@ -1456,7 +1489,7 @@ function MarkdownImpl({
1456
1489
  }),
1457
1490
  [children, streaming, renderInteractiveBlock, renderImage]
1458
1491
  );
1459
- return /* @__PURE__ */ jsx5(InteractiveContext.Provider, { value: interactiveContext, children: /* @__PURE__ */ jsx5(TooltipProvider, { delayDuration: 400, children: /* @__PURE__ */ jsx5(
1492
+ return /* @__PURE__ */ jsx5(MarkdownLinkContext.Provider, { value: linkContext, children: /* @__PURE__ */ jsx5(InteractiveContext.Provider, { value: interactiveContext, children: /* @__PURE__ */ jsx5(TooltipProvider, { delayDuration: 400, children: /* @__PURE__ */ jsx5(
1460
1493
  "div",
1461
1494
  {
1462
1495
  ref: bodyRef,
@@ -1470,13 +1503,13 @@ function MarkdownImpl({
1470
1503
  {
1471
1504
  remarkPlugins: [remarkGfm],
1472
1505
  rehypePlugins,
1473
- components,
1506
+ components: markdownComponents,
1474
1507
  urlTransform: markdownUrlTransform,
1475
1508
  children: parseText
1476
1509
  }
1477
1510
  )
1478
1511
  }
1479
- ) }) });
1512
+ ) }) }) });
1480
1513
  }
1481
1514
  var Markdown = memo(function SearchableMarkdown(props) {
1482
1515
  const [retained, setRetained] = useState3(
@@ -1555,4 +1588,4 @@ export {
1555
1588
  retainedImageId,
1556
1589
  Markdown
1557
1590
  };
1558
- //# sourceMappingURL=chunk-D7GAPJ3V.js.map
1591
+ //# sourceMappingURL=chunk-SHRCATV6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/timeline/disclosure-context.tsx","../src/timeline/shared.tsx","../src/components/timeline-search.tsx","../src/components/copy-button.tsx","../src/components/markdown.tsx","../src/lib/motion.ts","../src/lib/motion-inspect.ts","../src/components/preview-loading.tsx","../src/components/soften-streaming-markdown.ts","../src/components/stream-reveal.ts"],"sourcesContent":["import { createContext, useContext, type ReactNode } from \"react\";\n\n/* ----------------------------------------------------------------------------\n Disclosure defaults context\n\n A tiny, opt-in context that lets an ancestor seed the INITIAL open state of\n every collapsible in the timeline (ActivityDisclosure rows and TurnSummary\n chips). Its sole intended use is deterministic screenshot capture: a tool can\n force every card open so a headless render shows expanded bodies.\n\n It is fully inert in normal app usage. With no provider, the hook returns\n `undefined`, every collapsible keeps its own author-chosen default, and there\n is zero change to how the components look or animate. Mounting the provider\n only changes the SEED of the initial `open` state — Radix still owns the\n open/close transition, so animations are untouched.\n -------------------------------------------------------------------------- */\n\nconst DisclosureDefaultsContext = createContext<boolean | undefined>(undefined);\n\n/**\n * Seed the initial open state of every timeline collapsible below this node.\n * Intended for screenshot/test instrumentation only; absent by default.\n */\nexport function DisclosureDefaultsProvider({\n defaultOpen,\n children,\n}: {\n defaultOpen: boolean;\n children: ReactNode;\n}) {\n return (\n <DisclosureDefaultsContext.Provider value={defaultOpen}>\n {children}\n </DisclosureDefaultsContext.Provider>\n );\n}\n\n/**\n * The forced initial-open seed from an ancestor {@link DisclosureDefaultsProvider},\n * or `undefined` when none is mounted (the inert, app-default case).\n */\nexport function useForcedDefaultOpen(): boolean | undefined {\n return useContext(DisclosureDefaultsContext);\n}\n","import { CameraIcon, CameraOffIcon, ChevronRightIcon } from \"lucide-react\";\nimport { createContext, useContext, useEffect, useState, type ReactNode } from \"react\";\nimport { useTimelineSearchReveal } from \"../components/timeline-search\";\nimport { cn } from \"../lib/cn\";\nimport { stringifyPayload } from \"../lib/format\";\nimport { useForcedDefaultOpen } from \"./disclosure-context\";\nimport { useLightboxOptional } from \"./screenshot-lightbox\";\nimport type { ToolCallTruncation } from \"./types\";\n\n/* ----------------------------------------------------------------------------\n Shared timeline primitives\n\n The restraint layer. One disclosure shape every tool renderer reuses, so the\n rail reads as a calm, aligned column: a chevron, a tinted icon, a title, an\n optional muted preview, and — at most — ONE quiet right-aligned signal\n (a settle chip). Compact by default; the body only mounts when expanded.\n\n CHIP DOCTRINE (closed set — do not extend):\n The right gutter carries at most ONE terse status token per row, and COLOR is\n spent only on the exception. Success is the default, so it never earns a hue:\n a settled-ok chip is bare muted text (or nothing). The colored dot is reserved\n for failure alone. In-flight state is NOT a gutter chip — the shimmering title\n carries it, so a running row has a clean right edge (no detached pulse badge).\n There is no bordered/filled pill. Anything narrative (a session id, \"approval\n rejected\", \"malformed V4A\") belongs in the muted preview line, never the gutter.\n\n ok a settled success quiet muted text (\"0\", \"done\") — no dot\n bad a settled failure red dot + red text (\"exit 6\") — the one hue\n muted quiet metadata subtle text (\"session 3\")\n -------------------------------------------------------------------------- */\n\n/**\n * A subtle settle signal — see the CHIP DOCTRINE above. The closed tone set.\n * `\"interrupted\"` is a calm neutral tone for cancelled items — no dot, same\n * quiet weight as `\"muted\"`, but semantically distinct from metadata.\n */\nexport type DisclosureChip = {\n tone: \"ok\" | \"bad\" | \"muted\" | \"interrupted\";\n text: string;\n};\n\nconst ToolCallTruncationContext = createContext<ToolCallTruncation | null>(null);\n\nexport function ToolCallTruncationProvider({\n value,\n children,\n}: {\n value: ToolCallTruncation | null;\n children: ReactNode;\n}) {\n return (\n <ToolCallTruncationContext.Provider value={value}>\n {children}\n </ToolCallTruncationContext.Provider>\n );\n}\n\nexport type ActivityDisclosureProps = {\n /** Override the rolling preview; null keeps rapidly streaming details out of the header. */\n compactPreview?: ReactNode;\n icon: ReactNode;\n /** Icon tint. Defaults to the muted foreground; renderers pass accent/failed. */\n iconTone?: \"accent\" | \"failed\" | \"running\" | \"muted\" | undefined;\n title: ReactNode;\n /** Render the title in the mono face (commands, paths). */\n titleMono?: boolean | undefined;\n /** Shimmer the title while the tool is in-flight. */\n running?: boolean | undefined;\n /**\n * Quiet single-line secondary text (truncated). It is detail-on-demand: hidden\n * when a media preview is set, AND hidden once the row is expanded (the body\n * then owns the detail), so a stat/path never appears twice at once.\n */\n preview?: ReactNode | undefined;\n /** A small inline media preview (a screenshot thumbnail) shown in place of `preview`. */\n media?: ReactNode | undefined;\n /** At most one quiet settle chip, right-aligned to the gutter. */\n chip?: DisclosureChip | undefined;\n /**\n * When true the row carries the standard failure affordance: the icon is tinted\n * red and a \"failed\" bad-chip appears in the right gutter (unless an explicit\n * `chip` is already supplied — the caller's chip wins). Output is still visible\n * on expand; this is a quiet status signal, not a blocking banner.\n *\n * Renderers should pass `failed={item.status === \"failed\"}` on their settled\n * (non-running) paths so any tool with a failed status shows a consistent\n * affordance without each renderer having to duplicate the logic.\n */\n failed?: boolean | undefined;\n /**\n * When true the row carries a calm \"interrupted\" affordance: the icon stays\n * muted (no red) and a quiet \"interrupted\" chip appears in the right gutter\n * (unless an explicit `chip` is already supplied — the caller's chip wins).\n * This is the cancelled-status analogue of `failed`, but deliberately calm\n * and neutral — it is NOT an error; the user chose to stop.\n *\n * Renderers should pass `cancelled={item.status === \"cancelled\"}` so any\n * in-flight item that was interrupted on turn.cancelled reads consistently.\n * `cancelled` is ignored when `failed` is also true (failure takes precedence).\n */\n cancelled?: boolean | undefined;\n /** When false the row is a static line (no expand affordance). */\n expandable?: boolean | undefined;\n /** Seed this individual disclosure open; user interaction still owns it afterwards. */\n defaultOpen?: boolean | undefined;\n children?: ReactNode | undefined;\n};\n\nconst ICON_TONE: Record<NonNullable<ActivityDisclosureProps[\"iconTone\"]>, string> = {\n accent: \"text-og-accent\",\n failed: \"text-og-status-failed\",\n running: \"text-og-status-running\",\n muted: \"text-og-fg-subtle\",\n};\n\n/**\n * The one disclosure row shape every activity row reuses (tool calls, reasoning,\n * sandbox ops): a chevron, a tinted icon, a title, an optional muted preview or\n * inline media, and at most one right-gutter settle chip. Compact by default;\n * the body mounts only when expanded.\n */\nexport const CompactActivityContext = createContext(false);\n\nexport function ActivityDisclosure({\n icon,\n iconTone: iconToneProp = \"muted\",\n title,\n titleMono,\n running,\n preview,\n compactPreview,\n media,\n chip: chipProp,\n failed,\n cancelled,\n expandable = true,\n defaultOpen,\n children,\n}: ActivityDisclosureProps) {\n // `failed` takes precedence over `cancelled` when both are set (shouldn't happen, but be safe).\n // When `failed` is set the icon goes red and a \"failed\" chip appears in the\n // gutter — unless the caller already supplied an explicit chip (their chip wins,\n // e.g. an exit-code chip that is more informative than a bare \"failed\" label).\n // When `cancelled` is set (and not failed) the icon stays muted and a calm\n // \"interrupted\" chip appears — no red, just a quiet neutral signal.\n const iconTone = failed && iconToneProp === \"muted\" ? \"failed\" : iconToneProp;\n const chip =\n chipProp ??\n (failed\n ? ({ tone: \"bad\", text: \"failed\" } satisfies DisclosureChip)\n : cancelled\n ? ({ tone: \"interrupted\", text: \"interrupted\" } satisfies DisclosureChip)\n : undefined);\n // An ancestor may seed the initial open state (screenshot instrumentation);\n // absent in normal app usage, where the row starts collapsed.\n const forcedDefaultOpen = useForcedDefaultOpen();\n const [open, setOpen] = useState(defaultOpen ?? forcedDefaultOpen ?? false);\n const searchReveal = useTimelineSearchReveal();\n useEffect(() => {\n if (searchReveal) setOpen(true);\n }, [searchReveal]);\n const truncation = useContext(ToolCallTruncationContext);\n const compact = useContext(CompactActivityContext);\n if (compact)\n return (\n <span className=\"og-rolling-label\">\n <span className={cn(\"shrink-0\", ICON_TONE[iconTone])}>{icon}</span>\n <span className=\"og-command-reel\">\n <span className=\"og-reel-title\">{title}</span>\n {(compactPreview === undefined ? preview : compactPreview) ? (\n <span className=\"og-reel-preview text-og-fg-subtle\">\n {compactPreview === undefined ? preview : compactPreview}\n </span>\n ) : null}\n </span>\n </span>\n );\n const hasBody = expandable && (children != null || truncation != null);\n\n // The preview is detail-on-demand: it is suppressed once the row is open so a\n // path/stat shown in the body never also sits in the collapsed row.\n const previewVisible = preview != null && !open;\n\n // ONE full-row layout for EVERY tool — media or not — so the hit target always\n // equals the visible row. The chevron → icon → title lead; a flex spacer (or\n // the preview) fills the middle; the right gutter (ml-auto) carries the chip OR\n // the media thumbnail. There is no media-vs-non-media fork: the screenshot card\n // toggles from anywhere on the row, exactly like every other row.\n //\n // The row is the single Collapsible.Trigger (via `asChild` onto a div), so it\n // is the toggle surface. A div — not a native <button> — so the interactive\n // media thumbnail (itself a <button>) is valid nested DOM; that thumbnail calls\n // stopPropagation so activating it never toggles the row.\n //\n // Radix forwards aria-expanded / aria-controls / data-state and a click handler\n // onto the asChild child, but it does NOT synthesize the button role, tab stop,\n // or Enter/Space activation for a non-button element. We add those ourselves\n // (role/tabIndex/onKeyDown) so the row is a fully keyboard-operable button to\n // AT and the keyboard, matching its native-<button> siblings (TurnSummary).\n const rowClass = cn(\n \"group/disclosure flex w-full min-w-0 items-center gap-2 rounded-og-sm px-1.5 py-1.5 text-left text-og-base\",\n \"text-og-fg-muted transition-colors duration-150\",\n // A tool row is a touch target on coarse pointers: grow its hit area to the\n // 44px mobile minimum without loosening the dense desktop rail.\n \"pointer-coarse:min-h-11 pointer-coarse:py-2.5\",\n );\n // The chevron rotates to point down when open; it tracks `data-state` on this\n // same row (the Trigger), so the affordance never freezes.\n const inner = (\n <>\n {hasBody ? (\n <ChevronRightIcon className=\"size-3.5 shrink-0 text-og-fg-subtle transition-transform duration-[var(--_og-duration-disclose)] ease-og-in-out group-data-[state=open]/disclosure:rotate-90\" />\n ) : (\n <span className=\"size-3.5 shrink-0\" />\n )}\n <span className={cn(\"shrink-0\", ICON_TONE[iconTone])}>{icon}</span>\n <span className={cn(\"og-command-reel\", running && \"og-command-reel-running\")}>\n <span\n className={cn(\n \"min-w-0 shrink truncate text-og-base font-medium\",\n titleMono && \"font-og-mono text-og-sm font-normal\",\n )}\n >\n {title}\n </span>\n {previewVisible && !media ? (\n <span className=\"min-w-0 flex-1 truncate text-og-sm text-og-fg-subtle\">{preview}</span>\n ) : (\n <span className=\"flex-1\" />\n )}\n </span>\n {/* The right gutter carries at most ONE signal: the media thumbnail, else a\n terse settle chip (hidden once expanded — the body owns the detail). */}\n {media && !open ? (\n <span className=\"ml-auto flex shrink-0 items-center gap-2 pl-2\">{media}</span>\n ) : chip && !open ? (\n <span className=\"ml-auto shrink-0 pl-2\">\n <Chip chip={chip} />\n </span>\n ) : null}\n </>\n );\n\n // A `data-status` attribute on the root lets tests (and AT) detect the item's\n // settled state regardless of whether the chip slot is occupied by media.\n const dataStatus = cancelled ? \"cancelled\" : failed ? \"failed\" : undefined;\n\n if (!hasBody) {\n return (\n <div className={cn(rowClass, \"cursor-default\")} data-status={dataStatus}>\n {inner}\n </div>\n );\n }\n\n // Native disclosure (not radix-ui Collapsible): importing `radix-ui` from the\n // timeline registry pulls Popper into the session share-graph and crashes\n // lazy workspace settings (`createPopperScope is not a function`).\n return (\n <div>\n <div\n role=\"button\"\n tabIndex={0}\n aria-expanded={open}\n data-state={open ? \"open\" : \"closed\"}\n onClick={() => setOpen((prev) => !prev)}\n onKeyDown={(event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n setOpen((prev) => !prev);\n }\n }}\n data-status={dataStatus}\n className={cn(\n rowClass,\n \"cursor-pointer outline-hidden hover:bg-og-surface-1 hover:text-og-fg\",\n \"focus-visible:ring-2 focus-visible:ring-og-accent focus-visible:ring-offset-0\",\n )}\n >\n {inner}\n </div>\n {open ? (\n <div className=\"mb-2 ml-7 mt-1.5 flex flex-col gap-2 overflow-hidden animate-og-expand\">\n {children}\n {truncation ? <ToolCallTruncationNotice truncation={truncation} /> : null}\n </div>\n ) : null}\n </div>\n );\n}\n\nfunction ToolCallTruncationNotice({ truncation }: { truncation: ToolCallTruncation }) {\n const fullEvidence = truncation.fullEvidence.available\n ? \"retained\"\n : (truncation.fullEvidence.reason ?? \"unavailable\");\n return (\n <div data-og-tool-output-truncation=\"\" className=\"text-og-sm leading-5 text-og-fg-subtle\">\n Output bounded at <code className=\"font-og-mono\">{truncation.surface}</code>\n {truncation.omittedBytes != null && truncation.omittedBytes > 0\n ? ` · ${truncation.omittedBytes.toLocaleString()} bytes omitted`\n : null}\n {\" · reason \"}\n <code className=\"font-og-mono\">{truncation.reason}</code>\n {\" · full evidence \"}\n <code className=\"font-og-mono\">{fullEvidence}</code>\n </div>\n );\n}\n\n/**\n * The right-gutter settle chip. Color is spent only on the exception: a failure\n * is a red dot + red text (the one colored token in a healthy run); success and\n * metadata are bare muted text with no dot. No box, no fill (see the CHIP\n * DOCTRINE above). A `bad` chip with empty text is a lone dot (no trailing void).\n */\nfunction Chip({ chip }: { chip: DisclosureChip }) {\n const base = \"inline-flex items-center font-og-mono text-og-xs leading-none\";\n const withText = chip.text ? \"gap-1.5\" : \"gap-0\";\n if (chip.tone === \"bad\") {\n return (\n <span className={cn(base, withText, \"text-og-status-failed\")}>\n <span className=\"size-1.5 rounded-full bg-og-status-failed\" />\n {chip.text}\n </span>\n );\n }\n // \"interrupted\" is a calm cancelled signal: same quiet weight as muted, no\n // dot, no red — just a slightly more prominent subtle text so \"interrupted\"\n // reads at a glance without demanding attention the way a failure does.\n if (chip.tone === \"interrupted\") {\n return <span className={cn(base, \"text-og-fg-subtle og-cancelled-chip\")}>{chip.text}</span>;\n }\n // ok and muted are the same quiet weight — success never earns a hue.\n return <span className={cn(base, \"text-og-fg-subtle\")}>{chip.text}</span>;\n}\n\n/* --- terminal output block (exec / write_stdin) ---------------------------- */\n\nexport function TermBlock({\n command,\n workdir,\n output,\n live,\n tailLines = 12,\n failed,\n}: {\n /**\n * The command shown in the prompt header. Pass `null` when the row title\n * already carries it (e.g. an exec row titled `$ cmd`): the header then drops\n * the command — and the whole prompt line if there is no workdir either — so\n * the command never reads twice, stacked, above the output.\n */\n command: string | null;\n workdir?: string | null | undefined;\n /** The FULL output. TermBlock owns the tail/full slicing internally. */\n output: string;\n live?: boolean | undefined;\n /** A non-zero exit / failed call — tints the left accent red (the one hue). */\n failed?: boolean | undefined;\n /**\n * When the output exceeds the tail window, only the last `tailLines` are shown\n * with a \"show full output\" toggle. The component holds the full text, so the\n * toggle reveals the rest (never a dead affordance). Defaults to 12.\n */\n tailLines?: number | undefined;\n}) {\n const [full, setFull] = useState(false);\n const empty = output.trim() === \"\";\n const lines = output.split(\"\\n\");\n const big = lines.length > tailLines + 4;\n const shown = full || !big ? output : lines.slice(-tailLines).join(\"\\n\");\n const showMore = big && !full;\n const showHeader = command != null || workdir != null;\n\n // No frame, no fill: a quiet monospace run flush on the page, marked only by a\n // 2px left accent so it reads as terminal output at a glance without becoming\n // yet another nested box. Color is spent only on the exception — a settled run\n // gets a calm neutral rule, a live one the running hue, a failed one the red.\n return (\n <div\n className={cn(\n \"min-w-0 border-l-2 pl-3\",\n failed\n ? \"border-og-status-failed/50\"\n : live\n ? \"border-og-status-running/50\"\n : \"border-og-border\",\n )}\n >\n {showHeader ? (\n <div className=\"flex items-center gap-2 pb-1\">\n <span className=\"select-none text-og-status-idle\">$</span>\n {command != null ? (\n <span className=\"min-w-0 flex-1 truncate font-og-mono text-og-sm text-og-fg-muted\">\n {command}\n </span>\n ) : (\n <span className=\"flex-1\" />\n )}\n {workdir ? (\n <span className=\"shrink-0 font-og-mono text-og-xs text-og-fg-subtle\">{workdir}</span>\n ) : null}\n </div>\n ) : null}\n {empty ? (\n <p className=\"font-og-mono text-og-xs italic text-og-fg-subtle\">(no output)</p>\n ) : (\n <pre className=\"max-h-72 overflow-auto whitespace-pre-wrap break-all font-og-mono text-og-xs leading-5 text-og-fg-muted\">\n {shown}\n {live ? (\n <span className=\"ml-px inline-block h-[1em] w-[2px] translate-y-[2px] animate-og-blink bg-og-accent align-middle\" />\n ) : null}\n </pre>\n )}\n {showMore ? (\n <button\n type=\"button\"\n onClick={() => setFull(true)}\n className=\"mt-1 text-left text-og-xs text-og-fg-subtle transition-colors hover:text-og-fg\"\n >\n show full output ({lines.length} lines)\n </button>\n ) : null}\n </div>\n );\n}\n\n/* --- generic payload block -------------------------------------------------- */\n\nexport function PayloadBlock({\n label,\n value,\n failed,\n}: {\n label: string;\n value: unknown;\n failed?: boolean | undefined;\n}) {\n const text = typeof value === \"string\" ? value : stringifyPayload(value);\n if (!text || text.trim() === \"\") {\n return null;\n }\n // Flush on the page, no frame — a labelled monospace run marked only by a 2px\n // left accent (red when the call failed, otherwise a calm neutral rule), so a\n // payload reads as detail hanging off the row, never a nested card.\n return (\n <div\n className={cn(\n \"min-w-0 border-l-2 pl-3\",\n failed ? \"border-og-status-failed/50\" : \"border-og-border\",\n )}\n >\n <p className=\"mb-1 text-og-xs font-medium uppercase tracking-[0.08em] text-og-fg-subtle\">\n {label}\n </p>\n <pre\n className={cn(\n \"max-h-64 overflow-auto whitespace-pre-wrap break-all font-og-mono text-og-xs leading-5\",\n failed ? \"text-og-status-failed\" : \"text-og-fg-muted\",\n )}\n >\n {text}\n </pre>\n </div>\n );\n}\n\n/** A quiet inline note inside an expanded body (lost output, empty frame, …). */\nexport function BodyNote({\n children,\n tone,\n}: {\n children: ReactNode;\n tone?: \"error\" | \"muted\" | undefined;\n}) {\n if (tone === \"error\") {\n // A quiet error run marked by a 2px red accent — the same flush, frameless\n // language as the payload/output blocks, not a filled callout box.\n return (\n <div className=\"border-l-2 border-og-status-failed/50 pl-3 font-og-mono text-og-xs leading-5 text-og-status-failed\">\n {children}\n </div>\n );\n }\n return <p className=\"px-0.5 text-og-sm italic leading-5 text-og-fg-subtle\">{children}</p>;\n}\n\n/* --- screenshot thumbnail + media states ------------------------------------ */\n\n/**\n * The shared inline-media footprint, so thumb / skeleton / empty align. Sized to\n * the row's line height (~28px) so a media row never out-weighs a text row and\n * the single-column rhythm holds.\n */\nconst MEDIA_BOX = \"h-7 w-[52px] shrink-0 rounded-og-xs border border-og-border\";\n\n/**\n * A loading screenshot placeholder. A faint camera glyph over a shimmering box,\n * so a still frame of the running state reads unambiguously as \"capturing\" — not\n * a broken thumbnail.\n */\nexport function MediaSkeleton() {\n return (\n <span\n className={cn(\n MEDIA_BOX,\n \"relative inline-flex items-center justify-center overflow-hidden bg-og-surface-2\",\n )}\n >\n <span className=\"absolute inset-0 animate-og-pulse bg-og-surface-3/50\" />\n <CameraIcon className=\"relative size-3.5 text-og-fg-subtle\" />\n </span>\n );\n}\n\n/** A standardized \"tool ran, produced no image\" placeholder in the media slot. */\nexport function MediaEmpty() {\n return (\n <span className={cn(MEDIA_BOX, \"inline-flex items-center justify-center bg-og-bg\")}>\n <CameraOffIcon className=\"size-3.5 text-og-fg-subtle\" />\n </span>\n );\n}\n\n/**\n * A small inline screenshot thumbnail that opens the app lightbox on click.\n *\n * Requires a `LightboxProvider` ancestor for the click-to-expand affordance.\n * Outside one it degrades to a plain, non-interactive image — never a dead\n * \"Expand\" button that announces an action it cannot perform.\n */\nexport function Thumbnail({\n src,\n caption,\n alt = \"screenshot\",\n expandLabel = \"Expand screenshot\",\n lightboxLabel = \"Screenshot\",\n downloadFilename,\n}: {\n src: string;\n caption?: string | undefined;\n alt?: string;\n expandLabel?: string | undefined;\n lightboxLabel?: string | undefined;\n downloadFilename?: string | undefined;\n}) {\n const lightbox = useLightboxOptional();\n const [failed, setFailed] = useState(false);\n if (failed) {\n return <MediaEmpty />;\n }\n // A plain <img> (not a framework Image): this is a framework-agnostic SDK, so\n // the host's image component is unavailable and unwanted here.\n const img = (\n <img\n src={src}\n alt={alt}\n onError={() => setFailed(true)}\n className=\"h-full w-full object-cover transition-opacity group-hover/thumb:opacity-80\"\n />\n );\n if (!lightbox) {\n return <span className={cn(MEDIA_BOX, \"inline-flex overflow-hidden bg-og-bg\")}>{img}</span>;\n }\n // The thumbnail is a real, independently-focusable button nested inside the\n // row's disclosure trigger. It stops both pointer AND keyboard activation from\n // bubbling, so opening the lightbox never also toggles the row.\n return (\n <button\n type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n lightbox.open(src, caption, event.currentTarget, lightboxLabel, downloadFilename);\n }}\n onKeyDown={(event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.stopPropagation();\n }\n }}\n className={cn(\n MEDIA_BOX,\n \"group/thumb relative inline-flex overflow-hidden bg-og-bg outline-hidden\",\n \"focus-visible:ring-2 focus-visible:ring-og-accent\",\n )}\n aria-label={expandLabel}\n >\n {img}\n </button>\n );\n}\n\n/**\n * The expanded screenshot inside a tool body: a contained, clickable preview\n * (opens the lightbox) with a quiet caption. Constrained height + object-contain\n * so it never breaks the row layout. Like {@link Thumbnail}, it degrades to a\n * plain image outside a `LightboxProvider`.\n */\nexport function ScreenshotFigure({\n src,\n caption,\n alt = \"screenshot\",\n expandLabel = \"Expand screenshot\",\n lightboxLabel = \"Screenshot\",\n downloadFilename,\n}: {\n src: string;\n caption?: string | undefined;\n alt?: string;\n expandLabel?: string | undefined;\n lightboxLabel?: string | undefined;\n downloadFilename?: string | undefined;\n}) {\n const lightbox = useLightboxOptional();\n const [failed, setFailed] = useState(false);\n const surface = \"block w-full overflow-hidden rounded-og-md border border-og-border bg-og-bg\";\n // A plain <img>, like {@link Thumbnail} — a framework-agnostic SDK has no host\n // Image component to defer to.\n const img = (\n <img\n src={src}\n alt={alt}\n onError={() => setFailed(true)}\n className=\"max-h-80 w-full object-contain\"\n />\n );\n return (\n <figure className=\"m-0 min-w-0\">\n {failed ? (\n <div className=\"rounded-og-md border border-og-border bg-og-bg px-3 py-6 text-center font-og-mono text-og-xs text-og-fg-subtle\">\n image unavailable\n </div>\n ) : lightbox ? (\n <button\n type=\"button\"\n onClick={(event) =>\n lightbox.open(src, caption, event.currentTarget, lightboxLabel, downloadFilename)\n }\n className={surface}\n aria-label={expandLabel}\n >\n {img}\n </button>\n ) : (\n <div className={surface}>{img}</div>\n )}\n {caption ? (\n <figcaption className=\"mt-1.5 font-og-mono text-og-xs text-og-fg-subtle\">\n {caption}\n </figcaption>\n ) : null}\n </figure>\n );\n}\n","import { createContext, useContext, useLayoutEffect, useRef, type RefObject } from \"react\";\nimport type { TimelineItem } from \"../timeline/types\";\nimport { annotatableText, rangeFromOffsets, visibleClientRect } from \"./timeline-annotation-shared\";\n\nexport type TimelineSearchTarget = {\n sequence: number;\n eventId?: string | undefined;\n /** Literal, case-insensitive text. No regex interpretation. */\n query: string;\n /** Zero-based occurrence within the source message. Defaults to zero. */\n occurrence?: number | undefined;\n /**\n * Zero-based UTF-16 offset of the match in the original message text, as\n * reported by backend all-occurrence search (messageMatchOffset). When set\n * it takes precedence over `occurrence` and is validated against `query`,\n * so a stale offset degrades to no highlight rather than a wrong one.\n */\n offset?: number | undefined;\n};\n\n/** Scoped to the target's group; disclosure expansion persists after closing find. */\nexport const TimelineSearchRevealContext = createContext<string | null>(null);\nexport const useTimelineSearchReveal = () => useContext(TimelineSearchRevealContext);\n\nexport function isTimelineSearchTarget(item: TimelineItem, target: TimelineSearchTarget): boolean {\n const sources =\n item.sourceEvents ??\n (\"annotationSource\" in item && item.annotationSource ? [item.annotationSource] : []);\n return sources.some(\n (source) =>\n source.sequence === target.sequence && (!target.eventId || source.eventId === target.eventId),\n );\n}\n\n/** Scan without allocating an offset array proportional to a huge message. */\nexport function literalSearchOffset(text: string, query: string, occurrence = 0): number {\n if (!query || !Number.isSafeInteger(occurrence) || occurrence < 0) return -1;\n // Regex escaping preserves UTF-16 offsets even for case folds whose lowercase\n // string representation changes length (for example dotted capital I).\n const pattern = new RegExp(query.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"), \"giu\");\n let match: RegExpExecArray | null;\n let index = 0;\n while ((match = pattern.exec(text))) {\n if (index++ === occurrence) return match.index;\n }\n return -1;\n}\n\n/**\n * Validate a caller-supplied UTF-16 offset against the text and query. The\n * sticky flag pins the match to exactly `offset`; like literalSearchOffset,\n * regex escaping keeps offsets in UTF-16 units even for length-changing case\n * folds. Returns -1 when the offset is stale or the text no longer matches.\n */\nexport function matchAtOffset(text: string, query: string, offset: number): number {\n if (!query || !Number.isSafeInteger(offset) || offset < 0 || offset > text.length) return -1;\n const pattern = new RegExp(query.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"), \"iyu\");\n pattern.lastIndex = offset;\n // Unicode regex engines may rewind a lastIndex inside a surrogate pair.\n return pattern.exec(text)?.index === offset ? offset : -1;\n}\n\n/** Resolve a target to a UTF-16 offset: explicit offset wins, else the ordinal scan. */\nexport function searchMatchOffset(text: string, target: TimelineSearchTarget): number {\n if (target.offset != null) return matchAtOffset(text, target.query, target.offset);\n return literalSearchOffset(text, target.query, target.occurrence);\n}\n\ntype HighlightRegistry = { set(name: string, value: unknown): void; delete(name: string): void };\nlet nextHighlightId = 0;\n\n/** Range highlighting does not mutate React-owned text or its layout. */\nexport function useTimelineSearchNavigation(\n root: RefObject<HTMLDivElement | null>,\n itemId: string | undefined,\n target: TimelineSearchTarget | null | undefined,\n releasePin: () => void,\n revision: unknown,\n) {\n const highlightName = useRef(\"\");\n if (!highlightName.current) highlightName.current = `og-search-${++nextHighlightId}`;\n const key = target\n ? JSON.stringify([\n itemId,\n target.sequence,\n target.eventId,\n target.query,\n target.occurrence ?? 0,\n target.offset ?? null,\n ])\n : null;\n const navigated = useRef<string | null>(null);\n useLayoutEffect(() => {\n if (!key) navigated.current = null;\n if (!key || !target || !itemId || !root.current) return;\n releasePin();\n const scroller = root.current;\n const registry = (\n globalThis.CSS as (typeof CSS & { highlights?: HighlightRegistry }) | undefined\n )?.highlights;\n const HighlightClass = (\n globalThis as unknown as { Highlight?: new (...ranges: Range[]) => unknown }\n ).Highlight;\n const style = document.createElement(\"style\");\n style.textContent = `::highlight(${highlightName.current}) { background: var(--og-accent, #facc15); color: var(--og-accent-fg, #171717); }`;\n scroller.append(style);\n let frame = 0;\n const update = () => {\n const source = [...scroller.querySelectorAll<HTMLElement>(\"[data-og-search-item]\")].find(\n (element) => element.dataset.ogSearchItem === itemId,\n );\n if (!source) return;\n // A virtualized renderer can explicitly identify the selected occurrence\n // when earlier occurrences are not mounted. Never accept a stale marker\n // belonging to a different sequence, occurrence, or query.\n const materialized = [\n ...source.querySelectorAll<HTMLElement>(\"[data-og-search-occurrence]\"),\n ].find(\n (element) =>\n element.dataset.ogSearchSequence === String(target.sequence) &&\n element.dataset.ogSearchQuery === target.query &&\n (target.offset != null\n ? element.dataset.ogSearchOffset === String(target.offset)\n : element.dataset.ogSearchOccurrence === String(target.occurrence ?? 0)),\n );\n const { text, spans } = annotatableText(materialized ?? source);\n // Inside a materialized chunk the match starts at the chunk start.\n // Raw source offsets must NEVER be interpreted as rendered DOM offsets:\n // even matching characters can identify a different repeated occurrence.\n const offset = materialized\n ? matchAtOffset(text, target.query, 0)\n : target.offset != null\n ? -1\n : searchMatchOffset(text, target);\n const range =\n offset < 0 ? null : rangeFromOffsets(spans, offset, offset + target.query.length);\n if (range && registry && HighlightClass)\n registry.set(highlightName.current, new HighlightClass(range));\n else registry?.delete(highlightName.current);\n const rect = range ? visibleClientRect(range) : null;\n // A custom renderer may mount the message body later (or materialize a\n // virtualized chunk in response to renderMessageText's search context).\n // Finding its empty shell is not proof that the occurrence was revealed.\n if (navigated.current === key || !range || !rect) return;\n const box = rect;\n if (!box.height && !box.width) return;\n scroller.scrollTop +=\n box.top - scroller.getBoundingClientRect().top - scroller.clientHeight / 2;\n navigated.current = key;\n };\n const schedule = () => {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(update);\n };\n const observer = new MutationObserver(schedule);\n observer.observe(scroller, { childList: true, subtree: true, characterData: true });\n const resize = typeof ResizeObserver === \"undefined\" ? null : new ResizeObserver(schedule);\n resize?.observe(scroller);\n schedule();\n return () => {\n observer.disconnect();\n resize?.disconnect();\n cancelAnimationFrame(frame);\n registry?.delete(highlightName.current);\n style.remove();\n };\n }, [key, root, itemId, target, releasePin, revision]);\n}\n","import { CheckIcon, CopyIcon } from \"lucide-react\";\nimport { useEffect, useRef, useState, type MouseEvent, type ReactNode } from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { copyTextToClipboard } from \"../lib/clipboard\";\nimport { Tooltip, TooltipContent, TooltipTrigger } from \"./tooltip\";\n\nconst COPIED_MS = 1600;\n\nexport type CopyButtonProps = {\n /** Plain text to copy, or a lazy getter (tables / late DOM). */\n text: string | (() => string);\n /** Accessible name + tooltip while idle. */\n label?: string | undefined;\n className?: string | undefined;\n /**\n * `always` — visible control.\n * `group-hover` — parent must use `group/copy`; fades in on hover/focus-visible.\n */\n reveal?: \"always\" | \"group-hover\" | undefined;\n};\n\n/**\n * Ghost icon copy control — no chrome box, no \"Copy code\" label.\n * Copy → check flash. Hover-reveal via `group/copy` on a parent.\n */\nexport function CopyButton({\n text,\n label = \"Copy\",\n className,\n reveal = \"group-hover\",\n}: CopyButtonProps) {\n const [copied, setCopied] = useState(false);\n const [tipOpen, setTipOpen] = useState(false);\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n return () => {\n if (timerRef.current !== null) {\n clearTimeout(timerRef.current);\n }\n };\n }, []);\n\n const onClick = async (event: MouseEvent<HTMLButtonElement>) => {\n event.preventDefault();\n event.stopPropagation();\n // Capture before any await: React nulls `currentTarget` after the\n // synchronous handler turn. Blurring via the stale target throws and used\n // to skip the copied-reset timer, leaving the tip stuck on \"Copied\".\n const button = event.currentTarget;\n const pointerActivated = event.detail > 0;\n const value = typeof text === \"function\" ? text() : text;\n const ok = await copyTextToClipboard(value);\n if (!ok) {\n return;\n }\n setCopied(true);\n setTipOpen(true);\n if (timerRef.current !== null) {\n clearTimeout(timerRef.current);\n }\n // Always arm the reset before blur — a throw here must not leave the\n // forced \"Copied\" tip open forever.\n timerRef.current = setTimeout(() => {\n timerRef.current = null;\n setCopied(false);\n setTipOpen(false);\n }, COPIED_MS);\n // Pointer activation focuses the button and leaves `group-focus-within`\n // stuck after the cursor leaves — chrome never \"unhovers\". Keyboard\n // activation (`detail === 0`) keeps focus for a11y.\n if (pointerActivated) {\n button.blur();\n }\n };\n\n const tip = copied ? \"Copied\" : label;\n\n return (\n <Tooltip\n open={copied ? true : tipOpen}\n onOpenChange={(next) => {\n if (!copied) {\n setTipOpen(next);\n }\n }}\n >\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n data-og-copy=\"\"\n data-state={copied ? \"copied\" : \"idle\"}\n aria-label={tip}\n onClick={onClick}\n className={cn(\n \"inline-flex size-7 shrink-0 items-center justify-center rounded-og-sm pointer-coarse:size-11\",\n \"text-og-fg-subtle transition-[opacity,color,background-color] duration-150\",\n \"hover:bg-og-surface-2/80 hover:text-og-fg\",\n \"focus-visible:opacity-100 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent/40\",\n reveal === \"group-hover\" &&\n \"opacity-0 group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 pointer-coarse:opacity-70\",\n copied && \"opacity-100 text-og-accent\",\n className,\n )}\n >\n {copied ? (\n <CheckIcon className=\"size-3.5\" aria-hidden />\n ) : (\n <CopyIcon className=\"size-3.5\" aria-hidden />\n )}\n </button>\n </TooltipTrigger>\n <TooltipContent side=\"top\">{tip}</TooltipContent>\n </Tooltip>\n );\n}\n\n/** Hover-reveal chrome around a message / turn so {@link CopyButton} can fade in. */\nexport function CopyHoverFrame({\n children,\n className,\n copyText,\n label,\n align = \"end\",\n trailing,\n}: {\n children: ReactNode;\n className?: string | undefined;\n copyText: string;\n label: string;\n align?: \"start\" | \"end\" | undefined;\n /** Optional footer meta (e.g. sent/finished clock) — immediately after the copy control. */\n trailing?: ReactNode | undefined;\n}) {\n if (copyText.trim().length === 0) {\n return <div className={className}>{children}</div>;\n }\n return (\n <div className={cn(\"group/copy\", className)}>\n {children}\n {/* Sit under the body — top-right overlay collided with the first line. */}\n <div\n data-og-annotation-chrome=\"\"\n className={cn(\n \"flex h-6 items-center gap-1.5 pointer-coarse:h-11\",\n align === \"end\" ? \"justify-end\" : \"justify-start\",\n )}\n >\n <CopyButton\n text={copyText}\n label={label}\n reveal=\"group-hover\"\n className=\"size-6 pointer-coarse:size-11\"\n />\n {trailing}\n </div>\n </div>\n );\n}\n","import {\n Children,\n createContext,\n useContext,\n isValidElement,\n memo,\n useEffect,\n useMemo,\n useReducer,\n useRef,\n useState,\n type ComponentPropsWithoutRef,\n type ReactNode,\n} from \"react\";\nimport ReactMarkdown, {\n defaultUrlTransform,\n type Components,\n type ExtraProps,\n type UrlTransform,\n} from \"react-markdown\";\nimport remarkGfm from \"remark-gfm\";\nimport { PanelsTopLeftIcon } from \"lucide-react\";\nimport { ActivityDisclosure } from \"../timeline/shared\";\nimport { cn } from \"../lib/cn\";\nimport { tableElementToTsv } from \"../lib/clipboard\";\nimport { prefersReducedMotion } from \"../lib/motion\";\nimport { MOTION_INSPECT_SCALE } from \"../lib/motion-inspect\";\nimport { CopyButton } from \"./copy-button\";\nimport { PreviewLoading } from \"./preview-loading\";\nimport type { observeMarkdownTableLayout } from \"./markdown-table-layout\";\nimport { softenStreamingMarkdown } from \"./soften-streaming-markdown\";\nimport { createStreamReveal, rehypeStreamReveal, type StreamReveal } from \"./stream-reveal\";\nimport { TooltipProvider } from \"./tooltip\";\nimport { searchMatchOffset, type TimelineSearchTarget } from \"./timeline-search\";\n\n/**\n * The default renderer for chat message bodies in {@link MessageTimeline}.\n *\n * Agent (and user) messages arrive as GitHub-flavored markdown. This turns the\n * raw text into styled HTML using `react-markdown` + `remark-gfm`, themed to the\n * package's `og-*` design tokens so it reads as one cohesive dark surface — no\n * stock Tailwind colors leak in.\n *\n * It re-parses on every render, which is exactly right for streaming: a body\n * that is still arriving (an unterminated `**`, a half-open code fence, a table\n * mid-row) renders as best-effort markdown and resolves cleanly as the rest of\n * the tokens land. Consumers who want a different renderer can still pass\n * `renderMessageText` to `MessageTimeline` to override this entirely.\n */\nexport type MarkdownInteractiveBlock = { kind: \"html\" | \"site\"; content: string };\nconst InteractiveContext = createContext<{\n source: string;\n streaming?: boolean | undefined;\n render?: (block: MarkdownInteractiveBlock) => ReactNode;\n renderImage?: (image: { src: string; alt: string }) => ReactNode;\n}>({ source: \"\" });\n\nexport type MarkdownProps = {\n /** Resolve a retained-file reference to the host's authenticated artifact page. */\n artifactHref?: ((artifactId: string) => string) | undefined;\n /** Reveals a bounded source excerpt; closing Find retains it until explicit restore. */\n searchTarget?: TimelineSearchTarget | null | undefined;\n renderImage?: ((image: { src: string; alt: string }) => ReactNode) | undefined;\n /** Host opt-in for assistant-authored interactive fences. */\n renderInteractiveBlock?: ((block: MarkdownInteractiveBlock) => ReactNode) | undefined;\n children: string;\n className?: string | undefined;\n /**\n * While true, newly arrived source fades in through tip ink (`.og-stream-ink`):\n * an age window over each append batch — fast streams keep a large soft band,\n * slow streams a tight tip. Paint-only: the DOM always holds the full truthful\n * text. Once streaming ends and trailing ink settles, the body re-renders as\n * plain markdown with a short local settle breath (no page-wide view\n * transition — that blinked the whole document).\n */\n streaming?: boolean | undefined;\n /**\n * Open a session-local file link in the host's Files surface. The path is the\n * exact decoded `sandbox:` payload; `line`, when present, is 1-based.\n */\n onSandboxFile?: ((path: string, line?: number) => void | Promise<void>) | undefined;\n};\n\n/** Exact target path plus optional 1-based line from a markdown href. */\nexport type SandboxFileLocation = {\n path: string;\n line: number | null;\n};\n\n/* --- element renderers (themed to og-* tokens) ------------------------------ */\n\nconst baseComponents: Components = {\n h1: ({ children, ...props }) => (\n <h1\n className=\"mt-5 mb-2.5 text-xl font-semibold tracking-tight text-og-fg first:mt-0\"\n {...props}\n >\n {children}\n </h1>\n ),\n h2: ({ children, ...props }) => (\n <h2 className=\"mt-5 mb-2 text-lg font-semibold tracking-tight text-og-fg first:mt-0\" {...props}>\n {children}\n </h2>\n ),\n h3: ({ children, ...props }) => (\n <h3\n className=\"mt-4 mb-1.5 text-og-md font-semibold tracking-tight text-og-fg first:mt-0\"\n {...props}\n >\n {children}\n </h3>\n ),\n h4: ({ children, ...props }) => (\n <h4\n className=\"mt-4 mb-1.5 text-og-sm font-semibold uppercase tracking-[0.04em] text-og-fg-muted first:mt-0\"\n {...props}\n >\n {children}\n </h4>\n ),\n p: ({ children, ...props }) => (\n <p className=\"my-2.5 leading-7 first:mt-0 last:mb-0\" {...props}>\n {children}\n </p>\n ),\n strong: ({ children, ...props }) => (\n <strong className=\"font-semibold text-og-fg\" {...props}>\n {children}\n </strong>\n ),\n em: ({ children, ...props }) => (\n <em className=\"italic\" {...props}>\n {children}\n </em>\n ),\n ul: ({ children, ...props }) => (\n <ul\n className=\"my-2.5 ml-5 flex list-disc flex-col gap-1 marker:text-og-fg-subtle first:mt-0 last:mb-0\"\n {...props}\n >\n {children}\n </ul>\n ),\n ol: ({ children, ...props }) => (\n <ol\n className=\"my-2.5 ml-5 flex list-decimal flex-col gap-1 marker:text-og-fg-subtle first:mt-0 last:mb-0\"\n {...props}\n >\n {children}\n </ol>\n ),\n // GFM task-list items carry a leading checkbox <input>; `list-none` + a\n // negative margin pull the checkbox back to the bullet column so it aligns\n // with the text.\n li: ({ children, ...props }) => (\n <li\n className=\"leading-7 marker:text-og-fg-subtle [&>ul]:my-1 [&>ol]:my-1 [&:has(>input)]:list-none [&:has(>input)]:-ml-5\"\n {...props}\n >\n {children}\n </li>\n ),\n input: ({ type, ...props }) =>\n type === \"checkbox\" ? (\n <input\n {...props}\n type=\"checkbox\"\n disabled\n className=\"mr-2 size-3.5 translate-y-[2px] cursor-default accent-og-accent align-baseline\"\n />\n ) : (\n <input type={type} {...props} />\n ),\n blockquote: ({ children, ...props }) => (\n <blockquote\n className=\"my-3 border-l-2 border-og-border-strong pl-3.5 text-og-fg-muted [&>p]:my-1.5 first:mt-0 last:mb-0\"\n {...props}\n >\n {children}\n </blockquote>\n ),\n hr: (props) => <hr className=\"my-4 border-0 border-t border-og-border\" {...props} />,\n // Inline `code` vs fenced code blocks. react-markdown v10 no longer passes an\n // `inline` flag; a fenced block is a <code> whose parent is <pre> (styled by\n // the `pre` renderer), so a `code` reaching here is treated as inline.\n code: ({ children, className: _className, ...props }) => (\n <code\n className=\"rounded-og-xs bg-og-surface-1 px-1 py-0.5 font-og-mono text-og-sm text-og-fg\"\n {...props}\n >\n {children}\n </code>\n ),\n // Fenced code — quiet mono block (no card / no nested vertical scroll).\n pre: InteractiveCodeBlock,\n // Tables stay unboxed (hairline rules); hover reveals a TSV copy control.\n table: ({ children, ...props }) => <MarkdownTable {...props}>{children}</MarkdownTable>,\n thead: ({ children, ...props }) => <thead {...props}>{children}</thead>,\n th: ({ children, ...props }) => (\n <th\n className=\"border-b border-og-border px-0 py-1.5 pr-4 text-left font-medium text-og-fg first:pl-0\"\n {...props}\n >\n {children}\n </th>\n ),\n td: ({ children, ...props }) => (\n <td\n className=\"border-b border-og-border/70 px-0 py-1.5 pr-4 align-top text-og-fg-muted [tr:last-child>&]:border-b-0\"\n {...props}\n >\n {children}\n </td>\n ),\n img: MarkdownImage,\n};\n\nconst MARKDOWN_LINK_CLASS =\n \"break-words font-medium text-og-accent-strong underline-offset-2 hover:underline\";\n\n// Keep renderer component types stable: rebuilding them remounts paragraphs,\n// destroys native selections, and resets embedded media on host updates.\nconst MarkdownLinkContext = createContext<Pick<MarkdownProps, \"onSandboxFile\" | \"artifactHref\">>(\n {},\n);\n\nconst markdownComponents: Components = {\n ...baseComponents,\n p: ({ children, node, ...props }) => {\n const containsArtifact = node?.children.some(\n (child) =>\n child.type === \"element\" &&\n child.tagName === \"img\" &&\n typeof child.properties.src === \"string\" &&\n retainedImageId(child.properties.src),\n );\n const Tag = containsArtifact ? \"div\" : \"p\";\n return (\n <Tag className=\"my-2.5 leading-7 first:mt-0 last:mb-0\" {...props}>\n {children}\n </Tag>\n );\n },\n a: ({ children, href, ...props }) => {\n const { onSandboxFile, artifactHref } = useContext(MarkdownLinkContext);\n const artifactId = href ? retainedImageId(href) : null;\n if (artifactId) {\n const destination = artifactHref?.(artifactId);\n return destination ? (\n <a\n className={MARKDOWN_LINK_CLASS}\n href={defaultUrlTransform(destination)}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n >\n {children}\n </a>\n ) : (\n <span title=\"This artifact requires a workspace-aware host\">\n {children} (artifact unavailable)\n </span>\n );\n }\n const location = sandboxFileLocationFromHref(href);\n if (location !== null) {\n return (\n <SandboxMarkdownLink location={location} onSandboxFile={onSandboxFile}>\n {children}\n </SandboxMarkdownLink>\n );\n }\n if (isSandboxHref(href) || !href) {\n return (\n <span\n className=\"break-words text-og-fg-subtle\"\n aria-disabled=\"true\"\n title={\n isSandboxHref(href)\n ? \"This sandbox file reference is invalid\"\n : \"This link is unavailable\"\n }\n >\n {children} (link unavailable)\n </span>\n );\n }\n return (\n <a\n className={MARKDOWN_LINK_CLASS}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n href={href}\n {...props}\n >\n {children}\n </a>\n );\n },\n};\n\nfunction SandboxMarkdownLink({\n location,\n onSandboxFile,\n children,\n}: {\n location: SandboxFileLocation;\n onSandboxFile: MarkdownProps[\"onSandboxFile\"];\n children: ReactNode;\n}) {\n const [state, setState] = useState<\"idle\" | \"loading\" | \"error\">(\"idle\");\n const { path, line } = location;\n if (!onSandboxFile) {\n return (\n <span\n className=\"break-words font-medium text-og-fg-subtle underline decoration-dotted underline-offset-2\"\n aria-disabled=\"true\"\n title=\"This sandbox file requires a session-aware handler\"\n >\n {children}\n </span>\n );\n }\n const idleTitle = line !== null ? `Open ${path} at line ${line}` : `Open ${path}`;\n return (\n <button\n type=\"button\"\n className={cn(\n MARKDOWN_LINK_CLASS,\n \"inline-flex min-h-7 cursor-pointer items-center disabled:cursor-wait disabled:opacity-70 pointer-coarse:min-h-11\",\n )}\n disabled={state === \"loading\"}\n aria-busy={state === \"loading\"}\n title={state === \"error\" ? \"Couldn't open this file. Select to retry.\" : idleTitle}\n onClick={() => {\n setState(\"loading\");\n void Promise.resolve(onSandboxFile(path, line ?? undefined)).then(\n () => setState(\"idle\"),\n () => setState(\"error\"),\n );\n }}\n >\n {children}\n {state === \"loading\" ? \" (opening…)\" : state === \"error\" ? \" (retry)\" : null}\n </button>\n );\n}\n\nconst SANDBOX_SCHEME = \"sandbox:\";\nconst LEGACY_WORKSPACE_PREFIX = \"/workspace/\";\nconst TRAILING_LINE = /:([0-9]+)$/u;\nconst TRAILING_INVALID_LINE = /:-(?:[0-9]+)$|:[0-9]+-[0-9]+$/u;\nconst CONTROL_CHARACTER = /[\\u0000-\\u001f\\u007f]/u;\n\nfunction isSandboxHref(href: string | undefined): boolean {\n return href?.startsWith(SANDBOX_SCHEME) ?? false;\n}\n\n/**\n * Parse an OpenGeni `sandbox:` application href (plus the historical bare\n * `/workspace/...` form). The decoded path is intentionally opaque here:\n * target selection, path policy, and filesystem authority belong to the\n * session-aware host and its FileSystem boundary.\n */\nexport function sandboxFileLocationFromHref(href: string | undefined): SandboxFileLocation | null {\n if (!href) return null;\n const encodedLocation = isSandboxHref(href)\n ? href.slice(SANDBOX_SCHEME.length)\n : href.startsWith(LEGACY_WORKSPACE_PREFIX)\n ? href\n : null;\n if (!encodedLocation || encodedLocation.includes(\"?\") || encodedLocation.includes(\"#\")) {\n return null;\n }\n if (TRAILING_INVALID_LINE.test(encodedLocation)) return null;\n const lineMatch = TRAILING_LINE.exec(encodedLocation);\n const encodedPath = lineMatch ? encodedLocation.slice(0, -lineMatch[0].length) : encodedLocation;\n const line = lineMatch?.[1] ? Number(lineMatch[1]) : null;\n if (line !== null && (!Number.isSafeInteger(line) || line < 1)) return null;\n if (!encodedPath) return null;\n try {\n const path = decodeURIComponent(encodedPath);\n return path && !CONTROL_CHARACTER.test(path) ? { path, line } : null;\n } catch {\n return null;\n }\n}\n\n/** Return the exact decoded path for a supported sandbox-link href. */\nexport function sandboxFilePathFromHref(href: string | undefined): string | null {\n return sandboxFileLocationFromHref(href)?.path ?? null;\n}\n\nexport function retainedImageId(src: string): string | null {\n return (\n /^artifact:([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i\n .exec(src)?.[1]\n ?.toLowerCase() ?? null\n );\n}\nfunction MarkdownImage({ src, alt }: ComponentPropsWithoutRef<\"img\">) {\n const { renderImage } = useContext(InteractiveContext);\n if (!src) return <span>{alt ?? \"Image unavailable\"}</span>;\n if (retainedImageId(src))\n return renderImage ? (\n <>{renderImage({ src, alt: alt ?? \"\" })}</>\n ) : (\n <span>{alt || \"Image\"} (preview unavailable)</span>\n );\n return <img src={src} alt={alt ?? \"\"} loading=\"lazy\" className=\"my-3 max-w-full rounded-og-md\" />;\n}\n\nconst markdownUrlTransform: UrlTransform = (url, key, node) =>\n (key === \"href\" && node.tagName === \"a\" && (isSandboxHref(url) || retainedImageId(url))) ||\n (key === \"src\" && node.tagName === \"img\" && retainedImageId(url))\n ? url\n : defaultUrlTransform(url);\n\n/** How long after the stream ends the reveal pipeline stays for trailing animations. */\n/** Trailing ink window after stream end — keep ≥ {@link INK_FADE_MS}. */\nconst REVEAL_LINGER_MS = 480 * MOTION_INSPECT_SCALE;\n/** Keep in sync with `--og-duration-markdown-crystallize` / view-transition CSS. */\nconst MARKDOWN_SETTLE_MS = 480 * MOTION_INSPECT_SCALE;\n\nfunction nodeText(node: ReactNode): string {\n if (node == null || typeof node === \"boolean\") {\n return \"\";\n }\n if (typeof node === \"string\" || typeof node === \"number\") {\n return String(node);\n }\n if (Array.isArray(node)) {\n return node.map(nodeText).join(\"\");\n }\n if (isValidElement<{ children?: ReactNode }>(node)) {\n return nodeText(node.props.children);\n }\n return \"\";\n}\n\nfunction fenceLanguage(children: ReactNode): string | null {\n let found: string | null = null;\n Children.forEach(children, (child) => {\n if (found || !isValidElement<{ className?: string }>(child)) {\n return;\n }\n const match = /language-([a-zA-Z0-9_+-]+)/.exec(child.props.className ?? \"\");\n if (match?.[1]) {\n found = match[1];\n }\n });\n return found;\n}\n\nfunction InteractiveCodeBlock({ children, node }: ComponentPropsWithoutRef<\"pre\"> & ExtraProps) {\n const { source, render, streaming } = useContext(InteractiveContext);\n const language = fenceLanguage(children);\n if (render && (language === \"opengeni-html\" || language === \"opengeni-site\")) {\n const start = node?.position?.start.offset;\n const end = node?.position?.end.offset;\n const original =\n start !== undefined && end !== undefined && end <= source.length\n ? source.slice(start, end)\n : \"\";\n const lines = original.trimEnd().split(\"\\n\");\n const opening = lines[0]?.match(/^ {0,3}([\\x60]{3,}|~{3,})/);\n\n const closing = lines\n .at(-1)\n ?.replace(/^\\s*(?:>\\s*)+/, \"\")\n .trim();\n const complete =\n opening &&\n lines.length > 1 &&\n closing &&\n closing.length >= opening[1]!.length &&\n [...closing].every((c) => c === opening[1]![0]);\n if (!complete) {\n return (\n <div role=\"status\" aria-live=\"polite\" aria-busy={streaming === true} className=\"my-3\">\n {streaming ? (\n <PreviewLoading />\n ) : (\n <ActivityDisclosure\n icon={<PanelsTopLeftIcon aria-hidden className=\"size-3.5\" />}\n title=\"Preview incomplete\"\n running={false}\n expandable={false}\n preview=\"Generation stopped before the preview was ready.\"\n />\n )}\n </div>\n );\n }\n return (\n <>\n {render({\n kind: language === \"opengeni-html\" ? \"html\" : \"site\",\n content: nodeText(children).replace(/\\n$/, \"\"),\n })}\n </>\n );\n }\n return <MarkdownCodeBlock>{children}</MarkdownCodeBlock>;\n}\n\nfunction MarkdownCodeBlock({ children }: { children?: ReactNode }) {\n const code = nodeText(children).replace(/\\n$/, \"\");\n const language = fenceLanguage(children);\n // mt-only (no mb / last:mb-0): next sibling streaming in used to flip a\n // previous block's bottom margin on and yank tip-follow. Copy is overlay-only.\n // Fixed leading + overflow-x:auto with stable block chrome so wide ASCII lines\n // don't pop a scrollbar gutters mid-stream (another tip bob).\n return (\n <div className=\"group/copy relative mt-3 first:mt-0\">\n <div className=\"pointer-events-none absolute top-1.5 right-1.5 z-10 flex items-center gap-1\">\n {language ? (\n <span className=\"rounded px-1 py-0.5 font-og-mono text-[10px] uppercase tracking-wide text-og-fg-subtle/80 opacity-0 transition-opacity group-hover/copy:opacity-100 pointer-coarse:opacity-70\">\n {language}\n </span>\n ) : null}\n <div className=\"pointer-events-auto\">\n <CopyButton text={code} label=\"Copy code\" reveal=\"group-hover\" />\n </div>\n </div>\n <pre\n tabIndex={0}\n className=\"overflow-x-auto overflow-y-hidden rounded-og-md bg-og-surface-1/70 px-3 py-2.5 font-og-mono text-og-sm leading-5 text-og-fg-muted [scrollbar-gutter:stable] [&>code]:block [&>code]:border-0 [&>code]:bg-transparent [&>code]:p-0 [&>code]:leading-5 [&>code]:text-inherit\"\n >\n {children}\n </pre>\n </div>\n );\n}\n\nfunction MarkdownTable({ children, className, ...props }: ComponentPropsWithoutRef<\"table\">) {\n const wrapperRef = useRef<HTMLDivElement>(null);\n const tableRef = useRef<HTMLTableElement>(null);\n const layoutRef = useRef<ReturnType<typeof observeMarkdownTableLayout>>(undefined);\n useEffect(() => {\n const wrapper = wrapperRef.current;\n const table = tableRef.current;\n if (!wrapper?.closest(\"[data-og-wide-table-message]\") || !table) return;\n let disposed = false;\n // Ordinary tables remain usable even if this optional layout chunk fails.\n void import(\"./markdown-table-layout\")\n .then(({ observeMarkdownTableLayout }) => {\n if (!disposed) layoutRef.current = observeMarkdownTableLayout(wrapper, table);\n })\n .catch(() => {});\n return () => {\n disposed = true;\n layoutRef.current?.disconnect();\n layoutRef.current = undefined;\n };\n }, []);\n // ReactMarkdown recreates table children even when only later prose changes.\n // Remeasure genuine content changes without collapsing the expanded wrapper\n // between cleanup and the asynchronous observer-module import on each update.\n useEffect(() => {\n layoutRef.current?.measure();\n }, [children]);\n return (\n <div ref={wrapperRef} className=\"group/copy relative mt-3 max-w-full first:mt-0\">\n <div className=\"pointer-events-none absolute top-0 right-0 z-10\">\n <div className=\"pointer-events-auto\">\n <CopyButton\n text={() => tableElementToTsv(tableRef.current)}\n label=\"Copy table\"\n reveal=\"group-hover\"\n />\n </div>\n </div>\n <div className=\"overflow-x-auto\" tabIndex={0}>\n <table\n ref={tableRef}\n className={cn(\"w-full min-w-0 border-collapse text-og-base\", className)}\n {...props}\n >\n {children}\n </table>\n </div>\n </div>\n );\n}\n\nfunction MarkdownImpl({\n children,\n artifactHref,\n className,\n streaming = false,\n onSandboxFile,\n renderInteractiveBlock,\n renderImage,\n}: MarkdownProps) {\n // Tip-ink engine for THIS body: created on the first streaming render, kept\n // through a short linger after the stream ends (so the last age window can\n // finish), then dropped so settled bodies pay zero cost. Observing during\n // render is idempotent per text length — StrictMode double-renders are safe.\n const revealRef = useRef<StreamReveal | null>(null);\n const bodyRef = useRef<HTMLDivElement | null>(null);\n const [, bump] = useReducer((n: number) => n + 1, 0);\n const [settling, setSettling] = useState(false);\n const hadRevealRef = useRef(false);\n const now = typeof performance !== \"undefined\" ? performance.now() : Date.now();\n\n // Local settle breath only — View Transitions with `::view-transition-*(*)`\n // were fading the document root and felt like a full-page blink / focus loss\n // right as a turn ended (just before the next user message in the seed loop).\n const crystallize = (mutate: () => void) => {\n mutate();\n setSettling(true);\n bump();\n return setTimeout(() => setSettling(false), MARKDOWN_SETTLE_MS);\n };\n if (streaming && revealRef.current === null && !prefersReducedMotion()) {\n revealRef.current = createStreamReveal();\n }\n const reveal = revealRef.current;\n if (reveal !== null && streaming) {\n reveal.observe(children, now);\n }\n if (reveal !== null) {\n hadRevealRef.current = true;\n }\n const revealActive = reveal !== null && (streaming || reveal.hasActive(now));\n // The plugin walks the standard hast tree; a structural local type keeps the\n // module dependency-free, at the cost of this narrowing cast at the seam.\n const rehypePlugins =\n revealActive && reveal !== null\n ? ([[rehypeStreamReveal, { reveal, now }]] as unknown as NonNullable<\n Parameters<typeof ReactMarkdown>[0][\"rehypePlugins\"]\n >)\n : undefined;\n\n // Drop tip ink after trailing age windows finish. Same commit crystallizes:\n // ink spans → final plain markdown (soften off) + a short local settle breath.\n useEffect(() => {\n if (streaming || revealRef.current === null) {\n return;\n }\n let clearSettle: ReturnType<typeof setTimeout> | undefined;\n const timer = setTimeout(() => {\n const shouldSettle = hadRevealRef.current && !prefersReducedMotion();\n if (shouldSettle) {\n hadRevealRef.current = false;\n }\n if (shouldSettle) {\n clearSettle = crystallize(() => {\n revealRef.current = null;\n });\n } else {\n revealRef.current = null;\n bump();\n }\n }, REVEAL_LINGER_MS);\n return () => {\n clearTimeout(timer);\n if (clearSettle !== undefined) {\n clearTimeout(clearSettle);\n }\n };\n }, [streaming]);\n\n // No-reveal path (stream ended before any ink batch): soften already dropped\n // with `streaming`; keep the same local settle breath.\n const wasStreamingRef = useRef(streaming);\n useEffect(() => {\n const ended = wasStreamingRef.current && !streaming;\n wasStreamingRef.current = streaming;\n if (!ended || revealRef.current !== null || prefersReducedMotion()) {\n return;\n }\n setSettling(true);\n const timer = setTimeout(() => setSettling(false), MARKDOWN_SETTLE_MS);\n return () => clearTimeout(timer);\n }, [streaming]);\n\n // Soften unfinished markers for DISPLAY while the reveal pipeline is alive\n // (stream + linger) — not only while `streaming` — so task lists / tables /\n // fences don't snap to final GFM one commit before the crystallize morph.\n // Reveal identity still tracks the true source (`children`).\n const parseText = streaming || revealActive ? softenStreamingMarkdown(children) : children;\n const linkContext = useMemo(\n () => ({ onSandboxFile, artifactHref }),\n [onSandboxFile, artifactHref],\n );\n\n const interactiveContext = useMemo(\n () => ({\n source: children,\n streaming,\n ...(renderInteractiveBlock ? { render: renderInteractiveBlock } : {}),\n ...(renderImage ? { renderImage } : {}),\n }),\n [children, streaming, renderInteractiveBlock, renderImage],\n );\n\n // `min-w-0` lets the prose shrink inside flex parents (message bubbles) so\n // long links and code blocks wrap/scroll instead of forcing overflow.\n return (\n <MarkdownLinkContext.Provider value={linkContext}>\n <InteractiveContext.Provider value={interactiveContext}>\n <TooltipProvider delayDuration={400}>\n <div\n ref={bodyRef}\n className={cn(\n \"og-markdown-body min-w-0 break-words\",\n settling && \"og-markdown-settle\",\n className,\n )}\n >\n <ReactMarkdown\n remarkPlugins={[remarkGfm]}\n rehypePlugins={rehypePlugins}\n components={markdownComponents}\n urlTransform={markdownUrlTransform}\n >\n {parseText}\n </ReactMarkdown>\n </div>\n </TooltipProvider>\n </InteractiveContext.Provider>\n </MarkdownLinkContext.Provider>\n );\n}\n\n/** Memoized so streaming re-renders of the parent don't re-parse settled bodies. */\nexport const Markdown = memo(function SearchableMarkdown(props: MarkdownProps) {\n const [retained, setRetained] = useState<{ text: string; target: TimelineSearchTarget } | null>(\n null,\n );\n if (\n props.searchTarget &&\n (retained?.target !== props.searchTarget || retained.text !== props.children)\n ) {\n setRetained({ text: props.children, target: props.searchTarget });\n }\n // Closing Find only removes the highlight. Retain the source window so a huge\n // formatted message cannot replace it and unexpectedly move the reading point.\n const target = props.searchTarget ?? (retained?.text === props.children ? retained.target : null);\n if (!target) return <MarkdownImpl {...props} />;\n const offset = searchMatchOffset(props.children, target);\n if (offset < 0)\n return props.searchTarget ? (\n <div role=\"status\">This match is no longer in the message source.</div>\n ) : (\n <MarkdownImpl {...props} />\n );\n // Bound mounted text even for multi-megabyte messages. Do not split a UTF-16 pair\n // at excerpt edges; the selected range itself is validated against the source.\n let start = Math.max(0, offset - 240);\n let end = Math.min(props.children.length, offset + target.query.length + 240);\n if (start > 0 && /[\\uDC00-\\uDFFF]/.test(props.children[start]!)) start--;\n if (end < props.children.length && /[\\uDC00-\\uDFFF]/.test(props.children[end]!)) end++;\n const MatchTag = props.searchTarget ? \"mark\" : \"span\";\n return (\n <div className={cn(\"og-markdown-body min-w-0 break-words\", props.className)}>\n <div data-og-annotation-chrome=\"\" className=\"text-xs opacity-70\">\n Match in message source · Excerpt stays in place when Find closes\n <button\n type=\"button\"\n disabled={!!props.searchTarget}\n title={props.searchTarget ? \"Close Find to show the formatted message\" : undefined}\n className=\"ml-2 inline-flex min-h-7 items-center rounded-og-sm px-1.5 text-og-xs font-medium text-og-fg-muted outline-hidden hover:text-og-fg focus-visible:ring-2 focus-visible:ring-og-accent/45 pointer-coarse:min-h-11 disabled:opacity-50\"\n onClick={() => setRetained(null)}\n >\n Show formatted message\n </button>\n </div>\n <div style={{ whiteSpace: \"pre-wrap\", overflowWrap: \"anywhere\" }}>\n {start > 0 ? \"…\" : \"\"}\n {props.children.slice(start, offset)}\n <MatchTag\n data-og-search-occurrence={target.occurrence ?? 0}\n data-og-search-sequence={target.sequence}\n data-og-search-query={target.query}\n data-og-search-offset={offset}\n >\n {props.children.slice(offset, offset + target.query.length)}\n </MatchTag>\n {props.children.slice(offset + target.query.length, end)}\n {end < props.children.length ? \"…\" : \"\"}\n </div>\n </div>\n );\n});\n","import { flushSync } from \"react-dom\";\n\n/**\n * Live `prefers-reduced-motion` check for JS-driven motion (scroll glides,\n * streaming word reveals). CSS animations are already neutralized globally in\n * styles/index.css; this lets the imperative paths skip their work entirely.\n */\nlet query: MediaQueryList | null | undefined;\n\nexport function prefersReducedMotion(): boolean {\n if (query === undefined) {\n query =\n typeof window !== \"undefined\" && typeof window.matchMedia === \"function\"\n ? window.matchMedia(\"(prefers-reduced-motion: reduce)\")\n : null;\n }\n return query?.matches ?? false;\n}\n\n/** Test seam: forget the cached MediaQueryList so a stubbed matchMedia applies. */\nexport function resetReducedMotionCache(): void {\n query = undefined;\n}\n\ntype ViewTransitionResult = { finished: Promise<void> };\ntype ViewTransitionCallback = () => void;\ntype ViewTransitionOptions = {\n update: ViewTransitionCallback;\n types?: string[];\n};\ntype ViewTransitionStarter = {\n (callback: ViewTransitionCallback): ViewTransitionResult;\n (options: ViewTransitionOptions): ViewTransitionResult;\n};\n\n/** Active VT type for markdown crystallize — scoped CSS, not a full-page blink. */\nexport const MARKDOWN_CRYSTALLIZE_VT_TYPE = \"og-md-crystallize\";\n\n/**\n * Run a DOM update inside the browser View Transition API when available.\n * Snapshots the before/after paint and cross-fades (or custom CSS morphs)\n * between them — the platform \"morph\" primitive React's experimental\n * `<ViewTransition>` wraps. Falls back to a synchronous update.\n */\nexport function runViewTransition(update: () => void, options?: { types?: string[] }): void {\n if (prefersReducedMotion()) {\n update();\n return;\n }\n const doc = typeof document !== \"undefined\" ? document : null;\n const start = doc\n ? ((doc as Document & { startViewTransition?: ViewTransitionStarter }).startViewTransition ??\n null)\n : null;\n if (typeof start !== \"function\") {\n update();\n return;\n }\n const types = options?.types;\n if (types && types.length > 0) {\n try {\n Reflect.apply(start, doc, [\n {\n update: () => {\n flushSync(update);\n },\n types,\n },\n ]);\n return;\n } catch {\n // Older engines only accept the callback form.\n }\n }\n Reflect.apply(start, doc, [\n () => {\n flushSync(update);\n },\n ]);\n}\n","/**\n * TEMP eyeball scale for vertical motion (tip-follow, settle fold, CSS).\n * Set back to `1` before landing — not a product knob.\n */\nexport const MOTION_INSPECT_SCALE = 1;\n","import { useEffect, useRef } from \"react\";\n\n/** Private loading surface: never receives or executes the unfinished fence. */\nexport function PreviewLoading() {\n const ref = useRef<HTMLCanvasElement>(null);\n useEffect(() => {\n const canvas = ref.current;\n if (!canvas) return;\n let ctx: CanvasRenderingContext2D | null;\n try {\n ctx = canvas.getContext(\"2d\");\n } catch {\n return; // The CSS grid and status remain when canvas is unavailable.\n }\n if (!ctx) return;\n const context = ctx;\n const motion = window.matchMedia?.(\"(prefers-reduced-motion: reduce)\");\n let frame: number | undefined;\n let time = 2;\n let last: number | undefined;\n let disposed = false;\n // Without visibility observation, keep a static surface rather than doing\n // unbounded work for previews that may be outside the viewport.\n let visible = false;\n const canAnimate = () => !disposed && visible && !document.hidden && motion?.matches === false;\n\n function draw() {\n const w = canvas!.clientWidth;\n const h = canvas!.clientHeight;\n if (!w || !h || document.hidden || disposed) return;\n const d = Math.min(window.devicePixelRatio || 1, 2);\n if (canvas!.width !== Math.round(w * d) || canvas!.height !== Math.round(h * d)) {\n canvas!.width = Math.round(w * d);\n canvas!.height = Math.round(h * d);\n }\n const style = getComputedStyle(canvas!);\n const ink = style.getPropertyValue(\"--_og-preview-ink\").trim() || \"#bca3ef\";\n const glowColor = style.getPropertyValue(\"--_og-preview-glow\").trim() || \"#8f6ccc\";\n context.setTransform(d, 0, 0, d, 0, 0);\n context.clearRect(0, 0, w, h);\n const cx = w * (0.5 + 0.13 * Math.sin(time * 0.21));\n const cy = h * (0.5 + 0.12 * Math.cos(time * 0.27));\n const glow = context.createRadialGradient(cx, cy, 0, cx, cy, Math.max(w, h) * 0.65);\n glow.addColorStop(0, glowColor);\n glow.addColorStop(1, \"transparent\");\n context.globalAlpha = 0.09;\n context.fillStyle = glow;\n context.fillRect(0, 0, w, h);\n const rows = Math.ceil(h / 18) + 7;\n const cols = Math.ceil(w / 18) + 7;\n const points = Array.from({ length: rows }, (_row, r) =>\n Array.from({ length: cols }, (_col, c) => {\n const x = (c - 3) * 18;\n const y = (r - 3) * 18;\n const dist = Math.hypot((x - cx) * 0.8, y - cy);\n const wave = Math.sin(dist * 0.032 - time * 0.95);\n return {\n x: x + ((x - cx) / Math.max(dist, 1)) * wave * 4,\n y: y + ((y - cy) / Math.max(dist, 1)) * wave * 4,\n l: Math.pow((wave + 1) / 2, 5),\n };\n }),\n );\n context.strokeStyle = ink;\n context.fillStyle = ink;\n context.lineWidth = 0.65;\n points.forEach((row, r) =>\n row.forEach((p, c) => {\n context.globalAlpha = 0.035 + p.l * 0.055;\n context.beginPath();\n if (c + 1 < cols) {\n context.moveTo(p.x, p.y);\n context.lineTo(row[c + 1]!.x, row[c + 1]!.y);\n }\n if (r + 1 < rows) {\n context.moveTo(p.x, p.y);\n context.lineTo(points[r + 1]![c]!.x, points[r + 1]![c]!.y);\n }\n context.stroke();\n context.globalAlpha = 0.15 + p.l * 0.55;\n context.beginPath();\n context.arc(p.x, p.y, 0.85 + p.l * 0.65, 0, Math.PI * 2);\n context.fill();\n }),\n );\n context.globalAlpha = 1;\n canvas!.dataset.painted = \"true\";\n }\n\n function stop() {\n if (frame !== undefined) window.cancelAnimationFrame(frame);\n frame = undefined;\n last = undefined;\n }\n function tick(stamp: number) {\n frame = undefined;\n if (!canAnimate()) return;\n if (last !== undefined) time += Math.min((stamp - last) / 1000, 0.05);\n last = stamp;\n draw();\n frame = window.requestAnimationFrame(tick);\n }\n function refresh() {\n stop();\n if (disposed || document.hidden || (!visible && intersection)) return;\n draw();\n if (canAnimate() && typeof window.requestAnimationFrame === \"function\") {\n frame = window.requestAnimationFrame(tick);\n }\n }\n const intersection =\n typeof IntersectionObserver === \"function\"\n ? new IntersectionObserver(([entry]) => {\n visible = entry?.isIntersecting === true;\n refresh();\n })\n : undefined;\n intersection?.observe(canvas);\n const resize = typeof ResizeObserver === \"function\" ? new ResizeObserver(refresh) : undefined;\n resize?.observe(canvas);\n // Observe only ancestors, not the document subtree. This also updates the\n // static reduced-motion canvas when a host changes its local theme.\n const theme =\n typeof MutationObserver === \"function\" ? new MutationObserver(refresh) : undefined;\n for (let ancestor = canvas.parentElement; ancestor; ancestor = ancestor.parentElement) {\n theme?.observe(ancestor, {\n attributes: true,\n attributeFilter: [\"class\", \"style\", \"data-og-theme\"],\n });\n }\n motion?.addEventListener?.(\"change\", refresh);\n document.addEventListener(\"visibilitychange\", refresh);\n window.addEventListener(\"resize\", refresh);\n // Old hosts get one still frame; observed surfaces wait until onscreen.\n if (!intersection) draw();\n return () => {\n disposed = true;\n stop();\n intersection?.disconnect();\n resize?.disconnect();\n theme?.disconnect();\n motion?.removeEventListener?.(\"change\", refresh);\n document.removeEventListener(\"visibilitychange\", refresh);\n window.removeEventListener(\"resize\", refresh);\n };\n }, []);\n\n return (\n <div className=\"og-preview-loading\">\n <canvas ref={ref} aria-hidden=\"true\" />\n <span className=\"og-preview-loading-label\">Preparing preview…</span>\n </div>\n );\n}\n","/**\n * Display-only softener for mid-stream markdown.\n *\n * While tokens are still arriving, incomplete markers (`**bold`, an open\n * fence, a half-finished link) would otherwise render as raw punctuation —\n * the hard \"breaking\" flash. This closes a small set of common unfinished\n * constructs so the parse looks settled; the SOURCE string is never mutated\n * (callers pass the softened copy only into the markdown parser).\n *\n * When the real closers land, the softened and true parses converge and the\n * crystallize breath covers the remount.\n */\n\nexport function softenStreamingMarkdown(source: string): string {\n if (source.length === 0) {\n return source;\n }\n let text = source;\n\n // Unclosed fenced code block: an odd count of ``` line-openers means the\n // trailing fence is still in flight. Always soft-close — including an empty\n // opener — so code chrome (padding/tint) appears once at ``` and lines only\n // grow the body. Stripping empty openers deferred chrome until the first\n // body line, which landed as one fat height spike (yank around fences).\n //\n // If the body already ends with `\\n`, append only ``` — a second `\\n``` would\n // invent a blank line that vanishes when the real closer lands (height shrink\n // → tip-follow compensate + mid soft-rise cancel = up/down flicker).\n if (countFences(text) % 2 === 1) {\n text += text.endsWith(\"\\n\") ? \"```\" : \"\\n```\";\n }\n\n // Work outside fenced blocks so we don't \"fix\" markers that are literal\n // code content.\n const segments = splitByFences(text);\n for (const segment of segments) {\n if (segment.kind === \"fence\") {\n continue;\n }\n segment.value = closeInlineMarkers(segment.value);\n }\n let result = segments.map((segment) => segment.value).join(\"\");\n // split(\"\\n\") drops a trailing newline into an empty final line; restore it\n // so balanced finished fences round-trip byte-identical.\n if (text.endsWith(\"\\n\") && !result.endsWith(\"\\n\")) {\n result += \"\\n\";\n }\n return result;\n}\n\nfunction countFences(text: string): number {\n let count = 0;\n for (const line of text.split(\"\\n\")) {\n if (/^ {0,3}```/.test(line)) {\n count += 1;\n }\n }\n return count;\n}\n\ntype Segment = { kind: \"prose\" | \"fence\"; value: string };\n\nfunction splitByFences(text: string): Segment[] {\n const segments: Segment[] = [];\n const lines = text.split(\"\\n\");\n let buf: string[] = [];\n let inFence = false;\n /**\n * `boundary`: prose → fence. `join` alone drops a blank line before ```\n * (`[\"a\",\"\"].join` → `\"a\\n\"`, need `\"a\\n\\n\"`), so append an extra `\\n`.\n * `end`: final segment. Trailing `\"\"` from `split(\"\\n\")` already encodes a\n * terminating newline — do NOT append another (that was inventing `\\n\\n`\n * on every streamed line and shrinking it away at crystallize).\n */\n const flush = (kind: Segment[\"kind\"], mode: \"boundary\" | \"end\") => {\n if (buf.length === 0) {\n return;\n }\n let value = buf.join(\"\\n\");\n if (mode === \"boundary\") {\n value += \"\\n\";\n }\n segments.push({ kind, value });\n buf = [];\n };\n for (const line of lines) {\n const isFence = /^ {0,3}```/.test(line);\n if (isFence) {\n if (inFence) {\n buf.push(line);\n flush(\"fence\", \"end\");\n inFence = false;\n } else {\n flush(\"prose\", \"boundary\");\n buf.push(line);\n inFence = true;\n }\n continue;\n }\n buf.push(line);\n }\n flush(inFence ? \"fence\" : \"prose\", \"end\");\n return segments;\n}\n\n/**\n * Close unmatched **, *, ~~, and ` in prose. Order matters: handle the\n * longer bold/strike markers before single `*` so we don't steal from `**`.\n */\nfunction closeInlineMarkers(prose: string): string {\n let text = prose;\n text = closeDelimiter(text, \"~~\");\n text = closeDelimiter(text, \"**\");\n text = closeDelimiter(text, \"__\");\n text = closeSingleBackticks(text);\n text = closeSingleAsterisks(text);\n text = closeUnfinishedLink(text);\n return text;\n}\n\nfunction closeDelimiter(text: string, delimiter: string): string {\n // Count occurrences that aren't escaped. Odd → append a closer.\n let count = 0;\n let i = 0;\n while (i < text.length) {\n if (text[i] === \"\\\\\" && i + 1 < text.length) {\n i += 2;\n continue;\n }\n if (text.startsWith(delimiter, i)) {\n count += 1;\n i += delimiter.length;\n continue;\n }\n i += 1;\n }\n return count % 2 === 1 ? insertBeforeTrailingNewlines(text, delimiter) : text;\n}\n\n/** Soft closers belong before trailing `\\n`s so restore/round-trip stay stable. */\nfunction insertBeforeTrailingNewlines(text: string, suffix: string): string {\n let end = text.length;\n while (end > 0 && text[end - 1] === \"\\n\") {\n end -= 1;\n }\n return text.slice(0, end) + suffix + text.slice(end);\n}\n\nfunction closeSingleBackticks(text: string): string {\n // Ignore ``` (fences already stripped) and even pairs. A trailing unmatched\n // ` is the common \"inline code still typing\" case.\n let count = 0;\n for (let i = 0; i < text.length; i += 1) {\n if (text[i] === \"\\\\\" && i + 1 < text.length) {\n i += 1;\n continue;\n }\n if (text[i] === \"`\") {\n // Skip triple backticks if any leaked into prose.\n if (text.startsWith(\"```\", i)) {\n i += 2;\n continue;\n }\n count += 1;\n }\n }\n return count % 2 === 1 ? insertBeforeTrailingNewlines(text, \"`\") : text;\n}\n\nfunction closeSingleAsterisks(text: string): string {\n // Count `*` that are NOT part of `**` (already handled). Odd → close italic.\n let count = 0;\n for (let i = 0; i < text.length; i += 1) {\n if (text[i] === \"\\\\\" && i + 1 < text.length) {\n i += 1;\n continue;\n }\n if (text[i] === \"*\" && text[i + 1] === \"*\") {\n i += 1;\n continue;\n }\n if (text[i] === \"*\") {\n count += 1;\n }\n }\n return count % 2 === 1 ? insertBeforeTrailingNewlines(text, \"*\") : text;\n}\n\nfunction closeUnfinishedLink(text: string): string {\n // `[label](url` without the trailing `)` — close it. Bare `[label` without\n // `](` is left alone (too ambiguous with footnotes / prose brackets).\n const open = text.lastIndexOf(\"](\");\n if (open === -1) {\n return text;\n }\n const after = text.slice(open + 2);\n if (after.includes(\")\")) {\n return text;\n }\n // Don't close if a later `[` started something else after.\n if (after.includes(\"[\")) {\n return text;\n }\n return insertBeforeTrailingNewlines(text, \")\");\n}\n","/* ----------------------------------------------------------------------------\n Streaming tip ink (age-window fade)\n\n Append-only markdown grows at the tip. Rather than staggering per-word\n onsets (glitter under fast models), each append batch is born at wall time\n and fades in over a fixed age window:\n\n opacity progress = clamp((now − birth) / INK_FADE_MS, 0, 1)\n\n Fast arrival → many recent characters still young → a large soft band.\n Slow arrival → only the tip is young → a tight tip wash.\n Same rule both ways; time does the work. No per-word stagger, no artificial\n content lag — layout always holds the truthful text; paint only.\n\n Identity is the character offset into the source. CSS animation-delay =\n birth − now (often negative) so mid-fade re-renders resume, never restart.\n Runs settle back to plain text once age ≥ INK_FADE_MS (zero span cost).\n -------------------------------------------------------------------------- */\n\nimport { MOTION_INSPECT_SCALE } from \"../lib/motion-inspect\";\n\n/** Keep in sync with `--og-duration-stream` / `.og-stream-ink` in styles. */\nexport const INK_FADE_MS = 420 * MOTION_INSPECT_SCALE;\n/** @deprecated Use {@link INK_FADE_MS}. */\nexport const WORD_ANIMATION_MS = INK_FADE_MS;\n\n/**\n * A first observation larger than this is pre-existing history (timeline\n * opened mid-stream), not live tip — it renders instantly.\n */\nconst INITIAL_ANIMATED_LIMIT = 400;\n\ntype InkBatch = {\n start: number;\n end: number;\n birth: number;\n};\n\nexport type StreamReveal = {\n /** Register the full current source text; new suffix becomes an ink batch. */\n observe(text: string, now: number): void;\n /**\n * CSS animation-delay (ms) for the run covering `offset`, or null when that\n * ink has settled and should render as plain text. Negative = resume mid-fade.\n */\n delayFor(offset: number, now: number): number | null;\n /** True while any batch is still inside its fade window. */\n hasActive(now: number): boolean;\n};\n\nexport function createStreamReveal(): StreamReveal {\n let batches: InkBatch[] = [];\n let revealedChars = -1;\n\n return {\n observe(text, now) {\n if (revealedChars === -1) {\n revealedChars = text.length > INITIAL_ANIMATED_LIMIT ? text.length : 0;\n }\n if (text.length <= revealedChars) {\n // Idempotent re-observation (StrictMode) or defensive no-op on shrink.\n revealedChars = Math.max(revealedChars, text.length);\n return;\n }\n batches.push({ start: revealedChars, end: text.length, birth: now });\n revealedChars = text.length;\n batches = batches.filter((batch) => batch.birth + INK_FADE_MS > now);\n },\n\n delayFor(offset, now) {\n const batch = findBatch(batches, offset);\n if (!batch) {\n return null;\n }\n const delay = batch.birth - now;\n return delay + INK_FADE_MS <= 0 ? null : delay;\n },\n\n hasActive(now) {\n const last = batches[batches.length - 1];\n return last !== undefined && last.birth + INK_FADE_MS > now;\n },\n };\n}\n\nfunction findBatch(batches: readonly InkBatch[], offset: number): InkBatch | null {\n let low = 0;\n let high = batches.length - 1;\n while (low <= high) {\n const mid = (low + high) >> 1;\n const batch = batches[mid]!;\n if (offset < batch.start) {\n high = mid - 1;\n } else if (offset >= batch.end) {\n low = mid + 1;\n } else {\n return batch;\n }\n }\n return null;\n}\n\n/* ----------------------------------------------------------------------------\n rehype: wrap still-fading source runs in `.og-stream-ink` spans.\n\n Contiguous offsets that share one birth (one append batch) become ONE span —\n a continuous ink surface, not per-word glitter. Code/pre stay plain. Nodes\n without source positions (syntax rewritten around them) render plain too.\n -------------------------------------------------------------------------- */\n\ntype HastText = {\n type: \"text\";\n value: string;\n position?: { start?: { offset?: number | undefined } | undefined } | undefined;\n};\n\ntype HastParent = {\n type: string;\n tagName?: string;\n value?: string;\n children?: HastChild[];\n};\n\ntype HastChild = (HastText | HastParent) & Record<string, unknown>;\n\nexport function rehypeStreamReveal(options: { reveal: StreamReveal; now: number }) {\n const { reveal, now } = options;\n return (tree: HastParent) => {\n walk(tree, false);\n };\n\n function walk(node: HastParent, insideVerbatim: boolean): void {\n if (!node.children) {\n return;\n }\n const verbatim = insideVerbatim || node.tagName === \"code\" || node.tagName === \"pre\";\n const next: HastChild[] = [];\n for (const child of node.children) {\n if (child.type === \"text\" && !verbatim) {\n next.push(...splitTextNode(child as HastText));\n continue;\n }\n walk(child as HastParent, verbatim);\n next.push(child);\n }\n node.children = next;\n }\n\n function splitTextNode(node: HastText): HastChild[] {\n const sourceOffset = node.position?.start?.offset;\n if (sourceOffset === undefined) {\n return [node as HastChild];\n }\n const value = node.value;\n const out: HastChild[] = [];\n let plain = \"\";\n const flushPlain = () => {\n if (plain.length > 0) {\n out.push({ type: \"text\", value: plain } as HastChild);\n plain = \"\";\n }\n };\n\n let i = 0;\n while (i < value.length) {\n const delay = reveal.delayFor(sourceOffset + i, now);\n let j = i + 1;\n while (j < value.length) {\n const nextDelay = reveal.delayFor(sourceOffset + j, now);\n if (nextDelay !== delay) {\n break;\n }\n j += 1;\n }\n const slice = value.slice(i, j);\n if (delay === null) {\n plain += slice;\n } else {\n flushPlain();\n out.push({\n type: \"element\",\n tagName: \"span\",\n properties: {\n className: [\"og-stream-ink\"],\n style: `animation-delay:${Math.round(delay)}ms`,\n },\n children: [{ type: \"text\", value: slice }],\n } as unknown as HastChild);\n }\n i = j;\n }\n flushPlain();\n return out.length > 0 ? out : [node as HastChild];\n }\n}\n"],"mappings":"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","names":["createContext","useContext","createContext","useContext","createContext","useContext","query","jsx","createContext","useContext","useEffect","useRef","useState","jsx","jsxs","useState","useRef","useEffect","createContext","useContext","useEffect","useRef","useState","useEffect","useRef","jsx","jsxs","Fragment","jsx","jsxs","createContext","useContext","useState","useRef","useEffect"]}
@@ -8,15 +8,6 @@ import {
8
8
  cn
9
9
  } from "./chunk-22KP6LR5.js";
10
10
 
11
- // src/lib/interaction-errors.ts
12
- import { OpenGeniApiError } from "@opengeni/sdk";
13
- function isNonRetryableInteractionError(error) {
14
- return error instanceof OpenGeniApiError && error.retryable === false;
15
- }
16
- function isSourcePlacementChangedError(error, resource) {
17
- return isNonRetryableInteractionError(error) && error.status === 409 && error.details?.interactionResource === resource && error.details?.interactionFailureCode === "source_placement_changed" && error.details?.interactionLifecycle === "lost";
18
- }
19
-
20
11
  // src/hooks/use-interaction-interventions.ts
21
12
  import { useCallback, useEffect as useEffect2, useRef as useRef2, useState } from "react";
22
13
 
@@ -232,6 +223,15 @@ function asError(cause) {
232
223
  return cause instanceof Error ? cause : new Error(String(cause));
233
224
  }
234
225
 
226
+ // src/lib/interaction-errors.ts
227
+ import { OpenGeniApiError } from "@opengeni/sdk";
228
+ function isNonRetryableInteractionError(error) {
229
+ return error instanceof OpenGeniApiError && error.retryable === false;
230
+ }
231
+ function isSourcePlacementChangedError(error, resource) {
232
+ return isNonRetryableInteractionError(error) && error.status === 409 && error.details?.interactionResource === resource && error.details?.interactionFailureCode === "source_placement_changed" && error.details?.interactionLifecycle === "lost";
233
+ }
234
+
235
235
  // src/components/interaction-intervention-banner.tsx
236
236
  import { CheckIcon, CircleAlertIcon, LoaderCircleIcon, XIcon } from "lucide-react";
237
237
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -321,9 +321,9 @@ function interventionTitle(kind) {
321
321
 
322
322
  export {
323
323
  useInteractionInvalidation,
324
+ useInteractionInterventions,
324
325
  isNonRetryableInteractionError,
325
326
  isSourcePlacementChangedError,
326
- useInteractionInterventions,
327
327
  InteractionInterventionBanner
328
328
  };
329
- //# sourceMappingURL=chunk-H5YGYNXS.js.map
329
+ //# sourceMappingURL=chunk-TV5PP2PE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/use-interaction-interventions.ts","../src/hooks/use-interaction-invalidation.ts","../src/lib/interaction-errors.ts","../src/components/interaction-intervention-banner.tsx"],"sourcesContent":["import type {\n CreateInteractionInterventionRequest,\n InteractionIntervention,\n InteractionInterventionMutationResponse,\n ResolveInteractionInterventionRequest,\n} from \"@opengeni/sdk/interaction\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { EmbeddedInterventionClientLike } from \"../client\";\nimport {\n type EmbeddedInterventionClientOverride,\n useEmbeddedInterventions,\n} from \"../session-context\";\nimport { useInteractionInvalidation } from \"./use-interaction-invalidation\";\n\nexport type UseInteractionInterventionsOptions = EmbeddedInterventionClientOverride & {\n enabled?: boolean | undefined;\n resourceKind?: \"browser_session\" | \"computer_session\" | undefined;\n resourceId?: string | null | undefined;\n includeSettled?: boolean | undefined;\n pollIntervalMs?: number | undefined;\n};\n\nexport type UseInteractionInterventionsResult = {\n interventions: InteractionIntervention[];\n loading: boolean;\n refreshing: boolean;\n mutating: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n create: (\n request: Omit<CreateInteractionInterventionRequest, \"operationId\"> & {\n operationId?: string;\n },\n ) => Promise<InteractionInterventionMutationResponse>;\n resolve: (\n interventionId: string,\n request: Omit<ResolveInteractionInterventionRequest, \"operationId\"> & {\n operationId?: string;\n },\n ) => Promise<InteractionInterventionMutationResponse>;\n};\n\n/** Human-action bridge shared by Browser and Computer surfaces. */\nexport function useInteractionInterventions(\n options: UseInteractionInterventionsOptions = {},\n): UseInteractionInterventionsResult {\n const { client, workspaceId, workspaceInteractionEvent, workspaceInteractionConnectionState } =\n useEmbeddedInterventions(options);\n const enabled = (options.enabled ?? true) && options.resourceId !== null;\n const resourceKind = options.resourceKind;\n const resourceId = options.resourceId ?? undefined;\n const includeSettled = options.includeSettled ?? false;\n const key = `${workspaceId}:${resourceKind ?? \"*\"}:${resourceId ?? \"*\"}:${includeSettled}`;\n const [state, setState] = useState(() => emptyState(key, enabled));\n const [mutationCount, setMutationCount] = useState(0);\n const requestRef = useRef<{ id: number; controller: AbortController | null }>({\n id: 0,\n controller: null,\n });\n const mountedRef = useRef(true);\n const visible = state.key === key ? state : emptyState(key, enabled);\n\n const cancelLoad = useCallback(() => {\n requestRef.current.controller?.abort();\n requestRef.current = { id: requestRef.current.id + 1, controller: null };\n }, []);\n\n const load = useCallback(\n async (foreground: boolean, replace = true): Promise<void> => {\n if (!enabled) return;\n if (requestRef.current.controller && !replace) return;\n const id = requestRef.current.id + 1;\n requestRef.current.controller?.abort();\n const controller = new AbortController();\n requestRef.current = { id, controller };\n if (foreground || replace) {\n setState((current) => ({\n ...(current.key === key ? current : emptyState(key, true)),\n loading: foreground,\n refreshing: !foreground,\n error: foreground ? null : current.error,\n }));\n }\n try {\n const response = await client.listInteractionInterventions(workspaceId, {\n ...(resourceKind ? { resourceKind } : {}),\n ...(resourceId ? { resourceId } : {}),\n includeSettled,\n signal: controller.signal,\n });\n if (!mountedRef.current || requestRef.current.id !== id) return;\n setState({\n key,\n interventions: sortInterventions(response.interventions),\n loading: false,\n refreshing: false,\n error: null,\n });\n } catch (cause) {\n if (controller.signal.aborted || !mountedRef.current || requestRef.current.id !== id)\n return;\n setState((current) => ({\n ...(current.key === key ? current : emptyState(key, true)),\n loading: false,\n refreshing: false,\n error: asError(cause),\n }));\n } finally {\n if (requestRef.current.id === id) requestRef.current = { id, controller: null };\n }\n },\n [client, enabled, includeSettled, key, resourceId, resourceKind, workspaceId],\n );\n\n const refresh = useCallback(async () => await load(false, true), [load]);\n\n useEffect(() => {\n mountedRef.current = true;\n if (!enabled) {\n cancelLoad();\n setState(emptyState(key, false));\n return;\n }\n void load(true);\n return () => {\n mountedRef.current = false;\n cancelLoad();\n };\n }, [cancelLoad, enabled, key, load]);\n\n useInteractionInvalidation({\n workspaceId,\n key,\n enabled,\n event: workspaceInteractionEvent,\n connectionState: workspaceInteractionConnectionState,\n refresh,\n fallbackPollIntervalMs: options.pollIntervalMs ?? 2_000,\n });\n\n const mutate = useCallback(async <T>(operation: () => Promise<T>): Promise<T> => {\n setMutationCount((count) => count + 1);\n try {\n return await operation();\n } finally {\n if (mountedRef.current) setMutationCount((count) => Math.max(0, count - 1));\n }\n }, []);\n\n const merge = useCallback(\n (intervention: InteractionIntervention) => {\n setState((current) => {\n if (current.key !== key) return current;\n const interventions = current.interventions.filter(\n (candidate) => candidate.id !== intervention.id,\n );\n const matches =\n (!resourceKind || intervention.resourceKind === resourceKind) &&\n (!resourceId || intervention.resourceId === resourceId);\n if (matches && (includeSettled || intervention.status === \"open\")) {\n interventions.push(intervention);\n }\n return {\n ...current,\n interventions: sortInterventions(interventions),\n error: null,\n };\n });\n },\n [includeSettled, key, resourceId, resourceKind],\n );\n\n const create = useCallback(\n async (\n request: Omit<CreateInteractionInterventionRequest, \"operationId\"> & {\n operationId?: string;\n },\n ): Promise<InteractionInterventionMutationResponse> =>\n await mutate(async () => {\n const response = await client.createInteractionIntervention(workspaceId, {\n ...request,\n operationId: request.operationId ?? crypto.randomUUID(),\n });\n merge(response.intervention);\n return response;\n }),\n [client, merge, mutate, workspaceId],\n );\n\n const resolve = useCallback(\n async (\n interventionId: string,\n request: Omit<ResolveInteractionInterventionRequest, \"operationId\"> & {\n operationId?: string;\n },\n ): Promise<InteractionInterventionMutationResponse> =>\n await mutate(async () => {\n const response = await client.resolveInteractionIntervention(workspaceId, interventionId, {\n ...request,\n operationId: request.operationId ?? crypto.randomUUID(),\n });\n merge(response.intervention);\n return response;\n }),\n [client, merge, mutate, workspaceId],\n );\n\n return {\n interventions: visible.interventions,\n loading: visible.loading,\n refreshing: visible.refreshing,\n mutating: mutationCount > 0,\n error: visible.error,\n refresh,\n create,\n resolve,\n };\n}\n\nfunction emptyState(key: string, loading: boolean) {\n return {\n key,\n interventions: [] as InteractionIntervention[],\n loading,\n refreshing: false,\n error: null as Error | null,\n };\n}\n\nfunction sortInterventions(\n interventions: readonly InteractionIntervention[],\n): InteractionIntervention[] {\n return [...interventions].sort(\n (left, right) => Date.parse(right.updatedAt) - Date.parse(left.updatedAt),\n );\n}\n\nfunction asError(cause: unknown): Error {\n return cause instanceof Error ? cause : new Error(String(cause));\n}\n\nexport type InteractionInterventionsClient = EmbeddedInterventionClientLike;\n","import type { StreamConnectionState, WorkspaceInteractionRevisionEvent } from \"@opengeni/sdk\";\nimport { useEffect, useRef } from \"react\";\nimport { usePageLiveActivity } from \"./internal\";\n\nexport type InteractionInvalidationOptions = {\n workspaceId: string;\n /** Distinguishes filtered projections of the same workspace catalog. */\n key: string;\n enabled: boolean;\n event: WorkspaceInteractionRevisionEvent | null;\n connectionState: StreamConnectionState | \"idle\" | \"error\";\n /** Catalog responses carry the workspace revision; live-state lists do not. */\n knownRevision?: number | undefined;\n refresh: () => Promise<void>;\n fallbackPollIntervalMs?: number | undefined;\n};\n\n/**\n * Refetch authoritative lists on the shared latest-revision stream. Poll only\n * while that stream is unavailable, preserving standalone-hook degradation\n * without multiplying healthy-provider traffic.\n */\nexport function useInteractionInvalidation(options: InteractionInvalidationOptions): void {\n const {\n workspaceId,\n key,\n enabled,\n event,\n connectionState,\n knownRevision,\n refresh,\n fallbackPollIntervalMs,\n } = options;\n const pageLive = usePageLiveActivity();\n const lastUnversionedEvent = useRef<{ key: string; sequence: number }>({\n key,\n sequence: 0,\n });\n const eventSequence = event?.workspaceId === workspaceId ? event.sequence : 0;\n\n useEffect(() => {\n if (!enabled || eventSequence <= 0) return;\n if (knownRevision !== undefined) {\n if (eventSequence > knownRevision) void refresh();\n return;\n }\n if (lastUnversionedEvent.current.key !== key) {\n lastUnversionedEvent.current = { key, sequence: 0 };\n }\n if (eventSequence <= lastUnversionedEvent.current.sequence) return;\n lastUnversionedEvent.current = { key, sequence: eventSequence };\n void refresh();\n }, [enabled, eventSequence, key, knownRevision, refresh]);\n\n useEffect(() => {\n if (!enabled || !pageLive || connectionState === \"live\") return;\n const interval = boundedPollInterval(fallbackPollIntervalMs ?? 3_000);\n const timer = setInterval(() => void refresh(), interval);\n return () => clearInterval(timer);\n }, [connectionState, enabled, fallbackPollIntervalMs, pageLive, refresh]);\n}\n\nfunction boundedPollInterval(value: number): number {\n if (!Number.isFinite(value)) return 3_000;\n return Math.min(60_000, Math.max(1_000, Math.floor(value)));\n}\n","import { OpenGeniApiError } from \"@opengeni/sdk\";\n\nexport function isNonRetryableInteractionError(error: unknown): error is OpenGeniApiError {\n return error instanceof OpenGeniApiError && error.retryable === false;\n}\n\nexport function isSourcePlacementChangedError(\n error: unknown,\n resource: \"browser_session\" | \"computer_session\",\n): error is OpenGeniApiError {\n return (\n isNonRetryableInteractionError(error) &&\n error.status === 409 &&\n error.details?.interactionResource === resource &&\n error.details?.interactionFailureCode === \"source_placement_changed\" &&\n error.details?.interactionLifecycle === \"lost\"\n );\n}\n","import type { InteractionIntervention } from \"@opengeni/sdk/interaction\";\nimport { CheckIcon, CircleAlertIcon, LoaderCircleIcon, XIcon } from \"lucide-react\";\nimport { cn } from \"../lib/cn\";\n\nexport type InteractionInterventionBannerProps = {\n interventions: InteractionIntervention[];\n activeTargetId?: string | null | undefined;\n mutating?: boolean | undefined;\n className?: string | undefined;\n onOpen?: ((intervention: InteractionIntervention) => void) | undefined;\n onResolve: (intervention: InteractionIntervention, outcome: \"completed\" | \"dismissed\") => void;\n};\n\n/** Compact durable human-action prompt shared by Browser and Computer viewers. */\nexport function InteractionInterventionBanner({\n interventions,\n activeTargetId,\n mutating = false,\n className,\n onOpen,\n onResolve,\n}: InteractionInterventionBannerProps) {\n const intervention = interventions[0];\n if (!intervention) return null;\n const targetOpen = activeTargetId === undefined || activeTargetId === intervention.targetId;\n return (\n <div\n className={cn(\n \"flex shrink-0 items-center gap-2 border-b border-og-status-waiting/25 bg-og-status-waiting/[0.07] px-3 py-2\",\n className,\n )}\n role=\"status\"\n >\n <span className=\"grid size-7 shrink-0 place-items-center rounded-og-sm bg-og-status-waiting/10 text-og-status-waiting\">\n <CircleAlertIcon className=\"size-3.5\" />\n </span>\n <span className=\"min-w-0 flex-1\">\n <span className=\"flex items-center gap-1.5 text-og-control font-medium text-og-fg\">\n {interventionTitle(intervention.kind)}\n {interventions.length > 1 ? (\n <span className=\"rounded-full bg-og-surface-2 px-1.5 py-0.5 text-[10px] font-normal text-og-muted\">\n +{interventions.length - 1}\n </span>\n ) : null}\n </span>\n <span className=\"block truncate text-og-xs text-og-muted\">{intervention.reason}</span>\n </span>\n {!targetOpen && onOpen ? (\n <button\n type=\"button\"\n onClick={() => onOpen(intervention)}\n disabled={mutating}\n className=\"h-7 shrink-0 rounded-og-sm border border-og-border bg-og-surface-1 px-2.5 text-og-control font-medium text-og-fg transition hover:bg-og-surface-2 disabled:opacity-50\"\n >\n Open\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={() => onResolve(intervention, \"completed\")}\n disabled={mutating}\n className=\"inline-flex h-7 shrink-0 items-center gap-1 rounded-og-sm bg-og-accent px-2.5 text-og-control font-medium text-og-accent-fg transition hover:brightness-105 disabled:opacity-50\"\n >\n {mutating ? (\n <LoaderCircleIcon className=\"size-3 animate-spin\" />\n ) : (\n <CheckIcon className=\"size-3\" />\n )}\n Done\n </button>\n )}\n <button\n type=\"button\"\n onClick={() => onResolve(intervention, \"dismissed\")}\n disabled={mutating}\n className=\"grid size-7 shrink-0 place-items-center rounded-og-sm text-og-muted transition hover:bg-og-surface-2 hover:text-og-fg disabled:opacity-50\"\n aria-label=\"Cancel request\"\n title=\"Cancel request\"\n >\n <XIcon className=\"size-3.5\" />\n </button>\n </div>\n );\n}\n\nfunction interventionTitle(kind: InteractionIntervention[\"kind\"]): string {\n switch (kind) {\n case \"manual_login\":\n return \"Sign in needed\";\n case \"mfa\":\n return \"Verification needed\";\n case \"external_action\":\n return \"Action needed\";\n case \"confirmation\":\n return \"Confirmation needed\";\n case \"other\":\n return \"Agent needs your help\";\n }\n}\n"],"mappings":"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","names":["useEffect","useRef","useRef","useEffect"]}