@mgamadeus/ddd-agents 0.1.0-alpha.25 → 0.1.0-alpha.26

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 (23) hide show
  1. package/dist/chat.js +6 -6
  2. package/dist/chunks/{ConversationMessageInputComposerWidgets-BxyaFBA6.js → ConversationMessageInputComposerWidgets-CzQnpW0h.js} +4 -4
  3. package/dist/chunks/{ConversationMessageInputComposerWidgets-BxyaFBA6.js.map → ConversationMessageInputComposerWidgets-CzQnpW0h.js.map} +1 -1
  4. package/dist/chunks/{WebSearchResultsConversationWidget-as3p5bHw.js → WebSearchResultsConversationWidget-tDhawe9f.js} +55 -50
  5. package/dist/chunks/{WebSearchResultsConversationWidget-as3p5bHw.js.map → WebSearchResultsConversationWidget-tDhawe9f.js.map} +1 -1
  6. package/dist/chunks/{canvas-reducers-Bfp_i7lc.js → canvas-reducers-BdIPZW_6.js} +2 -2
  7. package/dist/chunks/{canvas-reducers-Bfp_i7lc.js.map → canvas-reducers-BdIPZW_6.js.map} +1 -1
  8. package/dist/chunks/{canvasBodies-Cvu3l38p.js → canvasBodies-BiUt8MUG.js} +3 -3
  9. package/dist/chunks/{canvasBodies-Cvu3l38p.js.map → canvasBodies-BiUt8MUG.js.map} +1 -1
  10. package/dist/chunks/{widget-click-signal-sources-DulRkNvZ.js → widget-click-signal-sources-BR82QygL.js} +3 -3
  11. package/dist/chunks/{widget-click-signal-sources-DulRkNvZ.js.map → widget-click-signal-sources-BR82QygL.js.map} +1 -1
  12. package/dist/chunks/{widget-name-resolver-BGN3HpoG.js → widget-name-resolver-BaxKaRa3.js} +2 -2
  13. package/dist/chunks/{widget-name-resolver-BGN3HpoG.js.map → widget-name-resolver-BaxKaRa3.js.map} +1 -1
  14. package/dist/client-testing.js +3 -3
  15. package/dist/image-render.js +4 -4
  16. package/dist/index.js +3 -3
  17. package/dist/kit.js +2 -2
  18. package/dist/preview.js +5 -5
  19. package/dist/renderer.js +7 -7
  20. package/dist/types/entries/widgets.d.ts +1 -0
  21. package/dist/widgets.js +34 -32
  22. package/package.json +1 -1
  23. package/public-api.json +2 -0
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { c as canvasBehaviourOf } from "./canvas-behaviours-AvzKSYyv.js";
3
- import { s as sameCanvasIdentity } from "./WebSearchResultsConversationWidget-as3p5bHw.js";
3
+ import { s as sameCanvasIdentity } from "./WebSearchResultsConversationWidget-tDhawe9f.js";
4
4
  const ACCEPT = { kind: "accept" };
5
5
  const ignore = (reason) => ({ kind: "ignore", reason });
6
6
  const decideCanvasOpen = (behaviour, openCanvas, lastClosedCanvas, request) => {
@@ -85,4 +85,4 @@ export {
85
85
  applySetCanvasCreatedEntityId as e,
86
86
  decideCanvasOpen as f
87
87
  };
88
- //# sourceMappingURL=canvas-reducers-Bfp_i7lc.js.map
88
+ //# sourceMappingURL=canvas-reducers-BdIPZW_6.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"canvas-reducers-Bfp_i7lc.js","sources":["../../src/renderer/canvas/decide-canvas-open.ts","../../src/renderer/canvas/canvas-reducers.ts"],"sourcesContent":["import type { CanvasBehaviour } from \"./canvas-behaviours\";\nimport { CanvasOpenRequest, CanvasState, LastClosedCanvas, sameCanvasIdentity } from \"./canvas-state\";\n\n/**\n * Why an open request is ignored. Every reason is a sentence the shell could log:\n * - `unknownCanvas` no behaviour is declared for the id (a stale stored pointer, a typo)\n * - `noSurface` the host renders no split (`disableSplitView`), so there is nowhere to open\n * - `replay` a delivery-origin request for a message that was NOT just delivered — history\n * replay, a reload, scroll-back; rejected BEFORE anything is read or written\n * - `alreadyOpen` a delivery-origin request for the identity that is already open (the agent re-emits\n * an editor widget after a rebuild) — the open canvas is kept, never remounted\n * - `customerClosedIt` the customer closed exactly this identity and nothing has cleared the veto yet\n * - `noTakeoverOnNarrowViewport` a live delivery on a narrow viewport of a canvas that declares no overlay — the\n * card stays, the customer taps it\n */\nexport type CanvasOpenIgnoreReason =\n | \"unknownCanvas\"\n | \"noSurface\"\n | \"replay\"\n | \"alreadyOpen\"\n | \"customerClosedIt\"\n | \"noTakeoverOnNarrowViewport\";\n\nexport type CanvasOpenDecision =\n | { readonly kind: \"accept\" }\n | { readonly kind: \"ignore\"; readonly reason: CanvasOpenIgnoreReason };\n\nconst ACCEPT: CanvasOpenDecision = { kind: \"accept\" };\n\nconst ignore = (reason: CanvasOpenIgnoreReason): CanvasOpenDecision => ({ kind: \"ignore\", reason });\n\n/**\n * THE opening rule — the one place that answers \"may this canvas open now?\". Pure: it reads the behaviour, the open\n * canvas, the last closed one and the request, and returns Accept or Ignore. It never mutates; the reducer\n * (`canvas-reducers.ts`) applies an Accept. There is no merge rung: the same identity opened again from a card click\n * is a fresh Accept (remount), and the refresh contract of a pinned work surface is a value the body derives from the\n * message list, not a reducer merge.\n *\n * A `cardClick`, an `openedByPage` and a `restoredAfterReload` request always open (the customer, the page or the\n * stored pointer decided); only `deliveredInMessage` is judged.\n */\nexport const decideCanvasOpen = (\n behaviour: CanvasBehaviour | undefined,\n openCanvas: CanvasState | null,\n lastClosedCanvas: LastClosedCanvas | null,\n request: CanvasOpenRequest,\n): CanvasOpenDecision => {\n if (behaviour === undefined) return ignore(\"unknownCanvas\");\n if (!request.hostHasCanvasSurface) return ignore(\"noSurface\");\n if (request.origin !== \"deliveredInMessage\") return ACCEPT;\n\n // Replay is rejected first: a replayed message must not touch the open canvas in any way.\n if (!request.justDelivered) return ignore(\"replay\");\n if (openCanvas !== null && sameCanvasIdentity(openCanvas, request)) return ignore(\"alreadyOpen\");\n if (lastClosedCanvas !== null && lastClosedCanvas.closedByCustomer && sameCanvasIdentity(lastClosedCanvas, request)) {\n return ignore(\"customerClosedIt\");\n }\n if (!request.viewportViable && behaviour.narrowViewport === \"none\") return ignore(\"noTakeoverOnNarrowViewport\");\n return ACCEPT;\n};\n","import { canvasBehaviourOf } from \"./canvas-behaviours\";\nimport { CanvasOpenRequest, CanvasState, LastClosedCanvas, sameCanvasIdentity } from \"./canvas-state\";\nimport { CanvasOpenDecision, decideCanvasOpen } from \"./decide-canvas-open\";\n\n/**\n * The canvas part of a chat slice partition — the two fields every transition reads and writes. Both chat doors carry\n * them (`ConversationState`); the shell is shared, so the state must have the same shape in both partitions.\n */\nexport interface CanvasSliceState {\n canvas: CanvasState | null;\n lastClosedCanvas: LastClosedCanvas | null;\n /**\n * How many canvases this partition has mounted so far — the source of `mountGeneration`. Never reset by a close,\n * a send or a clear (and exempt from `resetState`), so a late asynchronous write from a body that was replaced,\n * cleared or belonged to the previous conversation can never match a new canvas's generation.\n */\n canvasMountCount: number;\n}\n\n/**\n * The lifetime state machine as REDUCERS. Hand-written, not generated setters:\n * a generated setter is a blind overwrite of a snapshot the caller read a render earlier, and two opens in one commit\n * would both see the same stale state. Each function mutates the Immer draft it receives and returns the decision it\n * applied, so the slice can expose them as case reducers and a spec can assert every transition on a plain object.\n */\n\nconst toLastClosed = (canvas: CanvasState, closedByCustomer: boolean): LastClosedCanvas => ({\n canvasId: canvas.canvasId,\n instanceId: canvas.instanceId,\n anchor: canvas.anchor,\n mountGeneration: canvas.mountGeneration,\n closedByCustomer,\n ...(canvas.createdEntityId !== undefined ? { createdEntityId: canvas.createdEntityId } : {}),\n});\n\n/** Next render key: strictly increasing for the partition's lifetime, so no two mounts ever share a generation. */\nconst nextMountGeneration = (state: CanvasSliceState): number => {\n state.canvasMountCount += 1;\n return state.canvasMountCount;\n};\n\n/**\n * Open a canvas — the ONE way to open, for all four origins. On Accept: the replaced canvas (a different identity)\n * becomes `lastClosedCanvas`; a `lastClosedCanvas` of the accepted identity is consumed (its veto is cleared — the\n * customer, the page or the restore decided to have it back); the created entity survives a re-open of the same\n * identity so a restored editor keeps its draft.\n */\nexport const applyOpenCanvas = (state: CanvasSliceState, request: CanvasOpenRequest): CanvasOpenDecision => {\n const decision = decideCanvasOpen(canvasBehaviourOf(request.canvasId), state.canvas, state.lastClosedCanvas, request);\n if (decision.kind === \"ignore\") return decision;\n\n const previous = state.canvas;\n const mountGeneration = nextMountGeneration(state);\n const carriedCreatedEntityId =\n request.createdEntityId ??\n (previous !== null && sameCanvasIdentity(previous, request) ? previous.createdEntityId : undefined) ??\n (state.lastClosedCanvas !== null && sameCanvasIdentity(state.lastClosedCanvas, request)\n ? state.lastClosedCanvas.createdEntityId\n : undefined);\n\n if (previous !== null && !sameCanvasIdentity(previous, request)) {\n state.lastClosedCanvas = toLastClosed(previous, false);\n } else if (state.lastClosedCanvas !== null && sameCanvasIdentity(state.lastClosedCanvas, request)) {\n state.lastClosedCanvas = null;\n }\n state.canvas = {\n canvasId: request.canvasId,\n instanceId: request.instanceId,\n anchor: request.anchor,\n origin: request.origin,\n mountGeneration,\n ...(carriedCreatedEntityId !== undefined ? { createdEntityId: carriedCreatedEntityId } : {}),\n };\n return decision;\n};\n\n/**\n * Close the open canvas but keep it re-openable: it becomes `lastClosedCanvas`. `byCustomer` marks the customer's own\n * close (the canvas close control) — a live delivery of the same identity is then ignored until a send, a card click\n * or a page hand-over. A shell-driven close (compact mode, the side panel opening, a narrow crossing) passes false.\n */\nexport const applyCloseCanvas = (state: CanvasSliceState, byCustomer: boolean): void => {\n if (state.canvas === null) return;\n state.lastClosedCanvas = toLastClosed(state.canvas, byCustomer);\n state.canvas = null;\n};\n\n/**\n * The SEND boundary: a new user message starts a new turn. A `turn` canvas is over for good — it leaves the open slot\n * AND `lastClosedCanvas` (the card in the transcript is the way back). A `pinned` canvas stays open,\n * and a closed pinned one stays re-openable with its customer veto cleared (the send is a clearing edge).\n */\nexport const applyResetCanvasForNewTurn = (state: CanvasSliceState): void => {\n if (state.canvas !== null && canvasBehaviourOf(state.canvas.canvasId)?.lifetime === \"turn\") {\n state.canvas = null;\n }\n if (state.lastClosedCanvas !== null) {\n if (canvasBehaviourOf(state.lastClosedCanvas.canvasId)?.lifetime === \"turn\") {\n state.lastClosedCanvas = null;\n } else if (state.lastClosedCanvas.closedByCustomer) {\n state.lastClosedCanvas = { ...state.lastClosedCanvas, closedByCustomer: false };\n }\n }\n};\n\n/** Forget the canvas entirely — a conversation switch, `closeAgent`, the modal unmount. Nothing is re-openable. */\nexport const applyClearCanvas = (state: CanvasSliceState): void => {\n state.canvas = null;\n state.lastClosedCanvas = null;\n};\n\nexport interface CanvasCreatedEntity {\n readonly canvasId: CanvasState[\"canvasId\"];\n readonly instanceId: string;\n /** The generation the body was mounted with — a late write from a body that was replaced meanwhile is dropped. */\n readonly mountGeneration: number;\n readonly createdEntityId: number;\n}\n\n/** A body created its entity (the post editor's draft): remember the id on the canvas it was created for. */\nexport const applySetCanvasCreatedEntityId = (state: CanvasSliceState, created: CanvasCreatedEntity): boolean => {\n const canvas = state.canvas;\n if (canvas === null || !sameCanvasIdentity(canvas, created) || canvas.mountGeneration !== created.mountGeneration) {\n return false;\n }\n state.canvas = { ...canvas, createdEntityId: created.createdEntityId };\n return true;\n};\n"],"names":[],"mappings":";;;AA2BA,MAAM,SAA6B,EAAE,MAAM,SAAA;AAE3C,MAAM,SAAS,CAAC,YAAwD,EAAE,MAAM,UAAU,OAAA;AAYnF,MAAM,mBAAmB,CAC9B,WACA,YACA,kBACA,YACuB;AACvB,MAAI,cAAc,OAAW,QAAO,OAAO,eAAe;AAC1D,MAAI,CAAC,QAAQ,qBAAsB,QAAO,OAAO,WAAW;AAC5D,MAAI,QAAQ,WAAW,qBAAsB,QAAO;AAGpD,MAAI,CAAC,QAAQ,cAAe,QAAO,OAAO,QAAQ;AAClD,MAAI,eAAe,QAAQ,mBAAmB,YAAY,OAAO,EAAG,QAAO,OAAO,aAAa;AAC/F,MAAI,qBAAqB,QAAQ,iBAAiB,oBAAoB,mBAAmB,kBAAkB,OAAO,GAAG;AACnH,WAAO,OAAO,kBAAkB;AAAA,EAClC;AACA,MAAI,CAAC,QAAQ,kBAAkB,UAAU,mBAAmB,OAAQ,QAAO,OAAO,4BAA4B;AAC9G,SAAO;AACT;ACjCA,MAAM,eAAe,CAAC,QAAqB,sBAAiD;AAAA,EAC1F,UAAU,OAAO;AAAA,EACjB,YAAY,OAAO;AAAA,EACnB,QAAQ,OAAO;AAAA,EACf,iBAAiB,OAAO;AAAA,EACxB;AAAA,EACA,GAAI,OAAO,oBAAoB,SAAY,EAAE,iBAAiB,OAAO,oBAAoB,CAAA;AAC3F;AAGA,MAAM,sBAAsB,CAAC,UAAoC;AAC/D,QAAM,oBAAoB;AAC1B,SAAO,MAAM;AACf;AAQO,MAAM,kBAAkB,CAAC,OAAyB,YAAmD;AAC1G,QAAM,WAAW,iBAAiB,kBAAkB,QAAQ,QAAQ,GAAG,MAAM,QAAQ,MAAM,kBAAkB,OAAO;AACpH,MAAI,SAAS,SAAS,SAAU,QAAO;AAEvC,QAAM,WAAW,MAAM;AACvB,QAAM,kBAAkB,oBAAoB,KAAK;AACjD,QAAM,yBACJ,QAAQ,oBACP,aAAa,QAAQ,mBAAmB,UAAU,OAAO,IAAI,SAAS,kBAAkB,YACxF,MAAM,qBAAqB,QAAQ,mBAAmB,MAAM,kBAAkB,OAAO,IAClF,MAAM,iBAAiB,kBACvB;AAEN,MAAI,aAAa,QAAQ,CAAC,mBAAmB,UAAU,OAAO,GAAG;AAC/D,UAAM,mBAAmB,aAAa,UAAU,KAAK;AAAA,EACvD,WAAW,MAAM,qBAAqB,QAAQ,mBAAmB,MAAM,kBAAkB,OAAO,GAAG;AACjG,UAAM,mBAAmB;AAAA,EAC3B;AACA,QAAM,SAAS;AAAA,IACb,UAAU,QAAQ;AAAA,IAClB,YAAY,QAAQ;AAAA,IACpB,QAAQ,QAAQ;AAAA,IAChB,QAAQ,QAAQ;AAAA,IAChB;AAAA,IACA,GAAI,2BAA2B,SAAY,EAAE,iBAAiB,uBAAA,IAA2B,CAAA;AAAA,EAAC;AAE5F,SAAO;AACT;AAOO,MAAM,mBAAmB,CAAC,OAAyB,eAA8B;AACtF,MAAI,MAAM,WAAW,KAAM;AAC3B,QAAM,mBAAmB,aAAa,MAAM,QAAQ,UAAU;AAC9D,QAAM,SAAS;AACjB;AAOO,MAAM,6BAA6B,CAAC,UAAkC;AAC3E,MAAI,MAAM,WAAW,QAAQ,kBAAkB,MAAM,OAAO,QAAQ,GAAG,aAAa,QAAQ;AAC1F,UAAM,SAAS;AAAA,EACjB;AACA,MAAI,MAAM,qBAAqB,MAAM;AACnC,QAAI,kBAAkB,MAAM,iBAAiB,QAAQ,GAAG,aAAa,QAAQ;AAC3E,YAAM,mBAAmB;AAAA,IAC3B,WAAW,MAAM,iBAAiB,kBAAkB;AAClD,YAAM,mBAAmB,EAAE,GAAG,MAAM,kBAAkB,kBAAkB,MAAA;AAAA,IAC1E;AAAA,EACF;AACF;AAGO,MAAM,mBAAmB,CAAC,UAAkC;AACjE,QAAM,SAAS;AACf,QAAM,mBAAmB;AAC3B;AAWO,MAAM,gCAAgC,CAAC,OAAyB,YAA0C;AAC/G,QAAM,SAAS,MAAM;AACrB,MAAI,WAAW,QAAQ,CAAC,mBAAmB,QAAQ,OAAO,KAAK,OAAO,oBAAoB,QAAQ,iBAAiB;AACjH,WAAO;AAAA,EACT;AACA,QAAM,SAAS,EAAE,GAAG,QAAQ,iBAAiB,QAAQ,gBAAA;AACrD,SAAO;AACT;"}
1
+ {"version":3,"file":"canvas-reducers-BdIPZW_6.js","sources":["../../src/renderer/canvas/decide-canvas-open.ts","../../src/renderer/canvas/canvas-reducers.ts"],"sourcesContent":["import type { CanvasBehaviour } from \"./canvas-behaviours\";\nimport { CanvasOpenRequest, CanvasState, LastClosedCanvas, sameCanvasIdentity } from \"./canvas-state\";\n\n/**\n * Why an open request is ignored. Every reason is a sentence the shell could log:\n * - `unknownCanvas` no behaviour is declared for the id (a stale stored pointer, a typo)\n * - `noSurface` the host renders no split (`disableSplitView`), so there is nowhere to open\n * - `replay` a delivery-origin request for a message that was NOT just delivered — history\n * replay, a reload, scroll-back; rejected BEFORE anything is read or written\n * - `alreadyOpen` a delivery-origin request for the identity that is already open (the agent re-emits\n * an editor widget after a rebuild) — the open canvas is kept, never remounted\n * - `customerClosedIt` the customer closed exactly this identity and nothing has cleared the veto yet\n * - `noTakeoverOnNarrowViewport` a live delivery on a narrow viewport of a canvas that declares no overlay — the\n * card stays, the customer taps it\n */\nexport type CanvasOpenIgnoreReason =\n | \"unknownCanvas\"\n | \"noSurface\"\n | \"replay\"\n | \"alreadyOpen\"\n | \"customerClosedIt\"\n | \"noTakeoverOnNarrowViewport\";\n\nexport type CanvasOpenDecision =\n | { readonly kind: \"accept\" }\n | { readonly kind: \"ignore\"; readonly reason: CanvasOpenIgnoreReason };\n\nconst ACCEPT: CanvasOpenDecision = { kind: \"accept\" };\n\nconst ignore = (reason: CanvasOpenIgnoreReason): CanvasOpenDecision => ({ kind: \"ignore\", reason });\n\n/**\n * THE opening rule — the one place that answers \"may this canvas open now?\". Pure: it reads the behaviour, the open\n * canvas, the last closed one and the request, and returns Accept or Ignore. It never mutates; the reducer\n * (`canvas-reducers.ts`) applies an Accept. There is no merge rung: the same identity opened again from a card click\n * is a fresh Accept (remount), and the refresh contract of a pinned work surface is a value the body derives from the\n * message list, not a reducer merge.\n *\n * A `cardClick`, an `openedByPage` and a `restoredAfterReload` request always open (the customer, the page or the\n * stored pointer decided); only `deliveredInMessage` is judged.\n */\nexport const decideCanvasOpen = (\n behaviour: CanvasBehaviour | undefined,\n openCanvas: CanvasState | null,\n lastClosedCanvas: LastClosedCanvas | null,\n request: CanvasOpenRequest,\n): CanvasOpenDecision => {\n if (behaviour === undefined) return ignore(\"unknownCanvas\");\n if (!request.hostHasCanvasSurface) return ignore(\"noSurface\");\n if (request.origin !== \"deliveredInMessage\") return ACCEPT;\n\n // Replay is rejected first: a replayed message must not touch the open canvas in any way.\n if (!request.justDelivered) return ignore(\"replay\");\n if (openCanvas !== null && sameCanvasIdentity(openCanvas, request)) return ignore(\"alreadyOpen\");\n if (lastClosedCanvas !== null && lastClosedCanvas.closedByCustomer && sameCanvasIdentity(lastClosedCanvas, request)) {\n return ignore(\"customerClosedIt\");\n }\n if (!request.viewportViable && behaviour.narrowViewport === \"none\") return ignore(\"noTakeoverOnNarrowViewport\");\n return ACCEPT;\n};\n","import { canvasBehaviourOf } from \"./canvas-behaviours\";\nimport { CanvasOpenRequest, CanvasState, LastClosedCanvas, sameCanvasIdentity } from \"./canvas-state\";\nimport { CanvasOpenDecision, decideCanvasOpen } from \"./decide-canvas-open\";\n\n/**\n * The canvas part of a chat slice partition — the two fields every transition reads and writes. Both chat doors carry\n * them (`ConversationState`); the shell is shared, so the state must have the same shape in both partitions.\n */\nexport interface CanvasSliceState {\n canvas: CanvasState | null;\n lastClosedCanvas: LastClosedCanvas | null;\n /**\n * How many canvases this partition has mounted so far — the source of `mountGeneration`. Never reset by a close,\n * a send or a clear (and exempt from `resetState`), so a late asynchronous write from a body that was replaced,\n * cleared or belonged to the previous conversation can never match a new canvas's generation.\n */\n canvasMountCount: number;\n}\n\n/**\n * The lifetime state machine as REDUCERS. Hand-written, not generated setters:\n * a generated setter is a blind overwrite of a snapshot the caller read a render earlier, and two opens in one commit\n * would both see the same stale state. Each function mutates the Immer draft it receives and returns the decision it\n * applied, so the slice can expose them as case reducers and a spec can assert every transition on a plain object.\n */\n\nconst toLastClosed = (canvas: CanvasState, closedByCustomer: boolean): LastClosedCanvas => ({\n canvasId: canvas.canvasId,\n instanceId: canvas.instanceId,\n anchor: canvas.anchor,\n mountGeneration: canvas.mountGeneration,\n closedByCustomer,\n ...(canvas.createdEntityId !== undefined ? { createdEntityId: canvas.createdEntityId } : {}),\n});\n\n/** Next render key: strictly increasing for the partition's lifetime, so no two mounts ever share a generation. */\nconst nextMountGeneration = (state: CanvasSliceState): number => {\n state.canvasMountCount += 1;\n return state.canvasMountCount;\n};\n\n/**\n * Open a canvas — the ONE way to open, for all four origins. On Accept: the replaced canvas (a different identity)\n * becomes `lastClosedCanvas`; a `lastClosedCanvas` of the accepted identity is consumed (its veto is cleared — the\n * customer, the page or the restore decided to have it back); the created entity survives a re-open of the same\n * identity so a restored editor keeps its draft.\n */\nexport const applyOpenCanvas = (state: CanvasSliceState, request: CanvasOpenRequest): CanvasOpenDecision => {\n const decision = decideCanvasOpen(canvasBehaviourOf(request.canvasId), state.canvas, state.lastClosedCanvas, request);\n if (decision.kind === \"ignore\") return decision;\n\n const previous = state.canvas;\n const mountGeneration = nextMountGeneration(state);\n const carriedCreatedEntityId =\n request.createdEntityId ??\n (previous !== null && sameCanvasIdentity(previous, request) ? previous.createdEntityId : undefined) ??\n (state.lastClosedCanvas !== null && sameCanvasIdentity(state.lastClosedCanvas, request)\n ? state.lastClosedCanvas.createdEntityId\n : undefined);\n\n if (previous !== null && !sameCanvasIdentity(previous, request)) {\n state.lastClosedCanvas = toLastClosed(previous, false);\n } else if (state.lastClosedCanvas !== null && sameCanvasIdentity(state.lastClosedCanvas, request)) {\n state.lastClosedCanvas = null;\n }\n state.canvas = {\n canvasId: request.canvasId,\n instanceId: request.instanceId,\n anchor: request.anchor,\n origin: request.origin,\n mountGeneration,\n ...(carriedCreatedEntityId !== undefined ? { createdEntityId: carriedCreatedEntityId } : {}),\n };\n return decision;\n};\n\n/**\n * Close the open canvas but keep it re-openable: it becomes `lastClosedCanvas`. `byCustomer` marks the customer's own\n * close (the canvas close control) — a live delivery of the same identity is then ignored until a send, a card click\n * or a page hand-over. A shell-driven close (compact mode, the side panel opening, a narrow crossing) passes false.\n */\nexport const applyCloseCanvas = (state: CanvasSliceState, byCustomer: boolean): void => {\n if (state.canvas === null) return;\n state.lastClosedCanvas = toLastClosed(state.canvas, byCustomer);\n state.canvas = null;\n};\n\n/**\n * The SEND boundary: a new user message starts a new turn. A `turn` canvas is over for good — it leaves the open slot\n * AND `lastClosedCanvas` (the card in the transcript is the way back). A `pinned` canvas stays open,\n * and a closed pinned one stays re-openable with its customer veto cleared (the send is a clearing edge).\n */\nexport const applyResetCanvasForNewTurn = (state: CanvasSliceState): void => {\n if (state.canvas !== null && canvasBehaviourOf(state.canvas.canvasId)?.lifetime === \"turn\") {\n state.canvas = null;\n }\n if (state.lastClosedCanvas !== null) {\n if (canvasBehaviourOf(state.lastClosedCanvas.canvasId)?.lifetime === \"turn\") {\n state.lastClosedCanvas = null;\n } else if (state.lastClosedCanvas.closedByCustomer) {\n state.lastClosedCanvas = { ...state.lastClosedCanvas, closedByCustomer: false };\n }\n }\n};\n\n/** Forget the canvas entirely — a conversation switch, `closeAgent`, the modal unmount. Nothing is re-openable. */\nexport const applyClearCanvas = (state: CanvasSliceState): void => {\n state.canvas = null;\n state.lastClosedCanvas = null;\n};\n\nexport interface CanvasCreatedEntity {\n readonly canvasId: CanvasState[\"canvasId\"];\n readonly instanceId: string;\n /** The generation the body was mounted with — a late write from a body that was replaced meanwhile is dropped. */\n readonly mountGeneration: number;\n readonly createdEntityId: number;\n}\n\n/** A body created its entity (the post editor's draft): remember the id on the canvas it was created for. */\nexport const applySetCanvasCreatedEntityId = (state: CanvasSliceState, created: CanvasCreatedEntity): boolean => {\n const canvas = state.canvas;\n if (canvas === null || !sameCanvasIdentity(canvas, created) || canvas.mountGeneration !== created.mountGeneration) {\n return false;\n }\n state.canvas = { ...canvas, createdEntityId: created.createdEntityId };\n return true;\n};\n"],"names":[],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useMemo, useCallback, useContext, createContext, useState } from "react";
4
- import { p as payloadHashOfInstanceId, E as useConversationWidgetContext, aq as parseConversationSearchResultsPayload, ar as WebSearchAnswer, as as WebSearchSourcesList, at as parseWebSearchResultsPayload, au as ENTITY_MENTION_WIDGET_NAME, av as buildEntityMentionFields, aw as normalizeEntityMentionFields, ax as entityMentionKey, ay as MAX_ENTITY_MENTIONS_PER_MESSAGE, az as toEntityMentionWireFields, F as useIsEmailWidgetRender, aA as ConversationWidgetButton } from "./WebSearchResultsConversationWidget-as3p5bHw.js";
5
- import { m as findWidgetFenceByPayloadHash, n as findWidgetFencesInMarkdown, K as readEntityDetailInstanceId, L as readMapNodeKeyOfInstanceId, M as findMapNodeInMarkdown, D as readOpenEntityActionTarget, N as canvasInstanceIdForEntityDetail, O as mapStyles, P as MapPresentationContext, e as ComposedViewRenderer } from "./widget-name-resolver-BGN3HpoG.js";
4
+ import { p as payloadHashOfInstanceId, E as useConversationWidgetContext, as as parseConversationSearchResultsPayload, at as WebSearchAnswer, au as WebSearchSourcesList, av as parseWebSearchResultsPayload, aw as ENTITY_MENTION_WIDGET_NAME, ax as buildEntityMentionFields, ay as normalizeEntityMentionFields, az as entityMentionKey, aA as MAX_ENTITY_MENTIONS_PER_MESSAGE, aB as toEntityMentionWireFields, F as useIsEmailWidgetRender, aC as ConversationWidgetButton } from "./WebSearchResultsConversationWidget-tDhawe9f.js";
5
+ import { m as findWidgetFenceByPayloadHash, n as findWidgetFencesInMarkdown, K as readEntityDetailInstanceId, L as readMapNodeKeyOfInstanceId, M as findMapNodeInMarkdown, D as readOpenEntityActionTarget, N as canvasInstanceIdForEntityDetail, O as mapStyles, P as MapPresentationContext, e as ComposedViewRenderer } from "./widget-name-resolver-BaxKaRa3.js";
6
6
  import { q as Text, a as ConversationSearchResultsList } from "./index-7Qc_3GbG.js";
7
7
  import { E as EntityDetailNameReportContext } from "./entity-detail-name-3A82gDAp.js";
8
8
  const useAnchorWidgetPayload = (canvas, anchorMessageMarkdown, widgetName) => useMemo(() => {
@@ -292,4 +292,4 @@ export {
292
292
  readEntityDetailBodyDefinitions as r,
293
293
  useAnchorWidgetPayload as u
294
294
  };
295
- //# sourceMappingURL=canvasBodies-Cvu3l38p.js.map
295
+ //# sourceMappingURL=canvasBodies-BiUt8MUG.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"canvasBodies-Cvu3l38p.js","sources":["../../src/renderer/canvas/use-anchor-widget-payload.ts","../../src/widgets/ConversationSearchResultsConversationWidget/ConversationSearchResultsCanvas.tsx","../../src/widgets/ConversationSearchResultsConversationWidget/ConversationSearchResultsCanvasBody.tsx","../../src/widgets/WebSearchResultsConversationWidget/WebSearchResultsCanvas.tsx","../../src/widgets/WebSearchResultsConversationWidget/WebSearchResultsCanvasBody.tsx","../../src/widgets/EntityMentionConversationWidget/shared/use-entity-mention-composer.ts","../../src/widgets/EntityMentionConversationWidget/EntityMentionAskButton/EntityMentionAskButton.tsx","../../src/kit/entity-detail/entity-detail-bodies.ts","../../src/kit/entity-detail/EntityDetailCanvasBody.tsx","../../src/kit/map-full-screen/MapFullScreenCanvasBody.tsx","../../src/renderer/canvas/canvasBodies.tsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport type { CanvasState } from \"./canvas-state\";\nimport { payloadHashOfInstanceId } from \"./canvas-state\";\nimport { findWidgetFenceByPayloadHash, findWidgetFencesInMarkdown } from \"./find-widget-fences-in-markdown\";\n\n/**\n * The payload string of the widget a message-anchored canvas was opened from: the fence of `widgetName` in the anchor\n * message whose payload hash the canvas instance id carries (`canvasInstanceIdForMessagePayload`); when the instance\n * id carries no hash (one canvas per message), the first fence of that name. Undefined for a page anchor, a missing\n * anchor message, or a message that no longer carries the fence — the body then renders nothing.\n */\nexport const useAnchorWidgetPayload = (\n canvas: CanvasState,\n anchorMessageMarkdown: string | null,\n widgetName: string,\n): string | undefined =>\n useMemo(() => {\n if (canvas.anchor.kind !== \"message\" || anchorMessageMarkdown === null) return undefined;\n const payloadHash = payloadHashOfInstanceId(canvas.instanceId);\n if (payloadHash !== undefined) {\n return findWidgetFenceByPayloadHash(anchorMessageMarkdown, widgetName, payloadHash)?.payload;\n }\n return findWidgetFencesInMarkdown(anchorMessageMarkdown).find((fence) => fence.name === widgetName)?.payload;\n }, [canvas.anchor, canvas.instanceId, anchorMessageMarkdown, widgetName]);\n","import { ConversationSearchResultsList, Text } from \"../../provider/components\";\nimport React from \"react\";\nimport { parseConversationSearchResultsPayload } from \"./shared/conversation-search-results-widget-payload\";\n\ntype ConversationSearchResultsCanvasProps = {\n query: string;\n groups: ReturnType<typeof parseConversationSearchResultsPayload>[\"groups\"];\n onOpenConversation: (conversationId: number, messageId?: number) => void;\n};\n\n// Canvas body styles — the same as every list canvas body, so the full-screen panel behaves the same:\n// a full-height flex column whose inner area SCROLLS (scrollStyle: flex 1 + overflowY auto — without it the content\n// overflows the panel with no scrollbar), plus a horizontal inset (contentStyle) because the results list is built\n// for the narrow inline chat column where the bubble provides the gutter — in the canvas there is no bubble, so it would\n// otherwise render edge-to-edge.\nconst canvasStyle: React.CSSProperties = {\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n minHeight: 0,\n background: \"var(--agents-bg)\",\n};\nconst scrollStyle: React.CSSProperties = {\n flex: 1,\n minHeight: 0,\n overflowY: \"auto\",\n};\nconst contentStyle: React.CSSProperties = {\n padding: \"16px 24px 24px\",\n};\n\n/** The canvas body — a titled wrapper around the same grouped results, scrollable + inset like the other canvas widgets. */\nexport const ConversationSearchResultsCanvas = (props: ConversationSearchResultsCanvasProps) => (\n <div style={canvasStyle} data-testid=\"conversation-search-results-widget-canvas\">\n <div style={scrollStyle}>\n <div style={contentStyle} data-testid=\"conversation-search-results-widget-canvas-results\">\n <Text\n role=\"title\"\n replacements={{ query: props.query }}\n testId=\"conversation-search-results-widget-canvas-title\"\n >\n {'Search results for \"%query%\"'}\n </Text>\n <ConversationSearchResultsList groups={props.groups} query={props.query} onOpenConversation={props.onOpenConversation} />\n </div>\n </div>\n </div>\n);\n","import React, { useMemo } from \"react\";\nimport type { CanvasBodyProps } from \"../../renderer/canvas/canvas-body-props\";\nimport { useConversationWidgetContext } from \"../../renderer/ConversationWidgetCallbacksContext\";\nimport { useAnchorWidgetPayload } from \"../../renderer/canvas/use-anchor-widget-payload\";\nimport { parseConversationSearchResultsPayload } from \"./shared/conversation-search-results-widget-payload\";\nimport { ConversationSearchResultsCanvas } from \"./ConversationSearchResultsCanvas\";\n\n/**\n * The `conversationSearchResults` CANVAS BODY (`canvasBodies.tsx`): re-reads the `ConversationSearchResults` fence of\n * the anchor message — the same payload the inline widget rendered from — and shows the grouped results. Nothing was\n * stored for it but the pointer. Renders nothing when the anchor message no longer carries the fence.\n */\nexport const ConversationSearchResultsCanvasBody = ({ canvas, anchorMessageMarkdown }: CanvasBodyProps) => {\n const { onOpenConversation } = useConversationWidgetContext();\n const payload = useAnchorWidgetPayload(canvas, anchorMessageMarkdown, \"ConversationSearchResults\");\n const parsed = useMemo(\n () => (payload === undefined ? null : parseConversationSearchResultsPayload(payload)),\n [payload],\n );\n if (parsed === null || parsed.groups.length === 0) return null;\n return (\n <ConversationSearchResultsCanvas\n query={parsed.query}\n groups={parsed.groups}\n onOpenConversation={(conversationId, messageId) => onOpenConversation?.(conversationId, messageId)}\n />\n );\n};\n","import { Text } from \"../../provider/components\";\nimport React from \"react\";\nimport { WebSearchResultSource } from \"./shared/web-search-results-widget-payload\";\nimport { WebSearchAnswer } from \"./WebSearchAnswer\";\nimport { WebSearchSourcesList } from \"./WebSearchSourcesList\";\n\ntype WebSearchResultsCanvasProps = {\n query: string;\n answer: string;\n sources: WebSearchResultSource[];\n provider?: string;\n};\n\n// Canvas body styles — mirror the ConversationSearchResults / listReviews drawer widgets: a full-height flex column\n// whose inner area SCROLLS (flex 1 + overflowY auto) with a horizontal inset (there is no chat bubble gutter in the canvas).\nconst canvasStyle: React.CSSProperties = {\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n minHeight: 0,\n background: \"var(--agents-bg)\",\n};\nconst scrollStyle: React.CSSProperties = { flex: 1, minHeight: 0, overflowY: \"auto\" };\nconst contentStyle: React.CSSProperties = { padding: \"16px 24px 24px\" };\n\n/** The canvas body — the full answer + sources, scrollable + inset like the other canvas widgets. */\nexport const WebSearchResultsCanvas = (props: WebSearchResultsCanvasProps) => (\n <div style={canvasStyle} data-testid=\"web-search-results-widget-canvas\">\n <div style={scrollStyle}>\n <div style={contentStyle}>\n {props.query ? (\n <Text\n role=\"title\"\n replacements={{ query: props.query }}\n testId=\"web-search-results-widget-canvas-title\"\n >\n {'\"%query%\"'}\n </Text>\n ) : (\n <Text role=\"title\" testId=\"web-search-results-widget-canvas-title\">\n Web search\n </Text>\n )}\n <div style={{ marginTop: 12 }}>\n <WebSearchAnswer answer={props.answer} />\n </div>\n <WebSearchSourcesList\n sources={props.sources}\n provider={props.provider}\n testId=\"web-search-results-widget-canvas\"\n />\n </div>\n </div>\n </div>\n);\n","import React, { useMemo } from \"react\";\nimport type { CanvasBodyProps } from \"../../renderer/canvas/canvas-body-props\";\nimport { useAnchorWidgetPayload } from \"../../renderer/canvas/use-anchor-widget-payload\";\nimport { parseWebSearchResultsPayload } from \"./shared/web-search-results-widget-payload\";\nimport { WebSearchResultsCanvas } from \"./WebSearchResultsCanvas\";\n\n/**\n * The `webSearchResults` CANVAS BODY (`canvasBodies.tsx`): re-reads the `WebSearchResults` fence of the anchor message\n * — the answer and its cited sources the inline widget rendered from — and shows them in the canvas. Renders nothing\n * when the anchor message no longer carries the fence.\n */\nexport const WebSearchResultsCanvasBody = ({ canvas, anchorMessageMarkdown }: CanvasBodyProps) => {\n const payload = useAnchorWidgetPayload(canvas, anchorMessageMarkdown, \"WebSearchResults\");\n const parsed = useMemo(() => (payload === undefined ? null : parseWebSearchResultsPayload(payload)), [payload]);\n if (parsed === null || (parsed.answer.trim() === \"\" && parsed.sources.length === 0)) return null;\n return (\n <WebSearchResultsCanvas\n query={parsed.query}\n answer={parsed.answer}\n sources={parsed.sources}\n provider={parsed.provider}\n />\n );\n};\n","import { useCallback } from \"react\";\nimport { useConversationWidgetContext } from \"../../../renderer/ConversationWidgetCallbacksContext\";\nimport {\n buildEntityMentionFields,\n ENTITY_MENTION_WIDGET_NAME,\n entityMentionKey,\n EntityMentionTarget,\n MAX_ENTITY_MENTIONS_PER_MESSAGE,\n normalizeEntityMentionFields,\n toEntityMentionWireFields,\n} from \"./entity-mention\";\n\nexport interface UseEntityMentionComposerResult {\n /** Whether this entity is already mentioned in the composer. */\n isMentioned: (target: EntityMentionTarget) => boolean;\n /**\n * Puts the entity into the composer as an `EntityMention` chip — once per entity (a second click is a no-op); past\n * {@link MAX_ENTITY_MENTIONS_PER_MESSAGE} the new entity takes the place of the oldest mention (one write: a remove\n * and an add in one event would both read the list before either). Sends nothing: the owner writes the question.\n * Returns false for a target that names no entity.\n */\n mentionEntity: (target: EntityMentionTarget) => boolean;\n}\n\n/**\n * The opener side of the `EntityMention` composer widget: what the detail panel's \"Ask about this\"\n * button calls. Reads and writes the composer widgets of the chat's widget context; the composer strip renders them.\n */\nexport const useEntityMentionComposer = (): UseEntityMentionComposerResult => {\n const { composerWidgets, addComposerWidget, replaceComposerWidgetFields } = useConversationWidgetContext();\n\n const findMentionWidgets = useCallback(\n () => composerWidgets.filter((composerWidget) => composerWidget.widgetName === ENTITY_MENTION_WIDGET_NAME),\n [composerWidgets],\n );\n\n const isMentioned = useCallback(\n (target: EntityMentionTarget) => {\n const mention = buildEntityMentionFields(target);\n if (mention === null) return false;\n return findMentionWidgets().some((composerWidget) => {\n const heldMention = normalizeEntityMentionFields(composerWidget.fields);\n return heldMention !== null && entityMentionKey(heldMention) === entityMentionKey(mention);\n });\n },\n [findMentionWidgets],\n );\n\n const mentionEntity = useCallback(\n (target: EntityMentionTarget) => {\n const mention = buildEntityMentionFields(target);\n if (mention === null) return false;\n if (isMentioned(target)) return true;\n const mentionWidgets = findMentionWidgets();\n if (mentionWidgets.length >= MAX_ENTITY_MENTIONS_PER_MESSAGE) {\n replaceComposerWidgetFields(mentionWidgets[0].composerWidgetId, toEntityMentionWireFields(mention));\n } else {\n addComposerWidget(ENTITY_MENTION_WIDGET_NAME, toEntityMentionWireFields(mention));\n }\n return true;\n },\n [isMentioned, findMentionWidgets, replaceComposerWidgetFields, addComposerWidget],\n );\n\n return { isMentioned, mentionEntity };\n};\n","import { useIsEmailWidgetRender } from \"../../../renderer/ConversationWidgetRenderContext\";\nimport { ConversationWidgetButton } from \"../../_shared/ConversationWidgetButton\";\nimport type { EntityMentionTarget } from \"../shared/entity-mention\";\nimport { useEntityMentionComposer } from \"../shared/use-entity-mention-composer\";\n\nexport interface EntityMentionAskButtonProps extends EntityMentionTarget {\n testId?: string;\n}\n\n/**\n * The detail panel's \"Ask about this\" button (ChatGPT's \"Anschlussfrage stellen\"):\n * puts the entity into the chat input as an `EntityMention` chip and sends NOTHING — the owner writes the question,\n * the chip rides in that message as a fence, and the backend reads which entity the question is about. Not a\n * next-step CTA: it posts no prompt. A second click on the same entity changes nothing; the\n * label turns into \"Added to your message\". Mounted by the `entityDetail` canvas (the shell or a domain body) with\n * the entity the panel shows; renders nothing in the e-mail capture or for a target that names no entity.\n */\nexport const EntityMentionAskButton = (props: EntityMentionAskButtonProps) => {\n const { entityType, entityId, label, agentSpaceKey, testId = \"entity-mention-ask\" } = props;\n const isEmailRender = useIsEmailWidgetRender();\n const { isMentioned, mentionEntity } = useEntityMentionComposer();\n const target = { entityType, entityId, label, agentSpaceKey };\n if (isEmailRender) return null;\n const isAlreadyMentioned = isMentioned(target);\n return (\n <ConversationWidgetButton\n variant=\"primary\"\n size=\"medium\"\n icon={isAlreadyMentioned ? \"check\" : \"message\"}\n disabled={isAlreadyMentioned}\n onClick={() => mentionEntity(target)}\n testId={testId}\n >\n {isAlreadyMentioned ? \"Added to your message\" : \"Ask about this\"}\n </ConversationWidgetButton>\n );\n};\n","import type React from \"react\";\nimport { createContext, useContext } from \"react\";\n\n/**\n * The bodies of the `entityDetail` canvas, one per entity type: every `*.entity-detail-body.tsx` below\n * `widgets/` that exports `entityDetailBody` registers itself by folder glob — an entity type is added with\n * the backend `<Type>EntityReferenceResolver` and this one file, and edits no list. A second body for one entity type\n * is a build error (thrown at module load).\n */\n\nexport interface EntityDetailBodyProps {\n entityType: string;\n /** The id as the backend resolved it (a row id or an external id). */\n entityId: string;\n /** The key of the agent space the chat is scoped to, for a body whose read endpoint is agent-space-scoped. */\n agentSpaceKey?: number;\n}\n\nexport interface EntityDetailBodyDefinition {\n /** The backend resolver's `ENTITY_TYPE`. */\n entityType: string;\n /** Self-hydrates the entity by id through its own read endpoint (the endpoint's entity rights apply). */\n component: React.ComponentType<EntityDetailBodyProps>;\n}\n\nconst bodyModules = import.meta.glob<{ entityDetailBody?: EntityDetailBodyDefinition }>(\n [\"../../widgets/**/*.entity-detail-body.tsx\", \"../**/*.entity-detail-body.tsx\"],\n { eager: true },\n);\n\nexport const buildEntityDetailBodyRegistry = (\n definitions: EntityDetailBodyDefinition[],\n): Readonly<Record<string, EntityDetailBodyDefinition>> => {\n const registry: Record<string, EntityDetailBodyDefinition> = {};\n for (const definition of definitions) {\n if (Object.prototype.hasOwnProperty.call(registry, definition.entityType)) {\n throw new Error(`Two entity detail bodies register the entity type \"${definition.entityType}\".`);\n }\n registry[definition.entityType] = definition;\n }\n return registry;\n};\n\n/** The body definitions a set of `*.entity-detail-body.tsx` modules export (the package's or an app's glob). */\nexport const readEntityDetailBodyDefinitions = (\n modules: Record<string, { entityDetailBody?: EntityDetailBodyDefinition }>,\n): EntityDetailBodyDefinition[] =>\n Object.values(modules)\n .map((bodyModule) => bodyModule.entityDetailBody)\n .filter((definition): definition is EntityDetailBodyDefinition => definition !== undefined);\n\n/** Entity type → body: the package's folder, then what an app registers (`registerEntityDetailBodies`). */\nexport const entityDetailBodies: Record<string, EntityDetailBodyDefinition> = {\n ...buildEntityDetailBodyRegistry(readEntityDetailBodyDefinitions(bodyModules)),\n};\n\n/** Adds an app's entity detail bodies. Runs outside React, before the first render; an entity type registers once. */\nexport const registerEntityDetailBodies = (definitions: readonly EntityDetailBodyDefinition[]): void => {\n Object.assign(\n entityDetailBodies,\n buildEntityDetailBodyRegistry([...Object.values(entityDetailBodies), ...definitions]),\n );\n};\n\n/** The body registered for one entity type (the package's or an app's), or undefined when none is. */\nexport const getEntityDetailBody = (entityType: string): EntityDetailBodyDefinition | undefined =>\n Object.prototype.hasOwnProperty.call(entityDetailBodies, entityType) ? entityDetailBodies[entityType] : undefined;\n\n/** The registry the canvas body uses — the folder's by default; a spec or a story may provide stubs. */\nexport const EntityDetailBodiesContext =\n createContext<Readonly<Record<string, EntityDetailBodyDefinition>>>(entityDetailBodies);\n\nexport const useEntityDetailBodies = () => useContext(EntityDetailBodiesContext);\n","import { Text } from \"../../provider/components\";\nimport { useCallback, useState } from \"react\";\nimport type { CanvasBodyProps } from \"../../renderer/canvas/canvas-body-props\";\nimport { readEntityDetailInstanceId } from \"../../renderer/canvas/entity-detail-instance\";\nimport { EntityMentionAskButton } from \"../../widgets/EntityMentionConversationWidget/EntityMentionAskButton/EntityMentionAskButton\";\nimport { useEntityDetailBodies } from \"./entity-detail-bodies\";\nimport { EntityDetailNameReportContext } from \"./entity-detail-name\";\nimport styles from \"./EntityDetailCanvasBody.module.scss\";\n\n/**\n * The `entityDetail` CANVAS BODY (`canvasBodies.tsx`): the shell around one entity's detail. It reads\n * WHICH entity from the canvas instance id (`entityDetail:<entityType>:<id>`) and mounts the body registered for that\n * entity type; the body self-hydrates by id. An entity type without a body renders a short notice instead of nothing,\n * so a click never lands on an empty pane. \"Ask about this\" stays at the pane's foot while the detail scrolls and\n * names the entity in its chip once the body reported the name (`useReportEntityDetailName`).\n */\nexport const EntityDetailCanvasBody = ({ canvas, agentSpaceKey }: CanvasBodyProps) => {\n const bodies = useEntityDetailBodies();\n const [reportedEntity, setReportedEntity] = useState<{ instanceId: string; name: string | undefined }>();\n const reportEntityName = useCallback(\n (entityName: string | undefined) => setReportedEntity({ instanceId: canvas.instanceId, name: entityName }),\n [canvas.instanceId],\n );\n const target = readEntityDetailInstanceId(canvas.instanceId);\n if (target === undefined) return null;\n const definition = Object.prototype.hasOwnProperty.call(bodies, target.entityType)\n ? bodies[target.entityType]\n : undefined;\n if (!definition) {\n return (\n <div className={styles.entityDetail} data-testid=\"entity-detail-canvas\">\n <Text role=\"help\" color=\"var(--agents-fg-light)\" testId=\"entity-detail-canvas-unsupported\">\n {\"This detail cannot be shown here yet.\"}\n </Text>\n </div>\n );\n }\n const Body = definition.component;\n // A name reported for the previous entity never labels the next one's chip.\n const entityName = reportedEntity?.instanceId === canvas.instanceId ? reportedEntity.name : undefined;\n return (\n <div className={styles.entityDetail} data-testid=\"entity-detail-canvas\">\n <EntityDetailNameReportContext.Provider value={reportEntityName}>\n <Body entityType={target.entityType} entityId={target.entityId} agentSpaceKey={agentSpaceKey} />\n </EntityDetailNameReportContext.Provider>\n <div className={styles.entityDetailFooter}>\n <EntityMentionAskButton\n entityType={target.entityType}\n entityId={target.entityId}\n label={entityName}\n agentSpaceKey={agentSpaceKey}\n />\n </div>\n </div>\n );\n};\n","import { Text } from \"../../provider/components\";\nimport { useCallback, useMemo } from \"react\";\nimport type { CanvasBodyProps } from \"../../renderer/canvas/canvas-body-props\";\nimport { useConversationWidgetContext } from \"../../renderer/ConversationWidgetCallbacksContext\";\nimport {\n canvasInstanceIdForEntityDetail,\n readOpenEntityActionTarget,\n} from \"../../renderer/canvas/entity-detail-instance\";\nimport type {\n ComposedViewAction,\n ComposedViewTree,\n} from \"../../renderer/widget-tree/composed-view-tree\";\nimport type { ExpressionValue } from \"../../renderer/widget-tree/expression/evaluate-expression\";\nimport { ComposedViewRenderer } from \"../ComposedViewRenderer\";\nimport { findMapNodeInMarkdown, readMapNodeKeyOfInstanceId } from \"../components/map/map-full-screen-instance\";\nimport { MapPresentationContext } from \"../components/map/map-presentation-context\";\nimport mapStyles from \"../components/map/Map.module.scss\";\n\n/** The body reports nothing of its own: the inline block already reported this map to the model. */\nconst ignoreRenderReport = () => {};\n\n/**\n * The `mapFullScreen` CANVAS BODY (`canvasBodies.tsx`): one `Map` node of the anchor\n * message, full screen — the map filling the pane beside the list of its points. It finds the node through the\n * message's `ComposedView` fences and the node key its instance id carries (`map-full-screen-instance.ts`), then\n * renders it through the same `ComposedViewRenderer` with the node's own tree, so its bindings (`$data`, `$from`,\n * `$source`, `$computed`, `$state` at its initial value) and its actions resolve exactly as inline; the\n * `MapPresentationContext` switches the node to its full-screen layout. A point's `openEntity` action opens the\n * `entityDetail` canvas on the same anchor message (it replaces this canvas, as a click on an inline point would).\n */\nexport const MapFullScreenCanvasBody = (props: CanvasBodyProps) => {\n const { canvas, anchorMessageMarkdown, agentSpaceKey } = props;\n const { openCanvas } = useConversationWidgetContext();\n const mapNodeKey = readMapNodeKeyOfInstanceId(canvas.instanceId);\n\n const mapTree = useMemo<ComposedViewTree | undefined>(() => {\n if (anchorMessageMarkdown === null || mapNodeKey === undefined) return undefined;\n const mapNodeInComposedView = findMapNodeInMarkdown(anchorMessageMarkdown, mapNodeKey);\n if (!mapNodeInComposedView) return undefined;\n // The map alone, with its tree's state, data, computed values and actions; the tree's findings were the inline\n // block's to report.\n return { ...mapNodeInComposedView.tree, title: null, view: mapNodeInComposedView.mapNode, findings: [] };\n }, [anchorMessageMarkdown, mapNodeKey]);\n\n const anchor = canvas.anchor;\n const onOpenEntity = useCallback(\n (action: ComposedViewAction, currentRow?: ExpressionValue) => {\n const target = readOpenEntityActionTarget(action, currentRow);\n if (target === undefined || anchor.kind !== \"message\") return;\n openCanvas({\n canvasId: \"entityDetail\",\n instanceId: canvasInstanceIdForEntityDetail(target),\n anchor,\n origin: \"cardClick\",\n justDelivered: false,\n });\n },\n [openCanvas, anchor],\n );\n\n if (!mapTree) {\n return (\n <div\n className={mapStyles.fullScreenNotice}\n data-canvas-instance={canvas.instanceId}\n data-testid=\"map-full-screen-canvas-unavailable\"\n >\n <Text role=\"help\" color=\"var(--agents-fg-light)\">\n {\"This map is no longer available.\"}\n </Text>\n </div>\n );\n }\n return (\n <div\n className={mapStyles.fullScreenBody}\n data-canvas-instance={canvas.instanceId}\n data-testid=\"map-full-screen-canvas\"\n >\n <MapPresentationContext.Provider value=\"fullScreen\">\n <ComposedViewRenderer\n tree={mapTree}\n interactive\n isEmailRender={false}\n messageId={anchor.kind === \"message\" ? anchor.messageId : undefined}\n agentSpaceKey={agentSpaceKey}\n onReport={ignoreRenderReport}\n onOpenEntity={onOpenEntity}\n testId=\"map-full-screen-canvas-view\"\n />\n </MapPresentationContext.Provider>\n </div>\n );\n};\n","import type React from \"react\";\nimport type { CanvasId } from \"./canvas-behaviours\";\nimport type { CanvasBodyProps } from \"./canvas-body-props\";\nimport { ConversationSearchResultsCanvasBody } from \"../../widgets/ConversationSearchResultsConversationWidget/ConversationSearchResultsCanvasBody\";\nimport { WebSearchResultsCanvasBody } from \"../../widgets/WebSearchResultsConversationWidget/WebSearchResultsCanvasBody\";\nimport { EntityDetailCanvasBody } from \"../../kit/entity-detail/EntityDetailCanvasBody\";\nimport { MapFullScreenCanvasBody } from \"../../kit/map-full-screen/MapFullScreenCanvasBody\";\n\nexport type { CanvasBodyProps } from \"./canvas-body-props\";\nexport { canvasFeatureFlagOf } from \"./canvas-feature-flags\";\n\n/**\n * THE CANVAS BODIES — canvas id → the React component the shell mounts in the canvas pane. The one module that knows\n * both the canvas ids and React components, and it is imported ONLY by `CanvasPane` (the shell), never by\n * the slice, the behaviours or a widget — that is the import layering that keeps the store-init temporal-dead-zone\n * cycle away. The bodies themselves are small: a body that hosts a heavy page (an app's list page or editor)\n * lazy-loads that page, exactly as its widget did. Every canvas has a body; nothing\n * else hands the pane a rendered node.\n *\n * The package's own bodies are listed here; an app adds the bodies of its canvases with `registerCanvasBodies` before\n * the first render. The object is filled in place, so the shell reads every registration.\n */\nexport const canvasBodies = {\n conversationSearchResults: ConversationSearchResultsCanvasBody,\n webSearchResults: WebSearchResultsCanvasBody,\n entityDetail: EntityDetailCanvasBody,\n mapFullScreen: MapFullScreenCanvasBody,\n} as Record<CanvasId, React.ComponentType<CanvasBodyProps>>;\n\n/** Adds an app's canvas bodies (canvas id → body). Runs outside React, before the first render. */\nexport const registerCanvasBodies = (\n bodies: Readonly<Partial<Record<CanvasId, React.ComponentType<CanvasBodyProps>>>>,\n): void => {\n Object.assign(canvasBodies, bodies);\n};\n"],"names":["canvasStyle","scrollStyle","contentStyle","entityName"],"mappings":"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"}
1
+ {"version":3,"file":"canvasBodies-BiUt8MUG.js","sources":["../../src/renderer/canvas/use-anchor-widget-payload.ts","../../src/widgets/ConversationSearchResultsConversationWidget/ConversationSearchResultsCanvas.tsx","../../src/widgets/ConversationSearchResultsConversationWidget/ConversationSearchResultsCanvasBody.tsx","../../src/widgets/WebSearchResultsConversationWidget/WebSearchResultsCanvas.tsx","../../src/widgets/WebSearchResultsConversationWidget/WebSearchResultsCanvasBody.tsx","../../src/widgets/EntityMentionConversationWidget/shared/use-entity-mention-composer.ts","../../src/widgets/EntityMentionConversationWidget/EntityMentionAskButton/EntityMentionAskButton.tsx","../../src/kit/entity-detail/entity-detail-bodies.ts","../../src/kit/entity-detail/EntityDetailCanvasBody.tsx","../../src/kit/map-full-screen/MapFullScreenCanvasBody.tsx","../../src/renderer/canvas/canvasBodies.tsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport type { CanvasState } from \"./canvas-state\";\nimport { payloadHashOfInstanceId } from \"./canvas-state\";\nimport { findWidgetFenceByPayloadHash, findWidgetFencesInMarkdown } from \"./find-widget-fences-in-markdown\";\n\n/**\n * The payload string of the widget a message-anchored canvas was opened from: the fence of `widgetName` in the anchor\n * message whose payload hash the canvas instance id carries (`canvasInstanceIdForMessagePayload`); when the instance\n * id carries no hash (one canvas per message), the first fence of that name. Undefined for a page anchor, a missing\n * anchor message, or a message that no longer carries the fence — the body then renders nothing.\n */\nexport const useAnchorWidgetPayload = (\n canvas: CanvasState,\n anchorMessageMarkdown: string | null,\n widgetName: string,\n): string | undefined =>\n useMemo(() => {\n if (canvas.anchor.kind !== \"message\" || anchorMessageMarkdown === null) return undefined;\n const payloadHash = payloadHashOfInstanceId(canvas.instanceId);\n if (payloadHash !== undefined) {\n return findWidgetFenceByPayloadHash(anchorMessageMarkdown, widgetName, payloadHash)?.payload;\n }\n return findWidgetFencesInMarkdown(anchorMessageMarkdown).find((fence) => fence.name === widgetName)?.payload;\n }, [canvas.anchor, canvas.instanceId, anchorMessageMarkdown, widgetName]);\n","import { ConversationSearchResultsList, Text } from \"../../provider/components\";\nimport React from \"react\";\nimport { parseConversationSearchResultsPayload } from \"./shared/conversation-search-results-widget-payload\";\n\ntype ConversationSearchResultsCanvasProps = {\n query: string;\n groups: ReturnType<typeof parseConversationSearchResultsPayload>[\"groups\"];\n onOpenConversation: (conversationId: number, messageId?: number) => void;\n};\n\n// Canvas body styles — the same as every list canvas body, so the full-screen panel behaves the same:\n// a full-height flex column whose inner area SCROLLS (scrollStyle: flex 1 + overflowY auto — without it the content\n// overflows the panel with no scrollbar), plus a horizontal inset (contentStyle) because the results list is built\n// for the narrow inline chat column where the bubble provides the gutter — in the canvas there is no bubble, so it would\n// otherwise render edge-to-edge.\nconst canvasStyle: React.CSSProperties = {\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n minHeight: 0,\n background: \"var(--agents-bg)\",\n};\nconst scrollStyle: React.CSSProperties = {\n flex: 1,\n minHeight: 0,\n overflowY: \"auto\",\n};\nconst contentStyle: React.CSSProperties = {\n padding: \"16px 24px 24px\",\n};\n\n/** The canvas body — a titled wrapper around the same grouped results, scrollable + inset like the other canvas widgets. */\nexport const ConversationSearchResultsCanvas = (props: ConversationSearchResultsCanvasProps) => (\n <div style={canvasStyle} data-testid=\"conversation-search-results-widget-canvas\">\n <div style={scrollStyle}>\n <div style={contentStyle} data-testid=\"conversation-search-results-widget-canvas-results\">\n <Text\n role=\"title\"\n replacements={{ query: props.query }}\n testId=\"conversation-search-results-widget-canvas-title\"\n >\n {'Search results for \"%query%\"'}\n </Text>\n <ConversationSearchResultsList groups={props.groups} query={props.query} onOpenConversation={props.onOpenConversation} />\n </div>\n </div>\n </div>\n);\n","import React, { useMemo } from \"react\";\nimport type { CanvasBodyProps } from \"../../renderer/canvas/canvas-body-props\";\nimport { useConversationWidgetContext } from \"../../renderer/ConversationWidgetCallbacksContext\";\nimport { useAnchorWidgetPayload } from \"../../renderer/canvas/use-anchor-widget-payload\";\nimport { parseConversationSearchResultsPayload } from \"./shared/conversation-search-results-widget-payload\";\nimport { ConversationSearchResultsCanvas } from \"./ConversationSearchResultsCanvas\";\n\n/**\n * The `conversationSearchResults` CANVAS BODY (`canvasBodies.tsx`): re-reads the `ConversationSearchResults` fence of\n * the anchor message — the same payload the inline widget rendered from — and shows the grouped results. Nothing was\n * stored for it but the pointer. Renders nothing when the anchor message no longer carries the fence.\n */\nexport const ConversationSearchResultsCanvasBody = ({ canvas, anchorMessageMarkdown }: CanvasBodyProps) => {\n const { onOpenConversation } = useConversationWidgetContext();\n const payload = useAnchorWidgetPayload(canvas, anchorMessageMarkdown, \"ConversationSearchResults\");\n const parsed = useMemo(\n () => (payload === undefined ? null : parseConversationSearchResultsPayload(payload)),\n [payload],\n );\n if (parsed === null || parsed.groups.length === 0) return null;\n return (\n <ConversationSearchResultsCanvas\n query={parsed.query}\n groups={parsed.groups}\n onOpenConversation={(conversationId, messageId) => onOpenConversation?.(conversationId, messageId)}\n />\n );\n};\n","import { Text } from \"../../provider/components\";\nimport React from \"react\";\nimport { WebSearchResultSource } from \"./shared/web-search-results-widget-payload\";\nimport { WebSearchAnswer } from \"./WebSearchAnswer\";\nimport { WebSearchSourcesList } from \"./WebSearchSourcesList\";\n\ntype WebSearchResultsCanvasProps = {\n query: string;\n answer: string;\n sources: WebSearchResultSource[];\n provider?: string;\n};\n\n// Canvas body styles — mirror the ConversationSearchResults / listReviews drawer widgets: a full-height flex column\n// whose inner area SCROLLS (flex 1 + overflowY auto) with a horizontal inset (there is no chat bubble gutter in the canvas).\nconst canvasStyle: React.CSSProperties = {\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n minHeight: 0,\n background: \"var(--agents-bg)\",\n};\nconst scrollStyle: React.CSSProperties = { flex: 1, minHeight: 0, overflowY: \"auto\" };\nconst contentStyle: React.CSSProperties = { padding: \"16px 24px 24px\" };\n\n/** The canvas body — the full answer + sources, scrollable + inset like the other canvas widgets. */\nexport const WebSearchResultsCanvas = (props: WebSearchResultsCanvasProps) => (\n <div style={canvasStyle} data-testid=\"web-search-results-widget-canvas\">\n <div style={scrollStyle}>\n <div style={contentStyle}>\n {props.query ? (\n <Text\n role=\"title\"\n replacements={{ query: props.query }}\n testId=\"web-search-results-widget-canvas-title\"\n >\n {'\"%query%\"'}\n </Text>\n ) : (\n <Text role=\"title\" testId=\"web-search-results-widget-canvas-title\">\n Web search\n </Text>\n )}\n <div style={{ marginTop: 12 }}>\n <WebSearchAnswer answer={props.answer} />\n </div>\n <WebSearchSourcesList\n sources={props.sources}\n provider={props.provider}\n testId=\"web-search-results-widget-canvas\"\n />\n </div>\n </div>\n </div>\n);\n","import React, { useMemo } from \"react\";\nimport type { CanvasBodyProps } from \"../../renderer/canvas/canvas-body-props\";\nimport { useAnchorWidgetPayload } from \"../../renderer/canvas/use-anchor-widget-payload\";\nimport { parseWebSearchResultsPayload } from \"./shared/web-search-results-widget-payload\";\nimport { WebSearchResultsCanvas } from \"./WebSearchResultsCanvas\";\n\n/**\n * The `webSearchResults` CANVAS BODY (`canvasBodies.tsx`): re-reads the `WebSearchResults` fence of the anchor message\n * — the answer and its cited sources the inline widget rendered from — and shows them in the canvas. Renders nothing\n * when the anchor message no longer carries the fence.\n */\nexport const WebSearchResultsCanvasBody = ({ canvas, anchorMessageMarkdown }: CanvasBodyProps) => {\n const payload = useAnchorWidgetPayload(canvas, anchorMessageMarkdown, \"WebSearchResults\");\n const parsed = useMemo(() => (payload === undefined ? null : parseWebSearchResultsPayload(payload)), [payload]);\n if (parsed === null || (parsed.answer.trim() === \"\" && parsed.sources.length === 0)) return null;\n return (\n <WebSearchResultsCanvas\n query={parsed.query}\n answer={parsed.answer}\n sources={parsed.sources}\n provider={parsed.provider}\n />\n );\n};\n","import { useCallback } from \"react\";\nimport { useConversationWidgetContext } from \"../../../renderer/ConversationWidgetCallbacksContext\";\nimport {\n buildEntityMentionFields,\n ENTITY_MENTION_WIDGET_NAME,\n entityMentionKey,\n EntityMentionTarget,\n MAX_ENTITY_MENTIONS_PER_MESSAGE,\n normalizeEntityMentionFields,\n toEntityMentionWireFields,\n} from \"./entity-mention\";\n\nexport interface UseEntityMentionComposerResult {\n /** Whether this entity is already mentioned in the composer. */\n isMentioned: (target: EntityMentionTarget) => boolean;\n /**\n * Puts the entity into the composer as an `EntityMention` chip — once per entity (a second click is a no-op); past\n * {@link MAX_ENTITY_MENTIONS_PER_MESSAGE} the new entity takes the place of the oldest mention (one write: a remove\n * and an add in one event would both read the list before either). Sends nothing: the owner writes the question.\n * Returns false for a target that names no entity.\n */\n mentionEntity: (target: EntityMentionTarget) => boolean;\n}\n\n/**\n * The opener side of the `EntityMention` composer widget: what the detail panel's \"Ask about this\"\n * button calls. Reads and writes the composer widgets of the chat's widget context; the composer strip renders them.\n */\nexport const useEntityMentionComposer = (): UseEntityMentionComposerResult => {\n const { composerWidgets, addComposerWidget, replaceComposerWidgetFields } = useConversationWidgetContext();\n\n const findMentionWidgets = useCallback(\n () => composerWidgets.filter((composerWidget) => composerWidget.widgetName === ENTITY_MENTION_WIDGET_NAME),\n [composerWidgets],\n );\n\n const isMentioned = useCallback(\n (target: EntityMentionTarget) => {\n const mention = buildEntityMentionFields(target);\n if (mention === null) return false;\n return findMentionWidgets().some((composerWidget) => {\n const heldMention = normalizeEntityMentionFields(composerWidget.fields);\n return heldMention !== null && entityMentionKey(heldMention) === entityMentionKey(mention);\n });\n },\n [findMentionWidgets],\n );\n\n const mentionEntity = useCallback(\n (target: EntityMentionTarget) => {\n const mention = buildEntityMentionFields(target);\n if (mention === null) return false;\n if (isMentioned(target)) return true;\n const mentionWidgets = findMentionWidgets();\n if (mentionWidgets.length >= MAX_ENTITY_MENTIONS_PER_MESSAGE) {\n replaceComposerWidgetFields(mentionWidgets[0].composerWidgetId, toEntityMentionWireFields(mention));\n } else {\n addComposerWidget(ENTITY_MENTION_WIDGET_NAME, toEntityMentionWireFields(mention));\n }\n return true;\n },\n [isMentioned, findMentionWidgets, replaceComposerWidgetFields, addComposerWidget],\n );\n\n return { isMentioned, mentionEntity };\n};\n","import { useIsEmailWidgetRender } from \"../../../renderer/ConversationWidgetRenderContext\";\nimport { ConversationWidgetButton } from \"../../_shared/ConversationWidgetButton\";\nimport type { EntityMentionTarget } from \"../shared/entity-mention\";\nimport { useEntityMentionComposer } from \"../shared/use-entity-mention-composer\";\n\nexport interface EntityMentionAskButtonProps extends EntityMentionTarget {\n testId?: string;\n}\n\n/**\n * The detail panel's \"Ask about this\" button (ChatGPT's \"Anschlussfrage stellen\"):\n * puts the entity into the chat input as an `EntityMention` chip and sends NOTHING — the owner writes the question,\n * the chip rides in that message as a fence, and the backend reads which entity the question is about. Not a\n * next-step CTA: it posts no prompt. A second click on the same entity changes nothing; the\n * label turns into \"Added to your message\". Mounted by the `entityDetail` canvas (the shell or a domain body) with\n * the entity the panel shows; renders nothing in the e-mail capture or for a target that names no entity.\n */\nexport const EntityMentionAskButton = (props: EntityMentionAskButtonProps) => {\n const { entityType, entityId, label, agentSpaceKey, testId = \"entity-mention-ask\" } = props;\n const isEmailRender = useIsEmailWidgetRender();\n const { isMentioned, mentionEntity } = useEntityMentionComposer();\n const target = { entityType, entityId, label, agentSpaceKey };\n if (isEmailRender) return null;\n const isAlreadyMentioned = isMentioned(target);\n return (\n <ConversationWidgetButton\n variant=\"primary\"\n size=\"medium\"\n icon={isAlreadyMentioned ? \"check\" : \"message\"}\n disabled={isAlreadyMentioned}\n onClick={() => mentionEntity(target)}\n testId={testId}\n >\n {isAlreadyMentioned ? \"Added to your message\" : \"Ask about this\"}\n </ConversationWidgetButton>\n );\n};\n","import type React from \"react\";\nimport { createContext, useContext } from \"react\";\n\n/**\n * The bodies of the `entityDetail` canvas, one per entity type: every `*.entity-detail-body.tsx` below\n * `widgets/` that exports `entityDetailBody` registers itself by folder glob — an entity type is added with\n * the backend `<Type>EntityReferenceResolver` and this one file, and edits no list. A second body for one entity type\n * is a build error (thrown at module load).\n */\n\nexport interface EntityDetailBodyProps {\n entityType: string;\n /** The id as the backend resolved it (a row id or an external id). */\n entityId: string;\n /** The key of the agent space the chat is scoped to, for a body whose read endpoint is agent-space-scoped. */\n agentSpaceKey?: number;\n}\n\nexport interface EntityDetailBodyDefinition {\n /** The backend resolver's `ENTITY_TYPE`. */\n entityType: string;\n /** Self-hydrates the entity by id through its own read endpoint (the endpoint's entity rights apply). */\n component: React.ComponentType<EntityDetailBodyProps>;\n}\n\nconst bodyModules = import.meta.glob<{ entityDetailBody?: EntityDetailBodyDefinition }>(\n [\"../../widgets/**/*.entity-detail-body.tsx\", \"../**/*.entity-detail-body.tsx\"],\n { eager: true },\n);\n\nexport const buildEntityDetailBodyRegistry = (\n definitions: EntityDetailBodyDefinition[],\n): Readonly<Record<string, EntityDetailBodyDefinition>> => {\n const registry: Record<string, EntityDetailBodyDefinition> = {};\n for (const definition of definitions) {\n if (Object.prototype.hasOwnProperty.call(registry, definition.entityType)) {\n throw new Error(`Two entity detail bodies register the entity type \"${definition.entityType}\".`);\n }\n registry[definition.entityType] = definition;\n }\n return registry;\n};\n\n/** The body definitions a set of `*.entity-detail-body.tsx` modules export (the package's or an app's glob). */\nexport const readEntityDetailBodyDefinitions = (\n modules: Record<string, { entityDetailBody?: EntityDetailBodyDefinition }>,\n): EntityDetailBodyDefinition[] =>\n Object.values(modules)\n .map((bodyModule) => bodyModule.entityDetailBody)\n .filter((definition): definition is EntityDetailBodyDefinition => definition !== undefined);\n\n/** Entity type → body: the package's folder, then what an app registers (`registerEntityDetailBodies`). */\nexport const entityDetailBodies: Record<string, EntityDetailBodyDefinition> = {\n ...buildEntityDetailBodyRegistry(readEntityDetailBodyDefinitions(bodyModules)),\n};\n\n/** Adds an app's entity detail bodies. Runs outside React, before the first render; an entity type registers once. */\nexport const registerEntityDetailBodies = (definitions: readonly EntityDetailBodyDefinition[]): void => {\n Object.assign(\n entityDetailBodies,\n buildEntityDetailBodyRegistry([...Object.values(entityDetailBodies), ...definitions]),\n );\n};\n\n/** The body registered for one entity type (the package's or an app's), or undefined when none is. */\nexport const getEntityDetailBody = (entityType: string): EntityDetailBodyDefinition | undefined =>\n Object.prototype.hasOwnProperty.call(entityDetailBodies, entityType) ? entityDetailBodies[entityType] : undefined;\n\n/** The registry the canvas body uses — the folder's by default; a spec or a story may provide stubs. */\nexport const EntityDetailBodiesContext =\n createContext<Readonly<Record<string, EntityDetailBodyDefinition>>>(entityDetailBodies);\n\nexport const useEntityDetailBodies = () => useContext(EntityDetailBodiesContext);\n","import { Text } from \"../../provider/components\";\nimport { useCallback, useState } from \"react\";\nimport type { CanvasBodyProps } from \"../../renderer/canvas/canvas-body-props\";\nimport { readEntityDetailInstanceId } from \"../../renderer/canvas/entity-detail-instance\";\nimport { EntityMentionAskButton } from \"../../widgets/EntityMentionConversationWidget/EntityMentionAskButton/EntityMentionAskButton\";\nimport { useEntityDetailBodies } from \"./entity-detail-bodies\";\nimport { EntityDetailNameReportContext } from \"./entity-detail-name\";\nimport styles from \"./EntityDetailCanvasBody.module.scss\";\n\n/**\n * The `entityDetail` CANVAS BODY (`canvasBodies.tsx`): the shell around one entity's detail. It reads\n * WHICH entity from the canvas instance id (`entityDetail:<entityType>:<id>`) and mounts the body registered for that\n * entity type; the body self-hydrates by id. An entity type without a body renders a short notice instead of nothing,\n * so a click never lands on an empty pane. \"Ask about this\" stays at the pane's foot while the detail scrolls and\n * names the entity in its chip once the body reported the name (`useReportEntityDetailName`).\n */\nexport const EntityDetailCanvasBody = ({ canvas, agentSpaceKey }: CanvasBodyProps) => {\n const bodies = useEntityDetailBodies();\n const [reportedEntity, setReportedEntity] = useState<{ instanceId: string; name: string | undefined }>();\n const reportEntityName = useCallback(\n (entityName: string | undefined) => setReportedEntity({ instanceId: canvas.instanceId, name: entityName }),\n [canvas.instanceId],\n );\n const target = readEntityDetailInstanceId(canvas.instanceId);\n if (target === undefined) return null;\n const definition = Object.prototype.hasOwnProperty.call(bodies, target.entityType)\n ? bodies[target.entityType]\n : undefined;\n if (!definition) {\n return (\n <div className={styles.entityDetail} data-testid=\"entity-detail-canvas\">\n <Text role=\"help\" color=\"var(--agents-fg-light)\" testId=\"entity-detail-canvas-unsupported\">\n {\"This detail cannot be shown here yet.\"}\n </Text>\n </div>\n );\n }\n const Body = definition.component;\n // A name reported for the previous entity never labels the next one's chip.\n const entityName = reportedEntity?.instanceId === canvas.instanceId ? reportedEntity.name : undefined;\n return (\n <div className={styles.entityDetail} data-testid=\"entity-detail-canvas\">\n <EntityDetailNameReportContext.Provider value={reportEntityName}>\n <Body entityType={target.entityType} entityId={target.entityId} agentSpaceKey={agentSpaceKey} />\n </EntityDetailNameReportContext.Provider>\n <div className={styles.entityDetailFooter}>\n <EntityMentionAskButton\n entityType={target.entityType}\n entityId={target.entityId}\n label={entityName}\n agentSpaceKey={agentSpaceKey}\n />\n </div>\n </div>\n );\n};\n","import { Text } from \"../../provider/components\";\nimport { useCallback, useMemo } from \"react\";\nimport type { CanvasBodyProps } from \"../../renderer/canvas/canvas-body-props\";\nimport { useConversationWidgetContext } from \"../../renderer/ConversationWidgetCallbacksContext\";\nimport {\n canvasInstanceIdForEntityDetail,\n readOpenEntityActionTarget,\n} from \"../../renderer/canvas/entity-detail-instance\";\nimport type {\n ComposedViewAction,\n ComposedViewTree,\n} from \"../../renderer/widget-tree/composed-view-tree\";\nimport type { ExpressionValue } from \"../../renderer/widget-tree/expression/evaluate-expression\";\nimport { ComposedViewRenderer } from \"../ComposedViewRenderer\";\nimport { findMapNodeInMarkdown, readMapNodeKeyOfInstanceId } from \"../components/map/map-full-screen-instance\";\nimport { MapPresentationContext } from \"../components/map/map-presentation-context\";\nimport mapStyles from \"../components/map/Map.module.scss\";\n\n/** The body reports nothing of its own: the inline block already reported this map to the model. */\nconst ignoreRenderReport = () => {};\n\n/**\n * The `mapFullScreen` CANVAS BODY (`canvasBodies.tsx`): one `Map` node of the anchor\n * message, full screen — the map filling the pane beside the list of its points. It finds the node through the\n * message's `ComposedView` fences and the node key its instance id carries (`map-full-screen-instance.ts`), then\n * renders it through the same `ComposedViewRenderer` with the node's own tree, so its bindings (`$data`, `$from`,\n * `$source`, `$computed`, `$state` at its initial value) and its actions resolve exactly as inline; the\n * `MapPresentationContext` switches the node to its full-screen layout. A point's `openEntity` action opens the\n * `entityDetail` canvas on the same anchor message (it replaces this canvas, as a click on an inline point would).\n */\nexport const MapFullScreenCanvasBody = (props: CanvasBodyProps) => {\n const { canvas, anchorMessageMarkdown, agentSpaceKey } = props;\n const { openCanvas } = useConversationWidgetContext();\n const mapNodeKey = readMapNodeKeyOfInstanceId(canvas.instanceId);\n\n const mapTree = useMemo<ComposedViewTree | undefined>(() => {\n if (anchorMessageMarkdown === null || mapNodeKey === undefined) return undefined;\n const mapNodeInComposedView = findMapNodeInMarkdown(anchorMessageMarkdown, mapNodeKey);\n if (!mapNodeInComposedView) return undefined;\n // The map alone, with its tree's state, data, computed values and actions; the tree's findings were the inline\n // block's to report.\n return { ...mapNodeInComposedView.tree, title: null, view: mapNodeInComposedView.mapNode, findings: [] };\n }, [anchorMessageMarkdown, mapNodeKey]);\n\n const anchor = canvas.anchor;\n const onOpenEntity = useCallback(\n (action: ComposedViewAction, currentRow?: ExpressionValue) => {\n const target = readOpenEntityActionTarget(action, currentRow);\n if (target === undefined || anchor.kind !== \"message\") return;\n openCanvas({\n canvasId: \"entityDetail\",\n instanceId: canvasInstanceIdForEntityDetail(target),\n anchor,\n origin: \"cardClick\",\n justDelivered: false,\n });\n },\n [openCanvas, anchor],\n );\n\n if (!mapTree) {\n return (\n <div\n className={mapStyles.fullScreenNotice}\n data-canvas-instance={canvas.instanceId}\n data-testid=\"map-full-screen-canvas-unavailable\"\n >\n <Text role=\"help\" color=\"var(--agents-fg-light)\">\n {\"This map is no longer available.\"}\n </Text>\n </div>\n );\n }\n return (\n <div\n className={mapStyles.fullScreenBody}\n data-canvas-instance={canvas.instanceId}\n data-testid=\"map-full-screen-canvas\"\n >\n <MapPresentationContext.Provider value=\"fullScreen\">\n <ComposedViewRenderer\n tree={mapTree}\n interactive\n isEmailRender={false}\n messageId={anchor.kind === \"message\" ? anchor.messageId : undefined}\n agentSpaceKey={agentSpaceKey}\n onReport={ignoreRenderReport}\n onOpenEntity={onOpenEntity}\n testId=\"map-full-screen-canvas-view\"\n />\n </MapPresentationContext.Provider>\n </div>\n );\n};\n","import type React from \"react\";\nimport type { CanvasId } from \"./canvas-behaviours\";\nimport type { CanvasBodyProps } from \"./canvas-body-props\";\nimport { ConversationSearchResultsCanvasBody } from \"../../widgets/ConversationSearchResultsConversationWidget/ConversationSearchResultsCanvasBody\";\nimport { WebSearchResultsCanvasBody } from \"../../widgets/WebSearchResultsConversationWidget/WebSearchResultsCanvasBody\";\nimport { EntityDetailCanvasBody } from \"../../kit/entity-detail/EntityDetailCanvasBody\";\nimport { MapFullScreenCanvasBody } from \"../../kit/map-full-screen/MapFullScreenCanvasBody\";\n\nexport type { CanvasBodyProps } from \"./canvas-body-props\";\nexport { canvasFeatureFlagOf } from \"./canvas-feature-flags\";\n\n/**\n * THE CANVAS BODIES — canvas id → the React component the shell mounts in the canvas pane. The one module that knows\n * both the canvas ids and React components, and it is imported ONLY by `CanvasPane` (the shell), never by\n * the slice, the behaviours or a widget — that is the import layering that keeps the store-init temporal-dead-zone\n * cycle away. The bodies themselves are small: a body that hosts a heavy page (an app's list page or editor)\n * lazy-loads that page, exactly as its widget did. Every canvas has a body; nothing\n * else hands the pane a rendered node.\n *\n * The package's own bodies are listed here; an app adds the bodies of its canvases with `registerCanvasBodies` before\n * the first render. The object is filled in place, so the shell reads every registration.\n */\nexport const canvasBodies = {\n conversationSearchResults: ConversationSearchResultsCanvasBody,\n webSearchResults: WebSearchResultsCanvasBody,\n entityDetail: EntityDetailCanvasBody,\n mapFullScreen: MapFullScreenCanvasBody,\n} as Record<CanvasId, React.ComponentType<CanvasBodyProps>>;\n\n/** Adds an app's canvas bodies (canvas id → body). Runs outside React, before the first render. */\nexport const registerCanvasBodies = (\n bodies: Readonly<Partial<Record<CanvasId, React.ComponentType<CanvasBodyProps>>>>,\n): void => {\n Object.assign(canvasBodies, bodies);\n};\n"],"names":["canvasStyle","scrollStyle","contentStyle","entityName"],"mappings":"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"}
@@ -1,11 +1,11 @@
1
1
  "use client";
2
- import { j as conversationWidgetRegistry, F as resolveCanonicalWidgetName, I as exceedsJsonContainerDepth, p as ConversationWidgetFeatureGate, o as ConversationWidgetErrorBoundary, J as remarkWidgetFencePlugin, y as getConversationWidgetRegistryRevision } from "./widget-name-resolver-BGN3HpoG.js";
2
+ import { j as conversationWidgetRegistry, F as resolveCanonicalWidgetName, I as exceedsJsonContainerDepth, p as ConversationWidgetFeatureGate, o as ConversationWidgetErrorBoundary, J as remarkWidgetFencePlugin, y as getConversationWidgetRegistryRevision } from "./widget-name-resolver-BaxKaRa3.js";
3
3
  import { jsx, jsxs } from "react/jsx-runtime";
4
4
  import React, { createContext, useContext, useState, useEffect, useRef, useMemo } from "react";
5
5
  import { f as InteractiveSurface, q as Text, $ as translateText, h as Markdown, N as getFurtherWidgetClickSignalSources } from "./index-7Qc_3GbG.js";
6
6
  import remarkDirective from "remark-directive";
7
7
  import { visit } from "unist-util-visit";
8
- import { A as readWidgetAgentSpaceKeyAttribute, W as WidgetRenderReportContext } from "./WebSearchResultsConversationWidget-as3p5bHw.js";
8
+ import { A as readWidgetAgentSpaceKeyAttribute, W as WidgetRenderReportContext } from "./WebSearchResultsConversationWidget-tDhawe9f.js";
9
9
  const resolveDeepLinkableWidget = (widgetName) => {
10
10
  if (!widgetName) return null;
11
11
  return conversationWidgetRegistry.widgets.find((entry) => entry.name === widgetName && entry.deepLinkable === true) ?? null;
@@ -443,4 +443,4 @@ export {
443
443
  parseWidgetRenderReportSignal as p,
444
444
  resolveDeepLinkableWidget as r
445
445
  };
446
- //# sourceMappingURL=widget-click-signal-sources-DulRkNvZ.js.map
446
+ //# sourceMappingURL=widget-click-signal-sources-BR82QygL.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"widget-click-signal-sources-DulRkNvZ.js","sources":["../../src/renderer/resolve-deep-linkable-widget.ts","../../src/renderer/render-report/widget-render-report-collector.ts","../../src/renderer/widget-windowing.tsx","../../src/renderer/conversationWidgetAstPlugin.ts","../../src/renderer/restoreTextDirectivesPlugin.ts","../../src/renderer/WindowedWidget/WindowedWidget.tsx","../../src/renderer/ConversationWidgetHost.tsx","../../src/renderer/MarkdownLink.tsx","../../src/renderer/ConversationMarkdown.tsx","../../src/renderer/widget-click-signal-sources.ts"],"sourcesContent":["import type { CanvasId } from \"./canvas/canvas-behaviours\";\nimport { ConversationWidgetEntry, conversationWidgetRegistry } from \"./conversationWidgetRegistry\";\n\n/**\n * Resolves the widget a `?agentWidget=<name>` deep-link (or a restored pinned canvas) may mount, by its CANONICAL\n * registry wire name.\n *\n * A URL is untrusted input, so this is an allow-list rather than a registry lookup: only an entry that declares\n * `deepLinkable: true` can be opened this way. Every other widget is emitted by the agent inside a message, where the\n * conversation itself is the authorisation — a link must not be able to mount an arbitrary widget (some carry\n * mount-time side effects) just because its name was typed into the address bar.\n *\n * Returns the entry (the caller renders its `component`, applying the feature gate the entry declares) or null when the\n * name is empty, unknown, or not deep-linkable.\n */\nexport const resolveDeepLinkableWidget = (widgetName?: string | null): ConversationWidgetEntry | null => {\n if (!widgetName) return null;\n return (\n conversationWidgetRegistry.widgets.find((entry) => entry.name === widgetName && entry.deepLinkable === true) ?? null\n );\n};\n\n/**\n * The reverse direction: which deep-linkable widget re-opens THIS canvas. The open canvas of the agent chat is\n * mirrored into `?agentWidget=` (AgentTrigger) so a reload brings the customer back to the surface they were working\n * on — an editor a cold chat opened has no conversation id yet, so the canvas model's own pointer storage\n * has nothing to restore from.\n *\n * Null for every canvas no deep-linkable widget declares: it is the agent's to open inside a message, and a URL must\n * not be able to mount it (the allow-list above).\n */\nexport const deepLinkableWidgetNameForCanvas = (canvasId: CanvasId | null | undefined): string | null => {\n if (!canvasId) return null;\n return (\n conversationWidgetRegistry.widgets.find(\n (entry) => entry.deepLinkable === true && entry.opensCanvases?.includes(canvasId),\n )?.name ?? null\n );\n};\n","import { createContext } from \"react\";\nimport type { WidgetRenderReporter, WidgetRenderReportEntry } from \"./widget-render-report\";\n\n/**\n * The render reports of the widget blocks of live messages, collected per message. The chat host\n * (`AgentChat`) owns one collector and hands it down; `ConversationWidgetHost` registers every block of a JUST-DELIVERED\n * message (never a replayed one) and gives the widget a reporter for it. A block that reports nothing counts as\n * `mounted`. Once a message's blocks have settled — none `pending` for `settleMs`, or `maxWaitMs` after the first block\n * at the latest — the collector calls `onSettled` ONCE for that message with its blocks in document order; the host\n * sends the signal only when a block is not `mounted` or rendered with a note (a `mounted` entry with a code: figures\n * without a source), so a clean render costs nothing.\n */\n\nexport interface WidgetRenderBlockReport {\n widgetName: string;\n /** The block's offset in the message's markdown — document order, and the block's identity in the message. */\n sourceOffset: number;\n entry: WidgetRenderReportEntry;\n}\n\nexport interface WidgetRenderReportCollector {\n /** Counts one block of one live message as `mounted` unless it already reported (a child reports before its host mounts). */\n registerBlock: (messageId: string | number, widgetName: string, sourceOffset: number) => void;\n /** The reporter of one block of one live message. */\n reporterForBlock: (messageId: string | number, widgetName: string, sourceOffset: number) => WidgetRenderReporter;\n /** Forget every pending timer (the host unmounts or the conversation switches). */\n dispose: () => void;\n}\n\nexport interface WidgetRenderReportCollectorOptions {\n onSettled: (messageId: string, blocks: WidgetRenderBlockReport[]) => void;\n settleMs?: number;\n maxWaitMs?: number;\n}\n\ninterface MessageReports {\n blocksByKey: Map<string, WidgetRenderBlockReport>;\n firstReportAt: number;\n settleTimer: ReturnType<typeof setTimeout> | null;\n isSent: boolean;\n}\n\nexport const createWidgetRenderReportCollector = (\n options: WidgetRenderReportCollectorOptions,\n): WidgetRenderReportCollector => {\n const { onSettled, settleMs = 1500, maxWaitMs = 20000 } = options;\n const reportsByMessageId = new Map<string, MessageReports>();\n\n const scheduleSettleCheck = (messageId: string) => {\n const messageReports = reportsByMessageId.get(messageId);\n if (!messageReports || messageReports.isSent) return;\n if (messageReports.settleTimer !== null) clearTimeout(messageReports.settleTimer);\n const hasPendingBlock = [...messageReports.blocksByKey.values()].some((block) => block.entry.status === \"pending\");\n const waitedMs = Date.now() - messageReports.firstReportAt;\n const delayMs = hasPendingBlock ? Math.max(0, Math.min(settleMs * 4, maxWaitMs - waitedMs)) : settleMs;\n messageReports.settleTimer = setTimeout(() => {\n messageReports.settleTimer = null;\n const isStillPending = [...messageReports.blocksByKey.values()].some((block) => block.entry.status === \"pending\");\n if (isStillPending && Date.now() - messageReports.firstReportAt < maxWaitMs) {\n scheduleSettleCheck(messageId);\n return;\n }\n messageReports.isSent = true;\n onSettled(\n messageId,\n [...messageReports.blocksByKey.values()].sort((first, second) => first.sourceOffset - second.sourceOffset),\n );\n }, delayMs);\n };\n\n const recordBlock = (\n messageId: string,\n widgetName: string,\n sourceOffset: number,\n entry: WidgetRenderReportEntry | null,\n ) => {\n let messageReports = reportsByMessageId.get(messageId);\n if (!messageReports) {\n messageReports = { blocksByKey: new Map(), firstReportAt: Date.now(), settleTimer: null, isSent: false };\n reportsByMessageId.set(messageId, messageReports);\n }\n if (messageReports.isSent) return;\n const blockKey = `${widgetName}@${sourceOffset}`;\n const knownBlock = messageReports.blocksByKey.get(blockKey);\n if (entry === null && knownBlock) return;\n messageReports.blocksByKey.set(blockKey, { widgetName, sourceOffset, entry: entry ?? { status: \"mounted\" } });\n scheduleSettleCheck(messageId);\n };\n\n return {\n registerBlock: (messageId, widgetName, sourceOffset) =>\n recordBlock(String(messageId), widgetName, sourceOffset, null),\n reporterForBlock: (messageId, widgetName, sourceOffset) => ({\n report: (entry: WidgetRenderReportEntry) => recordBlock(String(messageId), widgetName, sourceOffset, entry),\n }),\n dispose: () => {\n for (const messageReports of reportsByMessageId.values()) {\n if (messageReports.settleTimer !== null) clearTimeout(messageReports.settleTimer);\n }\n reportsByMessageId.clear();\n },\n };\n};\n\n/** The collector of the chat the widget lives in; absent = nobody listens (another host, a story, the e-mail capture). */\nexport const WidgetRenderReportCollectorContext = createContext<WidgetRenderReportCollector | null>(null);\n\n/** The stored prefix of a render-report signal — the inspector and the tests read it; never reworded. */\nexport const WIDGET_RENDER_REPORT_SIGNAL_PREFIX = \"Widget render report for message\";\n\n/** The signal source (backend `AIConversationSignalSource::FRONTEND_WIDGET_RENDER_REPORT`). */\nexport const WIDGET_RENDER_REPORT_SIGNAL_SOURCE = \"FRONTEND_WIDGET_RENDER_REPORT\";\n\nconst MAX_DETAIL_CHARACTERS = 400;\n\n/** The backend's cap on a signal text (`AIConversationMessage::MAX_SIGNAL_TEXT_CHARS`). */\nconst MAX_SIGNAL_TEXT_CHARS = 2000;\n/** Room kept for the \"… and N more\" line. */\nconst OMITTED_LINE_RESERVE = 80;\n\n/**\n * The model-visible signal for a settled message — null when every block mounted cleanly (nothing to tell). One line\n * per block that is not `mounted`, or that mounted with a note (a code: `INLINE_VALUE_WITHOUT_SOURCE`): its position\n * among the message's widgets, its name and its occurrence among widgets of that name, its status and code, the\n * detail. The closing sentence asks for the honest answer about a block the owner did not see, and for\n * a note that the next widget fixes it.\n */\nexport const buildWidgetRenderReportSignal = (messageId: string, blocks: WidgetRenderBlockReport[]): string | null => {\n const occurrenceByName = new Map<string, number>();\n const blockLines: string[] = [];\n blocks.forEach((block, blockPosition) => {\n const occurrence = (occurrenceByName.get(block.widgetName) ?? 0) + 1;\n occurrenceByName.set(block.widgetName, occurrence);\n if (block.entry.status === \"mounted\" && !block.entry.code) return;\n const status = block.entry.status === \"pending\" ? \"pending (did not finish loading)\" : block.entry.status;\n const code = block.entry.code ? ` ${block.entry.code}` : \"\";\n const detail = block.entry.detail ? ` — ${block.entry.detail.slice(0, MAX_DETAIL_CHARACTERS)}` : \"\";\n blockLines.push(`- widget ${blockPosition + 1} (${block.widgetName} #${occurrence}): ${status}${code}${detail}`);\n });\n if (blockLines.length === 0) return null;\n const header = `${WIDGET_RENDER_REPORT_SIGNAL_PREFIX} ${messageId}:`;\n const hasBlockNotShown = blocks.some((block) => block.entry.status !== \"mounted\");\n const hasNote = blocks.some((block) => block.entry.status === \"mounted\" && block.entry.code);\n const closing = [\n hasBlockNotShown\n ? \"The owner did not see these widgets as intended. In your next answer say so in one line where it matters and \" +\n 'give the content in prose; never refer to \"the widget above\" for them.'\n : \"\",\n hasNote\n ? \"The widgets marked mounted were shown; fix the noted part in every widget you write from now on (a figure you \" +\n \"researched names its publisher and page in `source`). Do not resend the widget for the note alone.\"\n : \"\",\n ]\n .filter((sentence) => sentence !== \"\")\n .join(\" \");\n // The backend keeps the first MAX_SIGNAL_TEXT_CHARS of a signal: whole lines only, the rest counted in one line.\n const keptLines: string[] = [];\n let signalLength = header.length + 1 + closing.length + OMITTED_LINE_RESERVE;\n for (const blockLine of blockLines) {\n if (signalLength + blockLine.length + 1 > MAX_SIGNAL_TEXT_CHARS) break;\n keptLines.push(blockLine);\n signalLength += blockLine.length + 1;\n }\n const omittedCount = blockLines.length - keptLines.length;\n if (omittedCount > 0) keptLines.push(`- … and ${omittedCount} more widget(s) that did not render as intended`);\n return `${header}\\n${keptLines.join(\"\\n\")}\\n${closing}`;\n};\n\nexport interface ParsedWidgetRenderReport {\n messageId: string;\n blocks: Array<{\n position: number;\n widgetName: string;\n occurrence: number;\n status: string;\n code: string | null;\n detail: string | null;\n }>;\n}\n\nconst REPORT_LINE =\n /^- widget (\\d+) \\(([A-Za-z]+) #(\\d+)\\): (mounted|empty|failed|pending)(?: \\(did not finish loading\\))?(?: ([A-Z_]+))?(?: — (.*))?$/;\n\n/** The report a stored signal text carries, or null for another text — the admin inspector's reader. */\nexport const parseWidgetRenderReportSignal = (signalText: string): ParsedWidgetRenderReport | null => {\n const lines = signalText.split(\"\\n\");\n const header = new RegExp(`^${WIDGET_RENDER_REPORT_SIGNAL_PREFIX} (\\\\S+):$`).exec(lines[0] ?? \"\");\n if (!header) return null;\n const blocks: ParsedWidgetRenderReport[\"blocks\"] = [];\n for (const line of lines.slice(1)) {\n const match = REPORT_LINE.exec(line);\n if (!match) continue;\n blocks.push({\n position: Number(match[1]),\n widgetName: match[2],\n occurrence: Number(match[3]),\n status: match[4],\n code: match[5] ?? null,\n detail: match[6] ?? null,\n });\n }\n return { messageId: header[1], blocks };\n};\n","import React, { createContext, RefObject, useContext, useEffect, useState } from \"react\";\n\n/**\n * Viewport-windowing for chat widgets. Heavy interactive widgets (maps, lists, hydrating components) are mounted ONLY\n * while near the viewport and unmounted back to a placeholder when far away — so a long conversation keeps a CONSTANT\n * number of widgets mounted no matter how far you scroll. Two thresholds give hysteresis (reveal early, hide late) so a\n * small scroll wobble never thrashes mount/unmount.\n *\n * The IntersectionObserver root is the chat scroll container, supplied via {@link WidgetWindowRootContext} by the chat\n * host (ConversationViewBody). Observers are POOLED per (root, margin): all placeholders share two observers, not two each.\n */\nexport const WidgetWindowRootContext = createContext<HTMLElement | null>(null);\n\n/** Mount when the widget is within this margin of the viewport. */\nconst REVEAL_MARGIN_PX = 600;\n/** Unmount only once it is beyond this (larger) margin — the gap to REVEAL is the hysteresis band. */\nconst HIDE_MARGIN_PX = 2000;\n\ntype Pool = { observer: IntersectionObserver; callbacks: Map<Element, (entry: IntersectionObserverEntry) => void> };\n// Keyed by root (null = viewport) → margin string → the shared observer + its per-element callbacks.\nconst poolsByRoot = new Map<Element | null, Map<string, Pool>>();\n\n/** Register `element` with the shared observer for (root, rootMargin); returns a disposer. */\nconst observeShared = (\n root: Element | null,\n rootMargin: string,\n element: Element,\n onEntry: (entry: IntersectionObserverEntry) => void,\n): (() => void) => {\n let byMargin = poolsByRoot.get(root);\n if (!byMargin) {\n byMargin = new Map();\n poolsByRoot.set(root, byMargin);\n }\n let pool = byMargin.get(rootMargin);\n if (!pool) {\n const callbacks = new Map<Element, (entry: IntersectionObserverEntry) => void>();\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n callbacks.get(entry.target)?.(entry);\n }\n },\n { root, rootMargin },\n );\n pool = { observer, callbacks };\n byMargin.set(rootMargin, pool);\n }\n pool.callbacks.set(element, onEntry);\n pool.observer.observe(element);\n\n return () => {\n const currentByMargin = poolsByRoot.get(root);\n const currentPool = currentByMargin?.get(rootMargin);\n if (!currentPool) return;\n currentPool.observer.unobserve(element);\n currentPool.callbacks.delete(element);\n if (currentPool.callbacks.size === 0) {\n currentPool.observer.disconnect();\n currentByMargin!.delete(rootMargin);\n if (currentByMargin!.size === 0) poolsByRoot.delete(root);\n }\n };\n};\n\n/**\n * Drives a widget's mounted/unmounted state from its viewport proximity.\n *\n * - `pinned` (the freshly-delivered widget) → always revealed, never observed: a live widget must not unmount while its\n * one-shot effects (canvas auto-open) are in play, and re-mounting it later would re-fire them.\n * - otherwise: starts at `initialRevealed` (the cheap index-window guess for first paint), then the reveal observer\n * mounts it when it scrolls within REVEAL_MARGIN and the hide observer unmounts it past HIDE_MARGIN.\n *\n * Returns `[revealed, reveal]` — `reveal` lets the placeholder force a manual mount (the \"Show\" affordance).\n */\nexport const useWidgetWindow = (\n targetRef: RefObject<HTMLElement | null>,\n options: { initialRevealed: boolean; pinned: boolean },\n): [boolean, () => void] => {\n const { initialRevealed, pinned } = options;\n const root = useContext(WidgetWindowRootContext);\n const [revealed, setRevealed] = useState(initialRevealed || pinned);\n\n useEffect(() => {\n if (pinned) {\n setRevealed(true);\n return undefined;\n }\n const element = targetRef.current;\n if (!element || typeof IntersectionObserver === \"undefined\") return undefined;\n\n const disposeReveal = observeShared(root, `${REVEAL_MARGIN_PX}px 0px`, element, (entry) => {\n if (entry.isIntersecting) setRevealed(true);\n });\n const disposeHide = observeShared(root, `${HIDE_MARGIN_PX}px 0px`, element, (entry) => {\n if (!entry.isIntersecting) setRevealed(false);\n });\n return () => {\n disposeReveal();\n disposeHide();\n };\n // Re-attach when the scroll root resolves (it is null on the first frame, then the container element).\n }, [root, pinned, targetRef]);\n\n return [revealed, () => setRevealed(true)];\n};\n","import { visit } from \"unist-util-visit\";\nimport { conversationWidgetRegistry, type ConversationWidgetEntry } from \"./conversationWidgetRegistry\";\nimport { resolveCanonicalWidgetName } from \"./widget-name-resolver\";\nimport { exceedsJsonContainerDepth } from \"./widget-tree/bounded-json\";\n\n/** Whether a `::Name{payload='…'}` payload nests deeper than the backend decodes — MALFORMED there, dropped here. */\nconst isPayloadTooDeep = (payload: unknown): boolean => {\n if (typeof payload !== \"string\") return false;\n try {\n return exceedsJsonContainerDepth(JSON.parse(payload));\n } catch {\n return false;\n }\n};\n\n/**\n * Maps the `::Name` nodes a message carries onto the registered components by rewriting `data.hName` to the CANONICAL\n * registry name — which is what `ConversationMarkdown` keys its component map by.\n *\n * The name on the node is whatever the message contains, so a stored conversation delivers the legacy\n * `::ExampleWidgetDirective{payload='…'}` form (and stored server `component` parts carry `\"name\":\"<Canonical>Directive\"`,\n * which `normalize-server-message-to-markdown` turns into exactly that). Resolution therefore goes\n * through `resolveCanonicalWidgetName` — the one legacy rule — never a direct set lookup, or every old message would\n * degrade to the `:name` plain-text fallback.\n */\nexport default function conversationWidgetAstPlugin() {\n const canonicalNames = new Set(conversationWidgetRegistry.widgets.map((w: ConversationWidgetEntry) => w.name));\n\n return (tree: any) => {\n // `\"leafDirective\"` is remark-directive's node type — its contract, not our vocabulary. It is the only form a\n // widget ever travels in: `remarkWidgetFencePlugin` produces it from a ```widget fence, and the stored `::Name`\n // syntax parses into it.\n visit(tree, [\"leafDirective\"], (node, index, parent) => {\n const canonicalName = resolveCanonicalWidgetName(node.name, canonicalNames);\n\n if (\n canonicalName &&\n index !== undefined &&\n index !== null &&\n parent &&\n isPayloadTooDeep(node.attributes?.payload)\n ) {\n parent.children[index] = { type: \"text\", value: \"\" };\n return;\n }\n\n if (canonicalName) {\n // Map properties to attributes for react-markdown\n node.data = {\n hName: canonicalName,\n hProperties: node.attributes || {},\n ...node.data,\n };\n return node;\n }\n\n // `index === 0` is falsy — a plain `!index` check would skip the text\n // fallback for a node that is its parent's first child.\n if (index === undefined || index === null || !parent) {\n return node;\n }\n\n // An unknown name becomes plain text. Keep the node's children so a `::Unknown[label]` form degrades to its\n // label instead of silently swallowing it.\n const asText = { type: \"text\", value: `:${node.name}` };\n parent.children.splice(index, 1, asText, ...(node.children ?? []));\n const prev = parent.children[index - 1];\n if (prev?.type === \"text\") {\n prev.value += asText.value;\n parent.children.splice(index, 1);\n }\n });\n };\n}\n","import { visit } from \"unist-util-visit\";\n\n/**\n * remark-directive parses EVERY `:name` run as a text directive — including the `:00` / `:30` inside a time such as\n * `08:00–16:30` (digits are valid directive names). Text directives are not part of our widget vocabulary (widgets\n * travel as leaf directives only, see `conversationWidgetAstPlugin`), so a text directive is always prose that the\n * parser misread: without this pass the customer chat rendered \"Montag: 08:00–16:30\" as \"08\" / \"–16\" (the `:00`\n * and `:30` swallowed, the unknown nodes rendered as breaks) while the admin inspector, which does not load\n * remark-directive, showed the line intact.\n *\n * Restores every text directive to the exact source slice it was parsed from, merging it into the neighbouring text\n * nodes so the paragraph reads as one run again.\n */\nexport default function restoreTextDirectivesPlugin() {\n return (tree: any, file: any) => {\n const source = String(file?.value ?? \"\");\n visit(tree, \"textDirective\", (node: any, index: number | undefined, parent: any) => {\n if (index === undefined || index === null || !parent) {\n return;\n }\n const start = node.position?.start?.offset;\n const end = node.position?.end?.offset;\n const literal =\n typeof start === \"number\" && typeof end === \"number\" && end > start\n ? source.slice(start, end)\n : `:${node.name}${node.children?.length ? `[${node.children.map((c: any) => c.value ?? \"\").join(\"\")}]` : \"\"}`;\n const asText = { type: \"text\", value: literal };\n parent.children.splice(index, 1, asText);\n // Merge with the neighbours so \"08\" + \":00\" + \"–16\" becomes one text node again.\n const prev = parent.children[index - 1];\n if (prev?.type === \"text\") {\n prev.value += asText.value;\n parent.children.splice(index, 1);\n const next = parent.children[index];\n if (next?.type === \"text\") {\n prev.value += next.value;\n parent.children.splice(index, 1);\n }\n return index - 1;\n }\n const next = parent.children[index + 1];\n if (next?.type === \"text\") {\n asText.value += next.value;\n parent.children.splice(index + 1, 1);\n }\n return index;\n });\n };\n}\n","import { InteractiveSurface, Text } from \"../../provider/components\";\nimport React, { useEffect, useRef } from \"react\";\nimport { translateText } from \"../../provider/environment\";\nimport { useWidgetWindow } from \"../widget-windowing\";\nimport styles from \"../HiddenWidgetPlaceholder/HiddenWidgetPlaceholder.module.scss\";\n\nexport interface WindowedWidgetProps {\n children: React.ReactNode;\n /** First-paint guess from the index window: recent messages start mounted, older ones start as a placeholder. */\n initialRevealed: boolean;\n /** The freshly-delivered widget — kept mounted (never windowed out) so its one-shot effects don't re-fire. */\n pinned: boolean;\n}\n\n/** Default reserved height for a widget that has never been measured (an old one that has not been scrolled to yet). */\nconst DEFAULT_PLACEHOLDER_MIN_HEIGHT = 80;\n\n/**\n * Mounts a chat widget only while it is near the viewport (see {@link useWidgetWindow}); far from the viewport it\n * collapses to a lightweight placeholder. The wrapper element is STABLE across the toggle so the shared\n * IntersectionObserver keeps tracking it. The placeholder reserves the widget's last measured height, so re-hiding it\n * causes ZERO layout shift (no scroll jump); revealing again restores the real widget (self-hydrating widgets re-fetch\n * with a brief skeleton).\n */\nexport const WindowedWidget = ({ children, initialRevealed, pinned }: WindowedWidgetProps) => {\n const wrapperRef = useRef<HTMLDivElement>(null);\n const [revealed, reveal] = useWidgetWindow(wrapperRef, { initialRevealed, pinned });\n // Remembered so the placeholder can reserve exactly the height the widget occupied.\n const lastHeightRef = useRef<number | undefined>(undefined);\n\n useEffect(() => {\n const element = wrapperRef.current;\n if (!revealed || !element || typeof ResizeObserver === \"undefined\") return undefined;\n const measure = () => {\n if (wrapperRef.current) lastHeightRef.current = wrapperRef.current.offsetHeight;\n };\n measure();\n const resizeObserver = new ResizeObserver(measure);\n resizeObserver.observe(element);\n return () => resizeObserver.disconnect();\n }, [revealed]);\n\n return (\n <div ref={wrapperRef}>\n {revealed ? (\n children\n ) : (\n <InteractiveSurface\n hoverable\n className={styles.placeholder}\n style={{ minHeight: lastHeightRef.current ?? DEFAULT_PLACEHOLDER_MIN_HEIGHT }}\n >\n <button\n type=\"button\"\n className={styles.placeholderButton}\n onClick={reveal}\n aria-label={translateText(\"Show hidden widget\")}\n >\n <Text>Older interactive content is hidden.</Text>\n <span className={styles.cta}>\n <Text>Show</Text>\n </span>\n </button>\n </InteractiveSurface>\n )}\n </div>\n );\n};\n","import React, { useContext, useEffect, useMemo } from \"react\";\nimport styles from \"./ConversationMarkdown.module.scss\";\nimport { ConversationWidgetEntry } from \"./conversationWidgetRegistry\";\nimport { ConversationWidgetFeatureGate } from \"./_access/ConversationWidgetFeatureGate\";\nimport { readWidgetAgentSpaceKeyAttribute } from \"./_agent-space/widget-agent-space-key-attribute\";\nimport { ConversationWidgetErrorBoundary } from \"./ConversationWidgetErrorBoundary\";\nimport { WindowedWidget } from \"./WindowedWidget/WindowedWidget\";\nimport { WidgetRenderReportContext } from \"./render-report/widget-render-report\";\nimport { WidgetRenderReportCollectorContext } from \"./render-report/widget-render-report-collector\";\n\nexport type ConversationWidgetRenderProps = Record<string, unknown> & {\n children?: React.ReactNode;\n fresh?: boolean;\n interactive?: boolean;\n justDelivered?: boolean;\n messageId?: string | number;\n};\n\ninterface ConversationWidgetHostProps {\n widget: ConversationWidgetEntry;\n /** Forwarded into the widget (and as its deprecated alias `fresh`): the widget's controls may be used. */\n interactive: boolean;\n /** Forwarded into the widget: the host message was delivered live right now — the one-shot side-effect gate. */\n justDelivered: boolean;\n /** Id of the host message, forwarded so a widget can identify the canvas it opens. */\n messageId?: string | number;\n /** When true, the widget renders as a lightweight placeholder (cheap scroll). */\n hideWidgets: boolean;\n /** Parsed markdown attributes for this widget instance. */\n conversationWidgetRenderProps: ConversationWidgetRenderProps;\n}\n\n/**\n * Single render path for every widget. Each concern is applied as a sequential gate\n * (innermost → outermost):\n * 1. the widget itself, with `interactive` / `justDelivered`\n * 2. feature-flag gate\n * 3. viewport-windowing (mount near the viewport, placeholder when far — WindowedWidget)\n * 4. block spacing wrapper\n *\n * A block of a JUST-DELIVERED message is also registered with the chat's render-report collector: it\n * counts as `mounted` unless the widget reports otherwise through `useReportWidgetRenderStatus`, and a crash caught by\n * the error boundary reports `failed` (`RENDER_FAILED`). A replayed message reports nothing.\n */\nexport const ConversationWidgetHost = ({\n widget,\n interactive,\n justDelivered,\n messageId,\n hideWidgets,\n conversationWidgetRenderProps,\n}: ConversationWidgetHostProps) => {\n const { component: Component, featureFlag, upsellTrigger } = widget;\n const renderReportCollector = useContext(WidgetRenderReportCollectorContext);\n // The block's offset in the message markdown (react-markdown hands every component its hast node) — its identity and\n // its document order in the render report.\n const sourceOffset = (\n conversationWidgetRenderProps.node as { position?: { start?: { offset?: number } } } | undefined\n )?.position?.start?.offset;\n const isReported = renderReportCollector !== null && justDelivered && messageId !== undefined;\n const renderReporter = useMemo(\n () =>\n isReported && messageId !== undefined\n ? renderReportCollector.reporterForBlock(messageId, widget.name, sourceOffset ?? 0)\n : null,\n [isReported, renderReportCollector, messageId, widget.name, sourceOffset],\n );\n useEffect(() => {\n if (isReported && messageId !== undefined)\n renderReportCollector.registerBlock(messageId, widget.name, sourceOffset ?? 0);\n }, [isReported, renderReportCollector, messageId, widget.name, sourceOffset]);\n\n let node = (\n <Component\n {...conversationWidgetRenderProps}\n fresh={interactive}\n interactive={interactive}\n justDelivered={justDelivered}\n messageId={messageId}\n />\n );\n\n if (featureFlag) {\n node = (\n <ConversationWidgetFeatureGate\n featureFlag={featureFlag}\n UpsellTrigger={upsellTrigger}\n agentSpaceKeyAttribute={readWidgetAgentSpaceKeyAttribute(conversationWidgetRenderProps)}\n >\n {node}\n </ConversationWidgetFeatureGate>\n );\n }\n\n // Viewport-windowing: a widget is mounted only while near the viewport and collapses to a height-preserving\n // placeholder when far away — so a long conversation keeps a constant number of widgets mounted. The INTERACTIVE\n // widget (the live reply, and the newest reply after a reload) is pinned — never unmounted, so its controls stay\n // and its one-shot effects don't re-fire; older widgets start from the cheap index-window guess and reveal/hide as\n // they scroll in and out. The feature gate lives INSIDE, so it only runs once the widget is actually mounted.\n node = (\n // Pinned on delivery as well as on controls: a just-delivered widget that lost its controls (a newer widget of the\n // same type superseded it) must not unmount when scrolled past, or its remount re-fires the one-shot canvas open.\n <WindowedWidget initialRevealed={!hideWidgets} pinned={interactive || justDelivered}>\n {node}\n </WindowedWidget>\n );\n\n // Outermost gate: widget props come from LLM output, so any throw inside the\n // widget (or its gates) must not unmount the whole chat message.\n node = (\n <ConversationWidgetErrorBoundary\n widgetName={widget.name}\n onCrash={() =>\n renderReporter?.report({ status: \"failed\", code: \"RENDER_FAILED\", detail: \"the widget threw while rendering\" })\n }\n >\n <WidgetRenderReportContext.Provider value={renderReporter}>{node}</WidgetRenderReportContext.Provider>\n </ConversationWidgetErrorBoundary>\n );\n\n return <div className={styles.conversationWidgetWrapper}>{node}</div>;\n};\n","import React from \"react\";\nimport styles from \"./ConversationMarkdown.module.scss\";\n\nexport type MarkdownLinkProps = React.AnchorHTMLAttributes<HTMLAnchorElement> & {\n /** The hast node react-markdown passes to every custom component; not a valid DOM attribute. */\n node?: unknown;\n};\n\n// Render every markdown link as a new-tab anchor with a dashed underline. `node` is dropped before the\n// rest is spread onto <a> so react-markdown's hast node never lands on the DOM element.\nexport const MarkdownLink = ({ node, children, ...anchorProps }: MarkdownLinkProps) => (\n <a {...anchorProps} target=\"_blank\" rel=\"noopener noreferrer\" className={styles.markdownLink}>\n {children}\n </a>\n);\n","import React, { useContext, useMemo } from \"react\";\nimport { type AgentsMarkdownProps, Markdown } from \"../provider/components\";\nimport remarkDirective from \"remark-directive\";\nimport remarkWidgetFencePlugin from \"./remarkWidgetFencePlugin\";\nimport conversationWidgetAstPlugin from \"./conversationWidgetAstPlugin\";\nimport restoreTextDirectivesPlugin from \"./restoreTextDirectivesPlugin\";\nimport { conversationWidgetRegistry, getConversationWidgetRegistryRevision } from \"./conversationWidgetRegistry\";\nimport { ConversationWidgetHost, ConversationWidgetRenderProps } from \"./ConversationWidgetHost\";\nimport { MarkdownLink } from \"./MarkdownLink\";\n\nexport interface ConversationMarkdownProps {\n markdown: string;\n /**\n * Forwarded into every widget instance as `interactive` (and its deprecated alias `fresh`): the widget's controls\n * may be used. True for a live-delivered message and for the newest assistant reply after a reload.\n */\n interactive?: boolean;\n /**\n * Forwarded into every widget instance as `justDelivered`: the message was delivered live right now. The only gate\n * for one-shot side effects (canvas open, draft creation); never true after a reload or on replay.\n */\n justDelivered?: boolean;\n /** Id of the host message, forwarded so a widget can identify the canvas it opens. */\n messageId?: string | number;\n /**\n * When true, widgets are replaced with a lightweight placeholder.\n * Used for old messages to keep the chat scroll cheap.\n */\n hideWidgets?: boolean;\n}\n\n/**\n * Render flags delivered via context so the widget component map can live at module\n * scope. The map must keep stable component identities across renders: react-markdown\n * reconciles by element type, so a per-render `Wrapped` closure makes React unmount and\n * remount every widget on each streaming tick — resetting one-shot refs (duplicate\n * drawer opens / POSTs) and re-triggering hydration.\n */\nconst ConversationWidgetRenderContext = React.createContext<{\n interactive: boolean;\n justDelivered: boolean;\n hideWidgets: boolean;\n messageId?: string | number;\n}>({\n interactive: false,\n justDelivered: false,\n hideWidgets: false,\n});\n\n// Map every widget name to a component that delegates rendering to ConversationWidgetHost,\n// which owns the per-widget concerns (interactive / justDelivered, feature-flag gate, hidden placeholder, block spacing).\n// Built lazily: the registry imports every widget and a widget can import AgentChat, which renders this file.\nconst buildConversationWidgetComponents = () =>\n conversationWidgetRegistry.widgets.reduce<Record<string, React.ComponentType<ConversationWidgetRenderProps>>>(\n (acc, widget) => {\n const Wrapped: React.ComponentType<ConversationWidgetRenderProps> = (innerProps) => {\n const { interactive, justDelivered, hideWidgets, messageId } = useContext(ConversationWidgetRenderContext);\n\n return (\n <ConversationWidgetHost\n widget={widget}\n interactive={interactive}\n justDelivered={justDelivered}\n messageId={messageId}\n hideWidgets={hideWidgets}\n conversationWidgetRenderProps={innerProps}\n />\n );\n };\n Wrapped.displayName = `ConversationWidget(${widget.name})`;\n acc[widget.name] = Wrapped;\n return acc;\n },\n {},\n );\n\nlet markdownComponents: AgentsMarkdownProps[\"components\"] | undefined;\nlet markdownComponentsRevision = -1;\n\n// Rebuilt only when an app registered more widgets since (`registerConversationWidgets`): the identities stay stable.\nconst getMarkdownComponents = (): AgentsMarkdownProps[\"components\"] => {\n if (markdownComponents === undefined || markdownComponentsRevision !== getConversationWidgetRegistryRevision()) {\n markdownComponents = {\n ...buildConversationWidgetComponents(),\n a: MarkdownLink,\n } as AgentsMarkdownProps[\"components\"];\n markdownComponentsRevision = getConversationWidgetRegistryRevision();\n }\n return markdownComponents;\n};\n\n// remarkWidgetFencePlugin rewrites ```widget fenced blocks into the node type remark-directive parses `::Name{}` into,\n// BEFORE conversationWidgetAstPlugin maps them to components — so the model's ```widget egress and the legacy\n// `::Name{}` egress share one render path (ConversationWidgetHost + feature-gate + hydration).\n// restoreTextDirectivesPlugin runs LAST: remark-directive also parses the `:00` inside \"08:00–16:30\" as a text\n// directive (digits are valid names) and swallowed it in the customer chat — text directives are never widgets.\nconst remarkPlugins = [\n remarkDirective,\n remarkWidgetFencePlugin,\n conversationWidgetAstPlugin,\n restoreTextDirectivesPlugin,\n];\n\nexport const ConversationMarkdown = (props: ConversationMarkdownProps) => {\n const { markdown, interactive = false, justDelivered = false, hideWidgets = false, messageId } = props;\n\n const renderContext = useMemo(\n () => ({ interactive, justDelivered, hideWidgets, messageId }),\n [interactive, justDelivered, hideWidgets, messageId],\n );\n\n return (\n <ConversationWidgetRenderContext.Provider value={renderContext}>\n <Markdown remarkPlugins={remarkPlugins} components={getMarkdownComponents()} markdown={markdown} />\n </ConversationWidgetRenderContext.Provider>\n );\n};\n","import { getFurtherWidgetClickSignalSources } from \"../provider/environment\";\n\n/**\n * The `signalSource`s a widget CLICK posts into the conversation (`injectSignal`): the generic widget click and the\n * dedicated source the former owner-action consent widget sent (no component posts it any more; stored rows keep it),\n * plus the further sources an app's own widgets post (`AgentsEnvironment.furtherWidgetClickSignalSources`, e.g. a form\n * submit that starts its own owner turn). The message list reads them to tell an answered widget from an open one after\n * a reload — a widget the owner already clicked must never come back interactive (a consent widget re-armed is a second\n * confirmation for a destructive tool). Add a source here when a widget of the package starts posting a new one.\n */\nexport const FRONTEND_WIDGET_CLICK_SIGNAL_SOURCE = \"FRONTEND_WIDGET_CLICK\";\nexport const OWNER_ACTION_CONFIRM_SIGNAL_SOURCE = \"OWNER_ACTION_CONFIRM\";\n\n/** The package's own widget click sources (the backend's sources of the package that carry an owner statement). */\nexport const PACKAGE_WIDGET_CLICK_SIGNAL_SOURCES: readonly string[] = [\n FRONTEND_WIDGET_CLICK_SIGNAL_SOURCE,\n OWNER_ACTION_CONFIRM_SIGNAL_SOURCE,\n];\n\n/** Every widget click source: the package's own and the app's further ones. */\nexport const getWidgetClickSignalSources = (): readonly string[] => [\n ...PACKAGE_WIDGET_CLICK_SIGNAL_SOURCES,\n ...getFurtherWidgetClickSignalSources(),\n];\n\nexport const isWidgetClickSignalSource = (signalSource: string | undefined | null): boolean =>\n signalSource != null && getWidgetClickSignalSources().includes(signalSource);\n"],"names":["next","styles"],"mappings":"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"}
1
+ {"version":3,"file":"widget-click-signal-sources-BR82QygL.js","sources":["../../src/renderer/resolve-deep-linkable-widget.ts","../../src/renderer/render-report/widget-render-report-collector.ts","../../src/renderer/widget-windowing.tsx","../../src/renderer/conversationWidgetAstPlugin.ts","../../src/renderer/restoreTextDirectivesPlugin.ts","../../src/renderer/WindowedWidget/WindowedWidget.tsx","../../src/renderer/ConversationWidgetHost.tsx","../../src/renderer/MarkdownLink.tsx","../../src/renderer/ConversationMarkdown.tsx","../../src/renderer/widget-click-signal-sources.ts"],"sourcesContent":["import type { CanvasId } from \"./canvas/canvas-behaviours\";\nimport { ConversationWidgetEntry, conversationWidgetRegistry } from \"./conversationWidgetRegistry\";\n\n/**\n * Resolves the widget a `?agentWidget=<name>` deep-link (or a restored pinned canvas) may mount, by its CANONICAL\n * registry wire name.\n *\n * A URL is untrusted input, so this is an allow-list rather than a registry lookup: only an entry that declares\n * `deepLinkable: true` can be opened this way. Every other widget is emitted by the agent inside a message, where the\n * conversation itself is the authorisation — a link must not be able to mount an arbitrary widget (some carry\n * mount-time side effects) just because its name was typed into the address bar.\n *\n * Returns the entry (the caller renders its `component`, applying the feature gate the entry declares) or null when the\n * name is empty, unknown, or not deep-linkable.\n */\nexport const resolveDeepLinkableWidget = (widgetName?: string | null): ConversationWidgetEntry | null => {\n if (!widgetName) return null;\n return (\n conversationWidgetRegistry.widgets.find((entry) => entry.name === widgetName && entry.deepLinkable === true) ?? null\n );\n};\n\n/**\n * The reverse direction: which deep-linkable widget re-opens THIS canvas. The open canvas of the agent chat is\n * mirrored into `?agentWidget=` (AgentTrigger) so a reload brings the customer back to the surface they were working\n * on — an editor a cold chat opened has no conversation id yet, so the canvas model's own pointer storage\n * has nothing to restore from.\n *\n * Null for every canvas no deep-linkable widget declares: it is the agent's to open inside a message, and a URL must\n * not be able to mount it (the allow-list above).\n */\nexport const deepLinkableWidgetNameForCanvas = (canvasId: CanvasId | null | undefined): string | null => {\n if (!canvasId) return null;\n return (\n conversationWidgetRegistry.widgets.find(\n (entry) => entry.deepLinkable === true && entry.opensCanvases?.includes(canvasId),\n )?.name ?? null\n );\n};\n","import { createContext } from \"react\";\nimport type { WidgetRenderReporter, WidgetRenderReportEntry } from \"./widget-render-report\";\n\n/**\n * The render reports of the widget blocks of live messages, collected per message. The chat host\n * (`AgentChat`) owns one collector and hands it down; `ConversationWidgetHost` registers every block of a JUST-DELIVERED\n * message (never a replayed one) and gives the widget a reporter for it. A block that reports nothing counts as\n * `mounted`. Once a message's blocks have settled — none `pending` for `settleMs`, or `maxWaitMs` after the first block\n * at the latest — the collector calls `onSettled` ONCE for that message with its blocks in document order; the host\n * sends the signal only when a block is not `mounted` or rendered with a note (a `mounted` entry with a code: figures\n * without a source), so a clean render costs nothing.\n */\n\nexport interface WidgetRenderBlockReport {\n widgetName: string;\n /** The block's offset in the message's markdown — document order, and the block's identity in the message. */\n sourceOffset: number;\n entry: WidgetRenderReportEntry;\n}\n\nexport interface WidgetRenderReportCollector {\n /** Counts one block of one live message as `mounted` unless it already reported (a child reports before its host mounts). */\n registerBlock: (messageId: string | number, widgetName: string, sourceOffset: number) => void;\n /** The reporter of one block of one live message. */\n reporterForBlock: (messageId: string | number, widgetName: string, sourceOffset: number) => WidgetRenderReporter;\n /** Forget every pending timer (the host unmounts or the conversation switches). */\n dispose: () => void;\n}\n\nexport interface WidgetRenderReportCollectorOptions {\n onSettled: (messageId: string, blocks: WidgetRenderBlockReport[]) => void;\n settleMs?: number;\n maxWaitMs?: number;\n}\n\ninterface MessageReports {\n blocksByKey: Map<string, WidgetRenderBlockReport>;\n firstReportAt: number;\n settleTimer: ReturnType<typeof setTimeout> | null;\n isSent: boolean;\n}\n\nexport const createWidgetRenderReportCollector = (\n options: WidgetRenderReportCollectorOptions,\n): WidgetRenderReportCollector => {\n const { onSettled, settleMs = 1500, maxWaitMs = 20000 } = options;\n const reportsByMessageId = new Map<string, MessageReports>();\n\n const scheduleSettleCheck = (messageId: string) => {\n const messageReports = reportsByMessageId.get(messageId);\n if (!messageReports || messageReports.isSent) return;\n if (messageReports.settleTimer !== null) clearTimeout(messageReports.settleTimer);\n const hasPendingBlock = [...messageReports.blocksByKey.values()].some((block) => block.entry.status === \"pending\");\n const waitedMs = Date.now() - messageReports.firstReportAt;\n const delayMs = hasPendingBlock ? Math.max(0, Math.min(settleMs * 4, maxWaitMs - waitedMs)) : settleMs;\n messageReports.settleTimer = setTimeout(() => {\n messageReports.settleTimer = null;\n const isStillPending = [...messageReports.blocksByKey.values()].some((block) => block.entry.status === \"pending\");\n if (isStillPending && Date.now() - messageReports.firstReportAt < maxWaitMs) {\n scheduleSettleCheck(messageId);\n return;\n }\n messageReports.isSent = true;\n onSettled(\n messageId,\n [...messageReports.blocksByKey.values()].sort((first, second) => first.sourceOffset - second.sourceOffset),\n );\n }, delayMs);\n };\n\n const recordBlock = (\n messageId: string,\n widgetName: string,\n sourceOffset: number,\n entry: WidgetRenderReportEntry | null,\n ) => {\n let messageReports = reportsByMessageId.get(messageId);\n if (!messageReports) {\n messageReports = { blocksByKey: new Map(), firstReportAt: Date.now(), settleTimer: null, isSent: false };\n reportsByMessageId.set(messageId, messageReports);\n }\n if (messageReports.isSent) return;\n const blockKey = `${widgetName}@${sourceOffset}`;\n const knownBlock = messageReports.blocksByKey.get(blockKey);\n if (entry === null && knownBlock) return;\n messageReports.blocksByKey.set(blockKey, { widgetName, sourceOffset, entry: entry ?? { status: \"mounted\" } });\n scheduleSettleCheck(messageId);\n };\n\n return {\n registerBlock: (messageId, widgetName, sourceOffset) =>\n recordBlock(String(messageId), widgetName, sourceOffset, null),\n reporterForBlock: (messageId, widgetName, sourceOffset) => ({\n report: (entry: WidgetRenderReportEntry) => recordBlock(String(messageId), widgetName, sourceOffset, entry),\n }),\n dispose: () => {\n for (const messageReports of reportsByMessageId.values()) {\n if (messageReports.settleTimer !== null) clearTimeout(messageReports.settleTimer);\n }\n reportsByMessageId.clear();\n },\n };\n};\n\n/** The collector of the chat the widget lives in; absent = nobody listens (another host, a story, the e-mail capture). */\nexport const WidgetRenderReportCollectorContext = createContext<WidgetRenderReportCollector | null>(null);\n\n/** The stored prefix of a render-report signal — the inspector and the tests read it; never reworded. */\nexport const WIDGET_RENDER_REPORT_SIGNAL_PREFIX = \"Widget render report for message\";\n\n/** The signal source (backend `AIConversationSignalSource::FRONTEND_WIDGET_RENDER_REPORT`). */\nexport const WIDGET_RENDER_REPORT_SIGNAL_SOURCE = \"FRONTEND_WIDGET_RENDER_REPORT\";\n\nconst MAX_DETAIL_CHARACTERS = 400;\n\n/** The backend's cap on a signal text (`AIConversationMessage::MAX_SIGNAL_TEXT_CHARS`). */\nconst MAX_SIGNAL_TEXT_CHARS = 2000;\n/** Room kept for the \"… and N more\" line. */\nconst OMITTED_LINE_RESERVE = 80;\n\n/**\n * The model-visible signal for a settled message — null when every block mounted cleanly (nothing to tell). One line\n * per block that is not `mounted`, or that mounted with a note (a code: `INLINE_VALUE_WITHOUT_SOURCE`): its position\n * among the message's widgets, its name and its occurrence among widgets of that name, its status and code, the\n * detail. The closing sentence asks for the honest answer about a block the owner did not see, and for\n * a note that the next widget fixes it.\n */\nexport const buildWidgetRenderReportSignal = (messageId: string, blocks: WidgetRenderBlockReport[]): string | null => {\n const occurrenceByName = new Map<string, number>();\n const blockLines: string[] = [];\n blocks.forEach((block, blockPosition) => {\n const occurrence = (occurrenceByName.get(block.widgetName) ?? 0) + 1;\n occurrenceByName.set(block.widgetName, occurrence);\n if (block.entry.status === \"mounted\" && !block.entry.code) return;\n const status = block.entry.status === \"pending\" ? \"pending (did not finish loading)\" : block.entry.status;\n const code = block.entry.code ? ` ${block.entry.code}` : \"\";\n const detail = block.entry.detail ? ` — ${block.entry.detail.slice(0, MAX_DETAIL_CHARACTERS)}` : \"\";\n blockLines.push(`- widget ${blockPosition + 1} (${block.widgetName} #${occurrence}): ${status}${code}${detail}`);\n });\n if (blockLines.length === 0) return null;\n const header = `${WIDGET_RENDER_REPORT_SIGNAL_PREFIX} ${messageId}:`;\n const hasBlockNotShown = blocks.some((block) => block.entry.status !== \"mounted\");\n const hasNote = blocks.some((block) => block.entry.status === \"mounted\" && block.entry.code);\n const closing = [\n hasBlockNotShown\n ? \"The owner did not see these widgets as intended. In your next answer say so in one line where it matters and \" +\n 'give the content in prose; never refer to \"the widget above\" for them.'\n : \"\",\n hasNote\n ? \"The widgets marked mounted were shown; fix the noted part in every widget you write from now on (a figure you \" +\n \"researched names its publisher and page in `source`). Do not resend the widget for the note alone.\"\n : \"\",\n ]\n .filter((sentence) => sentence !== \"\")\n .join(\" \");\n // The backend keeps the first MAX_SIGNAL_TEXT_CHARS of a signal: whole lines only, the rest counted in one line.\n const keptLines: string[] = [];\n let signalLength = header.length + 1 + closing.length + OMITTED_LINE_RESERVE;\n for (const blockLine of blockLines) {\n if (signalLength + blockLine.length + 1 > MAX_SIGNAL_TEXT_CHARS) break;\n keptLines.push(blockLine);\n signalLength += blockLine.length + 1;\n }\n const omittedCount = blockLines.length - keptLines.length;\n if (omittedCount > 0) keptLines.push(`- … and ${omittedCount} more widget(s) that did not render as intended`);\n return `${header}\\n${keptLines.join(\"\\n\")}\\n${closing}`;\n};\n\nexport interface ParsedWidgetRenderReport {\n messageId: string;\n blocks: Array<{\n position: number;\n widgetName: string;\n occurrence: number;\n status: string;\n code: string | null;\n detail: string | null;\n }>;\n}\n\nconst REPORT_LINE =\n /^- widget (\\d+) \\(([A-Za-z]+) #(\\d+)\\): (mounted|empty|failed|pending)(?: \\(did not finish loading\\))?(?: ([A-Z_]+))?(?: — (.*))?$/;\n\n/** The report a stored signal text carries, or null for another text — the admin inspector's reader. */\nexport const parseWidgetRenderReportSignal = (signalText: string): ParsedWidgetRenderReport | null => {\n const lines = signalText.split(\"\\n\");\n const header = new RegExp(`^${WIDGET_RENDER_REPORT_SIGNAL_PREFIX} (\\\\S+):$`).exec(lines[0] ?? \"\");\n if (!header) return null;\n const blocks: ParsedWidgetRenderReport[\"blocks\"] = [];\n for (const line of lines.slice(1)) {\n const match = REPORT_LINE.exec(line);\n if (!match) continue;\n blocks.push({\n position: Number(match[1]),\n widgetName: match[2],\n occurrence: Number(match[3]),\n status: match[4],\n code: match[5] ?? null,\n detail: match[6] ?? null,\n });\n }\n return { messageId: header[1], blocks };\n};\n","import React, { createContext, RefObject, useContext, useEffect, useState } from \"react\";\n\n/**\n * Viewport-windowing for chat widgets. Heavy interactive widgets (maps, lists, hydrating components) are mounted ONLY\n * while near the viewport and unmounted back to a placeholder when far away — so a long conversation keeps a CONSTANT\n * number of widgets mounted no matter how far you scroll. Two thresholds give hysteresis (reveal early, hide late) so a\n * small scroll wobble never thrashes mount/unmount.\n *\n * The IntersectionObserver root is the chat scroll container, supplied via {@link WidgetWindowRootContext} by the chat\n * host (ConversationViewBody). Observers are POOLED per (root, margin): all placeholders share two observers, not two each.\n */\nexport const WidgetWindowRootContext = createContext<HTMLElement | null>(null);\n\n/** Mount when the widget is within this margin of the viewport. */\nconst REVEAL_MARGIN_PX = 600;\n/** Unmount only once it is beyond this (larger) margin — the gap to REVEAL is the hysteresis band. */\nconst HIDE_MARGIN_PX = 2000;\n\ntype Pool = { observer: IntersectionObserver; callbacks: Map<Element, (entry: IntersectionObserverEntry) => void> };\n// Keyed by root (null = viewport) → margin string → the shared observer + its per-element callbacks.\nconst poolsByRoot = new Map<Element | null, Map<string, Pool>>();\n\n/** Register `element` with the shared observer for (root, rootMargin); returns a disposer. */\nconst observeShared = (\n root: Element | null,\n rootMargin: string,\n element: Element,\n onEntry: (entry: IntersectionObserverEntry) => void,\n): (() => void) => {\n let byMargin = poolsByRoot.get(root);\n if (!byMargin) {\n byMargin = new Map();\n poolsByRoot.set(root, byMargin);\n }\n let pool = byMargin.get(rootMargin);\n if (!pool) {\n const callbacks = new Map<Element, (entry: IntersectionObserverEntry) => void>();\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n callbacks.get(entry.target)?.(entry);\n }\n },\n { root, rootMargin },\n );\n pool = { observer, callbacks };\n byMargin.set(rootMargin, pool);\n }\n pool.callbacks.set(element, onEntry);\n pool.observer.observe(element);\n\n return () => {\n const currentByMargin = poolsByRoot.get(root);\n const currentPool = currentByMargin?.get(rootMargin);\n if (!currentPool) return;\n currentPool.observer.unobserve(element);\n currentPool.callbacks.delete(element);\n if (currentPool.callbacks.size === 0) {\n currentPool.observer.disconnect();\n currentByMargin!.delete(rootMargin);\n if (currentByMargin!.size === 0) poolsByRoot.delete(root);\n }\n };\n};\n\n/**\n * Drives a widget's mounted/unmounted state from its viewport proximity.\n *\n * - `pinned` (the freshly-delivered widget) → always revealed, never observed: a live widget must not unmount while its\n * one-shot effects (canvas auto-open) are in play, and re-mounting it later would re-fire them.\n * - otherwise: starts at `initialRevealed` (the cheap index-window guess for first paint), then the reveal observer\n * mounts it when it scrolls within REVEAL_MARGIN and the hide observer unmounts it past HIDE_MARGIN.\n *\n * Returns `[revealed, reveal]` — `reveal` lets the placeholder force a manual mount (the \"Show\" affordance).\n */\nexport const useWidgetWindow = (\n targetRef: RefObject<HTMLElement | null>,\n options: { initialRevealed: boolean; pinned: boolean },\n): [boolean, () => void] => {\n const { initialRevealed, pinned } = options;\n const root = useContext(WidgetWindowRootContext);\n const [revealed, setRevealed] = useState(initialRevealed || pinned);\n\n useEffect(() => {\n if (pinned) {\n setRevealed(true);\n return undefined;\n }\n const element = targetRef.current;\n if (!element || typeof IntersectionObserver === \"undefined\") return undefined;\n\n const disposeReveal = observeShared(root, `${REVEAL_MARGIN_PX}px 0px`, element, (entry) => {\n if (entry.isIntersecting) setRevealed(true);\n });\n const disposeHide = observeShared(root, `${HIDE_MARGIN_PX}px 0px`, element, (entry) => {\n if (!entry.isIntersecting) setRevealed(false);\n });\n return () => {\n disposeReveal();\n disposeHide();\n };\n // Re-attach when the scroll root resolves (it is null on the first frame, then the container element).\n }, [root, pinned, targetRef]);\n\n return [revealed, () => setRevealed(true)];\n};\n","import { visit } from \"unist-util-visit\";\nimport { conversationWidgetRegistry, type ConversationWidgetEntry } from \"./conversationWidgetRegistry\";\nimport { resolveCanonicalWidgetName } from \"./widget-name-resolver\";\nimport { exceedsJsonContainerDepth } from \"./widget-tree/bounded-json\";\n\n/** Whether a `::Name{payload='…'}` payload nests deeper than the backend decodes — MALFORMED there, dropped here. */\nconst isPayloadTooDeep = (payload: unknown): boolean => {\n if (typeof payload !== \"string\") return false;\n try {\n return exceedsJsonContainerDepth(JSON.parse(payload));\n } catch {\n return false;\n }\n};\n\n/**\n * Maps the `::Name` nodes a message carries onto the registered components by rewriting `data.hName` to the CANONICAL\n * registry name — which is what `ConversationMarkdown` keys its component map by.\n *\n * The name on the node is whatever the message contains, so a stored conversation delivers the legacy\n * `::ExampleWidgetDirective{payload='…'}` form (and stored server `component` parts carry `\"name\":\"<Canonical>Directive\"`,\n * which `normalize-server-message-to-markdown` turns into exactly that). Resolution therefore goes\n * through `resolveCanonicalWidgetName` — the one legacy rule — never a direct set lookup, or every old message would\n * degrade to the `:name` plain-text fallback.\n */\nexport default function conversationWidgetAstPlugin() {\n const canonicalNames = new Set(conversationWidgetRegistry.widgets.map((w: ConversationWidgetEntry) => w.name));\n\n return (tree: any) => {\n // `\"leafDirective\"` is remark-directive's node type — its contract, not our vocabulary. It is the only form a\n // widget ever travels in: `remarkWidgetFencePlugin` produces it from a ```widget fence, and the stored `::Name`\n // syntax parses into it.\n visit(tree, [\"leafDirective\"], (node, index, parent) => {\n const canonicalName = resolveCanonicalWidgetName(node.name, canonicalNames);\n\n if (\n canonicalName &&\n index !== undefined &&\n index !== null &&\n parent &&\n isPayloadTooDeep(node.attributes?.payload)\n ) {\n parent.children[index] = { type: \"text\", value: \"\" };\n return;\n }\n\n if (canonicalName) {\n // Map properties to attributes for react-markdown\n node.data = {\n hName: canonicalName,\n hProperties: node.attributes || {},\n ...node.data,\n };\n return node;\n }\n\n // `index === 0` is falsy — a plain `!index` check would skip the text\n // fallback for a node that is its parent's first child.\n if (index === undefined || index === null || !parent) {\n return node;\n }\n\n // An unknown name becomes plain text. Keep the node's children so a `::Unknown[label]` form degrades to its\n // label instead of silently swallowing it.\n const asText = { type: \"text\", value: `:${node.name}` };\n parent.children.splice(index, 1, asText, ...(node.children ?? []));\n const prev = parent.children[index - 1];\n if (prev?.type === \"text\") {\n prev.value += asText.value;\n parent.children.splice(index, 1);\n }\n });\n };\n}\n","import { visit } from \"unist-util-visit\";\n\n/**\n * remark-directive parses EVERY `:name` run as a text directive — including the `:00` / `:30` inside a time such as\n * `08:00–16:30` (digits are valid directive names). Text directives are not part of our widget vocabulary (widgets\n * travel as leaf directives only, see `conversationWidgetAstPlugin`), so a text directive is always prose that the\n * parser misread: without this pass the customer chat rendered \"Montag: 08:00–16:30\" as \"08\" / \"–16\" (the `:00`\n * and `:30` swallowed, the unknown nodes rendered as breaks) while the admin inspector, which does not load\n * remark-directive, showed the line intact.\n *\n * Restores every text directive to the exact source slice it was parsed from, merging it into the neighbouring text\n * nodes so the paragraph reads as one run again.\n */\nexport default function restoreTextDirectivesPlugin() {\n return (tree: any, file: any) => {\n const source = String(file?.value ?? \"\");\n visit(tree, \"textDirective\", (node: any, index: number | undefined, parent: any) => {\n if (index === undefined || index === null || !parent) {\n return;\n }\n const start = node.position?.start?.offset;\n const end = node.position?.end?.offset;\n const literal =\n typeof start === \"number\" && typeof end === \"number\" && end > start\n ? source.slice(start, end)\n : `:${node.name}${node.children?.length ? `[${node.children.map((c: any) => c.value ?? \"\").join(\"\")}]` : \"\"}`;\n const asText = { type: \"text\", value: literal };\n parent.children.splice(index, 1, asText);\n // Merge with the neighbours so \"08\" + \":00\" + \"–16\" becomes one text node again.\n const prev = parent.children[index - 1];\n if (prev?.type === \"text\") {\n prev.value += asText.value;\n parent.children.splice(index, 1);\n const next = parent.children[index];\n if (next?.type === \"text\") {\n prev.value += next.value;\n parent.children.splice(index, 1);\n }\n return index - 1;\n }\n const next = parent.children[index + 1];\n if (next?.type === \"text\") {\n asText.value += next.value;\n parent.children.splice(index + 1, 1);\n }\n return index;\n });\n };\n}\n","import { InteractiveSurface, Text } from \"../../provider/components\";\nimport React, { useEffect, useRef } from \"react\";\nimport { translateText } from \"../../provider/environment\";\nimport { useWidgetWindow } from \"../widget-windowing\";\nimport styles from \"../HiddenWidgetPlaceholder/HiddenWidgetPlaceholder.module.scss\";\n\nexport interface WindowedWidgetProps {\n children: React.ReactNode;\n /** First-paint guess from the index window: recent messages start mounted, older ones start as a placeholder. */\n initialRevealed: boolean;\n /** The freshly-delivered widget — kept mounted (never windowed out) so its one-shot effects don't re-fire. */\n pinned: boolean;\n}\n\n/** Default reserved height for a widget that has never been measured (an old one that has not been scrolled to yet). */\nconst DEFAULT_PLACEHOLDER_MIN_HEIGHT = 80;\n\n/**\n * Mounts a chat widget only while it is near the viewport (see {@link useWidgetWindow}); far from the viewport it\n * collapses to a lightweight placeholder. The wrapper element is STABLE across the toggle so the shared\n * IntersectionObserver keeps tracking it. The placeholder reserves the widget's last measured height, so re-hiding it\n * causes ZERO layout shift (no scroll jump); revealing again restores the real widget (self-hydrating widgets re-fetch\n * with a brief skeleton).\n */\nexport const WindowedWidget = ({ children, initialRevealed, pinned }: WindowedWidgetProps) => {\n const wrapperRef = useRef<HTMLDivElement>(null);\n const [revealed, reveal] = useWidgetWindow(wrapperRef, { initialRevealed, pinned });\n // Remembered so the placeholder can reserve exactly the height the widget occupied.\n const lastHeightRef = useRef<number | undefined>(undefined);\n\n useEffect(() => {\n const element = wrapperRef.current;\n if (!revealed || !element || typeof ResizeObserver === \"undefined\") return undefined;\n const measure = () => {\n if (wrapperRef.current) lastHeightRef.current = wrapperRef.current.offsetHeight;\n };\n measure();\n const resizeObserver = new ResizeObserver(measure);\n resizeObserver.observe(element);\n return () => resizeObserver.disconnect();\n }, [revealed]);\n\n return (\n <div ref={wrapperRef}>\n {revealed ? (\n children\n ) : (\n <InteractiveSurface\n hoverable\n className={styles.placeholder}\n style={{ minHeight: lastHeightRef.current ?? DEFAULT_PLACEHOLDER_MIN_HEIGHT }}\n >\n <button\n type=\"button\"\n className={styles.placeholderButton}\n onClick={reveal}\n aria-label={translateText(\"Show hidden widget\")}\n >\n <Text>Older interactive content is hidden.</Text>\n <span className={styles.cta}>\n <Text>Show</Text>\n </span>\n </button>\n </InteractiveSurface>\n )}\n </div>\n );\n};\n","import React, { useContext, useEffect, useMemo } from \"react\";\nimport styles from \"./ConversationMarkdown.module.scss\";\nimport { ConversationWidgetEntry } from \"./conversationWidgetRegistry\";\nimport { ConversationWidgetFeatureGate } from \"./_access/ConversationWidgetFeatureGate\";\nimport { readWidgetAgentSpaceKeyAttribute } from \"./_agent-space/widget-agent-space-key-attribute\";\nimport { ConversationWidgetErrorBoundary } from \"./ConversationWidgetErrorBoundary\";\nimport { WindowedWidget } from \"./WindowedWidget/WindowedWidget\";\nimport { WidgetRenderReportContext } from \"./render-report/widget-render-report\";\nimport { WidgetRenderReportCollectorContext } from \"./render-report/widget-render-report-collector\";\n\nexport type ConversationWidgetRenderProps = Record<string, unknown> & {\n children?: React.ReactNode;\n fresh?: boolean;\n interactive?: boolean;\n justDelivered?: boolean;\n messageId?: string | number;\n};\n\ninterface ConversationWidgetHostProps {\n widget: ConversationWidgetEntry;\n /** Forwarded into the widget (and as its deprecated alias `fresh`): the widget's controls may be used. */\n interactive: boolean;\n /** Forwarded into the widget: the host message was delivered live right now — the one-shot side-effect gate. */\n justDelivered: boolean;\n /** Id of the host message, forwarded so a widget can identify the canvas it opens. */\n messageId?: string | number;\n /** When true, the widget renders as a lightweight placeholder (cheap scroll). */\n hideWidgets: boolean;\n /** Parsed markdown attributes for this widget instance. */\n conversationWidgetRenderProps: ConversationWidgetRenderProps;\n}\n\n/**\n * Single render path for every widget. Each concern is applied as a sequential gate\n * (innermost → outermost):\n * 1. the widget itself, with `interactive` / `justDelivered`\n * 2. feature-flag gate\n * 3. viewport-windowing (mount near the viewport, placeholder when far — WindowedWidget)\n * 4. block spacing wrapper\n *\n * A block of a JUST-DELIVERED message is also registered with the chat's render-report collector: it\n * counts as `mounted` unless the widget reports otherwise through `useReportWidgetRenderStatus`, and a crash caught by\n * the error boundary reports `failed` (`RENDER_FAILED`). A replayed message reports nothing.\n */\nexport const ConversationWidgetHost = ({\n widget,\n interactive,\n justDelivered,\n messageId,\n hideWidgets,\n conversationWidgetRenderProps,\n}: ConversationWidgetHostProps) => {\n const { component: Component, featureFlag, upsellTrigger } = widget;\n const renderReportCollector = useContext(WidgetRenderReportCollectorContext);\n // The block's offset in the message markdown (react-markdown hands every component its hast node) — its identity and\n // its document order in the render report.\n const sourceOffset = (\n conversationWidgetRenderProps.node as { position?: { start?: { offset?: number } } } | undefined\n )?.position?.start?.offset;\n const isReported = renderReportCollector !== null && justDelivered && messageId !== undefined;\n const renderReporter = useMemo(\n () =>\n isReported && messageId !== undefined\n ? renderReportCollector.reporterForBlock(messageId, widget.name, sourceOffset ?? 0)\n : null,\n [isReported, renderReportCollector, messageId, widget.name, sourceOffset],\n );\n useEffect(() => {\n if (isReported && messageId !== undefined)\n renderReportCollector.registerBlock(messageId, widget.name, sourceOffset ?? 0);\n }, [isReported, renderReportCollector, messageId, widget.name, sourceOffset]);\n\n let node = (\n <Component\n {...conversationWidgetRenderProps}\n fresh={interactive}\n interactive={interactive}\n justDelivered={justDelivered}\n messageId={messageId}\n />\n );\n\n if (featureFlag) {\n node = (\n <ConversationWidgetFeatureGate\n featureFlag={featureFlag}\n UpsellTrigger={upsellTrigger}\n agentSpaceKeyAttribute={readWidgetAgentSpaceKeyAttribute(conversationWidgetRenderProps)}\n >\n {node}\n </ConversationWidgetFeatureGate>\n );\n }\n\n // Viewport-windowing: a widget is mounted only while near the viewport and collapses to a height-preserving\n // placeholder when far away — so a long conversation keeps a constant number of widgets mounted. The INTERACTIVE\n // widget (the live reply, and the newest reply after a reload) is pinned — never unmounted, so its controls stay\n // and its one-shot effects don't re-fire; older widgets start from the cheap index-window guess and reveal/hide as\n // they scroll in and out. The feature gate lives INSIDE, so it only runs once the widget is actually mounted.\n node = (\n // Pinned on delivery as well as on controls: a just-delivered widget that lost its controls (a newer widget of the\n // same type superseded it) must not unmount when scrolled past, or its remount re-fires the one-shot canvas open.\n <WindowedWidget initialRevealed={!hideWidgets} pinned={interactive || justDelivered}>\n {node}\n </WindowedWidget>\n );\n\n // Outermost gate: widget props come from LLM output, so any throw inside the\n // widget (or its gates) must not unmount the whole chat message.\n node = (\n <ConversationWidgetErrorBoundary\n widgetName={widget.name}\n onCrash={() =>\n renderReporter?.report({ status: \"failed\", code: \"RENDER_FAILED\", detail: \"the widget threw while rendering\" })\n }\n >\n <WidgetRenderReportContext.Provider value={renderReporter}>{node}</WidgetRenderReportContext.Provider>\n </ConversationWidgetErrorBoundary>\n );\n\n return <div className={styles.conversationWidgetWrapper}>{node}</div>;\n};\n","import React from \"react\";\nimport styles from \"./ConversationMarkdown.module.scss\";\n\nexport type MarkdownLinkProps = React.AnchorHTMLAttributes<HTMLAnchorElement> & {\n /** The hast node react-markdown passes to every custom component; not a valid DOM attribute. */\n node?: unknown;\n};\n\n// Render every markdown link as a new-tab anchor with a dashed underline. `node` is dropped before the\n// rest is spread onto <a> so react-markdown's hast node never lands on the DOM element.\nexport const MarkdownLink = ({ node, children, ...anchorProps }: MarkdownLinkProps) => (\n <a {...anchorProps} target=\"_blank\" rel=\"noopener noreferrer\" className={styles.markdownLink}>\n {children}\n </a>\n);\n","import React, { useContext, useMemo } from \"react\";\nimport { type AgentsMarkdownProps, Markdown } from \"../provider/components\";\nimport remarkDirective from \"remark-directive\";\nimport remarkWidgetFencePlugin from \"./remarkWidgetFencePlugin\";\nimport conversationWidgetAstPlugin from \"./conversationWidgetAstPlugin\";\nimport restoreTextDirectivesPlugin from \"./restoreTextDirectivesPlugin\";\nimport { conversationWidgetRegistry, getConversationWidgetRegistryRevision } from \"./conversationWidgetRegistry\";\nimport { ConversationWidgetHost, ConversationWidgetRenderProps } from \"./ConversationWidgetHost\";\nimport { MarkdownLink } from \"./MarkdownLink\";\n\nexport interface ConversationMarkdownProps {\n markdown: string;\n /**\n * Forwarded into every widget instance as `interactive` (and its deprecated alias `fresh`): the widget's controls\n * may be used. True for a live-delivered message and for the newest assistant reply after a reload.\n */\n interactive?: boolean;\n /**\n * Forwarded into every widget instance as `justDelivered`: the message was delivered live right now. The only gate\n * for one-shot side effects (canvas open, draft creation); never true after a reload or on replay.\n */\n justDelivered?: boolean;\n /** Id of the host message, forwarded so a widget can identify the canvas it opens. */\n messageId?: string | number;\n /**\n * When true, widgets are replaced with a lightweight placeholder.\n * Used for old messages to keep the chat scroll cheap.\n */\n hideWidgets?: boolean;\n}\n\n/**\n * Render flags delivered via context so the widget component map can live at module\n * scope. The map must keep stable component identities across renders: react-markdown\n * reconciles by element type, so a per-render `Wrapped` closure makes React unmount and\n * remount every widget on each streaming tick — resetting one-shot refs (duplicate\n * drawer opens / POSTs) and re-triggering hydration.\n */\nconst ConversationWidgetRenderContext = React.createContext<{\n interactive: boolean;\n justDelivered: boolean;\n hideWidgets: boolean;\n messageId?: string | number;\n}>({\n interactive: false,\n justDelivered: false,\n hideWidgets: false,\n});\n\n// Map every widget name to a component that delegates rendering to ConversationWidgetHost,\n// which owns the per-widget concerns (interactive / justDelivered, feature-flag gate, hidden placeholder, block spacing).\n// Built lazily: the registry imports every widget and a widget can import AgentChat, which renders this file.\nconst buildConversationWidgetComponents = () =>\n conversationWidgetRegistry.widgets.reduce<Record<string, React.ComponentType<ConversationWidgetRenderProps>>>(\n (acc, widget) => {\n const Wrapped: React.ComponentType<ConversationWidgetRenderProps> = (innerProps) => {\n const { interactive, justDelivered, hideWidgets, messageId } = useContext(ConversationWidgetRenderContext);\n\n return (\n <ConversationWidgetHost\n widget={widget}\n interactive={interactive}\n justDelivered={justDelivered}\n messageId={messageId}\n hideWidgets={hideWidgets}\n conversationWidgetRenderProps={innerProps}\n />\n );\n };\n Wrapped.displayName = `ConversationWidget(${widget.name})`;\n acc[widget.name] = Wrapped;\n return acc;\n },\n {},\n );\n\nlet markdownComponents: AgentsMarkdownProps[\"components\"] | undefined;\nlet markdownComponentsRevision = -1;\n\n// Rebuilt only when an app registered more widgets since (`registerConversationWidgets`): the identities stay stable.\nconst getMarkdownComponents = (): AgentsMarkdownProps[\"components\"] => {\n if (markdownComponents === undefined || markdownComponentsRevision !== getConversationWidgetRegistryRevision()) {\n markdownComponents = {\n ...buildConversationWidgetComponents(),\n a: MarkdownLink,\n } as AgentsMarkdownProps[\"components\"];\n markdownComponentsRevision = getConversationWidgetRegistryRevision();\n }\n return markdownComponents;\n};\n\n// remarkWidgetFencePlugin rewrites ```widget fenced blocks into the node type remark-directive parses `::Name{}` into,\n// BEFORE conversationWidgetAstPlugin maps them to components — so the model's ```widget egress and the legacy\n// `::Name{}` egress share one render path (ConversationWidgetHost + feature-gate + hydration).\n// restoreTextDirectivesPlugin runs LAST: remark-directive also parses the `:00` inside \"08:00–16:30\" as a text\n// directive (digits are valid names) and swallowed it in the customer chat — text directives are never widgets.\nconst remarkPlugins = [\n remarkDirective,\n remarkWidgetFencePlugin,\n conversationWidgetAstPlugin,\n restoreTextDirectivesPlugin,\n];\n\nexport const ConversationMarkdown = (props: ConversationMarkdownProps) => {\n const { markdown, interactive = false, justDelivered = false, hideWidgets = false, messageId } = props;\n\n const renderContext = useMemo(\n () => ({ interactive, justDelivered, hideWidgets, messageId }),\n [interactive, justDelivered, hideWidgets, messageId],\n );\n\n return (\n <ConversationWidgetRenderContext.Provider value={renderContext}>\n <Markdown remarkPlugins={remarkPlugins} components={getMarkdownComponents()} markdown={markdown} />\n </ConversationWidgetRenderContext.Provider>\n );\n};\n","import { getFurtherWidgetClickSignalSources } from \"../provider/environment\";\n\n/**\n * The `signalSource`s a widget CLICK posts into the conversation (`injectSignal`): the generic widget click and the\n * dedicated source the former owner-action consent widget sent (no component posts it any more; stored rows keep it),\n * plus the further sources an app's own widgets post (`AgentsEnvironment.furtherWidgetClickSignalSources`, e.g. a form\n * submit that starts its own owner turn). The message list reads them to tell an answered widget from an open one after\n * a reload — a widget the owner already clicked must never come back interactive (a consent widget re-armed is a second\n * confirmation for a destructive tool). Add a source here when a widget of the package starts posting a new one.\n */\nexport const FRONTEND_WIDGET_CLICK_SIGNAL_SOURCE = \"FRONTEND_WIDGET_CLICK\";\nexport const OWNER_ACTION_CONFIRM_SIGNAL_SOURCE = \"OWNER_ACTION_CONFIRM\";\n\n/** The package's own widget click sources (the backend's sources of the package that carry an owner statement). */\nexport const PACKAGE_WIDGET_CLICK_SIGNAL_SOURCES: readonly string[] = [\n FRONTEND_WIDGET_CLICK_SIGNAL_SOURCE,\n OWNER_ACTION_CONFIRM_SIGNAL_SOURCE,\n];\n\n/** Every widget click source: the package's own and the app's further ones. */\nexport const getWidgetClickSignalSources = (): readonly string[] => [\n ...PACKAGE_WIDGET_CLICK_SIGNAL_SOURCES,\n ...getFurtherWidgetClickSignalSources(),\n];\n\nexport const isWidgetClickSignalSource = (signalSource: string | undefined | null): boolean =>\n signalSource != null && getWidgetClickSignalSources().includes(signalSource);\n"],"names":["next","styles"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
3
3
  import React, { useReducer, useRef, useCallback, useMemo, useEffect, useState, useContext, createContext, useLayoutEffect, useId, useSyncExternalStore, Fragment as Fragment$1 } from "react";
4
- import { b as registerWidgetHydrator, X as SkeletonLine, U as WidgetSurface, ac as WidgetKpiValue, A as readWidgetAgentSpaceKeyAttribute, W as WidgetRenderReportContext, j as buildWidgetAgentSpaceKeyProps, aB as readTextNodeContent, aC as readAllowedValue, E as useConversationWidgetContext, M as ModelText, a3 as DonePill, aA as ConversationWidgetButton, D as shortStringHash, K as WidgetImageReadinessContext, y as messageAnchor, C as ConversationWidgetActions, aD as useReportTextNodeRenderState, aE as readTextNodeRenderState, a7 as SkeletonPill, aF as buildFaviconUrl, aG as readSiteHostname, V as SkeletonCircle, aH as readSafeHttpUrl, H as useWidgetHydration, F as useIsEmailWidgetRender, G as useReportWidgetRenderStatus, _ as ClockConversationWidget, $ as ConceptExplainerConversationWidget, a1 as CurrencyConverterConversationWidget, a2 as CustomVisualizationConversationWidget, aw as normalizeEntityMentionFields, a4 as EntityMentionConversationWidget, ad as WritingDraftConversationWidget, a9 as SuggestedActions, a5 as MultiChoiceConversationWidget, a6 as QuickRepliesConversationWidget, aa as ToolTraceConversationWidget, a0 as ConversationSearchResultsConversationWidget, ab as WebSearchResultsConversationWidget } from "./WebSearchResultsConversationWidget-as3p5bHw.js";
4
+ import { b as registerWidgetHydrator, X as SkeletonLine, U as WidgetSurface, ad as WidgetKpiValue, A as readWidgetAgentSpaceKeyAttribute, W as WidgetRenderReportContext, j as buildWidgetAgentSpaceKeyProps, aD as readTextNodeContent, aE as readAllowedValue, E as useConversationWidgetContext, M as ModelText, a4 as DonePill, aC as ConversationWidgetButton, D as shortStringHash, K as WidgetImageReadinessContext, y as messageAnchor, C as ConversationWidgetActions, aF as useReportTextNodeRenderState, aG as readTextNodeRenderState, a8 as SkeletonPill, aH as buildFaviconUrl, aI as readSiteHostname, V as SkeletonCircle, aJ as readSafeHttpUrl, H as useWidgetHydration, F as useIsEmailWidgetRender, G as useReportWidgetRenderStatus, $ as ClockConversationWidget, a0 as ConceptExplainerConversationWidget, a2 as CurrencyConverterConversationWidget, a3 as CustomVisualizationConversationWidget, ay as normalizeEntityMentionFields, a5 as EntityMentionConversationWidget, ae as WritingDraftConversationWidget, aa as SuggestedActions, a6 as MultiChoiceConversationWidget, a7 as QuickRepliesConversationWidget, ab as ToolTraceConversationWidget, a1 as ConversationSearchResultsConversationWidget, ac as WebSearchResultsConversationWidget } from "./WebSearchResultsConversationWidget-tDhawe9f.js";
5
5
  import { V as getWidgetSelfHydrationKeyFieldName, J as getAgentsEnvironment, E as escapePlainTextForText, q as Text, I as Icon, a5 as useAgentsOwnerSettings, z as classNames, B as Button, S as SegmentBar, u as TrendDelta, v as WidgetText, $ as translateText, Z as reportWidgetCrash, W as WidgetFeatureGate, f as InteractiveSurface, C as Checkbox, d as DateInput, s as TextInput, R as RadioGroup, k as Select, j as RangeSlider, T as Tabs, r as TextArea, M as Map$1, g as MapMarker, a4 as useAgentsMapsLoader, l as Skeleton, X as openImageGallery, o as StatusBadge, h as Markdown } from "./index-7Qc_3GbG.js";
6
6
  import ReactApexChartImport from "react-apexcharts";
7
7
  import { i as isAgentsIconName } from "./icon-names-Di6c4zLo.js";
@@ -9696,4 +9696,4 @@ export {
9696
9696
  getConversationWidgetRegistryRevision as y,
9697
9697
  readComposedViewTree as z
9698
9698
  };
9699
- //# sourceMappingURL=widget-name-resolver-BGN3HpoG.js.map
9699
+ //# sourceMappingURL=widget-name-resolver-BaxKaRa3.js.map