@athenaintel/react 0.48.0 → 0.50.0

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 (179) hide show
  1. package/README.md +48 -10
  2. package/dist/agent-runtime/src/call-recap.cjs +319 -0
  3. package/dist/agent-runtime/src/call-recap.cjs.map +1 -0
  4. package/dist/agent-runtime/src/call-recap.d.ts +142 -0
  5. package/dist/agent-runtime/src/call-recap.js +319 -0
  6. package/dist/agent-runtime/src/call-recap.js.map +1 -0
  7. package/dist/agent-runtime/src/chat-outbox-store.cjs +1 -0
  8. package/dist/agent-runtime/src/chat-outbox-store.cjs.map +1 -1
  9. package/dist/agent-runtime/src/chat-outbox-store.d.ts +7 -0
  10. package/dist/agent-runtime/src/chat-outbox-store.js +1 -0
  11. package/dist/agent-runtime/src/chat-outbox-store.js.map +1 -1
  12. package/dist/agent-runtime/src/chat-outbox.cjs +46 -5
  13. package/dist/agent-runtime/src/chat-outbox.cjs.map +1 -1
  14. package/dist/agent-runtime/src/chat-outbox.d.ts +29 -0
  15. package/dist/agent-runtime/src/chat-outbox.js +46 -5
  16. package/dist/agent-runtime/src/chat-outbox.js.map +1 -1
  17. package/dist/agent-runtime/src/conversation-index.cjs.map +1 -1
  18. package/dist/agent-runtime/src/conversation-index.d.ts +7 -0
  19. package/dist/agent-runtime/src/conversation-index.js.map +1 -1
  20. package/dist/agent-runtime/src/conversation-list.cjs +2 -1
  21. package/dist/agent-runtime/src/conversation-list.cjs.map +1 -1
  22. package/dist/agent-runtime/src/conversation-list.d.ts +19 -3
  23. package/dist/agent-runtime/src/conversation-list.js +2 -1
  24. package/dist/agent-runtime/src/conversation-list.js.map +1 -1
  25. package/dist/agent-runtime/src/index.d.ts +6 -5
  26. package/dist/agent-runtime/src/interrupts.cjs +28 -1
  27. package/dist/agent-runtime/src/interrupts.cjs.map +1 -1
  28. package/dist/agent-runtime/src/interrupts.d.ts +21 -1
  29. package/dist/agent-runtime/src/interrupts.js +28 -1
  30. package/dist/agent-runtime/src/interrupts.js.map +1 -1
  31. package/dist/agent-runtime/src/model-context.cjs +3 -2
  32. package/dist/agent-runtime/src/model-context.cjs.map +1 -1
  33. package/dist/agent-runtime/src/model-context.d.ts +6 -1
  34. package/dist/agent-runtime/src/model-context.js +3 -2
  35. package/dist/agent-runtime/src/model-context.js.map +1 -1
  36. package/dist/agent-runtime/src/runtime.d.ts +3 -2
  37. package/dist/agent-runtime/src/send-recovery.cjs +37 -3
  38. package/dist/agent-runtime/src/send-recovery.cjs.map +1 -1
  39. package/dist/agent-runtime/src/send-recovery.d.ts +19 -0
  40. package/dist/agent-runtime/src/send-recovery.js +37 -3
  41. package/dist/agent-runtime/src/send-recovery.js.map +1 -1
  42. package/dist/agent-runtime/src/thread-handoff-flow-machine.d.ts +8 -8
  43. package/dist/agent-runtime/src/thread-handoff-protocol.d.ts +113 -1
  44. package/dist/agent-runtime/src/thread-handoff.d.ts +1 -1
  45. package/dist/agent-runtime/src/use-conversation-list.cjs +3 -2
  46. package/dist/agent-runtime/src/use-conversation-list.cjs.map +1 -1
  47. package/dist/agent-runtime/src/use-conversation-list.d.ts +6 -0
  48. package/dist/agent-runtime/src/use-conversation-list.js +3 -2
  49. package/dist/agent-runtime/src/use-conversation-list.js.map +1 -1
  50. package/dist/agent-runtime/src/use-deep-agent-chat-outbox.cjs +21 -1
  51. package/dist/agent-runtime/src/use-deep-agent-chat-outbox.cjs.map +1 -1
  52. package/dist/agent-runtime/src/use-deep-agent-chat-outbox.d.ts +8 -2
  53. package/dist/agent-runtime/src/use-deep-agent-chat-outbox.js +21 -1
  54. package/dist/agent-runtime/src/use-deep-agent-chat-outbox.js.map +1 -1
  55. package/dist/agent-runtime/src/use-deep-agent-runtime.cjs +24 -6
  56. package/dist/agent-runtime/src/use-deep-agent-runtime.cjs.map +1 -1
  57. package/dist/agent-runtime/src/use-deep-agent-runtime.d.ts +17 -0
  58. package/dist/agent-runtime/src/use-deep-agent-runtime.js +24 -6
  59. package/dist/agent-runtime/src/use-deep-agent-runtime.js.map +1 -1
  60. package/dist/agent-runtime/src/use-deep-agent-voice.cjs +44 -9
  61. package/dist/agent-runtime/src/use-deep-agent-voice.cjs.map +1 -1
  62. package/dist/agent-runtime/src/use-deep-agent-voice.d.ts +34 -1
  63. package/dist/agent-runtime/src/use-deep-agent-voice.js +44 -9
  64. package/dist/agent-runtime/src/use-deep-agent-voice.js.map +1 -1
  65. package/dist/agent-runtime/src/use-thread-handoff-flow.d.ts +8 -2
  66. package/dist/agent-runtime/src/voice-earcon.cjs +139 -0
  67. package/dist/agent-runtime/src/voice-earcon.cjs.map +1 -0
  68. package/dist/agent-runtime/src/voice-earcon.d.ts +38 -0
  69. package/dist/agent-runtime/src/voice-earcon.js +139 -0
  70. package/dist/agent-runtime/src/voice-earcon.js.map +1 -0
  71. package/dist/agent-runtime/src/voice-media.cjs.map +1 -1
  72. package/dist/agent-runtime/src/voice-media.d.ts +14 -0
  73. package/dist/agent-runtime/src/voice-media.js.map +1 -1
  74. package/dist/agent-runtime/src/voice-presentation.cjs +2 -0
  75. package/dist/agent-runtime/src/voice-presentation.cjs.map +1 -1
  76. package/dist/agent-runtime/src/voice-presentation.js +2 -0
  77. package/dist/agent-runtime/src/voice-presentation.js.map +1 -1
  78. package/dist/assets/AthenaAssetEmbed.cjs.map +1 -1
  79. package/dist/assets/AthenaAssetEmbed.d.ts +11 -5
  80. package/dist/assets/AthenaAssetEmbed.js.map +1 -1
  81. package/dist/assets/use-asset-embed.cjs.map +1 -1
  82. package/dist/assets/use-asset-embed.d.ts +12 -5
  83. package/dist/assets/use-asset-embed.js.map +1 -1
  84. package/dist/chat/StatewireApprovalCard.cjs +41 -6
  85. package/dist/chat/StatewireApprovalCard.cjs.map +1 -1
  86. package/dist/chat/StatewireApprovalCard.d.ts +18 -1
  87. package/dist/chat/StatewireApprovalCard.js +42 -7
  88. package/dist/chat/StatewireApprovalCard.js.map +1 -1
  89. package/dist/chat/StatewireQueuedMessages.cjs +56 -6
  90. package/dist/chat/StatewireQueuedMessages.cjs.map +1 -1
  91. package/dist/chat/StatewireQueuedMessages.d.ts +14 -0
  92. package/dist/chat/StatewireQueuedMessages.js +58 -8
  93. package/dist/chat/StatewireQueuedMessages.js.map +1 -1
  94. package/dist/chat/WaitingOnYouBanner.cjs +6 -2
  95. package/dist/chat/WaitingOnYouBanner.cjs.map +1 -1
  96. package/dist/chat/WaitingOnYouBanner.js +7 -3
  97. package/dist/chat/WaitingOnYouBanner.js.map +1 -1
  98. package/dist/chat/chat-voice-controls.cjs +3 -1
  99. package/dist/chat/chat-voice-controls.cjs.map +1 -1
  100. package/dist/chat/chat-voice-controls.d.ts +6 -1
  101. package/dist/chat/chat-voice-controls.js +3 -1
  102. package/dist/chat/chat-voice-controls.js.map +1 -1
  103. package/dist/chat-ui/index.cjs +3 -0
  104. package/dist/chat-ui/index.cjs.map +1 -1
  105. package/dist/chat-ui/index.d.ts +1 -0
  106. package/dist/chat-ui/index.js +3 -0
  107. package/dist/chat-ui/index.js.map +1 -1
  108. package/dist/chat-ui/messages/from-the-call-card.cjs +124 -0
  109. package/dist/chat-ui/messages/from-the-call-card.cjs.map +1 -0
  110. package/dist/chat-ui/messages/from-the-call-card.d.ts +17 -0
  111. package/dist/chat-ui/messages/from-the-call-card.js +124 -0
  112. package/dist/chat-ui/messages/from-the-call-card.js.map +1 -0
  113. package/dist/chat-ui/rich-text/chat-link.cjs +1 -0
  114. package/dist/chat-ui/rich-text/chat-link.cjs.map +1 -1
  115. package/dist/chat-ui/rich-text/chat-link.d.ts +4 -0
  116. package/dist/chat-ui/rich-text/chat-link.js +4 -3
  117. package/dist/chat-ui/rich-text/chat-link.js.map +1 -1
  118. package/dist/chat-ui/rich-text/chat-message-body.cjs +1 -0
  119. package/dist/chat-ui/rich-text/chat-message-body.cjs.map +1 -1
  120. package/dist/chat-ui/rich-text/chat-message-body.d.ts +12 -0
  121. package/dist/chat-ui/rich-text/chat-message-body.js +2 -1
  122. package/dist/chat-ui/rich-text/chat-message-body.js.map +1 -1
  123. package/dist/chat-ui/thread/chat-thread-shell.cjs +2 -0
  124. package/dist/chat-ui/thread/chat-thread-shell.cjs.map +1 -1
  125. package/dist/chat-ui/thread/chat-thread-shell.js +2 -0
  126. package/dist/chat-ui/thread/chat-thread-shell.js.map +1 -1
  127. package/dist/collab/client.cjs +15 -3
  128. package/dist/collab/client.cjs.map +1 -1
  129. package/dist/collab/client.d.ts +4 -2
  130. package/dist/collab/client.js +16 -4
  131. package/dist/collab/client.js.map +1 -1
  132. package/dist/composer/index.d.ts +1 -1
  133. package/dist/composer/useReadComposerDraft.cjs +26 -7
  134. package/dist/composer/useReadComposerDraft.cjs.map +1 -1
  135. package/dist/composer/useReadComposerDraft.d.ts +23 -1
  136. package/dist/composer/useReadComposerDraft.js +27 -8
  137. package/dist/composer/useReadComposerDraft.js.map +1 -1
  138. package/dist/index.cjs +3 -0
  139. package/dist/index.cjs.map +1 -1
  140. package/dist/index.d.ts +3 -1
  141. package/dist/index.js +4 -1
  142. package/dist/index.js.map +1 -1
  143. package/dist/keryx-http/src/unconfirmed-update-attributions.cjs +190 -0
  144. package/dist/keryx-http/src/unconfirmed-update-attributions.cjs.map +1 -0
  145. package/dist/keryx-http/src/unconfirmed-update-attributions.d.ts +132 -0
  146. package/dist/keryx-http/src/unconfirmed-update-attributions.js +190 -0
  147. package/dist/keryx-http/src/unconfirmed-update-attributions.js.map +1 -0
  148. package/dist/keryx-http/src/update-attributions.cjs +34 -4
  149. package/dist/keryx-http/src/update-attributions.cjs.map +1 -1
  150. package/dist/keryx-http/src/update-attributions.d.ts +36 -4
  151. package/dist/keryx-http/src/update-attributions.js +35 -5
  152. package/dist/keryx-http/src/update-attributions.js.map +1 -1
  153. package/dist/provider/AthenaProvider.cjs +4 -0
  154. package/dist/provider/AthenaProvider.cjs.map +1 -1
  155. package/dist/provider/AthenaProvider.d.ts +11 -1
  156. package/dist/provider/AthenaProvider.js +4 -0
  157. package/dist/provider/AthenaProvider.js.map +1 -1
  158. package/dist/react/package.json.cjs +1 -1
  159. package/dist/react/package.json.js +1 -1
  160. package/dist/threads/api.cjs.map +1 -1
  161. package/dist/threads/api.d.ts +15 -0
  162. package/dist/threads/api.js.map +1 -1
  163. package/dist/threads/conversation-list-fetcher.cjs +13 -5
  164. package/dist/threads/conversation-list-fetcher.cjs.map +1 -1
  165. package/dist/threads/conversation-list-fetcher.d.ts +4 -1
  166. package/dist/threads/conversation-list-fetcher.js +13 -5
  167. package/dist/threads/conversation-list-fetcher.js.map +1 -1
  168. package/dist/threads/statewire-thread-list.cjs +28 -6
  169. package/dist/threads/statewire-thread-list.cjs.map +1 -1
  170. package/dist/threads/statewire-thread-list.d.ts +20 -8
  171. package/dist/threads/statewire-thread-list.js +28 -6
  172. package/dist/threads/statewire-thread-list.js.map +1 -1
  173. package/dist/threads/use-athena-sessions.cjs +27 -5
  174. package/dist/threads/use-athena-sessions.cjs.map +1 -1
  175. package/dist/threads/use-athena-sessions.d.ts +10 -5
  176. package/dist/threads/use-athena-sessions.js +28 -6
  177. package/dist/threads/use-athena-sessions.js.map +1 -1
  178. package/dist/threads/use-rest-conversation-history.d.ts +1 -0
  179. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"AthenaAssetEmbed.cjs","sources":["../../src/assets/AthenaAssetEmbed.tsx"],"sourcesContent":["\"use client\";\n\nimport { AlertCircle, Loader2 } from \"lucide-react\";\nimport { type CSSProperties, type FC, type IframeHTMLAttributes, useCallback, useEffect, useMemo, useRef } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { useAthenaConfig } from \"../provider/AthenaContext\";\nimport {\n ATHENA_ASSET_EMBED_CONFIG_MESSAGE,\n type AthenaAssetCitationActionEvent,\n type AthenaAssetCitationBehavior,\n type AthenaAssetCitationClickEvent,\n type AthenaAssetCitationReference,\n isAthenaAssetCitationActionEvent,\n isAthenaAssetCitationClickEvent,\n isAthenaAssetEmbedReadyEvent,\n isAthenaAssetEmbedContentReadyEvent,\n resolveAthenaAssetCitationActionBehavior,\n resolveAthenaAssetCitationBehavior,\n} from \"./asset-embed-messages\";\nimport {\n buildSdkAssetEmbedUrl,\n DEFAULT_ASSET_EMBED_ALLOW_COMMENTS,\n DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT,\n DEFAULT_ASSET_EMBED_DISPLAY_MODE,\n DEFAULT_ASSET_EMBED_READ_ONLY,\n useAssetEmbed,\n} from \"./use-asset-embed\";\n\nexport interface AthenaAssetEmbedProps extends Omit<\n IframeHTMLAttributes<HTMLIFrameElement>,\n \"src\" | \"onError\" | \"onLoad\"\n> {\n assetId: string;\n /** Optional citation target to open inside the embedded asset, such as a PDF page. */\n reference?: AthenaAssetCitationReference;\n /** Defaults to the native full asset experience. */\n displayMode?: \"minimal\" | \"full\";\n /** Defaults to false so authorized users can edit the embedded asset. */\n readOnly?: boolean;\n /**\n * Offer Quick Edit (⌘J) in a minimal embed (`displayMode=\"minimal\"`): spreadsheets and Word\n * documents, on an editable link (`readOnly: false`) only. Billed to the asset's workspace, with\n * an hourly cap per link; the full mode's editors offer it to the signed-in person already.\n * Defaults to false. Ignored with `embedUrl`: a link the host minted says itself.\n */\n allowQuickEdit?: boolean;\n /**\n * Show the asset's comment threads, read-only, to everyone holding the link, a viewer who is not\n * signed in included. Without it a minimal embed shows them only to a signed-in viewer with their\n * own access to the asset. No embed writes a comment. Defaults to false. Ignored with\n * `embedUrl`: a link the host minted says itself.\n */\n allowComments?: boolean;\n expiresInSeconds?: number;\n citationBehavior?: AthenaAssetCitationBehavior;\n onCitationClick?: (event: AthenaAssetCitationClickEvent) => void;\n /** Controls the citation popover's actions, including the (+) Open in Spaces button. */\n citationActionBehavior?: AthenaAssetCitationBehavior;\n onCitationAction?: (event: AthenaAssetCitationActionEvent) => void;\n /**\n * An embed link the host minted itself (`POST /api/embed/generate-token`, its `/embed/<token>/`\n * page): the component shows it and mints none. Without it the component mints one with the\n * provider's credentials, as before.\n */\n embedUrl?: string;\n /** The embedded page said it is ready (its bridge's first message): Athena runs in the frame. */\n onReady?: () => void;\n /** The matching asset viewer has committed usable content in this frame. */\n onContentReady?: () => void;\n onLoad?: () => void;\n onError?: (error: Error) => void;\n loadingFallback?: React.ReactNode;\n errorFallback?: React.ReactNode | ((error: Error) => React.ReactNode);\n}\n\nconst DEFAULT_STYLE: CSSProperties = {\n width: \"100%\",\n height: \"100%\",\n border: 0,\n};\nconst AUTH_SYNC_INTERVAL_MS = 60_000;\n\nexport const AthenaAssetEmbed: FC<AthenaAssetEmbedProps> = ({\n assetId,\n reference,\n displayMode = DEFAULT_ASSET_EMBED_DISPLAY_MODE,\n readOnly = DEFAULT_ASSET_EMBED_READ_ONLY,\n allowQuickEdit = DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT,\n allowComments = DEFAULT_ASSET_EMBED_ALLOW_COMMENTS,\n expiresInSeconds,\n citationBehavior,\n onCitationClick,\n citationActionBehavior,\n onCitationAction,\n embedUrl: hostEmbedUrl,\n onReady,\n onContentReady,\n onLoad,\n onError,\n loadingFallback,\n errorFallback,\n className,\n style,\n title,\n allow = \"clipboard-read; clipboard-write; fullscreen\",\n ...iframeProps\n}) => {\n const iframeRef = useRef<HTMLIFrameElement | null>(null);\n const athenaConfig = useAthenaConfig();\n const { backendUrl, appUrl, apiKey } = athenaConfig;\n // A link the host minted is shown as given; the hook then mints nothing (no asset to mint for).\n const minted = useAssetEmbed(hostEmbedUrl ? null : assetId, {\n backendUrl,\n appUrl,\n apiKey,\n token: athenaConfig.token,\n getToken: athenaConfig.getToken,\n readOnly,\n expiresInSeconds,\n displayMode,\n allowQuickEdit,\n allowComments,\n });\n const embedUrl = hostEmbedUrl ?? minted.embedUrl;\n const isLoading = hostEmbedUrl ? false : minted.isLoading;\n const error = hostEmbedUrl ? null : minted.error;\n const targetOrigin = useMemo(() => {\n if (!embedUrl) return null;\n try {\n return new URL(embedUrl, window.location.href).origin;\n } catch {\n return null;\n }\n }, [embedUrl]);\n const iframeSrc = useMemo(\n () => (embedUrl ? buildSdkAssetEmbedUrl(embedUrl, reference) : null),\n [embedUrl, reference],\n );\n const effectiveCitationBehavior = resolveAthenaAssetCitationBehavior({\n citationBehavior,\n hasCitationClickHandler: !!onCitationClick,\n });\n const effectiveCitationActionBehavior = resolveAthenaAssetCitationActionBehavior({\n citationActionBehavior,\n hasCitationActionHandler: !!onCitationAction,\n });\n\n const sendConfig = useCallback(() => {\n if (!targetOrigin) return;\n iframeRef.current?.contentWindow?.postMessage(\n {\n type: ATHENA_ASSET_EMBED_CONFIG_MESSAGE,\n version: 1,\n ...(athenaConfig.token ? { token: athenaConfig.token } : {}),\n citationBehavior: effectiveCitationBehavior,\n citationActionBehavior: effectiveCitationActionBehavior,\n },\n targetOrigin,\n );\n }, [athenaConfig, effectiveCitationActionBehavior, effectiveCitationBehavior, targetOrigin]);\n\n useEffect(() => {\n if (!embedUrl || !targetOrigin) return;\n const handleMessage = (event: MessageEvent<unknown>) => {\n if (event.origin !== targetOrigin || event.source !== iframeRef.current?.contentWindow) return;\n if (isAthenaAssetEmbedReadyEvent(event.data)) {\n sendConfig();\n onReady?.();\n return;\n }\n if (isAthenaAssetEmbedContentReadyEvent(event.data)) {\n if (event.data.assetId === assetId) onContentReady?.();\n return;\n }\n if (isAthenaAssetCitationClickEvent(event.data)) {\n onCitationClick?.(event.data);\n return;\n }\n if (isAthenaAssetCitationActionEvent(event.data)) {\n onCitationAction?.(event.data);\n }\n };\n window.addEventListener(\"message\", handleMessage);\n return () => window.removeEventListener(\"message\", handleMessage);\n }, [assetId, embedUrl, onCitationAction, onCitationClick, onReady, onContentReady, sendConfig, targetOrigin]);\n\n useEffect(() => {\n if (!embedUrl) return;\n sendConfig();\n const interval = window.setInterval(sendConfig, AUTH_SYNC_INTERVAL_MS);\n return () => window.clearInterval(interval);\n }, [embedUrl, sendConfig]);\n\n useEffect(() => {\n if (error) onError?.(new Error(error));\n }, [error, onError]);\n\n if (isLoading) {\n return (\n loadingFallback ?? (\n <output className=\"athena-sdk-chrome flex h-full w-full items-center justify-center\">\n <Loader2 className=\"size-6 animate-spin text-muted-foreground\" />\n <span className=\"sr-only\">Loading asset</span>\n </output>\n )\n );\n }\n\n if (error) {\n const embedError = new Error(error);\n if (typeof errorFallback === \"function\") return errorFallback(embedError);\n return (\n errorFallback ?? (\n <div\n className=\"athena-sdk-chrome flex h-full w-full items-center justify-center gap-2 p-4 text-sm text-muted-foreground\"\n role=\"alert\"\n >\n <AlertCircle className=\"size-4 text-destructive\" />\n <span>{error}</span>\n </div>\n )\n );\n }\n\n if (!embedUrl) return null;\n\n return (\n <iframe\n {...iframeProps}\n ref={iframeRef}\n src={iframeSrc ?? undefined}\n title={title ?? `Athena asset ${assetId}`}\n allow={allow}\n className={cn(\"block h-full w-full\", className)}\n style={{ ...DEFAULT_STYLE, ...style }}\n onLoad={() => {\n sendConfig();\n onLoad?.();\n }}\n />\n );\n};\n"],"names":["DEFAULT_ASSET_EMBED_DISPLAY_MODE","DEFAULT_ASSET_EMBED_READ_ONLY","DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT","DEFAULT_ASSET_EMBED_ALLOW_COMMENTS","useRef","useAthenaConfig","useAssetEmbed","useMemo","buildSdkAssetEmbedUrl","resolveAthenaAssetCitationBehavior","resolveAthenaAssetCitationActionBehavior","useCallback","ATHENA_ASSET_EMBED_CONFIG_MESSAGE","useEffect","isAthenaAssetEmbedReadyEvent","isAthenaAssetEmbedContentReadyEvent","isAthenaAssetCitationClickEvent","isAthenaAssetCitationActionEvent","jsxs","jsx","Loader2","AlertCircle","cn"],"mappings":"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"}
1
+ {"version":3,"file":"AthenaAssetEmbed.cjs","sources":["../../src/assets/AthenaAssetEmbed.tsx"],"sourcesContent":["\"use client\";\n\nimport { AlertCircle, Loader2 } from \"lucide-react\";\nimport { type CSSProperties, type FC, type IframeHTMLAttributes, useCallback, useEffect, useMemo, useRef } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { useAthenaConfig } from \"../provider/AthenaContext\";\nimport {\n ATHENA_ASSET_EMBED_CONFIG_MESSAGE,\n type AthenaAssetCitationActionEvent,\n type AthenaAssetCitationBehavior,\n type AthenaAssetCitationClickEvent,\n type AthenaAssetCitationReference,\n isAthenaAssetCitationActionEvent,\n isAthenaAssetCitationClickEvent,\n isAthenaAssetEmbedReadyEvent,\n isAthenaAssetEmbedContentReadyEvent,\n resolveAthenaAssetCitationActionBehavior,\n resolveAthenaAssetCitationBehavior,\n} from \"./asset-embed-messages\";\nimport {\n buildSdkAssetEmbedUrl,\n DEFAULT_ASSET_EMBED_ALLOW_COMMENTS,\n DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT,\n DEFAULT_ASSET_EMBED_DISPLAY_MODE,\n DEFAULT_ASSET_EMBED_READ_ONLY,\n useAssetEmbed,\n} from \"./use-asset-embed\";\n\nexport interface AthenaAssetEmbedProps extends Omit<\n IframeHTMLAttributes<HTMLIFrameElement>,\n \"src\" | \"onError\" | \"onLoad\"\n> {\n assetId: string;\n /** Optional citation target to open inside the embedded asset, such as a PDF page. */\n reference?: AthenaAssetCitationReference;\n /** Defaults to the native full asset experience. */\n displayMode?: \"minimal\" | \"full\";\n /**\n * Defaults to false so authorized users can edit the embedded asset. A Word document, an Athena\n * document, a spreadsheet, a user interface or a deck edits only for a viewer who is signed in\n * to Athena: the provider's `token`, which this component hands the frame. Anyone else views it\n * read-only.\n */\n readOnly?: boolean;\n /**\n * Offer Quick Edit (⌘J) in a minimal embed (`displayMode=\"minimal\"`): spreadsheets, Word\n * documents and decks, on an editable link (`readOnly: false`) only, to a viewer who is signed in\n * to Athena (the provider's `token`). Billed to the asset's workspace, with an hourly cap per\n * link; the full mode's editors offer it to the signed-in person already. Defaults to false.\n * Ignored with `embedUrl`: a link the host minted says itself.\n */\n allowQuickEdit?: boolean;\n /**\n * Show the asset's comment threads, read-only, to everyone holding the link, a viewer who is not\n * signed in included. Without it a minimal embed shows them only to a signed-in viewer with their\n * own access to the asset. No embed writes a comment. Defaults to false. Ignored with\n * `embedUrl`: a link the host minted says itself.\n */\n allowComments?: boolean;\n expiresInSeconds?: number;\n citationBehavior?: AthenaAssetCitationBehavior;\n onCitationClick?: (event: AthenaAssetCitationClickEvent) => void;\n /** Controls the citation popover's actions, including the (+) Open in Spaces button. */\n citationActionBehavior?: AthenaAssetCitationBehavior;\n onCitationAction?: (event: AthenaAssetCitationActionEvent) => void;\n /**\n * An embed link the host minted itself (`POST /api/embed/generate-token`, its `/embed/<token>/`\n * page): the component shows it and mints none. Without it the component mints one with the\n * provider's credentials, as before.\n */\n embedUrl?: string;\n /** The embedded page said it is ready (its bridge's first message): Athena runs in the frame. */\n onReady?: () => void;\n /** The matching asset viewer has committed usable content in this frame. */\n onContentReady?: () => void;\n onLoad?: () => void;\n onError?: (error: Error) => void;\n loadingFallback?: React.ReactNode;\n errorFallback?: React.ReactNode | ((error: Error) => React.ReactNode);\n}\n\nconst DEFAULT_STYLE: CSSProperties = {\n width: \"100%\",\n height: \"100%\",\n border: 0,\n};\nconst AUTH_SYNC_INTERVAL_MS = 60_000;\n\nexport const AthenaAssetEmbed: FC<AthenaAssetEmbedProps> = ({\n assetId,\n reference,\n displayMode = DEFAULT_ASSET_EMBED_DISPLAY_MODE,\n readOnly = DEFAULT_ASSET_EMBED_READ_ONLY,\n allowQuickEdit = DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT,\n allowComments = DEFAULT_ASSET_EMBED_ALLOW_COMMENTS,\n expiresInSeconds,\n citationBehavior,\n onCitationClick,\n citationActionBehavior,\n onCitationAction,\n embedUrl: hostEmbedUrl,\n onReady,\n onContentReady,\n onLoad,\n onError,\n loadingFallback,\n errorFallback,\n className,\n style,\n title,\n allow = \"clipboard-read; clipboard-write; fullscreen\",\n ...iframeProps\n}) => {\n const iframeRef = useRef<HTMLIFrameElement | null>(null);\n const athenaConfig = useAthenaConfig();\n const { backendUrl, appUrl, apiKey } = athenaConfig;\n // A link the host minted is shown as given; the hook then mints nothing (no asset to mint for).\n const minted = useAssetEmbed(hostEmbedUrl ? null : assetId, {\n backendUrl,\n appUrl,\n apiKey,\n token: athenaConfig.token,\n getToken: athenaConfig.getToken,\n readOnly,\n expiresInSeconds,\n displayMode,\n allowQuickEdit,\n allowComments,\n });\n const embedUrl = hostEmbedUrl ?? minted.embedUrl;\n const isLoading = hostEmbedUrl ? false : minted.isLoading;\n const error = hostEmbedUrl ? null : minted.error;\n const targetOrigin = useMemo(() => {\n if (!embedUrl) return null;\n try {\n return new URL(embedUrl, window.location.href).origin;\n } catch {\n return null;\n }\n }, [embedUrl]);\n const iframeSrc = useMemo(\n () => (embedUrl ? buildSdkAssetEmbedUrl(embedUrl, reference) : null),\n [embedUrl, reference],\n );\n const effectiveCitationBehavior = resolveAthenaAssetCitationBehavior({\n citationBehavior,\n hasCitationClickHandler: !!onCitationClick,\n });\n const effectiveCitationActionBehavior = resolveAthenaAssetCitationActionBehavior({\n citationActionBehavior,\n hasCitationActionHandler: !!onCitationAction,\n });\n\n const sendConfig = useCallback(() => {\n if (!targetOrigin) return;\n iframeRef.current?.contentWindow?.postMessage(\n {\n type: ATHENA_ASSET_EMBED_CONFIG_MESSAGE,\n version: 1,\n ...(athenaConfig.token ? { token: athenaConfig.token } : {}),\n citationBehavior: effectiveCitationBehavior,\n citationActionBehavior: effectiveCitationActionBehavior,\n },\n targetOrigin,\n );\n }, [athenaConfig, effectiveCitationActionBehavior, effectiveCitationBehavior, targetOrigin]);\n\n useEffect(() => {\n if (!embedUrl || !targetOrigin) return;\n const handleMessage = (event: MessageEvent<unknown>) => {\n if (event.origin !== targetOrigin || event.source !== iframeRef.current?.contentWindow) return;\n if (isAthenaAssetEmbedReadyEvent(event.data)) {\n sendConfig();\n onReady?.();\n return;\n }\n if (isAthenaAssetEmbedContentReadyEvent(event.data)) {\n if (event.data.assetId === assetId) onContentReady?.();\n return;\n }\n if (isAthenaAssetCitationClickEvent(event.data)) {\n onCitationClick?.(event.data);\n return;\n }\n if (isAthenaAssetCitationActionEvent(event.data)) {\n onCitationAction?.(event.data);\n }\n };\n window.addEventListener(\"message\", handleMessage);\n return () => window.removeEventListener(\"message\", handleMessage);\n }, [assetId, embedUrl, onCitationAction, onCitationClick, onReady, onContentReady, sendConfig, targetOrigin]);\n\n useEffect(() => {\n if (!embedUrl) return;\n sendConfig();\n const interval = window.setInterval(sendConfig, AUTH_SYNC_INTERVAL_MS);\n return () => window.clearInterval(interval);\n }, [embedUrl, sendConfig]);\n\n useEffect(() => {\n if (error) onError?.(new Error(error));\n }, [error, onError]);\n\n if (isLoading) {\n return (\n loadingFallback ?? (\n <output className=\"athena-sdk-chrome flex h-full w-full items-center justify-center\">\n <Loader2 className=\"size-6 animate-spin text-muted-foreground\" />\n <span className=\"sr-only\">Loading asset</span>\n </output>\n )\n );\n }\n\n if (error) {\n const embedError = new Error(error);\n if (typeof errorFallback === \"function\") return errorFallback(embedError);\n return (\n errorFallback ?? (\n <div\n className=\"athena-sdk-chrome flex h-full w-full items-center justify-center gap-2 p-4 text-sm text-muted-foreground\"\n role=\"alert\"\n >\n <AlertCircle className=\"size-4 text-destructive\" />\n <span>{error}</span>\n </div>\n )\n );\n }\n\n if (!embedUrl) return null;\n\n return (\n <iframe\n {...iframeProps}\n ref={iframeRef}\n src={iframeSrc ?? undefined}\n title={title ?? `Athena asset ${assetId}`}\n allow={allow}\n className={cn(\"block h-full w-full\", className)}\n style={{ ...DEFAULT_STYLE, ...style }}\n onLoad={() => {\n sendConfig();\n onLoad?.();\n }}\n />\n );\n};\n"],"names":["DEFAULT_ASSET_EMBED_DISPLAY_MODE","DEFAULT_ASSET_EMBED_READ_ONLY","DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT","DEFAULT_ASSET_EMBED_ALLOW_COMMENTS","useRef","useAthenaConfig","useAssetEmbed","useMemo","buildSdkAssetEmbedUrl","resolveAthenaAssetCitationBehavior","resolveAthenaAssetCitationActionBehavior","useCallback","ATHENA_ASSET_EMBED_CONFIG_MESSAGE","useEffect","isAthenaAssetEmbedReadyEvent","isAthenaAssetEmbedContentReadyEvent","isAthenaAssetCitationClickEvent","isAthenaAssetCitationActionEvent","jsxs","jsx","Loader2","AlertCircle","cn"],"mappings":"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"}
@@ -6,13 +6,19 @@ export interface AthenaAssetEmbedProps extends Omit<IframeHTMLAttributes<HTMLIFr
6
6
  reference?: AthenaAssetCitationReference;
7
7
  /** Defaults to the native full asset experience. */
8
8
  displayMode?: "minimal" | "full";
9
- /** Defaults to false so authorized users can edit the embedded asset. */
9
+ /**
10
+ * Defaults to false so authorized users can edit the embedded asset. A Word document, an Athena
11
+ * document, a spreadsheet, a user interface or a deck edits only for a viewer who is signed in
12
+ * to Athena: the provider's `token`, which this component hands the frame. Anyone else views it
13
+ * read-only.
14
+ */
10
15
  readOnly?: boolean;
11
16
  /**
12
- * Offer Quick Edit (⌘J) in a minimal embed (`displayMode="minimal"`): spreadsheets and Word
13
- * documents, on an editable link (`readOnly: false`) only. Billed to the asset's workspace, with
14
- * an hourly cap per link; the full mode's editors offer it to the signed-in person already.
15
- * Defaults to false. Ignored with `embedUrl`: a link the host minted says itself.
17
+ * Offer Quick Edit (⌘J) in a minimal embed (`displayMode="minimal"`): spreadsheets, Word
18
+ * documents and decks, on an editable link (`readOnly: false`) only, to a viewer who is signed in
19
+ * to Athena (the provider's `token`). Billed to the asset's workspace, with an hourly cap per
20
+ * link; the full mode's editors offer it to the signed-in person already. Defaults to false.
21
+ * Ignored with `embedUrl`: a link the host minted says itself.
16
22
  */
17
23
  allowQuickEdit?: boolean;
18
24
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"AthenaAssetEmbed.js","sources":["../../src/assets/AthenaAssetEmbed.tsx"],"sourcesContent":["\"use client\";\n\nimport { AlertCircle, Loader2 } from \"lucide-react\";\nimport { type CSSProperties, type FC, type IframeHTMLAttributes, useCallback, useEffect, useMemo, useRef } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { useAthenaConfig } from \"../provider/AthenaContext\";\nimport {\n ATHENA_ASSET_EMBED_CONFIG_MESSAGE,\n type AthenaAssetCitationActionEvent,\n type AthenaAssetCitationBehavior,\n type AthenaAssetCitationClickEvent,\n type AthenaAssetCitationReference,\n isAthenaAssetCitationActionEvent,\n isAthenaAssetCitationClickEvent,\n isAthenaAssetEmbedReadyEvent,\n isAthenaAssetEmbedContentReadyEvent,\n resolveAthenaAssetCitationActionBehavior,\n resolveAthenaAssetCitationBehavior,\n} from \"./asset-embed-messages\";\nimport {\n buildSdkAssetEmbedUrl,\n DEFAULT_ASSET_EMBED_ALLOW_COMMENTS,\n DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT,\n DEFAULT_ASSET_EMBED_DISPLAY_MODE,\n DEFAULT_ASSET_EMBED_READ_ONLY,\n useAssetEmbed,\n} from \"./use-asset-embed\";\n\nexport interface AthenaAssetEmbedProps extends Omit<\n IframeHTMLAttributes<HTMLIFrameElement>,\n \"src\" | \"onError\" | \"onLoad\"\n> {\n assetId: string;\n /** Optional citation target to open inside the embedded asset, such as a PDF page. */\n reference?: AthenaAssetCitationReference;\n /** Defaults to the native full asset experience. */\n displayMode?: \"minimal\" | \"full\";\n /** Defaults to false so authorized users can edit the embedded asset. */\n readOnly?: boolean;\n /**\n * Offer Quick Edit (⌘J) in a minimal embed (`displayMode=\"minimal\"`): spreadsheets and Word\n * documents, on an editable link (`readOnly: false`) only. Billed to the asset's workspace, with\n * an hourly cap per link; the full mode's editors offer it to the signed-in person already.\n * Defaults to false. Ignored with `embedUrl`: a link the host minted says itself.\n */\n allowQuickEdit?: boolean;\n /**\n * Show the asset's comment threads, read-only, to everyone holding the link, a viewer who is not\n * signed in included. Without it a minimal embed shows them only to a signed-in viewer with their\n * own access to the asset. No embed writes a comment. Defaults to false. Ignored with\n * `embedUrl`: a link the host minted says itself.\n */\n allowComments?: boolean;\n expiresInSeconds?: number;\n citationBehavior?: AthenaAssetCitationBehavior;\n onCitationClick?: (event: AthenaAssetCitationClickEvent) => void;\n /** Controls the citation popover's actions, including the (+) Open in Spaces button. */\n citationActionBehavior?: AthenaAssetCitationBehavior;\n onCitationAction?: (event: AthenaAssetCitationActionEvent) => void;\n /**\n * An embed link the host minted itself (`POST /api/embed/generate-token`, its `/embed/<token>/`\n * page): the component shows it and mints none. Without it the component mints one with the\n * provider's credentials, as before.\n */\n embedUrl?: string;\n /** The embedded page said it is ready (its bridge's first message): Athena runs in the frame. */\n onReady?: () => void;\n /** The matching asset viewer has committed usable content in this frame. */\n onContentReady?: () => void;\n onLoad?: () => void;\n onError?: (error: Error) => void;\n loadingFallback?: React.ReactNode;\n errorFallback?: React.ReactNode | ((error: Error) => React.ReactNode);\n}\n\nconst DEFAULT_STYLE: CSSProperties = {\n width: \"100%\",\n height: \"100%\",\n border: 0,\n};\nconst AUTH_SYNC_INTERVAL_MS = 60_000;\n\nexport const AthenaAssetEmbed: FC<AthenaAssetEmbedProps> = ({\n assetId,\n reference,\n displayMode = DEFAULT_ASSET_EMBED_DISPLAY_MODE,\n readOnly = DEFAULT_ASSET_EMBED_READ_ONLY,\n allowQuickEdit = DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT,\n allowComments = DEFAULT_ASSET_EMBED_ALLOW_COMMENTS,\n expiresInSeconds,\n citationBehavior,\n onCitationClick,\n citationActionBehavior,\n onCitationAction,\n embedUrl: hostEmbedUrl,\n onReady,\n onContentReady,\n onLoad,\n onError,\n loadingFallback,\n errorFallback,\n className,\n style,\n title,\n allow = \"clipboard-read; clipboard-write; fullscreen\",\n ...iframeProps\n}) => {\n const iframeRef = useRef<HTMLIFrameElement | null>(null);\n const athenaConfig = useAthenaConfig();\n const { backendUrl, appUrl, apiKey } = athenaConfig;\n // A link the host minted is shown as given; the hook then mints nothing (no asset to mint for).\n const minted = useAssetEmbed(hostEmbedUrl ? null : assetId, {\n backendUrl,\n appUrl,\n apiKey,\n token: athenaConfig.token,\n getToken: athenaConfig.getToken,\n readOnly,\n expiresInSeconds,\n displayMode,\n allowQuickEdit,\n allowComments,\n });\n const embedUrl = hostEmbedUrl ?? minted.embedUrl;\n const isLoading = hostEmbedUrl ? false : minted.isLoading;\n const error = hostEmbedUrl ? null : minted.error;\n const targetOrigin = useMemo(() => {\n if (!embedUrl) return null;\n try {\n return new URL(embedUrl, window.location.href).origin;\n } catch {\n return null;\n }\n }, [embedUrl]);\n const iframeSrc = useMemo(\n () => (embedUrl ? buildSdkAssetEmbedUrl(embedUrl, reference) : null),\n [embedUrl, reference],\n );\n const effectiveCitationBehavior = resolveAthenaAssetCitationBehavior({\n citationBehavior,\n hasCitationClickHandler: !!onCitationClick,\n });\n const effectiveCitationActionBehavior = resolveAthenaAssetCitationActionBehavior({\n citationActionBehavior,\n hasCitationActionHandler: !!onCitationAction,\n });\n\n const sendConfig = useCallback(() => {\n if (!targetOrigin) return;\n iframeRef.current?.contentWindow?.postMessage(\n {\n type: ATHENA_ASSET_EMBED_CONFIG_MESSAGE,\n version: 1,\n ...(athenaConfig.token ? { token: athenaConfig.token } : {}),\n citationBehavior: effectiveCitationBehavior,\n citationActionBehavior: effectiveCitationActionBehavior,\n },\n targetOrigin,\n );\n }, [athenaConfig, effectiveCitationActionBehavior, effectiveCitationBehavior, targetOrigin]);\n\n useEffect(() => {\n if (!embedUrl || !targetOrigin) return;\n const handleMessage = (event: MessageEvent<unknown>) => {\n if (event.origin !== targetOrigin || event.source !== iframeRef.current?.contentWindow) return;\n if (isAthenaAssetEmbedReadyEvent(event.data)) {\n sendConfig();\n onReady?.();\n return;\n }\n if (isAthenaAssetEmbedContentReadyEvent(event.data)) {\n if (event.data.assetId === assetId) onContentReady?.();\n return;\n }\n if (isAthenaAssetCitationClickEvent(event.data)) {\n onCitationClick?.(event.data);\n return;\n }\n if (isAthenaAssetCitationActionEvent(event.data)) {\n onCitationAction?.(event.data);\n }\n };\n window.addEventListener(\"message\", handleMessage);\n return () => window.removeEventListener(\"message\", handleMessage);\n }, [assetId, embedUrl, onCitationAction, onCitationClick, onReady, onContentReady, sendConfig, targetOrigin]);\n\n useEffect(() => {\n if (!embedUrl) return;\n sendConfig();\n const interval = window.setInterval(sendConfig, AUTH_SYNC_INTERVAL_MS);\n return () => window.clearInterval(interval);\n }, [embedUrl, sendConfig]);\n\n useEffect(() => {\n if (error) onError?.(new Error(error));\n }, [error, onError]);\n\n if (isLoading) {\n return (\n loadingFallback ?? (\n <output className=\"athena-sdk-chrome flex h-full w-full items-center justify-center\">\n <Loader2 className=\"size-6 animate-spin text-muted-foreground\" />\n <span className=\"sr-only\">Loading asset</span>\n </output>\n )\n );\n }\n\n if (error) {\n const embedError = new Error(error);\n if (typeof errorFallback === \"function\") return errorFallback(embedError);\n return (\n errorFallback ?? (\n <div\n className=\"athena-sdk-chrome flex h-full w-full items-center justify-center gap-2 p-4 text-sm text-muted-foreground\"\n role=\"alert\"\n >\n <AlertCircle className=\"size-4 text-destructive\" />\n <span>{error}</span>\n </div>\n )\n );\n }\n\n if (!embedUrl) return null;\n\n return (\n <iframe\n {...iframeProps}\n ref={iframeRef}\n src={iframeSrc ?? undefined}\n title={title ?? `Athena asset ${assetId}`}\n allow={allow}\n className={cn(\"block h-full w-full\", className)}\n style={{ ...DEFAULT_STYLE, ...style }}\n onLoad={() => {\n sendConfig();\n onLoad?.();\n }}\n />\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AthenaAssetEmbed.js","sources":["../../src/assets/AthenaAssetEmbed.tsx"],"sourcesContent":["\"use client\";\n\nimport { AlertCircle, Loader2 } from \"lucide-react\";\nimport { type CSSProperties, type FC, type IframeHTMLAttributes, useCallback, useEffect, useMemo, useRef } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { useAthenaConfig } from \"../provider/AthenaContext\";\nimport {\n ATHENA_ASSET_EMBED_CONFIG_MESSAGE,\n type AthenaAssetCitationActionEvent,\n type AthenaAssetCitationBehavior,\n type AthenaAssetCitationClickEvent,\n type AthenaAssetCitationReference,\n isAthenaAssetCitationActionEvent,\n isAthenaAssetCitationClickEvent,\n isAthenaAssetEmbedReadyEvent,\n isAthenaAssetEmbedContentReadyEvent,\n resolveAthenaAssetCitationActionBehavior,\n resolveAthenaAssetCitationBehavior,\n} from \"./asset-embed-messages\";\nimport {\n buildSdkAssetEmbedUrl,\n DEFAULT_ASSET_EMBED_ALLOW_COMMENTS,\n DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT,\n DEFAULT_ASSET_EMBED_DISPLAY_MODE,\n DEFAULT_ASSET_EMBED_READ_ONLY,\n useAssetEmbed,\n} from \"./use-asset-embed\";\n\nexport interface AthenaAssetEmbedProps extends Omit<\n IframeHTMLAttributes<HTMLIFrameElement>,\n \"src\" | \"onError\" | \"onLoad\"\n> {\n assetId: string;\n /** Optional citation target to open inside the embedded asset, such as a PDF page. */\n reference?: AthenaAssetCitationReference;\n /** Defaults to the native full asset experience. */\n displayMode?: \"minimal\" | \"full\";\n /**\n * Defaults to false so authorized users can edit the embedded asset. A Word document, an Athena\n * document, a spreadsheet, a user interface or a deck edits only for a viewer who is signed in\n * to Athena: the provider's `token`, which this component hands the frame. Anyone else views it\n * read-only.\n */\n readOnly?: boolean;\n /**\n * Offer Quick Edit (⌘J) in a minimal embed (`displayMode=\"minimal\"`): spreadsheets, Word\n * documents and decks, on an editable link (`readOnly: false`) only, to a viewer who is signed in\n * to Athena (the provider's `token`). Billed to the asset's workspace, with an hourly cap per\n * link; the full mode's editors offer it to the signed-in person already. Defaults to false.\n * Ignored with `embedUrl`: a link the host minted says itself.\n */\n allowQuickEdit?: boolean;\n /**\n * Show the asset's comment threads, read-only, to everyone holding the link, a viewer who is not\n * signed in included. Without it a minimal embed shows them only to a signed-in viewer with their\n * own access to the asset. No embed writes a comment. Defaults to false. Ignored with\n * `embedUrl`: a link the host minted says itself.\n */\n allowComments?: boolean;\n expiresInSeconds?: number;\n citationBehavior?: AthenaAssetCitationBehavior;\n onCitationClick?: (event: AthenaAssetCitationClickEvent) => void;\n /** Controls the citation popover's actions, including the (+) Open in Spaces button. */\n citationActionBehavior?: AthenaAssetCitationBehavior;\n onCitationAction?: (event: AthenaAssetCitationActionEvent) => void;\n /**\n * An embed link the host minted itself (`POST /api/embed/generate-token`, its `/embed/<token>/`\n * page): the component shows it and mints none. Without it the component mints one with the\n * provider's credentials, as before.\n */\n embedUrl?: string;\n /** The embedded page said it is ready (its bridge's first message): Athena runs in the frame. */\n onReady?: () => void;\n /** The matching asset viewer has committed usable content in this frame. */\n onContentReady?: () => void;\n onLoad?: () => void;\n onError?: (error: Error) => void;\n loadingFallback?: React.ReactNode;\n errorFallback?: React.ReactNode | ((error: Error) => React.ReactNode);\n}\n\nconst DEFAULT_STYLE: CSSProperties = {\n width: \"100%\",\n height: \"100%\",\n border: 0,\n};\nconst AUTH_SYNC_INTERVAL_MS = 60_000;\n\nexport const AthenaAssetEmbed: FC<AthenaAssetEmbedProps> = ({\n assetId,\n reference,\n displayMode = DEFAULT_ASSET_EMBED_DISPLAY_MODE,\n readOnly = DEFAULT_ASSET_EMBED_READ_ONLY,\n allowQuickEdit = DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT,\n allowComments = DEFAULT_ASSET_EMBED_ALLOW_COMMENTS,\n expiresInSeconds,\n citationBehavior,\n onCitationClick,\n citationActionBehavior,\n onCitationAction,\n embedUrl: hostEmbedUrl,\n onReady,\n onContentReady,\n onLoad,\n onError,\n loadingFallback,\n errorFallback,\n className,\n style,\n title,\n allow = \"clipboard-read; clipboard-write; fullscreen\",\n ...iframeProps\n}) => {\n const iframeRef = useRef<HTMLIFrameElement | null>(null);\n const athenaConfig = useAthenaConfig();\n const { backendUrl, appUrl, apiKey } = athenaConfig;\n // A link the host minted is shown as given; the hook then mints nothing (no asset to mint for).\n const minted = useAssetEmbed(hostEmbedUrl ? null : assetId, {\n backendUrl,\n appUrl,\n apiKey,\n token: athenaConfig.token,\n getToken: athenaConfig.getToken,\n readOnly,\n expiresInSeconds,\n displayMode,\n allowQuickEdit,\n allowComments,\n });\n const embedUrl = hostEmbedUrl ?? minted.embedUrl;\n const isLoading = hostEmbedUrl ? false : minted.isLoading;\n const error = hostEmbedUrl ? null : minted.error;\n const targetOrigin = useMemo(() => {\n if (!embedUrl) return null;\n try {\n return new URL(embedUrl, window.location.href).origin;\n } catch {\n return null;\n }\n }, [embedUrl]);\n const iframeSrc = useMemo(\n () => (embedUrl ? buildSdkAssetEmbedUrl(embedUrl, reference) : null),\n [embedUrl, reference],\n );\n const effectiveCitationBehavior = resolveAthenaAssetCitationBehavior({\n citationBehavior,\n hasCitationClickHandler: !!onCitationClick,\n });\n const effectiveCitationActionBehavior = resolveAthenaAssetCitationActionBehavior({\n citationActionBehavior,\n hasCitationActionHandler: !!onCitationAction,\n });\n\n const sendConfig = useCallback(() => {\n if (!targetOrigin) return;\n iframeRef.current?.contentWindow?.postMessage(\n {\n type: ATHENA_ASSET_EMBED_CONFIG_MESSAGE,\n version: 1,\n ...(athenaConfig.token ? { token: athenaConfig.token } : {}),\n citationBehavior: effectiveCitationBehavior,\n citationActionBehavior: effectiveCitationActionBehavior,\n },\n targetOrigin,\n );\n }, [athenaConfig, effectiveCitationActionBehavior, effectiveCitationBehavior, targetOrigin]);\n\n useEffect(() => {\n if (!embedUrl || !targetOrigin) return;\n const handleMessage = (event: MessageEvent<unknown>) => {\n if (event.origin !== targetOrigin || event.source !== iframeRef.current?.contentWindow) return;\n if (isAthenaAssetEmbedReadyEvent(event.data)) {\n sendConfig();\n onReady?.();\n return;\n }\n if (isAthenaAssetEmbedContentReadyEvent(event.data)) {\n if (event.data.assetId === assetId) onContentReady?.();\n return;\n }\n if (isAthenaAssetCitationClickEvent(event.data)) {\n onCitationClick?.(event.data);\n return;\n }\n if (isAthenaAssetCitationActionEvent(event.data)) {\n onCitationAction?.(event.data);\n }\n };\n window.addEventListener(\"message\", handleMessage);\n return () => window.removeEventListener(\"message\", handleMessage);\n }, [assetId, embedUrl, onCitationAction, onCitationClick, onReady, onContentReady, sendConfig, targetOrigin]);\n\n useEffect(() => {\n if (!embedUrl) return;\n sendConfig();\n const interval = window.setInterval(sendConfig, AUTH_SYNC_INTERVAL_MS);\n return () => window.clearInterval(interval);\n }, [embedUrl, sendConfig]);\n\n useEffect(() => {\n if (error) onError?.(new Error(error));\n }, [error, onError]);\n\n if (isLoading) {\n return (\n loadingFallback ?? (\n <output className=\"athena-sdk-chrome flex h-full w-full items-center justify-center\">\n <Loader2 className=\"size-6 animate-spin text-muted-foreground\" />\n <span className=\"sr-only\">Loading asset</span>\n </output>\n )\n );\n }\n\n if (error) {\n const embedError = new Error(error);\n if (typeof errorFallback === \"function\") return errorFallback(embedError);\n return (\n errorFallback ?? (\n <div\n className=\"athena-sdk-chrome flex h-full w-full items-center justify-center gap-2 p-4 text-sm text-muted-foreground\"\n role=\"alert\"\n >\n <AlertCircle className=\"size-4 text-destructive\" />\n <span>{error}</span>\n </div>\n )\n );\n }\n\n if (!embedUrl) return null;\n\n return (\n <iframe\n {...iframeProps}\n ref={iframeRef}\n src={iframeSrc ?? undefined}\n title={title ?? `Athena asset ${assetId}`}\n allow={allow}\n className={cn(\"block h-full w-full\", className)}\n style={{ ...DEFAULT_STYLE, ...style }}\n onLoad={() => {\n sendConfig();\n onLoad?.();\n }}\n />\n );\n};\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-asset-embed.cjs","sources":["../../src/assets/use-asset-embed.ts"],"sourcesContent":["import { QueryClient, QueryClientContext, useQuery } from '@tanstack/react-query';\nimport { useContext, useRef } from 'react';\nimport { deriveAthenaAppUrl } from '../lib/athena-urls';\nimport { keyPartFingerprint } from '../lib/use-athena-rest-query';\nimport { sendWithCredentialRenewal } from '../runtime/credential-renewal';\nimport type { AthenaStatewireTokenGetter } from '../runtime/statewire-auth-headers';\nimport type { AthenaAssetCitationReference } from './asset-embed-messages';\n\nexport interface UseAssetEmbedOptions {\n readOnly?: boolean;\n expiresInSeconds?: number;\n /** Native Olympus surface rendered by the iframe. Defaults to `full`. */\n displayMode?: 'minimal' | 'full';\n /**\n * Offer Quick Edit (⌘J, an AI rewrite of the selection) in a minimal embed, where the viewer\n * does not sign in: spreadsheets and Word documents, on an editable link (`readOnly: false`)\n * only. Its runs are billed to the asset's workspace under the link's creator, and each link has\n * its own hourly cap. The full mode's editors offer it to the signed-in person already. Defaults\n * to `false`.\n */\n allowQuickEdit?: boolean;\n /**\n * Show the asset's comment threads, read-only, to everyone holding the link, a viewer who is not\n * signed in included. Without it, a minimal embed shows them only to a signed-in viewer who has\n * their own access to the asset (the bearer this component hands the iframe, or an Athena\n * session). No embed ever writes a comment. Defaults to `false`.\n */\n allowComments?: boolean;\n}\n\nexport const DEFAULT_ASSET_EMBED_READ_ONLY = false;\nexport const DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT = false;\nexport const DEFAULT_ASSET_EMBED_ALLOW_COMMENTS = false;\nexport const DEFAULT_ASSET_EMBED_DISPLAY_MODE = 'full' as const;\n\nexport interface UseAssetEmbedResult {\n embedUrl: string | null;\n isLoading: boolean;\n error: string | null;\n}\n\nexport function buildAssetEmbedRequestBody({\n assetId,\n readOnly,\n expiresInSeconds,\n displayMode,\n allowQuickEdit = DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT,\n allowComments = DEFAULT_ASSET_EMBED_ALLOW_COMMENTS,\n}: {\n assetId: string;\n readOnly: boolean;\n expiresInSeconds: number;\n displayMode: 'minimal' | 'full';\n allowQuickEdit?: boolean;\n allowComments?: boolean;\n}) {\n return {\n asset_id: assetId,\n read_only: readOnly,\n expires_in_seconds: expiresInSeconds,\n display_mode: displayMode,\n // Sent only when asked: an agora that predates it is never handed an unknown field.\n ...(allowQuickEdit && { allow_quick_edit: true }),\n ...(allowComments && { allow_comments: true }),\n };\n}\n\n/**\n * The embed links' query client when the host provides none: one for the page, so every embed of\n * one asset, mode and identity shares one minted link until it expires.\n */\nconst embedQueryClient = new QueryClient();\n\nconst ATHENA_APP_HOSTNAMES = new Set(['app.athenaintel.com', 'staging-app.athenaintel.com']);\nconst ATHENA_PREVIEW_HOSTNAME_SUFFIX = '.previews.athenaintel.com';\nconst ATHENA_WORKSPACE_HOSTNAME_PATTERN = /^.+--(\\d+)\\.(?:app\\.athenaintel\\.com|staging-app\\.athenaintel\\.com)$/;\nconst OLYMPUS_WORKSPACE_PORT = '8082';\n\nconst getUrl = (value?: string | null): URL | null => {\n if (!value) {\n return null;\n }\n\n try {\n return new URL(value);\n } catch {\n return null;\n }\n};\n\nconst getCurrentOrigin = (): string | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n\n return window.location.origin;\n};\n\nconst getOrigin = (value?: string | null): string | null => getUrl(value)?.origin ?? null;\n\nconst getAthenaWorkspacePort = (url: URL): string | null => {\n const match = url.hostname.toLowerCase().match(ATHENA_WORKSPACE_HOSTNAME_PATTERN);\n return match?.[1] ?? null;\n};\n\nconst isEmbedCapableAthenaHost = (url: URL): boolean => {\n const hostname = url.hostname.toLowerCase();\n\n if (ATHENA_APP_HOSTNAMES.has(hostname) || hostname.endsWith(ATHENA_PREVIEW_HOSTNAME_SUFFIX)) {\n return true;\n }\n\n return getAthenaWorkspacePort(url) === OLYMPUS_WORKSPACE_PORT;\n};\n\nconst shouldPreferDerivedEmbedAppUrl = ({\n appUrl,\n derivedAppUrl,\n currentOrigin,\n}: {\n appUrl: string;\n derivedAppUrl: string | null;\n currentOrigin?: string | null;\n}): boolean => {\n if (!derivedAppUrl) {\n return false;\n }\n\n const resolvedAppUrl = getUrl(appUrl);\n if (!resolvedAppUrl) {\n return true;\n }\n\n const workspacePort = getAthenaWorkspacePort(resolvedAppUrl);\n if (workspacePort && workspacePort !== OLYMPUS_WORKSPACE_PORT) {\n return true;\n }\n\n if (isEmbedCapableAthenaHost(resolvedAppUrl)) {\n return false;\n }\n\n const resolvedCurrentOrigin = getOrigin(currentOrigin ?? getCurrentOrigin());\n return !!resolvedCurrentOrigin && resolvedAppUrl.origin === resolvedCurrentOrigin;\n};\n\nconst resolveEmbedAppUrl = ({\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string | null => {\n const derivedAppUrl = deriveAthenaAppUrl({ backendUrl });\n\n if (!appUrl) {\n return derivedAppUrl;\n }\n\n if (shouldPreferDerivedEmbedAppUrl({ appUrl, derivedAppUrl, currentOrigin })) {\n return derivedAppUrl;\n }\n\n return appUrl;\n};\n\nexport function resolveAssetEmbedUrl({\n embedUrl,\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n embedUrl: string;\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string {\n try {\n const resolvedEmbedUrl = new URL(embedUrl);\n if (!resolvedEmbedUrl.pathname.startsWith('/embed/')) {\n return embedUrl;\n }\n\n const embedAppUrl = resolveEmbedAppUrl({\n appUrl,\n backendUrl,\n currentOrigin,\n });\n if (!embedAppUrl) {\n return embedUrl;\n }\n\n const resolvedAppUrl = new URL(embedAppUrl);\n resolvedEmbedUrl.protocol = resolvedAppUrl.protocol;\n resolvedEmbedUrl.host = resolvedAppUrl.host;\n return resolvedEmbedUrl.toString();\n } catch {\n return embedUrl;\n }\n}\n\nfunction setFiniteNumberParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'number' && Number.isFinite(value)) {\n params.set(key, String(value));\n }\n}\n\nfunction setNonEmptyStringParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'string' && value.length > 0) {\n params.set(key, value);\n }\n}\n\n/** The SDK's own embed parameter: it marks the bridge, never a place, so no anchor writes it. */\nconst SDK_BRIDGE_PARAM = 'athena_sdk_bridge';\n\n/**\n * The pairs of an anchor given as its raw query — the string an Athena link, citation or search\n * result carries (`anchor=slide&slide_index=3&slide_id=…`, `anchor=time&time=0:42`), alone or\n * after the `?` of an asset ref (`asset_x?anchor=…`) — read as URL parameters, or `null` when the\n * string holds none, or a parameter with no name. The SDK never interprets the anchor: the embed\n * page parses it with the references grammar, so every kind of place travels as written.\n */\nfunction rawAnchorQueryPairs(query: string): Array<[string, string]> | null {\n const queryIndex = query.indexOf('?');\n const search = queryIndex === -1 ? query : query.slice(queryIndex + 1);\n const pairs = [...new URLSearchParams(search)].filter(([key]) => key !== SDK_BRIDGE_PARAM);\n if (pairs.length === 0 || pairs.some(([key]) => key.length === 0)) return null;\n return pairs;\n}\n\nfunction appendRawAnchorQuery({ params, query }: { params: URLSearchParams; query: string }): void {\n const pairs = rawAnchorQueryPairs(query);\n if (!pairs) return;\n // Verbatim: a key the URL already has is replaced, and a repeated key keeps every value.\n for (const [key] of pairs) params.delete(key);\n for (const [key, value] of pairs) params.append(key, value);\n}\n\nfunction appendTypedAnchorParams({\n params,\n anchor,\n}: {\n params: URLSearchParams;\n anchor: object & { type: string };\n}): void {\n switch (anchor.type) {\n case 'page':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n break;\n case 'page_range':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'start_page',\n value: 'startPage' in anchor ? anchor.startPage : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'end_page',\n value: 'endPage' in anchor ? anchor.endPage : undefined,\n });\n break;\n case 'page_rect':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'x',\n value: 'x' in anchor ? anchor.x : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'y',\n value: 'y' in anchor ? anchor.y : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'width',\n value: 'width' in anchor ? anchor.width : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'height',\n value: 'height' in anchor ? anchor.height : undefined,\n });\n if (\n 'unit' in anchor &&\n (anchor.unit === 'percent' || anchor.unit === 'point')\n ) {\n params.set('unit', anchor.unit);\n }\n break;\n case 'page_text':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n if ('text' in anchor && typeof anchor.text === 'string') {\n params.set('text', anchor.text);\n }\n // Optional context that disambiguates a repeated phrase, and the hash of\n // the cited unit's normalized text; the embed parses both with the shared\n // `@athenaintel/references` parser.\n setNonEmptyStringParam({ params, key: 'prefix', value: 'prefix' in anchor ? anchor.prefix : undefined });\n setNonEmptyStringParam({ params, key: 'suffix', value: 'suffix' in anchor ? anchor.suffix : undefined });\n setNonEmptyStringParam({ params, key: 'hash', value: 'hash' in anchor ? anchor.hash : undefined });\n break;\n // Sheet anchors ride the same query contract the embed page parses with\n // the shared `@athenaintel/references` parser.\n case 'sheet_cell':\n case 'sheet_range':\n case 'sheet_tab':\n case 'sheet_table':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'sheet_id',\n value: 'sheetId' in anchor ? anchor.sheetId : undefined,\n });\n if ('cell' in anchor && typeof anchor.cell === 'string') {\n params.set('cell', anchor.cell);\n }\n if ('range' in anchor && typeof anchor.range === 'string') {\n params.set('range', anchor.range);\n }\n if ('tableName' in anchor && typeof anchor.tableName === 'string') {\n params.set('table_name', anchor.tableName);\n }\n if ('fallbackRange' in anchor && typeof anchor.fallbackRange === 'string') {\n params.set('fallback_range', anchor.fallbackRange);\n }\n break;\n }\n}\n\n/**\n * Writes a citation's place into an embed URL: its anchor and its excerpt (the passage the embed\n * finds by its words — alone, when the citation names no anchor). The anchor is either its raw\n * query string, forwarded as written whatever kind of place it names, or one of the typed page and\n * sheet anchors. Athena on the web's Spaces links read the same grammar, so a host that opens a\n * cited place there writes it the same way.\n */\nexport function appendCitationReferenceParams({\n params,\n reference,\n}: {\n params: URLSearchParams;\n reference?: AthenaAssetCitationReference;\n}): void {\n const anchor = reference?.anchor;\n if (typeof anchor === 'string') {\n appendRawAnchorQuery({ params, query: anchor });\n } else if (anchor && typeof anchor === 'object' && 'type' in anchor && typeof anchor.type === 'string') {\n appendTypedAnchorParams({ params, anchor: { ...anchor, type: anchor.type } });\n }\n\n const excerpt = reference?.meta?.excerpt;\n if (typeof excerpt === 'string' && excerpt) {\n params.set('excerpt', excerpt);\n }\n}\n\nexport function buildSdkAssetEmbedUrl(embedUrl: string, reference?: AthenaAssetCitationReference): string {\n try {\n const url = new URL(embedUrl);\n url.searchParams.set('athena_sdk_bridge', '1');\n appendCitationReferenceParams({ params: url.searchParams, reference });\n return url.toString();\n } catch {\n return embedUrl;\n }\n}\n\nconst getJwtPayload = (token: string): Record<string, unknown> | null => {\n try {\n const payloadSegment = token.split('.')[1];\n if (!payloadSegment || typeof atob !== 'function') return null;\n const normalized = payloadSegment.replace(/-/g, '+').replace(/_/g, '/');\n const payload: unknown = JSON.parse(\n atob(normalized.padEnd(normalized.length + ((4 - (normalized.length % 4)) % 4), '=')),\n );\n return payload && typeof payload === 'object' ? (payload as Record<string, unknown>) : null;\n } catch {\n return null;\n }\n};\n\nexport function getAssetEmbedAuthContext({ token, apiKey }: { token?: string | null; apiKey?: string }): string {\n if (!token) return apiKey ? `api-key:${apiKey}` : 'anonymous';\n\n const payload = getJwtPayload(token);\n const userId = payload?.user_id ?? payload?.sub;\n if (typeof userId !== 'string' || !userId) return `opaque-token:${token}`;\n\n return `jwt:${JSON.stringify({\n issuer: payload?.iss ?? null,\n userId,\n activeOrgId: payload?.active_org_id ?? payload?.org_id ?? null,\n workspaceId: payload?.workspace_id ?? null,\n orgMemberships: payload?.org_id_to_org_member_info ?? null,\n })}`;\n}\n\n/**\n * Hook to generate an embed URL for rendering an asset in an iframe.\n * Calls the Athena embed token endpoint through TanStack Query (the host's\n * QueryClient when there is one, else the page's embed client), keyed on the\n * asset, the mode and the identity.\n *\n * Note: The auth token is read via a ref and is intentionally NOT part\n * of the query key. PropelAuth and similar providers refresh the token value\n * periodically (e.g. on window focus), but the underlying session is the\n * same — minting again on every token rotation would force the iframe to\n * reload unnecessarily. The link is minted again only when the identity\n * (user or tenant), the asset or the mode changes.\n */\nexport function useAssetEmbed(\n assetId: string | null,\n options: UseAssetEmbedOptions & {\n backendUrl: string;\n appUrl?: string;\n apiKey?: string;\n token?: string | null;\n /**\n * Live token resolver (the provider's `config.getToken`) that renews the\n * held `token`: read before the embed-token request, and asked for a\n * renewed token (`force: true`) when the backend refuses it. Ignored when\n * no token is held, so the request always authenticates as the identity\n * the embed cache is keyed on.\n */\n getToken?: AthenaStatewireTokenGetter;\n } = {\n backendUrl: '',\n },\n): UseAssetEmbedResult {\n const {\n readOnly = DEFAULT_ASSET_EMBED_READ_ONLY,\n expiresInSeconds = 60 * 60 * 24 * 30,\n displayMode = DEFAULT_ASSET_EMBED_DISPLAY_MODE,\n allowQuickEdit = DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT,\n allowComments = DEFAULT_ASSET_EMBED_ALLOW_COMMENTS,\n backendUrl,\n appUrl,\n apiKey,\n token,\n getToken,\n } = options;\n // Keep the latest token in a ref so the query can read it without being\n // keyed on it: PropelAuth and similar providers rotate the token value.\n const tokenRef = useRef(token);\n tokenRef.current = token;\n const getTokenRef = useRef(getToken);\n getTokenRef.current = getToken;\n\n // Stable across normal JWT rotations, but changes when the user or tenant\n // changes so a signed URL can never be reused across auth boundaries.\n const authContext = getAssetEmbedAuthContext({ token, apiKey });\n const enabled = Boolean(assetId && backendUrl);\n\n const query = useQuery(\n {\n queryKey: [\n 'athena-sdk',\n 'asset-embed',\n assetId,\n readOnly,\n displayMode,\n allowQuickEdit,\n allowComments,\n // The identity, never the credential: an opaque token or a key is part of `authContext`.\n keyPartFingerprint(authContext),\n backendUrl,\n appUrl ?? '',\n ],\n queryFn: async ({ signal }): Promise<{ url: string; expiresAt: number }> => {\n // Derive the Athena API base from backendUrl before calling the embed endpoint.\n const apiBaseUrl = backendUrl.replace(/\\/api\\/assistant-ui\\/?$/, '');\n const heldToken = tokenRef.current;\n const res = await sendWithCredentialRenewal(\n { apiKey, token: heldToken, getToken: heldToken ? getTokenRef.current : undefined },\n (headers) =>\n fetch(`${apiBaseUrl}/api/embed/generate-token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...headers },\n body: JSON.stringify(\n buildAssetEmbedRequestBody({\n assetId: assetId ?? '',\n readOnly,\n expiresInSeconds,\n displayMode,\n allowQuickEdit,\n allowComments,\n }),\n ),\n signal,\n }),\n );\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`Failed to generate embed token (${res.status}): ${text}`);\n }\n const data: { embed_url: string; expires_at: number } = await res.json();\n return {\n url: resolveAssetEmbedUrl({ embedUrl: data.embed_url, appUrl, backendUrl }),\n expiresAt: data.expires_at,\n };\n },\n enabled,\n // A minted link is good until it expires; a new one would reload the frame, so it is never\n // minted again while the embed shows (no refetch on focus or reconnect) and never retried.\n staleTime: (cached) =>\n cached.state.data ? Math.max(0, cached.state.data.expiresAt * 1000 - Date.now()) : 0,\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n retry: false,\n },\n useContext(QueryClientContext) ?? embedQueryClient,\n );\n\n return {\n embedUrl: enabled ? (query.data?.url ?? null) : null,\n isLoading: enabled && query.isLoading,\n error: enabled && query.error ? query.error.message : null,\n };\n}\n"],"names":["QueryClient","deriveAthenaAppUrl","useRef","useQuery","keyPartFingerprint","sendWithCredentialRenewal","useContext","QueryClientContext"],"mappings":"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"}
1
+ {"version":3,"file":"use-asset-embed.cjs","sources":["../../src/assets/use-asset-embed.ts"],"sourcesContent":["import { QueryClient, QueryClientContext, useQuery } from '@tanstack/react-query';\nimport { useContext, useRef } from 'react';\nimport { deriveAthenaAppUrl } from '../lib/athena-urls';\nimport { keyPartFingerprint } from '../lib/use-athena-rest-query';\nimport { sendWithCredentialRenewal } from '../runtime/credential-renewal';\nimport type { AthenaStatewireTokenGetter } from '../runtime/statewire-auth-headers';\nimport type { AthenaAssetCitationReference } from './asset-embed-messages';\n\nexport interface UseAssetEmbedOptions {\n /**\n * Mint a view-only link. A ceiling, not a grant: the link edits only what its creator may edit,\n * and a Word document, an Athena document, a spreadsheet, a user interface or a deck edits only\n * for a viewer who is signed in to Athena (the provider's `token`, which `AthenaAssetEmbed`\n * hands the frame). Anyone else views such a link read-only. Defaults to `false`.\n */\n readOnly?: boolean;\n expiresInSeconds?: number;\n /** Native Olympus surface rendered by the iframe. Defaults to `full`. */\n displayMode?: 'minimal' | 'full';\n /**\n * Offer Quick Edit (⌘J, an AI rewrite of the selection) in a minimal embed, which runs on the\n * link rather than an Athena sign-in: spreadsheets, Word documents and decks, on an editable\n * link (`readOnly: false`) only, and only to a viewer who is signed in to Athena (the provider's\n * `token`, which `AthenaAssetEmbed` hands the frame). Its runs are billed to the asset's\n * workspace under the link's creator, and each link has its own hourly cap. The full mode's\n * editors offer it to the signed-in person already. Defaults to `false`.\n */\n allowQuickEdit?: boolean;\n /**\n * Show the asset's comment threads, read-only, to everyone holding the link, a viewer who is not\n * signed in included. Without it, a minimal embed shows them only to a signed-in viewer who has\n * their own access to the asset (the bearer this component hands the iframe, or an Athena\n * session). No embed ever writes a comment. Defaults to `false`.\n */\n allowComments?: boolean;\n}\n\nexport const DEFAULT_ASSET_EMBED_READ_ONLY = false;\nexport const DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT = false;\nexport const DEFAULT_ASSET_EMBED_ALLOW_COMMENTS = false;\nexport const DEFAULT_ASSET_EMBED_DISPLAY_MODE = 'full' as const;\n\nexport interface UseAssetEmbedResult {\n embedUrl: string | null;\n isLoading: boolean;\n error: string | null;\n}\n\nexport function buildAssetEmbedRequestBody({\n assetId,\n readOnly,\n expiresInSeconds,\n displayMode,\n allowQuickEdit = DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT,\n allowComments = DEFAULT_ASSET_EMBED_ALLOW_COMMENTS,\n}: {\n assetId: string;\n readOnly: boolean;\n expiresInSeconds: number;\n displayMode: 'minimal' | 'full';\n allowQuickEdit?: boolean;\n allowComments?: boolean;\n}) {\n return {\n asset_id: assetId,\n read_only: readOnly,\n expires_in_seconds: expiresInSeconds,\n display_mode: displayMode,\n // Sent only when asked: an agora that predates it is never handed an unknown field.\n ...(allowQuickEdit && { allow_quick_edit: true }),\n ...(allowComments && { allow_comments: true }),\n };\n}\n\n/**\n * The embed links' query client when the host provides none: one for the page, so every embed of\n * one asset, mode and identity shares one minted link until it expires.\n */\nconst embedQueryClient = new QueryClient();\n\nconst ATHENA_APP_HOSTNAMES = new Set(['app.athenaintel.com', 'staging-app.athenaintel.com']);\nconst ATHENA_PREVIEW_HOSTNAME_SUFFIX = '.previews.athenaintel.com';\nconst ATHENA_WORKSPACE_HOSTNAME_PATTERN = /^.+--(\\d+)\\.(?:app\\.athenaintel\\.com|staging-app\\.athenaintel\\.com)$/;\nconst OLYMPUS_WORKSPACE_PORT = '8082';\n\nconst getUrl = (value?: string | null): URL | null => {\n if (!value) {\n return null;\n }\n\n try {\n return new URL(value);\n } catch {\n return null;\n }\n};\n\nconst getCurrentOrigin = (): string | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n\n return window.location.origin;\n};\n\nconst getOrigin = (value?: string | null): string | null => getUrl(value)?.origin ?? null;\n\nconst getAthenaWorkspacePort = (url: URL): string | null => {\n const match = url.hostname.toLowerCase().match(ATHENA_WORKSPACE_HOSTNAME_PATTERN);\n return match?.[1] ?? null;\n};\n\nconst isEmbedCapableAthenaHost = (url: URL): boolean => {\n const hostname = url.hostname.toLowerCase();\n\n if (ATHENA_APP_HOSTNAMES.has(hostname) || hostname.endsWith(ATHENA_PREVIEW_HOSTNAME_SUFFIX)) {\n return true;\n }\n\n return getAthenaWorkspacePort(url) === OLYMPUS_WORKSPACE_PORT;\n};\n\nconst shouldPreferDerivedEmbedAppUrl = ({\n appUrl,\n derivedAppUrl,\n currentOrigin,\n}: {\n appUrl: string;\n derivedAppUrl: string | null;\n currentOrigin?: string | null;\n}): boolean => {\n if (!derivedAppUrl) {\n return false;\n }\n\n const resolvedAppUrl = getUrl(appUrl);\n if (!resolvedAppUrl) {\n return true;\n }\n\n const workspacePort = getAthenaWorkspacePort(resolvedAppUrl);\n if (workspacePort && workspacePort !== OLYMPUS_WORKSPACE_PORT) {\n return true;\n }\n\n if (isEmbedCapableAthenaHost(resolvedAppUrl)) {\n return false;\n }\n\n const resolvedCurrentOrigin = getOrigin(currentOrigin ?? getCurrentOrigin());\n return !!resolvedCurrentOrigin && resolvedAppUrl.origin === resolvedCurrentOrigin;\n};\n\nconst resolveEmbedAppUrl = ({\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string | null => {\n const derivedAppUrl = deriveAthenaAppUrl({ backendUrl });\n\n if (!appUrl) {\n return derivedAppUrl;\n }\n\n if (shouldPreferDerivedEmbedAppUrl({ appUrl, derivedAppUrl, currentOrigin })) {\n return derivedAppUrl;\n }\n\n return appUrl;\n};\n\nexport function resolveAssetEmbedUrl({\n embedUrl,\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n embedUrl: string;\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string {\n try {\n const resolvedEmbedUrl = new URL(embedUrl);\n if (!resolvedEmbedUrl.pathname.startsWith('/embed/')) {\n return embedUrl;\n }\n\n const embedAppUrl = resolveEmbedAppUrl({\n appUrl,\n backendUrl,\n currentOrigin,\n });\n if (!embedAppUrl) {\n return embedUrl;\n }\n\n const resolvedAppUrl = new URL(embedAppUrl);\n resolvedEmbedUrl.protocol = resolvedAppUrl.protocol;\n resolvedEmbedUrl.host = resolvedAppUrl.host;\n return resolvedEmbedUrl.toString();\n } catch {\n return embedUrl;\n }\n}\n\nfunction setFiniteNumberParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'number' && Number.isFinite(value)) {\n params.set(key, String(value));\n }\n}\n\nfunction setNonEmptyStringParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'string' && value.length > 0) {\n params.set(key, value);\n }\n}\n\n/** The SDK's own embed parameter: it marks the bridge, never a place, so no anchor writes it. */\nconst SDK_BRIDGE_PARAM = 'athena_sdk_bridge';\n\n/**\n * The pairs of an anchor given as its raw query — the string an Athena link, citation or search\n * result carries (`anchor=slide&slide_index=3&slide_id=…`, `anchor=time&time=0:42`), alone or\n * after the `?` of an asset ref (`asset_x?anchor=…`) — read as URL parameters, or `null` when the\n * string holds none, or a parameter with no name. The SDK never interprets the anchor: the embed\n * page parses it with the references grammar, so every kind of place travels as written.\n */\nfunction rawAnchorQueryPairs(query: string): Array<[string, string]> | null {\n const queryIndex = query.indexOf('?');\n const search = queryIndex === -1 ? query : query.slice(queryIndex + 1);\n const pairs = [...new URLSearchParams(search)].filter(([key]) => key !== SDK_BRIDGE_PARAM);\n if (pairs.length === 0 || pairs.some(([key]) => key.length === 0)) return null;\n return pairs;\n}\n\nfunction appendRawAnchorQuery({ params, query }: { params: URLSearchParams; query: string }): void {\n const pairs = rawAnchorQueryPairs(query);\n if (!pairs) return;\n // Verbatim: a key the URL already has is replaced, and a repeated key keeps every value.\n for (const [key] of pairs) params.delete(key);\n for (const [key, value] of pairs) params.append(key, value);\n}\n\nfunction appendTypedAnchorParams({\n params,\n anchor,\n}: {\n params: URLSearchParams;\n anchor: object & { type: string };\n}): void {\n switch (anchor.type) {\n case 'page':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n break;\n case 'page_range':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'start_page',\n value: 'startPage' in anchor ? anchor.startPage : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'end_page',\n value: 'endPage' in anchor ? anchor.endPage : undefined,\n });\n break;\n case 'page_rect':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'x',\n value: 'x' in anchor ? anchor.x : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'y',\n value: 'y' in anchor ? anchor.y : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'width',\n value: 'width' in anchor ? anchor.width : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'height',\n value: 'height' in anchor ? anchor.height : undefined,\n });\n if (\n 'unit' in anchor &&\n (anchor.unit === 'percent' || anchor.unit === 'point')\n ) {\n params.set('unit', anchor.unit);\n }\n break;\n case 'page_text':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n if ('text' in anchor && typeof anchor.text === 'string') {\n params.set('text', anchor.text);\n }\n // Optional context that disambiguates a repeated phrase, and the hash of\n // the cited unit's normalized text; the embed parses both with the shared\n // `@athenaintel/references` parser.\n setNonEmptyStringParam({ params, key: 'prefix', value: 'prefix' in anchor ? anchor.prefix : undefined });\n setNonEmptyStringParam({ params, key: 'suffix', value: 'suffix' in anchor ? anchor.suffix : undefined });\n setNonEmptyStringParam({ params, key: 'hash', value: 'hash' in anchor ? anchor.hash : undefined });\n break;\n // Sheet anchors ride the same query contract the embed page parses with\n // the shared `@athenaintel/references` parser.\n case 'sheet_cell':\n case 'sheet_range':\n case 'sheet_tab':\n case 'sheet_table':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'sheet_id',\n value: 'sheetId' in anchor ? anchor.sheetId : undefined,\n });\n if ('cell' in anchor && typeof anchor.cell === 'string') {\n params.set('cell', anchor.cell);\n }\n if ('range' in anchor && typeof anchor.range === 'string') {\n params.set('range', anchor.range);\n }\n if ('tableName' in anchor && typeof anchor.tableName === 'string') {\n params.set('table_name', anchor.tableName);\n }\n if ('fallbackRange' in anchor && typeof anchor.fallbackRange === 'string') {\n params.set('fallback_range', anchor.fallbackRange);\n }\n break;\n }\n}\n\n/**\n * Writes a citation's place into an embed URL: its anchor and its excerpt (the passage the embed\n * finds by its words — alone, when the citation names no anchor). The anchor is either its raw\n * query string, forwarded as written whatever kind of place it names, or one of the typed page and\n * sheet anchors. Athena on the web's Spaces links read the same grammar, so a host that opens a\n * cited place there writes it the same way.\n */\nexport function appendCitationReferenceParams({\n params,\n reference,\n}: {\n params: URLSearchParams;\n reference?: AthenaAssetCitationReference;\n}): void {\n const anchor = reference?.anchor;\n if (typeof anchor === 'string') {\n appendRawAnchorQuery({ params, query: anchor });\n } else if (anchor && typeof anchor === 'object' && 'type' in anchor && typeof anchor.type === 'string') {\n appendTypedAnchorParams({ params, anchor: { ...anchor, type: anchor.type } });\n }\n\n const excerpt = reference?.meta?.excerpt;\n if (typeof excerpt === 'string' && excerpt) {\n params.set('excerpt', excerpt);\n }\n}\n\nexport function buildSdkAssetEmbedUrl(embedUrl: string, reference?: AthenaAssetCitationReference): string {\n try {\n const url = new URL(embedUrl);\n url.searchParams.set('athena_sdk_bridge', '1');\n appendCitationReferenceParams({ params: url.searchParams, reference });\n return url.toString();\n } catch {\n return embedUrl;\n }\n}\n\nconst getJwtPayload = (token: string): Record<string, unknown> | null => {\n try {\n const payloadSegment = token.split('.')[1];\n if (!payloadSegment || typeof atob !== 'function') return null;\n const normalized = payloadSegment.replace(/-/g, '+').replace(/_/g, '/');\n const payload: unknown = JSON.parse(\n atob(normalized.padEnd(normalized.length + ((4 - (normalized.length % 4)) % 4), '=')),\n );\n return payload && typeof payload === 'object' ? (payload as Record<string, unknown>) : null;\n } catch {\n return null;\n }\n};\n\nexport function getAssetEmbedAuthContext({ token, apiKey }: { token?: string | null; apiKey?: string }): string {\n if (!token) return apiKey ? `api-key:${apiKey}` : 'anonymous';\n\n const payload = getJwtPayload(token);\n const userId = payload?.user_id ?? payload?.sub;\n if (typeof userId !== 'string' || !userId) return `opaque-token:${token}`;\n\n return `jwt:${JSON.stringify({\n issuer: payload?.iss ?? null,\n userId,\n activeOrgId: payload?.active_org_id ?? payload?.org_id ?? null,\n workspaceId: payload?.workspace_id ?? null,\n orgMemberships: payload?.org_id_to_org_member_info ?? null,\n })}`;\n}\n\n/**\n * Hook to generate an embed URL for rendering an asset in an iframe.\n * Calls the Athena embed token endpoint through TanStack Query (the host's\n * QueryClient when there is one, else the page's embed client), keyed on the\n * asset, the mode and the identity.\n *\n * Note: The auth token is read via a ref and is intentionally NOT part\n * of the query key. PropelAuth and similar providers refresh the token value\n * periodically (e.g. on window focus), but the underlying session is the\n * same — minting again on every token rotation would force the iframe to\n * reload unnecessarily. The link is minted again only when the identity\n * (user or tenant), the asset or the mode changes.\n */\nexport function useAssetEmbed(\n assetId: string | null,\n options: UseAssetEmbedOptions & {\n backendUrl: string;\n appUrl?: string;\n apiKey?: string;\n token?: string | null;\n /**\n * Live token resolver (the provider's `config.getToken`) that renews the\n * held `token`: read before the embed-token request, and asked for a\n * renewed token (`force: true`) when the backend refuses it. Ignored when\n * no token is held, so the request always authenticates as the identity\n * the embed cache is keyed on.\n */\n getToken?: AthenaStatewireTokenGetter;\n } = {\n backendUrl: '',\n },\n): UseAssetEmbedResult {\n const {\n readOnly = DEFAULT_ASSET_EMBED_READ_ONLY,\n expiresInSeconds = 60 * 60 * 24 * 30,\n displayMode = DEFAULT_ASSET_EMBED_DISPLAY_MODE,\n allowQuickEdit = DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT,\n allowComments = DEFAULT_ASSET_EMBED_ALLOW_COMMENTS,\n backendUrl,\n appUrl,\n apiKey,\n token,\n getToken,\n } = options;\n // Keep the latest token in a ref so the query can read it without being\n // keyed on it: PropelAuth and similar providers rotate the token value.\n const tokenRef = useRef(token);\n tokenRef.current = token;\n const getTokenRef = useRef(getToken);\n getTokenRef.current = getToken;\n\n // Stable across normal JWT rotations, but changes when the user or tenant\n // changes so a signed URL can never be reused across auth boundaries.\n const authContext = getAssetEmbedAuthContext({ token, apiKey });\n const enabled = Boolean(assetId && backendUrl);\n\n const query = useQuery(\n {\n queryKey: [\n 'athena-sdk',\n 'asset-embed',\n assetId,\n readOnly,\n displayMode,\n allowQuickEdit,\n allowComments,\n // The identity, never the credential: an opaque token or a key is part of `authContext`.\n keyPartFingerprint(authContext),\n backendUrl,\n appUrl ?? '',\n ],\n queryFn: async ({ signal }): Promise<{ url: string; expiresAt: number }> => {\n // Derive the Athena API base from backendUrl before calling the embed endpoint.\n const apiBaseUrl = backendUrl.replace(/\\/api\\/assistant-ui\\/?$/, '');\n const heldToken = tokenRef.current;\n const res = await sendWithCredentialRenewal(\n { apiKey, token: heldToken, getToken: heldToken ? getTokenRef.current : undefined },\n (headers) =>\n fetch(`${apiBaseUrl}/api/embed/generate-token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...headers },\n body: JSON.stringify(\n buildAssetEmbedRequestBody({\n assetId: assetId ?? '',\n readOnly,\n expiresInSeconds,\n displayMode,\n allowQuickEdit,\n allowComments,\n }),\n ),\n signal,\n }),\n );\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`Failed to generate embed token (${res.status}): ${text}`);\n }\n const data: { embed_url: string; expires_at: number } = await res.json();\n return {\n url: resolveAssetEmbedUrl({ embedUrl: data.embed_url, appUrl, backendUrl }),\n expiresAt: data.expires_at,\n };\n },\n enabled,\n // A minted link is good until it expires; a new one would reload the frame, so it is never\n // minted again while the embed shows (no refetch on focus or reconnect) and never retried.\n staleTime: (cached) =>\n cached.state.data ? Math.max(0, cached.state.data.expiresAt * 1000 - Date.now()) : 0,\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n retry: false,\n },\n useContext(QueryClientContext) ?? embedQueryClient,\n );\n\n return {\n embedUrl: enabled ? (query.data?.url ?? null) : null,\n isLoading: enabled && query.isLoading,\n error: enabled && query.error ? query.error.message : null,\n };\n}\n"],"names":["QueryClient","deriveAthenaAppUrl","useRef","useQuery","keyPartFingerprint","sendWithCredentialRenewal","useContext","QueryClientContext"],"mappings":"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"}
@@ -1,16 +1,23 @@
1
1
  import type { AthenaStatewireTokenGetter } from '../runtime/statewire-auth-headers';
2
2
  import type { AthenaAssetCitationReference } from './asset-embed-messages';
3
3
  export interface UseAssetEmbedOptions {
4
+ /**
5
+ * Mint a view-only link. A ceiling, not a grant: the link edits only what its creator may edit,
6
+ * and a Word document, an Athena document, a spreadsheet, a user interface or a deck edits only
7
+ * for a viewer who is signed in to Athena (the provider's `token`, which `AthenaAssetEmbed`
8
+ * hands the frame). Anyone else views such a link read-only. Defaults to `false`.
9
+ */
4
10
  readOnly?: boolean;
5
11
  expiresInSeconds?: number;
6
12
  /** Native Olympus surface rendered by the iframe. Defaults to `full`. */
7
13
  displayMode?: 'minimal' | 'full';
8
14
  /**
9
- * Offer Quick Edit (⌘J, an AI rewrite of the selection) in a minimal embed, where the viewer
10
- * does not sign in: spreadsheets and Word documents, on an editable link (`readOnly: false`)
11
- * only. Its runs are billed to the asset's workspace under the link's creator, and each link has
12
- * its own hourly cap. The full mode's editors offer it to the signed-in person already. Defaults
13
- * to `false`.
15
+ * Offer Quick Edit (⌘J, an AI rewrite of the selection) in a minimal embed, which runs on the
16
+ * link rather than an Athena sign-in: spreadsheets, Word documents and decks, on an editable
17
+ * link (`readOnly: false`) only, and only to a viewer who is signed in to Athena (the provider's
18
+ * `token`, which `AthenaAssetEmbed` hands the frame). Its runs are billed to the asset's
19
+ * workspace under the link's creator, and each link has its own hourly cap. The full mode's
20
+ * editors offer it to the signed-in person already. Defaults to `false`.
14
21
  */
15
22
  allowQuickEdit?: boolean;
16
23
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"use-asset-embed.js","sources":["../../src/assets/use-asset-embed.ts"],"sourcesContent":["import { QueryClient, QueryClientContext, useQuery } from '@tanstack/react-query';\nimport { useContext, useRef } from 'react';\nimport { deriveAthenaAppUrl } from '../lib/athena-urls';\nimport { keyPartFingerprint } from '../lib/use-athena-rest-query';\nimport { sendWithCredentialRenewal } from '../runtime/credential-renewal';\nimport type { AthenaStatewireTokenGetter } from '../runtime/statewire-auth-headers';\nimport type { AthenaAssetCitationReference } from './asset-embed-messages';\n\nexport interface UseAssetEmbedOptions {\n readOnly?: boolean;\n expiresInSeconds?: number;\n /** Native Olympus surface rendered by the iframe. Defaults to `full`. */\n displayMode?: 'minimal' | 'full';\n /**\n * Offer Quick Edit (⌘J, an AI rewrite of the selection) in a minimal embed, where the viewer\n * does not sign in: spreadsheets and Word documents, on an editable link (`readOnly: false`)\n * only. Its runs are billed to the asset's workspace under the link's creator, and each link has\n * its own hourly cap. The full mode's editors offer it to the signed-in person already. Defaults\n * to `false`.\n */\n allowQuickEdit?: boolean;\n /**\n * Show the asset's comment threads, read-only, to everyone holding the link, a viewer who is not\n * signed in included. Without it, a minimal embed shows them only to a signed-in viewer who has\n * their own access to the asset (the bearer this component hands the iframe, or an Athena\n * session). No embed ever writes a comment. Defaults to `false`.\n */\n allowComments?: boolean;\n}\n\nexport const DEFAULT_ASSET_EMBED_READ_ONLY = false;\nexport const DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT = false;\nexport const DEFAULT_ASSET_EMBED_ALLOW_COMMENTS = false;\nexport const DEFAULT_ASSET_EMBED_DISPLAY_MODE = 'full' as const;\n\nexport interface UseAssetEmbedResult {\n embedUrl: string | null;\n isLoading: boolean;\n error: string | null;\n}\n\nexport function buildAssetEmbedRequestBody({\n assetId,\n readOnly,\n expiresInSeconds,\n displayMode,\n allowQuickEdit = DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT,\n allowComments = DEFAULT_ASSET_EMBED_ALLOW_COMMENTS,\n}: {\n assetId: string;\n readOnly: boolean;\n expiresInSeconds: number;\n displayMode: 'minimal' | 'full';\n allowQuickEdit?: boolean;\n allowComments?: boolean;\n}) {\n return {\n asset_id: assetId,\n read_only: readOnly,\n expires_in_seconds: expiresInSeconds,\n display_mode: displayMode,\n // Sent only when asked: an agora that predates it is never handed an unknown field.\n ...(allowQuickEdit && { allow_quick_edit: true }),\n ...(allowComments && { allow_comments: true }),\n };\n}\n\n/**\n * The embed links' query client when the host provides none: one for the page, so every embed of\n * one asset, mode and identity shares one minted link until it expires.\n */\nconst embedQueryClient = new QueryClient();\n\nconst ATHENA_APP_HOSTNAMES = new Set(['app.athenaintel.com', 'staging-app.athenaintel.com']);\nconst ATHENA_PREVIEW_HOSTNAME_SUFFIX = '.previews.athenaintel.com';\nconst ATHENA_WORKSPACE_HOSTNAME_PATTERN = /^.+--(\\d+)\\.(?:app\\.athenaintel\\.com|staging-app\\.athenaintel\\.com)$/;\nconst OLYMPUS_WORKSPACE_PORT = '8082';\n\nconst getUrl = (value?: string | null): URL | null => {\n if (!value) {\n return null;\n }\n\n try {\n return new URL(value);\n } catch {\n return null;\n }\n};\n\nconst getCurrentOrigin = (): string | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n\n return window.location.origin;\n};\n\nconst getOrigin = (value?: string | null): string | null => getUrl(value)?.origin ?? null;\n\nconst getAthenaWorkspacePort = (url: URL): string | null => {\n const match = url.hostname.toLowerCase().match(ATHENA_WORKSPACE_HOSTNAME_PATTERN);\n return match?.[1] ?? null;\n};\n\nconst isEmbedCapableAthenaHost = (url: URL): boolean => {\n const hostname = url.hostname.toLowerCase();\n\n if (ATHENA_APP_HOSTNAMES.has(hostname) || hostname.endsWith(ATHENA_PREVIEW_HOSTNAME_SUFFIX)) {\n return true;\n }\n\n return getAthenaWorkspacePort(url) === OLYMPUS_WORKSPACE_PORT;\n};\n\nconst shouldPreferDerivedEmbedAppUrl = ({\n appUrl,\n derivedAppUrl,\n currentOrigin,\n}: {\n appUrl: string;\n derivedAppUrl: string | null;\n currentOrigin?: string | null;\n}): boolean => {\n if (!derivedAppUrl) {\n return false;\n }\n\n const resolvedAppUrl = getUrl(appUrl);\n if (!resolvedAppUrl) {\n return true;\n }\n\n const workspacePort = getAthenaWorkspacePort(resolvedAppUrl);\n if (workspacePort && workspacePort !== OLYMPUS_WORKSPACE_PORT) {\n return true;\n }\n\n if (isEmbedCapableAthenaHost(resolvedAppUrl)) {\n return false;\n }\n\n const resolvedCurrentOrigin = getOrigin(currentOrigin ?? getCurrentOrigin());\n return !!resolvedCurrentOrigin && resolvedAppUrl.origin === resolvedCurrentOrigin;\n};\n\nconst resolveEmbedAppUrl = ({\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string | null => {\n const derivedAppUrl = deriveAthenaAppUrl({ backendUrl });\n\n if (!appUrl) {\n return derivedAppUrl;\n }\n\n if (shouldPreferDerivedEmbedAppUrl({ appUrl, derivedAppUrl, currentOrigin })) {\n return derivedAppUrl;\n }\n\n return appUrl;\n};\n\nexport function resolveAssetEmbedUrl({\n embedUrl,\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n embedUrl: string;\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string {\n try {\n const resolvedEmbedUrl = new URL(embedUrl);\n if (!resolvedEmbedUrl.pathname.startsWith('/embed/')) {\n return embedUrl;\n }\n\n const embedAppUrl = resolveEmbedAppUrl({\n appUrl,\n backendUrl,\n currentOrigin,\n });\n if (!embedAppUrl) {\n return embedUrl;\n }\n\n const resolvedAppUrl = new URL(embedAppUrl);\n resolvedEmbedUrl.protocol = resolvedAppUrl.protocol;\n resolvedEmbedUrl.host = resolvedAppUrl.host;\n return resolvedEmbedUrl.toString();\n } catch {\n return embedUrl;\n }\n}\n\nfunction setFiniteNumberParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'number' && Number.isFinite(value)) {\n params.set(key, String(value));\n }\n}\n\nfunction setNonEmptyStringParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'string' && value.length > 0) {\n params.set(key, value);\n }\n}\n\n/** The SDK's own embed parameter: it marks the bridge, never a place, so no anchor writes it. */\nconst SDK_BRIDGE_PARAM = 'athena_sdk_bridge';\n\n/**\n * The pairs of an anchor given as its raw query — the string an Athena link, citation or search\n * result carries (`anchor=slide&slide_index=3&slide_id=…`, `anchor=time&time=0:42`), alone or\n * after the `?` of an asset ref (`asset_x?anchor=…`) — read as URL parameters, or `null` when the\n * string holds none, or a parameter with no name. The SDK never interprets the anchor: the embed\n * page parses it with the references grammar, so every kind of place travels as written.\n */\nfunction rawAnchorQueryPairs(query: string): Array<[string, string]> | null {\n const queryIndex = query.indexOf('?');\n const search = queryIndex === -1 ? query : query.slice(queryIndex + 1);\n const pairs = [...new URLSearchParams(search)].filter(([key]) => key !== SDK_BRIDGE_PARAM);\n if (pairs.length === 0 || pairs.some(([key]) => key.length === 0)) return null;\n return pairs;\n}\n\nfunction appendRawAnchorQuery({ params, query }: { params: URLSearchParams; query: string }): void {\n const pairs = rawAnchorQueryPairs(query);\n if (!pairs) return;\n // Verbatim: a key the URL already has is replaced, and a repeated key keeps every value.\n for (const [key] of pairs) params.delete(key);\n for (const [key, value] of pairs) params.append(key, value);\n}\n\nfunction appendTypedAnchorParams({\n params,\n anchor,\n}: {\n params: URLSearchParams;\n anchor: object & { type: string };\n}): void {\n switch (anchor.type) {\n case 'page':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n break;\n case 'page_range':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'start_page',\n value: 'startPage' in anchor ? anchor.startPage : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'end_page',\n value: 'endPage' in anchor ? anchor.endPage : undefined,\n });\n break;\n case 'page_rect':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'x',\n value: 'x' in anchor ? anchor.x : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'y',\n value: 'y' in anchor ? anchor.y : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'width',\n value: 'width' in anchor ? anchor.width : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'height',\n value: 'height' in anchor ? anchor.height : undefined,\n });\n if (\n 'unit' in anchor &&\n (anchor.unit === 'percent' || anchor.unit === 'point')\n ) {\n params.set('unit', anchor.unit);\n }\n break;\n case 'page_text':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n if ('text' in anchor && typeof anchor.text === 'string') {\n params.set('text', anchor.text);\n }\n // Optional context that disambiguates a repeated phrase, and the hash of\n // the cited unit's normalized text; the embed parses both with the shared\n // `@athenaintel/references` parser.\n setNonEmptyStringParam({ params, key: 'prefix', value: 'prefix' in anchor ? anchor.prefix : undefined });\n setNonEmptyStringParam({ params, key: 'suffix', value: 'suffix' in anchor ? anchor.suffix : undefined });\n setNonEmptyStringParam({ params, key: 'hash', value: 'hash' in anchor ? anchor.hash : undefined });\n break;\n // Sheet anchors ride the same query contract the embed page parses with\n // the shared `@athenaintel/references` parser.\n case 'sheet_cell':\n case 'sheet_range':\n case 'sheet_tab':\n case 'sheet_table':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'sheet_id',\n value: 'sheetId' in anchor ? anchor.sheetId : undefined,\n });\n if ('cell' in anchor && typeof anchor.cell === 'string') {\n params.set('cell', anchor.cell);\n }\n if ('range' in anchor && typeof anchor.range === 'string') {\n params.set('range', anchor.range);\n }\n if ('tableName' in anchor && typeof anchor.tableName === 'string') {\n params.set('table_name', anchor.tableName);\n }\n if ('fallbackRange' in anchor && typeof anchor.fallbackRange === 'string') {\n params.set('fallback_range', anchor.fallbackRange);\n }\n break;\n }\n}\n\n/**\n * Writes a citation's place into an embed URL: its anchor and its excerpt (the passage the embed\n * finds by its words — alone, when the citation names no anchor). The anchor is either its raw\n * query string, forwarded as written whatever kind of place it names, or one of the typed page and\n * sheet anchors. Athena on the web's Spaces links read the same grammar, so a host that opens a\n * cited place there writes it the same way.\n */\nexport function appendCitationReferenceParams({\n params,\n reference,\n}: {\n params: URLSearchParams;\n reference?: AthenaAssetCitationReference;\n}): void {\n const anchor = reference?.anchor;\n if (typeof anchor === 'string') {\n appendRawAnchorQuery({ params, query: anchor });\n } else if (anchor && typeof anchor === 'object' && 'type' in anchor && typeof anchor.type === 'string') {\n appendTypedAnchorParams({ params, anchor: { ...anchor, type: anchor.type } });\n }\n\n const excerpt = reference?.meta?.excerpt;\n if (typeof excerpt === 'string' && excerpt) {\n params.set('excerpt', excerpt);\n }\n}\n\nexport function buildSdkAssetEmbedUrl(embedUrl: string, reference?: AthenaAssetCitationReference): string {\n try {\n const url = new URL(embedUrl);\n url.searchParams.set('athena_sdk_bridge', '1');\n appendCitationReferenceParams({ params: url.searchParams, reference });\n return url.toString();\n } catch {\n return embedUrl;\n }\n}\n\nconst getJwtPayload = (token: string): Record<string, unknown> | null => {\n try {\n const payloadSegment = token.split('.')[1];\n if (!payloadSegment || typeof atob !== 'function') return null;\n const normalized = payloadSegment.replace(/-/g, '+').replace(/_/g, '/');\n const payload: unknown = JSON.parse(\n atob(normalized.padEnd(normalized.length + ((4 - (normalized.length % 4)) % 4), '=')),\n );\n return payload && typeof payload === 'object' ? (payload as Record<string, unknown>) : null;\n } catch {\n return null;\n }\n};\n\nexport function getAssetEmbedAuthContext({ token, apiKey }: { token?: string | null; apiKey?: string }): string {\n if (!token) return apiKey ? `api-key:${apiKey}` : 'anonymous';\n\n const payload = getJwtPayload(token);\n const userId = payload?.user_id ?? payload?.sub;\n if (typeof userId !== 'string' || !userId) return `opaque-token:${token}`;\n\n return `jwt:${JSON.stringify({\n issuer: payload?.iss ?? null,\n userId,\n activeOrgId: payload?.active_org_id ?? payload?.org_id ?? null,\n workspaceId: payload?.workspace_id ?? null,\n orgMemberships: payload?.org_id_to_org_member_info ?? null,\n })}`;\n}\n\n/**\n * Hook to generate an embed URL for rendering an asset in an iframe.\n * Calls the Athena embed token endpoint through TanStack Query (the host's\n * QueryClient when there is one, else the page's embed client), keyed on the\n * asset, the mode and the identity.\n *\n * Note: The auth token is read via a ref and is intentionally NOT part\n * of the query key. PropelAuth and similar providers refresh the token value\n * periodically (e.g. on window focus), but the underlying session is the\n * same — minting again on every token rotation would force the iframe to\n * reload unnecessarily. The link is minted again only when the identity\n * (user or tenant), the asset or the mode changes.\n */\nexport function useAssetEmbed(\n assetId: string | null,\n options: UseAssetEmbedOptions & {\n backendUrl: string;\n appUrl?: string;\n apiKey?: string;\n token?: string | null;\n /**\n * Live token resolver (the provider's `config.getToken`) that renews the\n * held `token`: read before the embed-token request, and asked for a\n * renewed token (`force: true`) when the backend refuses it. Ignored when\n * no token is held, so the request always authenticates as the identity\n * the embed cache is keyed on.\n */\n getToken?: AthenaStatewireTokenGetter;\n } = {\n backendUrl: '',\n },\n): UseAssetEmbedResult {\n const {\n readOnly = DEFAULT_ASSET_EMBED_READ_ONLY,\n expiresInSeconds = 60 * 60 * 24 * 30,\n displayMode = DEFAULT_ASSET_EMBED_DISPLAY_MODE,\n allowQuickEdit = DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT,\n allowComments = DEFAULT_ASSET_EMBED_ALLOW_COMMENTS,\n backendUrl,\n appUrl,\n apiKey,\n token,\n getToken,\n } = options;\n // Keep the latest token in a ref so the query can read it without being\n // keyed on it: PropelAuth and similar providers rotate the token value.\n const tokenRef = useRef(token);\n tokenRef.current = token;\n const getTokenRef = useRef(getToken);\n getTokenRef.current = getToken;\n\n // Stable across normal JWT rotations, but changes when the user or tenant\n // changes so a signed URL can never be reused across auth boundaries.\n const authContext = getAssetEmbedAuthContext({ token, apiKey });\n const enabled = Boolean(assetId && backendUrl);\n\n const query = useQuery(\n {\n queryKey: [\n 'athena-sdk',\n 'asset-embed',\n assetId,\n readOnly,\n displayMode,\n allowQuickEdit,\n allowComments,\n // The identity, never the credential: an opaque token or a key is part of `authContext`.\n keyPartFingerprint(authContext),\n backendUrl,\n appUrl ?? '',\n ],\n queryFn: async ({ signal }): Promise<{ url: string; expiresAt: number }> => {\n // Derive the Athena API base from backendUrl before calling the embed endpoint.\n const apiBaseUrl = backendUrl.replace(/\\/api\\/assistant-ui\\/?$/, '');\n const heldToken = tokenRef.current;\n const res = await sendWithCredentialRenewal(\n { apiKey, token: heldToken, getToken: heldToken ? getTokenRef.current : undefined },\n (headers) =>\n fetch(`${apiBaseUrl}/api/embed/generate-token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...headers },\n body: JSON.stringify(\n buildAssetEmbedRequestBody({\n assetId: assetId ?? '',\n readOnly,\n expiresInSeconds,\n displayMode,\n allowQuickEdit,\n allowComments,\n }),\n ),\n signal,\n }),\n );\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`Failed to generate embed token (${res.status}): ${text}`);\n }\n const data: { embed_url: string; expires_at: number } = await res.json();\n return {\n url: resolveAssetEmbedUrl({ embedUrl: data.embed_url, appUrl, backendUrl }),\n expiresAt: data.expires_at,\n };\n },\n enabled,\n // A minted link is good until it expires; a new one would reload the frame, so it is never\n // minted again while the embed shows (no refetch on focus or reconnect) and never retried.\n staleTime: (cached) =>\n cached.state.data ? Math.max(0, cached.state.data.expiresAt * 1000 - Date.now()) : 0,\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n retry: false,\n },\n useContext(QueryClientContext) ?? embedQueryClient,\n );\n\n return {\n embedUrl: enabled ? (query.data?.url ?? null) : null,\n isLoading: enabled && query.isLoading,\n error: enabled && query.error ? query.error.message : null,\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"use-asset-embed.js","sources":["../../src/assets/use-asset-embed.ts"],"sourcesContent":["import { QueryClient, QueryClientContext, useQuery } from '@tanstack/react-query';\nimport { useContext, useRef } from 'react';\nimport { deriveAthenaAppUrl } from '../lib/athena-urls';\nimport { keyPartFingerprint } from '../lib/use-athena-rest-query';\nimport { sendWithCredentialRenewal } from '../runtime/credential-renewal';\nimport type { AthenaStatewireTokenGetter } from '../runtime/statewire-auth-headers';\nimport type { AthenaAssetCitationReference } from './asset-embed-messages';\n\nexport interface UseAssetEmbedOptions {\n /**\n * Mint a view-only link. A ceiling, not a grant: the link edits only what its creator may edit,\n * and a Word document, an Athena document, a spreadsheet, a user interface or a deck edits only\n * for a viewer who is signed in to Athena (the provider's `token`, which `AthenaAssetEmbed`\n * hands the frame). Anyone else views such a link read-only. Defaults to `false`.\n */\n readOnly?: boolean;\n expiresInSeconds?: number;\n /** Native Olympus surface rendered by the iframe. Defaults to `full`. */\n displayMode?: 'minimal' | 'full';\n /**\n * Offer Quick Edit (⌘J, an AI rewrite of the selection) in a minimal embed, which runs on the\n * link rather than an Athena sign-in: spreadsheets, Word documents and decks, on an editable\n * link (`readOnly: false`) only, and only to a viewer who is signed in to Athena (the provider's\n * `token`, which `AthenaAssetEmbed` hands the frame). Its runs are billed to the asset's\n * workspace under the link's creator, and each link has its own hourly cap. The full mode's\n * editors offer it to the signed-in person already. Defaults to `false`.\n */\n allowQuickEdit?: boolean;\n /**\n * Show the asset's comment threads, read-only, to everyone holding the link, a viewer who is not\n * signed in included. Without it, a minimal embed shows them only to a signed-in viewer who has\n * their own access to the asset (the bearer this component hands the iframe, or an Athena\n * session). No embed ever writes a comment. Defaults to `false`.\n */\n allowComments?: boolean;\n}\n\nexport const DEFAULT_ASSET_EMBED_READ_ONLY = false;\nexport const DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT = false;\nexport const DEFAULT_ASSET_EMBED_ALLOW_COMMENTS = false;\nexport const DEFAULT_ASSET_EMBED_DISPLAY_MODE = 'full' as const;\n\nexport interface UseAssetEmbedResult {\n embedUrl: string | null;\n isLoading: boolean;\n error: string | null;\n}\n\nexport function buildAssetEmbedRequestBody({\n assetId,\n readOnly,\n expiresInSeconds,\n displayMode,\n allowQuickEdit = DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT,\n allowComments = DEFAULT_ASSET_EMBED_ALLOW_COMMENTS,\n}: {\n assetId: string;\n readOnly: boolean;\n expiresInSeconds: number;\n displayMode: 'minimal' | 'full';\n allowQuickEdit?: boolean;\n allowComments?: boolean;\n}) {\n return {\n asset_id: assetId,\n read_only: readOnly,\n expires_in_seconds: expiresInSeconds,\n display_mode: displayMode,\n // Sent only when asked: an agora that predates it is never handed an unknown field.\n ...(allowQuickEdit && { allow_quick_edit: true }),\n ...(allowComments && { allow_comments: true }),\n };\n}\n\n/**\n * The embed links' query client when the host provides none: one for the page, so every embed of\n * one asset, mode and identity shares one minted link until it expires.\n */\nconst embedQueryClient = new QueryClient();\n\nconst ATHENA_APP_HOSTNAMES = new Set(['app.athenaintel.com', 'staging-app.athenaintel.com']);\nconst ATHENA_PREVIEW_HOSTNAME_SUFFIX = '.previews.athenaintel.com';\nconst ATHENA_WORKSPACE_HOSTNAME_PATTERN = /^.+--(\\d+)\\.(?:app\\.athenaintel\\.com|staging-app\\.athenaintel\\.com)$/;\nconst OLYMPUS_WORKSPACE_PORT = '8082';\n\nconst getUrl = (value?: string | null): URL | null => {\n if (!value) {\n return null;\n }\n\n try {\n return new URL(value);\n } catch {\n return null;\n }\n};\n\nconst getCurrentOrigin = (): string | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n\n return window.location.origin;\n};\n\nconst getOrigin = (value?: string | null): string | null => getUrl(value)?.origin ?? null;\n\nconst getAthenaWorkspacePort = (url: URL): string | null => {\n const match = url.hostname.toLowerCase().match(ATHENA_WORKSPACE_HOSTNAME_PATTERN);\n return match?.[1] ?? null;\n};\n\nconst isEmbedCapableAthenaHost = (url: URL): boolean => {\n const hostname = url.hostname.toLowerCase();\n\n if (ATHENA_APP_HOSTNAMES.has(hostname) || hostname.endsWith(ATHENA_PREVIEW_HOSTNAME_SUFFIX)) {\n return true;\n }\n\n return getAthenaWorkspacePort(url) === OLYMPUS_WORKSPACE_PORT;\n};\n\nconst shouldPreferDerivedEmbedAppUrl = ({\n appUrl,\n derivedAppUrl,\n currentOrigin,\n}: {\n appUrl: string;\n derivedAppUrl: string | null;\n currentOrigin?: string | null;\n}): boolean => {\n if (!derivedAppUrl) {\n return false;\n }\n\n const resolvedAppUrl = getUrl(appUrl);\n if (!resolvedAppUrl) {\n return true;\n }\n\n const workspacePort = getAthenaWorkspacePort(resolvedAppUrl);\n if (workspacePort && workspacePort !== OLYMPUS_WORKSPACE_PORT) {\n return true;\n }\n\n if (isEmbedCapableAthenaHost(resolvedAppUrl)) {\n return false;\n }\n\n const resolvedCurrentOrigin = getOrigin(currentOrigin ?? getCurrentOrigin());\n return !!resolvedCurrentOrigin && resolvedAppUrl.origin === resolvedCurrentOrigin;\n};\n\nconst resolveEmbedAppUrl = ({\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string | null => {\n const derivedAppUrl = deriveAthenaAppUrl({ backendUrl });\n\n if (!appUrl) {\n return derivedAppUrl;\n }\n\n if (shouldPreferDerivedEmbedAppUrl({ appUrl, derivedAppUrl, currentOrigin })) {\n return derivedAppUrl;\n }\n\n return appUrl;\n};\n\nexport function resolveAssetEmbedUrl({\n embedUrl,\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n embedUrl: string;\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string {\n try {\n const resolvedEmbedUrl = new URL(embedUrl);\n if (!resolvedEmbedUrl.pathname.startsWith('/embed/')) {\n return embedUrl;\n }\n\n const embedAppUrl = resolveEmbedAppUrl({\n appUrl,\n backendUrl,\n currentOrigin,\n });\n if (!embedAppUrl) {\n return embedUrl;\n }\n\n const resolvedAppUrl = new URL(embedAppUrl);\n resolvedEmbedUrl.protocol = resolvedAppUrl.protocol;\n resolvedEmbedUrl.host = resolvedAppUrl.host;\n return resolvedEmbedUrl.toString();\n } catch {\n return embedUrl;\n }\n}\n\nfunction setFiniteNumberParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'number' && Number.isFinite(value)) {\n params.set(key, String(value));\n }\n}\n\nfunction setNonEmptyStringParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'string' && value.length > 0) {\n params.set(key, value);\n }\n}\n\n/** The SDK's own embed parameter: it marks the bridge, never a place, so no anchor writes it. */\nconst SDK_BRIDGE_PARAM = 'athena_sdk_bridge';\n\n/**\n * The pairs of an anchor given as its raw query — the string an Athena link, citation or search\n * result carries (`anchor=slide&slide_index=3&slide_id=…`, `anchor=time&time=0:42`), alone or\n * after the `?` of an asset ref (`asset_x?anchor=…`) — read as URL parameters, or `null` when the\n * string holds none, or a parameter with no name. The SDK never interprets the anchor: the embed\n * page parses it with the references grammar, so every kind of place travels as written.\n */\nfunction rawAnchorQueryPairs(query: string): Array<[string, string]> | null {\n const queryIndex = query.indexOf('?');\n const search = queryIndex === -1 ? query : query.slice(queryIndex + 1);\n const pairs = [...new URLSearchParams(search)].filter(([key]) => key !== SDK_BRIDGE_PARAM);\n if (pairs.length === 0 || pairs.some(([key]) => key.length === 0)) return null;\n return pairs;\n}\n\nfunction appendRawAnchorQuery({ params, query }: { params: URLSearchParams; query: string }): void {\n const pairs = rawAnchorQueryPairs(query);\n if (!pairs) return;\n // Verbatim: a key the URL already has is replaced, and a repeated key keeps every value.\n for (const [key] of pairs) params.delete(key);\n for (const [key, value] of pairs) params.append(key, value);\n}\n\nfunction appendTypedAnchorParams({\n params,\n anchor,\n}: {\n params: URLSearchParams;\n anchor: object & { type: string };\n}): void {\n switch (anchor.type) {\n case 'page':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n break;\n case 'page_range':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'start_page',\n value: 'startPage' in anchor ? anchor.startPage : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'end_page',\n value: 'endPage' in anchor ? anchor.endPage : undefined,\n });\n break;\n case 'page_rect':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'x',\n value: 'x' in anchor ? anchor.x : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'y',\n value: 'y' in anchor ? anchor.y : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'width',\n value: 'width' in anchor ? anchor.width : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'height',\n value: 'height' in anchor ? anchor.height : undefined,\n });\n if (\n 'unit' in anchor &&\n (anchor.unit === 'percent' || anchor.unit === 'point')\n ) {\n params.set('unit', anchor.unit);\n }\n break;\n case 'page_text':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n if ('text' in anchor && typeof anchor.text === 'string') {\n params.set('text', anchor.text);\n }\n // Optional context that disambiguates a repeated phrase, and the hash of\n // the cited unit's normalized text; the embed parses both with the shared\n // `@athenaintel/references` parser.\n setNonEmptyStringParam({ params, key: 'prefix', value: 'prefix' in anchor ? anchor.prefix : undefined });\n setNonEmptyStringParam({ params, key: 'suffix', value: 'suffix' in anchor ? anchor.suffix : undefined });\n setNonEmptyStringParam({ params, key: 'hash', value: 'hash' in anchor ? anchor.hash : undefined });\n break;\n // Sheet anchors ride the same query contract the embed page parses with\n // the shared `@athenaintel/references` parser.\n case 'sheet_cell':\n case 'sheet_range':\n case 'sheet_tab':\n case 'sheet_table':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'sheet_id',\n value: 'sheetId' in anchor ? anchor.sheetId : undefined,\n });\n if ('cell' in anchor && typeof anchor.cell === 'string') {\n params.set('cell', anchor.cell);\n }\n if ('range' in anchor && typeof anchor.range === 'string') {\n params.set('range', anchor.range);\n }\n if ('tableName' in anchor && typeof anchor.tableName === 'string') {\n params.set('table_name', anchor.tableName);\n }\n if ('fallbackRange' in anchor && typeof anchor.fallbackRange === 'string') {\n params.set('fallback_range', anchor.fallbackRange);\n }\n break;\n }\n}\n\n/**\n * Writes a citation's place into an embed URL: its anchor and its excerpt (the passage the embed\n * finds by its words — alone, when the citation names no anchor). The anchor is either its raw\n * query string, forwarded as written whatever kind of place it names, or one of the typed page and\n * sheet anchors. Athena on the web's Spaces links read the same grammar, so a host that opens a\n * cited place there writes it the same way.\n */\nexport function appendCitationReferenceParams({\n params,\n reference,\n}: {\n params: URLSearchParams;\n reference?: AthenaAssetCitationReference;\n}): void {\n const anchor = reference?.anchor;\n if (typeof anchor === 'string') {\n appendRawAnchorQuery({ params, query: anchor });\n } else if (anchor && typeof anchor === 'object' && 'type' in anchor && typeof anchor.type === 'string') {\n appendTypedAnchorParams({ params, anchor: { ...anchor, type: anchor.type } });\n }\n\n const excerpt = reference?.meta?.excerpt;\n if (typeof excerpt === 'string' && excerpt) {\n params.set('excerpt', excerpt);\n }\n}\n\nexport function buildSdkAssetEmbedUrl(embedUrl: string, reference?: AthenaAssetCitationReference): string {\n try {\n const url = new URL(embedUrl);\n url.searchParams.set('athena_sdk_bridge', '1');\n appendCitationReferenceParams({ params: url.searchParams, reference });\n return url.toString();\n } catch {\n return embedUrl;\n }\n}\n\nconst getJwtPayload = (token: string): Record<string, unknown> | null => {\n try {\n const payloadSegment = token.split('.')[1];\n if (!payloadSegment || typeof atob !== 'function') return null;\n const normalized = payloadSegment.replace(/-/g, '+').replace(/_/g, '/');\n const payload: unknown = JSON.parse(\n atob(normalized.padEnd(normalized.length + ((4 - (normalized.length % 4)) % 4), '=')),\n );\n return payload && typeof payload === 'object' ? (payload as Record<string, unknown>) : null;\n } catch {\n return null;\n }\n};\n\nexport function getAssetEmbedAuthContext({ token, apiKey }: { token?: string | null; apiKey?: string }): string {\n if (!token) return apiKey ? `api-key:${apiKey}` : 'anonymous';\n\n const payload = getJwtPayload(token);\n const userId = payload?.user_id ?? payload?.sub;\n if (typeof userId !== 'string' || !userId) return `opaque-token:${token}`;\n\n return `jwt:${JSON.stringify({\n issuer: payload?.iss ?? null,\n userId,\n activeOrgId: payload?.active_org_id ?? payload?.org_id ?? null,\n workspaceId: payload?.workspace_id ?? null,\n orgMemberships: payload?.org_id_to_org_member_info ?? null,\n })}`;\n}\n\n/**\n * Hook to generate an embed URL for rendering an asset in an iframe.\n * Calls the Athena embed token endpoint through TanStack Query (the host's\n * QueryClient when there is one, else the page's embed client), keyed on the\n * asset, the mode and the identity.\n *\n * Note: The auth token is read via a ref and is intentionally NOT part\n * of the query key. PropelAuth and similar providers refresh the token value\n * periodically (e.g. on window focus), but the underlying session is the\n * same — minting again on every token rotation would force the iframe to\n * reload unnecessarily. The link is minted again only when the identity\n * (user or tenant), the asset or the mode changes.\n */\nexport function useAssetEmbed(\n assetId: string | null,\n options: UseAssetEmbedOptions & {\n backendUrl: string;\n appUrl?: string;\n apiKey?: string;\n token?: string | null;\n /**\n * Live token resolver (the provider's `config.getToken`) that renews the\n * held `token`: read before the embed-token request, and asked for a\n * renewed token (`force: true`) when the backend refuses it. Ignored when\n * no token is held, so the request always authenticates as the identity\n * the embed cache is keyed on.\n */\n getToken?: AthenaStatewireTokenGetter;\n } = {\n backendUrl: '',\n },\n): UseAssetEmbedResult {\n const {\n readOnly = DEFAULT_ASSET_EMBED_READ_ONLY,\n expiresInSeconds = 60 * 60 * 24 * 30,\n displayMode = DEFAULT_ASSET_EMBED_DISPLAY_MODE,\n allowQuickEdit = DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT,\n allowComments = DEFAULT_ASSET_EMBED_ALLOW_COMMENTS,\n backendUrl,\n appUrl,\n apiKey,\n token,\n getToken,\n } = options;\n // Keep the latest token in a ref so the query can read it without being\n // keyed on it: PropelAuth and similar providers rotate the token value.\n const tokenRef = useRef(token);\n tokenRef.current = token;\n const getTokenRef = useRef(getToken);\n getTokenRef.current = getToken;\n\n // Stable across normal JWT rotations, but changes when the user or tenant\n // changes so a signed URL can never be reused across auth boundaries.\n const authContext = getAssetEmbedAuthContext({ token, apiKey });\n const enabled = Boolean(assetId && backendUrl);\n\n const query = useQuery(\n {\n queryKey: [\n 'athena-sdk',\n 'asset-embed',\n assetId,\n readOnly,\n displayMode,\n allowQuickEdit,\n allowComments,\n // The identity, never the credential: an opaque token or a key is part of `authContext`.\n keyPartFingerprint(authContext),\n backendUrl,\n appUrl ?? '',\n ],\n queryFn: async ({ signal }): Promise<{ url: string; expiresAt: number }> => {\n // Derive the Athena API base from backendUrl before calling the embed endpoint.\n const apiBaseUrl = backendUrl.replace(/\\/api\\/assistant-ui\\/?$/, '');\n const heldToken = tokenRef.current;\n const res = await sendWithCredentialRenewal(\n { apiKey, token: heldToken, getToken: heldToken ? getTokenRef.current : undefined },\n (headers) =>\n fetch(`${apiBaseUrl}/api/embed/generate-token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...headers },\n body: JSON.stringify(\n buildAssetEmbedRequestBody({\n assetId: assetId ?? '',\n readOnly,\n expiresInSeconds,\n displayMode,\n allowQuickEdit,\n allowComments,\n }),\n ),\n signal,\n }),\n );\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`Failed to generate embed token (${res.status}): ${text}`);\n }\n const data: { embed_url: string; expires_at: number } = await res.json();\n return {\n url: resolveAssetEmbedUrl({ embedUrl: data.embed_url, appUrl, backendUrl }),\n expiresAt: data.expires_at,\n };\n },\n enabled,\n // A minted link is good until it expires; a new one would reload the frame, so it is never\n // minted again while the embed shows (no refetch on focus or reconnect) and never retried.\n staleTime: (cached) =>\n cached.state.data ? Math.max(0, cached.state.data.expiresAt * 1000 - Date.now()) : 0,\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n retry: false,\n },\n useContext(QueryClientContext) ?? embedQueryClient,\n );\n\n return {\n embedUrl: enabled ? (query.data?.url ?? null) : null,\n isLoading: enabled && query.isLoading,\n error: enabled && query.error ? query.error.message : null,\n };\n}\n"],"names":[],"mappings":"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"}
@@ -117,7 +117,13 @@ const StatewireApprovalCardInner = ({
117
117
  if (requestId !== void 0 && replayedRequestIdRef.current !== void 0 && requestId !== replayedRequestIdRef.current) {
118
118
  replayedRequestIdRef.current = void 0;
119
119
  }
120
- const context = { request, hitl, actionRequests };
120
+ const title = interrupts.pendingApprovalTitle({ hitl, actionRequests });
121
+ const context = {
122
+ request,
123
+ hitl,
124
+ actionRequests,
125
+ ...title ? { title } : {}
126
+ };
121
127
  const handledElsewhere = (isHandledElsewhere == null ? void 0 : isHandledElsewhere(context)) ?? false;
122
128
  const replaying = replayingRequestId !== void 0 && replayingRequestId === requestId;
123
129
  const queuedLanesAwaitingHost = !isLive && !readOnly && !handledElsewhere && requestId !== void 0 && undecidedLaneIds !== null && undecidedLaneIds.length === 0 && !pending && !replaying;
@@ -291,6 +297,7 @@ const StatewireApprovalCardInner = ({
291
297
  decideEach
292
298
  }) : void 0;
293
299
  const customHeader = renderHeader == null ? void 0 : renderHeader(context);
300
+ const headerTitle = renderDetails === void 0 && editor == null ? title : void 0;
294
301
  return /* @__PURE__ */ jsxRuntime.jsxs(
295
302
  "section",
296
303
  {
@@ -310,7 +317,24 @@ const StatewireApprovalCardInner = ({
310
317
  " tool calls"
311
318
  ] }) : null
312
319
  ] }),
313
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "pt-1 text-muted-foreground text-sm leading-snug", children: hitl && pendingCount === 1 && toolName ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
320
+ headerTitle ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
321
+ /* @__PURE__ */ jsxRuntime.jsx(
322
+ "p",
323
+ {
324
+ className: "break-words pt-1 font-medium text-foreground text-sm leading-snug",
325
+ "data-testid": "statewire-approval-headline",
326
+ children: headerTitle.headline
327
+ }
328
+ ),
329
+ headerTitle.summary ? /* @__PURE__ */ jsxRuntime.jsx(
330
+ "p",
331
+ {
332
+ className: "break-words pt-1 text-muted-foreground text-sm leading-snug",
333
+ "data-testid": "statewire-approval-summary",
334
+ children: headerTitle.summary
335
+ }
336
+ ) : null
337
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "pt-1 text-muted-foreground text-sm leading-snug", children: hitl && pendingCount === 1 && toolName ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
314
338
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-foreground", children: toolName }),
315
339
  " requires approval before it runs."
316
340
  ] }) : message })
@@ -336,7 +360,17 @@ const StatewireApprovalCardInner = ({
336
360
  ]
337
361
  }
338
362
  ) : null,
339
- editor != null ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: cnfast.cn("mt-3 flex flex-col gap-2", classNames == null ? void 0 : classNames.details), children: editor }) : showDetails ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: cnfast.cn("mt-2 flex flex-col gap-2", classNames == null ? void 0 : classNames.details), children: renderDetails ? renderDetails(context) : /* @__PURE__ */ jsxRuntime.jsx(DefaultApprovalDetails, { actionRequests }) }) : null,
363
+ editor != null ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: cnfast.cn("mt-3 flex flex-col gap-2", classNames == null ? void 0 : classNames.details), children: editor }) : showDetails ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: cnfast.cn("mt-2 flex flex-col gap-2", classNames == null ? void 0 : classNames.details), children: renderDetails ? renderDetails(context) : (
364
+ // A titled card no longer names its tool in the header, so its
365
+ // details do, as a batch's do.
366
+ /* @__PURE__ */ jsxRuntime.jsx(
367
+ DefaultApprovalDetails,
368
+ {
369
+ actionRequests,
370
+ showNames: actionRequests.length > 1 || headerTitle !== void 0
371
+ }
372
+ )
373
+ ) }) : null,
340
374
  editor != null ? null : /* @__PURE__ */ jsxRuntime.jsx("div", { className: cnfast.cn("flex flex-wrap items-center gap-2 pt-3", classNames == null ? void 0 : classNames.actions), children: readOnly ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-sm", "data-testid": "statewire-approval-read-only", children: readOnlyNotice }) : hitl ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
341
375
  /* @__PURE__ */ jsxRuntime.jsxs(
342
376
  button.Button,
@@ -397,7 +431,7 @@ const StatewireApprovalCardInner = ({
397
431
  }
398
432
  );
399
433
  };
400
- const ApprovalContextFacts = ({ context }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 space-y-0.5", "data-testid": "statewire-approval-context", children: [
434
+ const ApprovalContextFacts = ({ context }) => context.facts.length === 0 && context.warnings.length === 0 ? null : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 space-y-0.5", "data-testid": "statewire-approval-context", children: [
401
435
  context.facts.map((fact) => /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
402
436
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold", children: [
403
437
  fact.label,
@@ -416,13 +450,14 @@ const ApprovalContextFacts = ({ context }) => /* @__PURE__ */ jsxRuntime.jsxs("d
416
450
  ))
417
451
  ] });
418
452
  const DefaultApprovalDetails = ({
419
- actionRequests
453
+ actionRequests,
454
+ showNames
420
455
  }) => /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: actionRequests.map((action, index) => /* @__PURE__ */ jsxRuntime.jsxs(
421
456
  "div",
422
457
  {
423
458
  className: "rounded-lg border border-border/60 bg-muted/40 p-2 text-foreground text-xs leading-relaxed",
424
459
  children: [
425
- actionRequests.length > 1 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-mono font-semibold", children: action.name }) : null,
460
+ showNames ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-mono font-semibold", children: action.name }) : null,
426
461
  action.approvalContext ? /* @__PURE__ */ jsxRuntime.jsx(ApprovalContextFacts, { context: action.approvalContext }) : null,
427
462
  Object.keys(action.args).length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "mt-1 max-h-32 overflow-auto whitespace-pre-wrap break-words rounded bg-background p-1.5 font-mono text-[11px]", children: JSON.stringify(action.args, null, 2) }) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: "No arguments." })
428
463
  ]