@athenaintel/react 0.25.0 → 0.26.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.
- package/README.md +6 -0
- package/dist/assets/AssetPanel.cjs +1 -1
- package/dist/assets/AssetPanel.cjs.map +1 -1
- package/dist/assets/AssetPanel.js +1 -1
- package/dist/assets/AssetPanel.js.map +1 -1
- package/dist/assets/AthenaAssetEmbed.cjs +1 -1
- package/dist/assets/AthenaAssetEmbed.cjs.map +1 -1
- package/dist/assets/AthenaAssetEmbed.js +1 -1
- package/dist/assets/AthenaAssetEmbed.js.map +1 -1
- package/dist/chat-ui/composer/run-input-rules-on-enter.cjs +14 -0
- package/dist/chat-ui/composer/run-input-rules-on-enter.cjs.map +1 -0
- package/dist/chat-ui/composer/run-input-rules-on-enter.d.ts +10 -0
- package/dist/chat-ui/composer/run-input-rules-on-enter.js +14 -0
- package/dist/chat-ui/composer/run-input-rules-on-enter.js.map +1 -0
- package/dist/chat-ui/composer/tiptap-composer.cjs +7 -4
- package/dist/chat-ui/composer/tiptap-composer.cjs.map +1 -1
- package/dist/chat-ui/composer/tiptap-composer.js +7 -4
- package/dist/chat-ui/composer/tiptap-composer.js.map +1 -1
- package/dist/chat-ui/markdown-storage.cjs +16 -0
- package/dist/chat-ui/markdown-storage.cjs.map +1 -0
- package/dist/chat-ui/markdown-storage.d.ts +21 -0
- package/dist/chat-ui/markdown-storage.js +16 -0
- package/dist/chat-ui/markdown-storage.js.map +1 -0
- package/dist/chat-ui/rich-text/chat-node-views.cjs.map +1 -1
- package/dist/chat-ui/rich-text/chat-node-views.js.map +1 -1
- package/dist/chat-ui/rich-text/chat-text-schema.cjs +15 -10
- package/dist/chat-ui/rich-text/chat-text-schema.cjs.map +1 -1
- package/dist/chat-ui/rich-text/chat-text-schema.js +11 -6
- package/dist/chat-ui/rich-text/chat-text-schema.js.map +1 -1
- package/dist/packages/agent-runtime/src/conversation-list.cjs +3 -0
- package/dist/packages/agent-runtime/src/conversation-list.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/conversation-list.js +3 -0
- package/dist/packages/agent-runtime/src/conversation-list.js.map +1 -1
- package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs +2 -2
- package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/generated/runconfig-contract.js +2 -2
- package/dist/packages/agent-runtime/src/generated/runconfig-contract.js.map +1 -1
- package/dist/packages/agent-runtime/src/use-conversation-list.cjs +1 -0
- package/dist/packages/agent-runtime/src/use-conversation-list.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/use-conversation-list.js +1 -0
- package/dist/packages/agent-runtime/src/use-conversation-list.js.map +1 -1
- package/dist/packages/react/package.json.cjs +1 -1
- package/dist/packages/react/package.json.js +1 -1
- package/dist/runtime/statewire-run-config.cjs +1 -1
- package/dist/runtime/statewire-run-config.cjs.map +1 -1
- package/dist/runtime/statewire-run-config.d.ts +1 -1
- package/dist/runtime/statewire-run-config.js +1 -1
- package/dist/runtime/statewire-run-config.js.map +1 -1
- package/dist/threads/use-rest-conversation-history.d.ts +1 -0
- package/dist/threads/use-statewire-conversation-history.d.ts +2 -0
- package/dist/tools/append-document-tool-ui.cjs +5 -0
- package/dist/tools/append-document-tool-ui.cjs.map +1 -1
- package/dist/tools/append-document-tool-ui.js +5 -0
- package/dist/tools/append-document-tool-ui.js.map +1 -1
- package/package.json +13 -16
package/README.md
CHANGED
|
@@ -462,6 +462,12 @@ Both use the same bearer / API key as session creation. The full recipe
|
|
|
462
462
|
template skill `voice-session-guide.md`. `createAthenaVoiceSession` is also
|
|
463
463
|
exported if you hold the bridge state yourself.
|
|
464
464
|
|
|
465
|
+
## What's new in 0.26.0
|
|
466
|
+
|
|
467
|
+
**The composer and message bodies run on Tiptap 3.** `@athenaintel/react/chat-ui` (the composer, the `@` mention menu and the chat message body) moves from Tiptap 2 to Tiptap 3.31.3, with `tiptap-markdown` 0.9. Drafts, markdown and rendered messages are unchanged: the schema keeps its node and mark names, Enter after a typed `1.` or `-` still starts a list instead of sending, and Shift+Enter still inserts a line break. An app that also imports Tiptap itself should move to Tiptap 3 as well, so its bundle carries one copy of `@tiptap/core` and ProseMirror.
|
|
468
|
+
|
|
469
|
+
No host code changes are needed.
|
|
470
|
+
|
|
465
471
|
## What's new in 0.25.0
|
|
466
472
|
|
|
467
473
|
**A refused token no longer leaves the app on "Unauthorized".** Before this release, a deploy or a long idle could close the chat connection, and the reconnect sent the token the server had just refused. Nothing renewed it, so the app stayed on "Unauthorized" until a reload. Now every sign-in mode feeds one credential source, `config.getToken`, and each mode renews its own token:
|
|
@@ -100,7 +100,7 @@ const AssetIframe = react.memo(
|
|
|
100
100
|
width: "100%",
|
|
101
101
|
height: "100%",
|
|
102
102
|
frameBorder: "0",
|
|
103
|
-
allow: "clipboard-write; fullscreen",
|
|
103
|
+
allow: "clipboard-read; clipboard-write; fullscreen",
|
|
104
104
|
title: tab.name ?? `Asset ${tab.id}`,
|
|
105
105
|
className: "h-full w-full",
|
|
106
106
|
onLoad: postNavigationMessage
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetPanel.cjs","sources":["../../src/assets/AssetPanel.tsx"],"sourcesContent":["\"use client\";\n\nimport { type FC, memo, useCallback, useEffect, useMemo, useRef } from \"react\";\nimport {\n X,\n Loader2,\n AlertCircle,\n Maximize2,\n Minimize2,\n FileText,\n FileSpreadsheet,\n Presentation,\n File,\n BookOpen,\n LayoutGrid,\n Layers,\n} from \"lucide-react\";\nimport { appendCitationReferenceParams, useAssetEmbed } from \"./use-asset-embed\";\nimport {\n useAssetPanelStore,\n type AssetTab,\n type AssetType,\n} from \"./asset-panel-store\";\nimport {\n buildAssetIframeSrc,\n buildPresentationNavigationMessage,\n} from \"./asset-panel-url\";\nimport { useAthenaConfig } from \"../provider/AthenaContext\";\n\nconst ASSET_TYPE_CONFIG: Record<\n AssetType,\n { icon: typeof File; label: string }\n> = {\n presentation: { icon: Presentation, label: \"Presentation\" },\n spreadsheet: { icon: FileSpreadsheet, label: \"Spreadsheet\" },\n document: { icon: FileText, label: \"Document\" },\n notebook: { icon: BookOpen, label: \"Notebook\" },\n unknown: { icon: File, label: \"Asset\" },\n};\n\n/** Memoized iframe for a single asset — only re-renders when iframe inputs change. */\nconst AssetIframe = memo<{ tab: AssetTab }>(\n ({ tab }) => {\n const iframeRef = useRef<HTMLIFrameElement | null>(null);\n const { backendUrl, appUrl, apiKey, token, getToken } = useAthenaConfig();\n const { embedUrl, isLoading, error } = useAssetEmbed(tab.id, {\n backendUrl,\n appUrl,\n apiKey,\n token,\n getToken,\n });\n const initialSlideRef = useRef<{\n assetId: string;\n slideNumber?: number;\n }>({\n assetId: tab.id,\n slideNumber: tab.slideNumber,\n });\n if (initialSlideRef.current.assetId !== tab.id) {\n initialSlideRef.current = {\n assetId: tab.id,\n slideNumber: tab.slideNumber,\n };\n }\n const initialSlideNumber = initialSlideRef.current.slideNumber;\n // The citation target rides the embed URL: the embed page parses the\n // anchor params and navigates (sheet range scroll/flash, PDF page). A\n // repeat click with a DIFFERENT target updates the tab's reference ->\n // new src -> the iframe reloads at the new range. Same target twice is a\n // no-op (already there).\n //\n // Deliberately does NOT set `athena_sdk_bridge=1` (buildSdkAssetEmbedUrl\n // is for AthenaAssetEmbed): that flag makes the embed page wait\n // indefinitely for a host config handshake that only AthenaAssetEmbed\n // implements — the panel's bare iframe never replies, so the embed would\n // hang on its loading bar forever.\n const iframeSrc = useMemo(() => {\n if (!embedUrl) return null;\n const base = buildAssetIframeSrc({\n embedUrl,\n assetType: tab.type,\n slideNumber: initialSlideNumber,\n });\n try {\n const url = new URL(base, window.location.href);\n appendCitationReferenceParams({ params: url.searchParams, reference: tab.reference });\n return url.toString();\n } catch {\n return base;\n }\n }, [embedUrl, initialSlideNumber, tab.type, tab.reference]);\n const navigationMessage = useMemo(\n () =>\n buildPresentationNavigationMessage({\n assetId: tab.id,\n assetType: tab.type,\n slideNumber: tab.slideNumber,\n }),\n [tab.id, tab.slideNumber, tab.type],\n );\n const navigationTargetOrigin = useMemo(() => {\n if (!iframeSrc) return null;\n\n try {\n return new URL(iframeSrc, window.location.href).origin;\n } catch {\n return null;\n }\n }, [iframeSrc]);\n const postNavigationMessage = useCallback(() => {\n if (!navigationMessage || !navigationTargetOrigin) return;\n iframeRef.current?.contentWindow?.postMessage(\n navigationMessage,\n navigationTargetOrigin,\n );\n }, [navigationMessage, navigationTargetOrigin]);\n\n useEffect(() => {\n postNavigationMessage();\n }, [postNavigationMessage]);\n\n if (isLoading) {\n return (\n <div className=\"flex h-full items-center justify-center\">\n <div className=\"text-center\">\n <Loader2 className=\"mx-auto size-6 animate-spin text-muted-foreground\" />\n <p className=\"mt-2 text-xs text-muted-foreground\">Loading...</p>\n </div>\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"flex h-full items-center justify-center p-4\">\n <div className=\"max-w-sm text-center\">\n <AlertCircle className=\"mx-auto size-5 text-red-400\" />\n <p className=\"mt-1.5 text-xs font-medium text-foreground\">\n Failed to load\n </p>\n <p className=\"mt-0.5 line-clamp-2 break-words text-[10px] text-muted-foreground\">\n {error}\n </p>\n </div>\n </div>\n );\n }\n\n if (!iframeSrc) return null;\n\n return (\n <iframe\n ref={iframeRef}\n src={iframeSrc}\n width=\"100%\"\n height=\"100%\"\n frameBorder=\"0\"\n allow=\"clipboard-write; fullscreen\"\n title={tab.name ?? `Asset ${tab.id}`}\n className=\"h-full w-full\"\n onLoad={postNavigationMessage}\n />\n );\n },\n (prev, next) =>\n prev.tab.id === next.tab.id &&\n prev.tab.name === next.tab.name &&\n prev.tab.type === next.tab.type &&\n prev.tab.slideNumber === next.tab.slideNumber,\n);\n\nAssetIframe.displayName = \"AssetIframe\";\n\nexport interface AssetPanelProps {\n /** Register this panel as an asset-panel host when used outside AthenaLayout. */\n manageAssetPanelHostRegistration?: boolean;\n}\n\n/**\n * Content area — renders all iframes in the same tree.\n * Tab mode: only active tab visible via CSS. Tiled mode: CSS grid. Iframes never unmount on switch.\n */\nconst PanelContent: FC<{ manageAssetPanelHostRegistration: boolean }> = ({\n manageAssetPanelHostRegistration,\n}) => {\n const {\n tabs,\n activeTabId,\n viewMode,\n closeTab,\n registerAssetPanelHost,\n unregisterAssetPanelHost,\n } =\n useAssetPanelStore();\n const isTiled = viewMode === \"tiled\" && tabs.length >= 2;\n\n useEffect(() => {\n if (!manageAssetPanelHostRegistration) {\n return;\n }\n\n registerAssetPanelHost();\n return () => {\n unregisterAssetPanelHost();\n };\n }, [manageAssetPanelHostRegistration, registerAssetPanelHost, unregisterAssetPanelHost]);\n\n const gridClass = useMemo(() => {\n const n = tabs.length;\n if (n <= 1) return \"\";\n if (n === 2) return \"grid grid-cols-2 grid-rows-1\";\n if (n <= 4) return \"grid grid-cols-2 grid-rows-2\";\n if (n <= 6) return \"grid grid-cols-3 grid-rows-2\";\n return \"grid grid-cols-3 grid-rows-3\";\n }, [tabs.length]);\n\n return (\n <div\n className={`flex-1 overflow-hidden ${isTiled ? `${gridClass} gap-px bg-border/60` : \"\"}`}\n >\n {tabs.map((tab) => {\n const isActive = tab.id === activeTabId;\n const config = ASSET_TYPE_CONFIG[tab.type];\n const TypeIcon = config.icon;\n\n if (isTiled) {\n return (\n <div\n key={tab.id}\n className=\"flex min-h-0 min-w-0 flex-col bg-background\"\n >\n <div className=\"flex shrink-0 items-center gap-1.5 border-b px-2 py-1\">\n <TypeIcon className=\"size-3 shrink-0 text-muted-foreground\" />\n <span className=\"flex-1 truncate text-[11px] font-medium text-foreground\">\n {tab.name ?? config.label}\n </span>\n <button\n onClick={() => closeTab(tab.id)}\n className=\"shrink-0 rounded p-0.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\"\n >\n <X className=\"size-2.5\" />\n </button>\n </div>\n <div className=\"flex-1 overflow-hidden\">\n <AssetIframe tab={tab} />\n </div>\n </div>\n );\n }\n\n return (\n <div\n key={tab.id}\n className={isActive ? \"h-full w-full\" : \"hidden\"}\n >\n <AssetIframe tab={tab} />\n </div>\n );\n })}\n </div>\n );\n};\n\n/** Tab bar for tab mode — scrollable row of tabs. */\nconst TabBar: FC = () => {\n const { tabs, activeTabId, setActiveTab, closeTab, viewMode } =\n useAssetPanelStore();\n\n if (tabs.length <= 1 || viewMode === \"tiled\") return null;\n\n return (\n <div className=\"flex shrink-0 items-center gap-0 overflow-x-auto border-b bg-muted/30\">\n {tabs.map((tab) => {\n const config = ASSET_TYPE_CONFIG[tab.type];\n const TypeIcon = config.icon;\n const isActive = tab.id === activeTabId;\n\n return (\n <div\n key={tab.id}\n className={`group flex max-w-[180px] shrink-0 cursor-pointer items-center gap-1.5 border-r border-border/40 px-3 py-1.5 text-xs ${\n isActive\n ? \"border-b-2 border-b-primary bg-background text-foreground\"\n : \"text-muted-foreground hover:bg-background/50 hover:text-foreground\"\n }`}\n onClick={() => setActiveTab(tab.id)}\n >\n <TypeIcon className=\"size-3 shrink-0\" />\n <span className=\"truncate\">{tab.name ?? config.label}</span>\n <button\n onClick={(e) => {\n e.stopPropagation();\n closeTab(tab.id);\n }}\n className=\"ml-auto shrink-0 rounded p-0.5 opacity-0 transition-opacity hover:bg-muted group-hover:opacity-100\"\n >\n <X className=\"size-2.5\" />\n </button>\n </div>\n );\n })}\n </div>\n );\n};\n\n/** Header with active tab info + panel controls. */\nconst PanelHeader: FC<{ fullscreen?: boolean }> = ({ fullscreen }) => {\n const {\n closePanel,\n toggleFullscreen,\n tabs,\n activeTabId,\n viewMode,\n setViewMode,\n } = useAssetPanelStore();\n const activeTab = tabs.find((t) => t.id === activeTabId);\n const config = ASSET_TYPE_CONFIG[activeTab?.type ?? \"unknown\"];\n const TypeIcon = config.icon;\n const isTiled = viewMode === \"tiled\";\n\n return (\n <div className=\"flex shrink-0 items-center gap-2 border-b px-4 py-2.5\">\n {!isTiled && (\n <TypeIcon className=\"size-4 shrink-0 text-muted-foreground\" />\n )}\n <p className=\"flex-1 truncate text-sm font-semibold text-foreground\">\n {isTiled\n ? `${tabs.length} Assets`\n : activeTab?.name ?? config.label}\n </p>\n\n {tabs.length >= 2 && (\n <button\n onClick={() => setViewMode(isTiled ? \"tabs\" : \"tiled\")}\n className={`flex size-7 items-center justify-center rounded-lg transition-colors ${\n isTiled\n ? \"bg-primary/10 text-primary\"\n : \"text-muted-foreground hover:bg-accent hover:text-foreground\"\n }`}\n title={isTiled ? \"Switch to tabs\" : \"Tile all assets\"}\n >\n {isTiled ? (\n <Layers className=\"size-3.5\" />\n ) : (\n <LayoutGrid className=\"size-3.5\" />\n )}\n </button>\n )}\n\n <button\n onClick={toggleFullscreen}\n className=\"flex size-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent hover:text-foreground\"\n title={fullscreen ? \"Exit fullscreen\" : \"Fullscreen\"}\n >\n {fullscreen ? (\n <Minimize2 className=\"size-3.5\" />\n ) : (\n <Maximize2 className=\"size-3.5\" />\n )}\n </button>\n <button\n onClick={closePanel}\n className=\"flex size-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent hover:text-foreground\"\n title=\"Close all\"\n >\n <X className=\"size-4\" />\n </button>\n </div>\n );\n};\n\nexport const AssetPanel: FC<AssetPanelProps> = ({\n manageAssetPanelHostRegistration = true,\n}) => {\n const isFullscreen = useAssetPanelStore((s) => s.isFullscreen);\n\n const content = (\n <>\n <PanelHeader fullscreen={isFullscreen} />\n <TabBar />\n <PanelContent manageAssetPanelHostRegistration={manageAssetPanelHostRegistration} />\n </>\n );\n\n if (isFullscreen) {\n return (\n <div className=\"fixed inset-0 z-50 flex flex-col bg-background\">\n {content}\n </div>\n );\n }\n\n return (\n <div className=\"flex h-full flex-col bg-background\">{content}</div>\n );\n};\n"],"names":["Presentation","FileSpreadsheet","FileText","BookOpen","File","memo","useRef","useAthenaConfig","useAssetEmbed","useMemo","buildAssetIframeSrc","appendCitationReferenceParams","buildPresentationNavigationMessage","useCallback","useEffect","jsxs","jsx","Loader2","AlertCircle","useAssetPanelStore","X","Layers","LayoutGrid","Minimize2","Maximize2","Fragment"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AssetPanel.cjs","sources":["../../src/assets/AssetPanel.tsx"],"sourcesContent":["\"use client\";\n\nimport { type FC, memo, useCallback, useEffect, useMemo, useRef } from \"react\";\nimport {\n X,\n Loader2,\n AlertCircle,\n Maximize2,\n Minimize2,\n FileText,\n FileSpreadsheet,\n Presentation,\n File,\n BookOpen,\n LayoutGrid,\n Layers,\n} from \"lucide-react\";\nimport { appendCitationReferenceParams, useAssetEmbed } from \"./use-asset-embed\";\nimport {\n useAssetPanelStore,\n type AssetTab,\n type AssetType,\n} from \"./asset-panel-store\";\nimport {\n buildAssetIframeSrc,\n buildPresentationNavigationMessage,\n} from \"./asset-panel-url\";\nimport { useAthenaConfig } from \"../provider/AthenaContext\";\n\nconst ASSET_TYPE_CONFIG: Record<\n AssetType,\n { icon: typeof File; label: string }\n> = {\n presentation: { icon: Presentation, label: \"Presentation\" },\n spreadsheet: { icon: FileSpreadsheet, label: \"Spreadsheet\" },\n document: { icon: FileText, label: \"Document\" },\n notebook: { icon: BookOpen, label: \"Notebook\" },\n unknown: { icon: File, label: \"Asset\" },\n};\n\n/** Memoized iframe for a single asset — only re-renders when iframe inputs change. */\nconst AssetIframe = memo<{ tab: AssetTab }>(\n ({ tab }) => {\n const iframeRef = useRef<HTMLIFrameElement | null>(null);\n const { backendUrl, appUrl, apiKey, token, getToken } = useAthenaConfig();\n const { embedUrl, isLoading, error } = useAssetEmbed(tab.id, {\n backendUrl,\n appUrl,\n apiKey,\n token,\n getToken,\n });\n const initialSlideRef = useRef<{\n assetId: string;\n slideNumber?: number;\n }>({\n assetId: tab.id,\n slideNumber: tab.slideNumber,\n });\n if (initialSlideRef.current.assetId !== tab.id) {\n initialSlideRef.current = {\n assetId: tab.id,\n slideNumber: tab.slideNumber,\n };\n }\n const initialSlideNumber = initialSlideRef.current.slideNumber;\n // The citation target rides the embed URL: the embed page parses the\n // anchor params and navigates (sheet range scroll/flash, PDF page). A\n // repeat click with a DIFFERENT target updates the tab's reference ->\n // new src -> the iframe reloads at the new range. Same target twice is a\n // no-op (already there).\n //\n // Deliberately does NOT set `athena_sdk_bridge=1` (buildSdkAssetEmbedUrl\n // is for AthenaAssetEmbed): that flag makes the embed page wait\n // indefinitely for a host config handshake that only AthenaAssetEmbed\n // implements — the panel's bare iframe never replies, so the embed would\n // hang on its loading bar forever.\n const iframeSrc = useMemo(() => {\n if (!embedUrl) return null;\n const base = buildAssetIframeSrc({\n embedUrl,\n assetType: tab.type,\n slideNumber: initialSlideNumber,\n });\n try {\n const url = new URL(base, window.location.href);\n appendCitationReferenceParams({ params: url.searchParams, reference: tab.reference });\n return url.toString();\n } catch {\n return base;\n }\n }, [embedUrl, initialSlideNumber, tab.type, tab.reference]);\n const navigationMessage = useMemo(\n () =>\n buildPresentationNavigationMessage({\n assetId: tab.id,\n assetType: tab.type,\n slideNumber: tab.slideNumber,\n }),\n [tab.id, tab.slideNumber, tab.type],\n );\n const navigationTargetOrigin = useMemo(() => {\n if (!iframeSrc) return null;\n\n try {\n return new URL(iframeSrc, window.location.href).origin;\n } catch {\n return null;\n }\n }, [iframeSrc]);\n const postNavigationMessage = useCallback(() => {\n if (!navigationMessage || !navigationTargetOrigin) return;\n iframeRef.current?.contentWindow?.postMessage(\n navigationMessage,\n navigationTargetOrigin,\n );\n }, [navigationMessage, navigationTargetOrigin]);\n\n useEffect(() => {\n postNavigationMessage();\n }, [postNavigationMessage]);\n\n if (isLoading) {\n return (\n <div className=\"flex h-full items-center justify-center\">\n <div className=\"text-center\">\n <Loader2 className=\"mx-auto size-6 animate-spin text-muted-foreground\" />\n <p className=\"mt-2 text-xs text-muted-foreground\">Loading...</p>\n </div>\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"flex h-full items-center justify-center p-4\">\n <div className=\"max-w-sm text-center\">\n <AlertCircle className=\"mx-auto size-5 text-red-400\" />\n <p className=\"mt-1.5 text-xs font-medium text-foreground\">\n Failed to load\n </p>\n <p className=\"mt-0.5 line-clamp-2 break-words text-[10px] text-muted-foreground\">\n {error}\n </p>\n </div>\n </div>\n );\n }\n\n if (!iframeSrc) return null;\n\n return (\n <iframe\n ref={iframeRef}\n src={iframeSrc}\n width=\"100%\"\n height=\"100%\"\n frameBorder=\"0\"\n allow=\"clipboard-read; clipboard-write; fullscreen\"\n title={tab.name ?? `Asset ${tab.id}`}\n className=\"h-full w-full\"\n onLoad={postNavigationMessage}\n />\n );\n },\n (prev, next) =>\n prev.tab.id === next.tab.id &&\n prev.tab.name === next.tab.name &&\n prev.tab.type === next.tab.type &&\n prev.tab.slideNumber === next.tab.slideNumber,\n);\n\nAssetIframe.displayName = \"AssetIframe\";\n\nexport interface AssetPanelProps {\n /** Register this panel as an asset-panel host when used outside AthenaLayout. */\n manageAssetPanelHostRegistration?: boolean;\n}\n\n/**\n * Content area — renders all iframes in the same tree.\n * Tab mode: only active tab visible via CSS. Tiled mode: CSS grid. Iframes never unmount on switch.\n */\nconst PanelContent: FC<{ manageAssetPanelHostRegistration: boolean }> = ({\n manageAssetPanelHostRegistration,\n}) => {\n const {\n tabs,\n activeTabId,\n viewMode,\n closeTab,\n registerAssetPanelHost,\n unregisterAssetPanelHost,\n } =\n useAssetPanelStore();\n const isTiled = viewMode === \"tiled\" && tabs.length >= 2;\n\n useEffect(() => {\n if (!manageAssetPanelHostRegistration) {\n return;\n }\n\n registerAssetPanelHost();\n return () => {\n unregisterAssetPanelHost();\n };\n }, [manageAssetPanelHostRegistration, registerAssetPanelHost, unregisterAssetPanelHost]);\n\n const gridClass = useMemo(() => {\n const n = tabs.length;\n if (n <= 1) return \"\";\n if (n === 2) return \"grid grid-cols-2 grid-rows-1\";\n if (n <= 4) return \"grid grid-cols-2 grid-rows-2\";\n if (n <= 6) return \"grid grid-cols-3 grid-rows-2\";\n return \"grid grid-cols-3 grid-rows-3\";\n }, [tabs.length]);\n\n return (\n <div\n className={`flex-1 overflow-hidden ${isTiled ? `${gridClass} gap-px bg-border/60` : \"\"}`}\n >\n {tabs.map((tab) => {\n const isActive = tab.id === activeTabId;\n const config = ASSET_TYPE_CONFIG[tab.type];\n const TypeIcon = config.icon;\n\n if (isTiled) {\n return (\n <div\n key={tab.id}\n className=\"flex min-h-0 min-w-0 flex-col bg-background\"\n >\n <div className=\"flex shrink-0 items-center gap-1.5 border-b px-2 py-1\">\n <TypeIcon className=\"size-3 shrink-0 text-muted-foreground\" />\n <span className=\"flex-1 truncate text-[11px] font-medium text-foreground\">\n {tab.name ?? config.label}\n </span>\n <button\n onClick={() => closeTab(tab.id)}\n className=\"shrink-0 rounded p-0.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\"\n >\n <X className=\"size-2.5\" />\n </button>\n </div>\n <div className=\"flex-1 overflow-hidden\">\n <AssetIframe tab={tab} />\n </div>\n </div>\n );\n }\n\n return (\n <div\n key={tab.id}\n className={isActive ? \"h-full w-full\" : \"hidden\"}\n >\n <AssetIframe tab={tab} />\n </div>\n );\n })}\n </div>\n );\n};\n\n/** Tab bar for tab mode — scrollable row of tabs. */\nconst TabBar: FC = () => {\n const { tabs, activeTabId, setActiveTab, closeTab, viewMode } =\n useAssetPanelStore();\n\n if (tabs.length <= 1 || viewMode === \"tiled\") return null;\n\n return (\n <div className=\"flex shrink-0 items-center gap-0 overflow-x-auto border-b bg-muted/30\">\n {tabs.map((tab) => {\n const config = ASSET_TYPE_CONFIG[tab.type];\n const TypeIcon = config.icon;\n const isActive = tab.id === activeTabId;\n\n return (\n <div\n key={tab.id}\n className={`group flex max-w-[180px] shrink-0 cursor-pointer items-center gap-1.5 border-r border-border/40 px-3 py-1.5 text-xs ${\n isActive\n ? \"border-b-2 border-b-primary bg-background text-foreground\"\n : \"text-muted-foreground hover:bg-background/50 hover:text-foreground\"\n }`}\n onClick={() => setActiveTab(tab.id)}\n >\n <TypeIcon className=\"size-3 shrink-0\" />\n <span className=\"truncate\">{tab.name ?? config.label}</span>\n <button\n onClick={(e) => {\n e.stopPropagation();\n closeTab(tab.id);\n }}\n className=\"ml-auto shrink-0 rounded p-0.5 opacity-0 transition-opacity hover:bg-muted group-hover:opacity-100\"\n >\n <X className=\"size-2.5\" />\n </button>\n </div>\n );\n })}\n </div>\n );\n};\n\n/** Header with active tab info + panel controls. */\nconst PanelHeader: FC<{ fullscreen?: boolean }> = ({ fullscreen }) => {\n const {\n closePanel,\n toggleFullscreen,\n tabs,\n activeTabId,\n viewMode,\n setViewMode,\n } = useAssetPanelStore();\n const activeTab = tabs.find((t) => t.id === activeTabId);\n const config = ASSET_TYPE_CONFIG[activeTab?.type ?? \"unknown\"];\n const TypeIcon = config.icon;\n const isTiled = viewMode === \"tiled\";\n\n return (\n <div className=\"flex shrink-0 items-center gap-2 border-b px-4 py-2.5\">\n {!isTiled && (\n <TypeIcon className=\"size-4 shrink-0 text-muted-foreground\" />\n )}\n <p className=\"flex-1 truncate text-sm font-semibold text-foreground\">\n {isTiled\n ? `${tabs.length} Assets`\n : activeTab?.name ?? config.label}\n </p>\n\n {tabs.length >= 2 && (\n <button\n onClick={() => setViewMode(isTiled ? \"tabs\" : \"tiled\")}\n className={`flex size-7 items-center justify-center rounded-lg transition-colors ${\n isTiled\n ? \"bg-primary/10 text-primary\"\n : \"text-muted-foreground hover:bg-accent hover:text-foreground\"\n }`}\n title={isTiled ? \"Switch to tabs\" : \"Tile all assets\"}\n >\n {isTiled ? (\n <Layers className=\"size-3.5\" />\n ) : (\n <LayoutGrid className=\"size-3.5\" />\n )}\n </button>\n )}\n\n <button\n onClick={toggleFullscreen}\n className=\"flex size-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent hover:text-foreground\"\n title={fullscreen ? \"Exit fullscreen\" : \"Fullscreen\"}\n >\n {fullscreen ? (\n <Minimize2 className=\"size-3.5\" />\n ) : (\n <Maximize2 className=\"size-3.5\" />\n )}\n </button>\n <button\n onClick={closePanel}\n className=\"flex size-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent hover:text-foreground\"\n title=\"Close all\"\n >\n <X className=\"size-4\" />\n </button>\n </div>\n );\n};\n\nexport const AssetPanel: FC<AssetPanelProps> = ({\n manageAssetPanelHostRegistration = true,\n}) => {\n const isFullscreen = useAssetPanelStore((s) => s.isFullscreen);\n\n const content = (\n <>\n <PanelHeader fullscreen={isFullscreen} />\n <TabBar />\n <PanelContent manageAssetPanelHostRegistration={manageAssetPanelHostRegistration} />\n </>\n );\n\n if (isFullscreen) {\n return (\n <div className=\"fixed inset-0 z-50 flex flex-col bg-background\">\n {content}\n </div>\n );\n }\n\n return (\n <div className=\"flex h-full flex-col bg-background\">{content}</div>\n );\n};\n"],"names":["Presentation","FileSpreadsheet","FileText","BookOpen","File","memo","useRef","useAthenaConfig","useAssetEmbed","useMemo","buildAssetIframeSrc","appendCitationReferenceParams","buildPresentationNavigationMessage","useCallback","useEffect","jsxs","jsx","Loader2","AlertCircle","useAssetPanelStore","X","Layers","LayoutGrid","Minimize2","Maximize2","Fragment"],"mappings":"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"}
|
|
@@ -98,7 +98,7 @@ const AssetIframe = memo(
|
|
|
98
98
|
width: "100%",
|
|
99
99
|
height: "100%",
|
|
100
100
|
frameBorder: "0",
|
|
101
|
-
allow: "clipboard-write; fullscreen",
|
|
101
|
+
allow: "clipboard-read; clipboard-write; fullscreen",
|
|
102
102
|
title: tab.name ?? `Asset ${tab.id}`,
|
|
103
103
|
className: "h-full w-full",
|
|
104
104
|
onLoad: postNavigationMessage
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetPanel.js","sources":["../../src/assets/AssetPanel.tsx"],"sourcesContent":["\"use client\";\n\nimport { type FC, memo, useCallback, useEffect, useMemo, useRef } from \"react\";\nimport {\n X,\n Loader2,\n AlertCircle,\n Maximize2,\n Minimize2,\n FileText,\n FileSpreadsheet,\n Presentation,\n File,\n BookOpen,\n LayoutGrid,\n Layers,\n} from \"lucide-react\";\nimport { appendCitationReferenceParams, useAssetEmbed } from \"./use-asset-embed\";\nimport {\n useAssetPanelStore,\n type AssetTab,\n type AssetType,\n} from \"./asset-panel-store\";\nimport {\n buildAssetIframeSrc,\n buildPresentationNavigationMessage,\n} from \"./asset-panel-url\";\nimport { useAthenaConfig } from \"../provider/AthenaContext\";\n\nconst ASSET_TYPE_CONFIG: Record<\n AssetType,\n { icon: typeof File; label: string }\n> = {\n presentation: { icon: Presentation, label: \"Presentation\" },\n spreadsheet: { icon: FileSpreadsheet, label: \"Spreadsheet\" },\n document: { icon: FileText, label: \"Document\" },\n notebook: { icon: BookOpen, label: \"Notebook\" },\n unknown: { icon: File, label: \"Asset\" },\n};\n\n/** Memoized iframe for a single asset — only re-renders when iframe inputs change. */\nconst AssetIframe = memo<{ tab: AssetTab }>(\n ({ tab }) => {\n const iframeRef = useRef<HTMLIFrameElement | null>(null);\n const { backendUrl, appUrl, apiKey, token, getToken } = useAthenaConfig();\n const { embedUrl, isLoading, error } = useAssetEmbed(tab.id, {\n backendUrl,\n appUrl,\n apiKey,\n token,\n getToken,\n });\n const initialSlideRef = useRef<{\n assetId: string;\n slideNumber?: number;\n }>({\n assetId: tab.id,\n slideNumber: tab.slideNumber,\n });\n if (initialSlideRef.current.assetId !== tab.id) {\n initialSlideRef.current = {\n assetId: tab.id,\n slideNumber: tab.slideNumber,\n };\n }\n const initialSlideNumber = initialSlideRef.current.slideNumber;\n // The citation target rides the embed URL: the embed page parses the\n // anchor params and navigates (sheet range scroll/flash, PDF page). A\n // repeat click with a DIFFERENT target updates the tab's reference ->\n // new src -> the iframe reloads at the new range. Same target twice is a\n // no-op (already there).\n //\n // Deliberately does NOT set `athena_sdk_bridge=1` (buildSdkAssetEmbedUrl\n // is for AthenaAssetEmbed): that flag makes the embed page wait\n // indefinitely for a host config handshake that only AthenaAssetEmbed\n // implements — the panel's bare iframe never replies, so the embed would\n // hang on its loading bar forever.\n const iframeSrc = useMemo(() => {\n if (!embedUrl) return null;\n const base = buildAssetIframeSrc({\n embedUrl,\n assetType: tab.type,\n slideNumber: initialSlideNumber,\n });\n try {\n const url = new URL(base, window.location.href);\n appendCitationReferenceParams({ params: url.searchParams, reference: tab.reference });\n return url.toString();\n } catch {\n return base;\n }\n }, [embedUrl, initialSlideNumber, tab.type, tab.reference]);\n const navigationMessage = useMemo(\n () =>\n buildPresentationNavigationMessage({\n assetId: tab.id,\n assetType: tab.type,\n slideNumber: tab.slideNumber,\n }),\n [tab.id, tab.slideNumber, tab.type],\n );\n const navigationTargetOrigin = useMemo(() => {\n if (!iframeSrc) return null;\n\n try {\n return new URL(iframeSrc, window.location.href).origin;\n } catch {\n return null;\n }\n }, [iframeSrc]);\n const postNavigationMessage = useCallback(() => {\n if (!navigationMessage || !navigationTargetOrigin) return;\n iframeRef.current?.contentWindow?.postMessage(\n navigationMessage,\n navigationTargetOrigin,\n );\n }, [navigationMessage, navigationTargetOrigin]);\n\n useEffect(() => {\n postNavigationMessage();\n }, [postNavigationMessage]);\n\n if (isLoading) {\n return (\n <div className=\"flex h-full items-center justify-center\">\n <div className=\"text-center\">\n <Loader2 className=\"mx-auto size-6 animate-spin text-muted-foreground\" />\n <p className=\"mt-2 text-xs text-muted-foreground\">Loading...</p>\n </div>\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"flex h-full items-center justify-center p-4\">\n <div className=\"max-w-sm text-center\">\n <AlertCircle className=\"mx-auto size-5 text-red-400\" />\n <p className=\"mt-1.5 text-xs font-medium text-foreground\">\n Failed to load\n </p>\n <p className=\"mt-0.5 line-clamp-2 break-words text-[10px] text-muted-foreground\">\n {error}\n </p>\n </div>\n </div>\n );\n }\n\n if (!iframeSrc) return null;\n\n return (\n <iframe\n ref={iframeRef}\n src={iframeSrc}\n width=\"100%\"\n height=\"100%\"\n frameBorder=\"0\"\n allow=\"clipboard-write; fullscreen\"\n title={tab.name ?? `Asset ${tab.id}`}\n className=\"h-full w-full\"\n onLoad={postNavigationMessage}\n />\n );\n },\n (prev, next) =>\n prev.tab.id === next.tab.id &&\n prev.tab.name === next.tab.name &&\n prev.tab.type === next.tab.type &&\n prev.tab.slideNumber === next.tab.slideNumber,\n);\n\nAssetIframe.displayName = \"AssetIframe\";\n\nexport interface AssetPanelProps {\n /** Register this panel as an asset-panel host when used outside AthenaLayout. */\n manageAssetPanelHostRegistration?: boolean;\n}\n\n/**\n * Content area — renders all iframes in the same tree.\n * Tab mode: only active tab visible via CSS. Tiled mode: CSS grid. Iframes never unmount on switch.\n */\nconst PanelContent: FC<{ manageAssetPanelHostRegistration: boolean }> = ({\n manageAssetPanelHostRegistration,\n}) => {\n const {\n tabs,\n activeTabId,\n viewMode,\n closeTab,\n registerAssetPanelHost,\n unregisterAssetPanelHost,\n } =\n useAssetPanelStore();\n const isTiled = viewMode === \"tiled\" && tabs.length >= 2;\n\n useEffect(() => {\n if (!manageAssetPanelHostRegistration) {\n return;\n }\n\n registerAssetPanelHost();\n return () => {\n unregisterAssetPanelHost();\n };\n }, [manageAssetPanelHostRegistration, registerAssetPanelHost, unregisterAssetPanelHost]);\n\n const gridClass = useMemo(() => {\n const n = tabs.length;\n if (n <= 1) return \"\";\n if (n === 2) return \"grid grid-cols-2 grid-rows-1\";\n if (n <= 4) return \"grid grid-cols-2 grid-rows-2\";\n if (n <= 6) return \"grid grid-cols-3 grid-rows-2\";\n return \"grid grid-cols-3 grid-rows-3\";\n }, [tabs.length]);\n\n return (\n <div\n className={`flex-1 overflow-hidden ${isTiled ? `${gridClass} gap-px bg-border/60` : \"\"}`}\n >\n {tabs.map((tab) => {\n const isActive = tab.id === activeTabId;\n const config = ASSET_TYPE_CONFIG[tab.type];\n const TypeIcon = config.icon;\n\n if (isTiled) {\n return (\n <div\n key={tab.id}\n className=\"flex min-h-0 min-w-0 flex-col bg-background\"\n >\n <div className=\"flex shrink-0 items-center gap-1.5 border-b px-2 py-1\">\n <TypeIcon className=\"size-3 shrink-0 text-muted-foreground\" />\n <span className=\"flex-1 truncate text-[11px] font-medium text-foreground\">\n {tab.name ?? config.label}\n </span>\n <button\n onClick={() => closeTab(tab.id)}\n className=\"shrink-0 rounded p-0.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\"\n >\n <X className=\"size-2.5\" />\n </button>\n </div>\n <div className=\"flex-1 overflow-hidden\">\n <AssetIframe tab={tab} />\n </div>\n </div>\n );\n }\n\n return (\n <div\n key={tab.id}\n className={isActive ? \"h-full w-full\" : \"hidden\"}\n >\n <AssetIframe tab={tab} />\n </div>\n );\n })}\n </div>\n );\n};\n\n/** Tab bar for tab mode — scrollable row of tabs. */\nconst TabBar: FC = () => {\n const { tabs, activeTabId, setActiveTab, closeTab, viewMode } =\n useAssetPanelStore();\n\n if (tabs.length <= 1 || viewMode === \"tiled\") return null;\n\n return (\n <div className=\"flex shrink-0 items-center gap-0 overflow-x-auto border-b bg-muted/30\">\n {tabs.map((tab) => {\n const config = ASSET_TYPE_CONFIG[tab.type];\n const TypeIcon = config.icon;\n const isActive = tab.id === activeTabId;\n\n return (\n <div\n key={tab.id}\n className={`group flex max-w-[180px] shrink-0 cursor-pointer items-center gap-1.5 border-r border-border/40 px-3 py-1.5 text-xs ${\n isActive\n ? \"border-b-2 border-b-primary bg-background text-foreground\"\n : \"text-muted-foreground hover:bg-background/50 hover:text-foreground\"\n }`}\n onClick={() => setActiveTab(tab.id)}\n >\n <TypeIcon className=\"size-3 shrink-0\" />\n <span className=\"truncate\">{tab.name ?? config.label}</span>\n <button\n onClick={(e) => {\n e.stopPropagation();\n closeTab(tab.id);\n }}\n className=\"ml-auto shrink-0 rounded p-0.5 opacity-0 transition-opacity hover:bg-muted group-hover:opacity-100\"\n >\n <X className=\"size-2.5\" />\n </button>\n </div>\n );\n })}\n </div>\n );\n};\n\n/** Header with active tab info + panel controls. */\nconst PanelHeader: FC<{ fullscreen?: boolean }> = ({ fullscreen }) => {\n const {\n closePanel,\n toggleFullscreen,\n tabs,\n activeTabId,\n viewMode,\n setViewMode,\n } = useAssetPanelStore();\n const activeTab = tabs.find((t) => t.id === activeTabId);\n const config = ASSET_TYPE_CONFIG[activeTab?.type ?? \"unknown\"];\n const TypeIcon = config.icon;\n const isTiled = viewMode === \"tiled\";\n\n return (\n <div className=\"flex shrink-0 items-center gap-2 border-b px-4 py-2.5\">\n {!isTiled && (\n <TypeIcon className=\"size-4 shrink-0 text-muted-foreground\" />\n )}\n <p className=\"flex-1 truncate text-sm font-semibold text-foreground\">\n {isTiled\n ? `${tabs.length} Assets`\n : activeTab?.name ?? config.label}\n </p>\n\n {tabs.length >= 2 && (\n <button\n onClick={() => setViewMode(isTiled ? \"tabs\" : \"tiled\")}\n className={`flex size-7 items-center justify-center rounded-lg transition-colors ${\n isTiled\n ? \"bg-primary/10 text-primary\"\n : \"text-muted-foreground hover:bg-accent hover:text-foreground\"\n }`}\n title={isTiled ? \"Switch to tabs\" : \"Tile all assets\"}\n >\n {isTiled ? (\n <Layers className=\"size-3.5\" />\n ) : (\n <LayoutGrid className=\"size-3.5\" />\n )}\n </button>\n )}\n\n <button\n onClick={toggleFullscreen}\n className=\"flex size-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent hover:text-foreground\"\n title={fullscreen ? \"Exit fullscreen\" : \"Fullscreen\"}\n >\n {fullscreen ? (\n <Minimize2 className=\"size-3.5\" />\n ) : (\n <Maximize2 className=\"size-3.5\" />\n )}\n </button>\n <button\n onClick={closePanel}\n className=\"flex size-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent hover:text-foreground\"\n title=\"Close all\"\n >\n <X className=\"size-4\" />\n </button>\n </div>\n );\n};\n\nexport const AssetPanel: FC<AssetPanelProps> = ({\n manageAssetPanelHostRegistration = true,\n}) => {\n const isFullscreen = useAssetPanelStore((s) => s.isFullscreen);\n\n const content = (\n <>\n <PanelHeader fullscreen={isFullscreen} />\n <TabBar />\n <PanelContent manageAssetPanelHostRegistration={manageAssetPanelHostRegistration} />\n </>\n );\n\n if (isFullscreen) {\n return (\n <div className=\"fixed inset-0 z-50 flex flex-col bg-background\">\n {content}\n </div>\n );\n }\n\n return (\n <div className=\"flex h-full flex-col bg-background\">{content}</div>\n );\n};\n"],"names":[],"mappings":";;;;;;;AA6BA,MAAM,oBAGF;AAAA,EACF,cAAc,EAAE,MAAM,cAAc,OAAO,eAAA;AAAA,EAC3C,aAAa,EAAE,MAAM,iBAAiB,OAAO,cAAA;AAAA,EAC7C,UAAU,EAAE,MAAM,UAAU,OAAO,WAAA;AAAA,EACnC,UAAU,EAAE,MAAM,UAAU,OAAO,WAAA;AAAA,EACnC,SAAS,EAAE,MAAM,MAAM,OAAO,QAAA;AAChC;AAGA,MAAM,cAAc;AAAA,EAClB,CAAC,EAAE,IAAA,MAAU;AACX,UAAM,YAAY,OAAiC,IAAI;AACvD,UAAM,EAAE,YAAY,QAAQ,QAAQ,OAAO,SAAA,IAAa,gBAAA;AACxD,UAAM,EAAE,UAAU,WAAW,UAAU,cAAc,IAAI,IAAI;AAAA,MAC3D;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA,CACD;AACD,UAAM,kBAAkB,OAGrB;AAAA,MACD,SAAS,IAAI;AAAA,MACb,aAAa,IAAI;AAAA,IAAA,CAClB;AACD,QAAI,gBAAgB,QAAQ,YAAY,IAAI,IAAI;AAC9C,sBAAgB,UAAU;AAAA,QACxB,SAAS,IAAI;AAAA,QACb,aAAa,IAAI;AAAA,MAAA;AAAA,IAErB;AACA,UAAM,qBAAqB,gBAAgB,QAAQ;AAYnD,UAAM,YAAY,QAAQ,MAAM;AAC9B,UAAI,CAAC,SAAU,QAAO;AACtB,YAAM,OAAO,oBAAoB;AAAA,QAC/B;AAAA,QACA,WAAW,IAAI;AAAA,QACf,aAAa;AAAA,MAAA,CACd;AACD,UAAI;AACF,cAAM,MAAM,IAAI,IAAI,MAAM,OAAO,SAAS,IAAI;AAC9C,sCAA8B,EAAE,QAAQ,IAAI,cAAc,WAAW,IAAI,WAAW;AACpF,eAAO,IAAI,SAAA;AAAA,MACb,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF,GAAG,CAAC,UAAU,oBAAoB,IAAI,MAAM,IAAI,SAAS,CAAC;AAC1D,UAAM,oBAAoB;AAAA,MACxB,MACE,mCAAmC;AAAA,QACjC,SAAS,IAAI;AAAA,QACb,WAAW,IAAI;AAAA,QACf,aAAa,IAAI;AAAA,MAAA,CAClB;AAAA,MACH,CAAC,IAAI,IAAI,IAAI,aAAa,IAAI,IAAI;AAAA,IAAA;AAEpC,UAAM,yBAAyB,QAAQ,MAAM;AAC3C,UAAI,CAAC,UAAW,QAAO;AAEvB,UAAI;AACF,eAAO,IAAI,IAAI,WAAW,OAAO,SAAS,IAAI,EAAE;AAAA,MAClD,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF,GAAG,CAAC,SAAS,CAAC;AACd,UAAM,wBAAwB,YAAY,MAAM;;AAC9C,UAAI,CAAC,qBAAqB,CAAC,uBAAwB;AACnD,4BAAU,YAAV,mBAAmB,kBAAnB,mBAAkC;AAAA,QAChC;AAAA,QACA;AAAA;AAAA,IAEJ,GAAG,CAAC,mBAAmB,sBAAsB,CAAC;AAE9C,cAAU,MAAM;AACd,4BAAA;AAAA,IACF,GAAG,CAAC,qBAAqB,CAAC;AAE1B,QAAI,WAAW;AACb,iCACG,OAAA,EAAI,WAAU,2CACb,UAAA,qBAAC,OAAA,EAAI,WAAU,eACb,UAAA;AAAA,QAAA,oBAAC,SAAA,EAAQ,WAAU,oDAAA,CAAoD;AAAA,QACvE,oBAAC,KAAA,EAAE,WAAU,sCAAqC,UAAA,aAAA,CAAU;AAAA,MAAA,EAAA,CAC9D,EAAA,CACF;AAAA,IAEJ;AAEA,QAAI,OAAO;AACT,iCACG,OAAA,EAAI,WAAU,+CACb,UAAA,qBAAC,OAAA,EAAI,WAAU,wBACb,UAAA;AAAA,QAAA,oBAAC,aAAA,EAAY,WAAU,8BAAA,CAA8B;AAAA,QACrD,oBAAC,KAAA,EAAE,WAAU,8CAA6C,UAAA,kBAE1D;AAAA,QACA,oBAAC,KAAA,EAAE,WAAU,qEACV,UAAA,MAAA,CACH;AAAA,MAAA,EAAA,CACF,EAAA,CACF;AAAA,IAEJ;AAEA,QAAI,CAAC,UAAW,QAAO;AAEvB,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,KAAK;AAAA,QACL,KAAK;AAAA,QACL,OAAM;AAAA,QACN,QAAO;AAAA,QACP,aAAY;AAAA,QACZ,OAAM;AAAA,QACN,OAAO,IAAI,QAAQ,SAAS,IAAI,EAAE;AAAA,QAClC,WAAU;AAAA,QACV,QAAQ;AAAA,MAAA;AAAA,IAAA;AAAA,EAGd;AAAA,EACA,CAAC,MAAM,SACL,KAAK,IAAI,OAAO,KAAK,IAAI,MACzB,KAAK,IAAI,SAAS,KAAK,IAAI,QAC3B,KAAK,IAAI,SAAS,KAAK,IAAI,QAC3B,KAAK,IAAI,gBAAgB,KAAK,IAAI;AACtC;AAEA,YAAY,cAAc;AAW1B,MAAM,eAAkE,CAAC;AAAA,EACvE;AACF,MAAM;AACJ,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,IAEA,mBAAA;AACF,QAAM,UAAU,aAAa,WAAW,KAAK,UAAU;AAEvD,YAAU,MAAM;AACd,QAAI,CAAC,kCAAkC;AACrC;AAAA,IACF;AAEA,2BAAA;AACA,WAAO,MAAM;AACX,+BAAA;AAAA,IACF;AAAA,EACF,GAAG,CAAC,kCAAkC,wBAAwB,wBAAwB,CAAC;AAEvF,QAAM,YAAY,QAAQ,MAAM;AAC9B,UAAM,IAAI,KAAK;AACf,QAAI,KAAK,EAAG,QAAO;AACnB,QAAI,MAAM,EAAG,QAAO;AACpB,QAAI,KAAK,EAAG,QAAO;AACnB,QAAI,KAAK,EAAG,QAAO;AACnB,WAAO;AAAA,EACT,GAAG,CAAC,KAAK,MAAM,CAAC;AAEhB,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW,0BAA0B,UAAU,GAAG,SAAS,yBAAyB,EAAE;AAAA,MAErF,UAAA,KAAK,IAAI,CAAC,QAAQ;AACjB,cAAM,WAAW,IAAI,OAAO;AAC5B,cAAM,SAAS,kBAAkB,IAAI,IAAI;AACzC,cAAM,WAAW,OAAO;AAExB,YAAI,SAAS;AACX,iBACE;AAAA,YAAC;AAAA,YAAA;AAAA,cAEC,WAAU;AAAA,cAEV,UAAA;AAAA,gBAAA,qBAAC,OAAA,EAAI,WAAU,yDACb,UAAA;AAAA,kBAAA,oBAAC,UAAA,EAAS,WAAU,wCAAA,CAAwC;AAAA,sCAC3D,QAAA,EAAK,WAAU,2DACb,UAAA,IAAI,QAAQ,OAAO,OACtB;AAAA,kBACA;AAAA,oBAAC;AAAA,oBAAA;AAAA,sBACC,SAAS,MAAM,SAAS,IAAI,EAAE;AAAA,sBAC9B,WAAU;AAAA,sBAEV,UAAA,oBAAC,GAAA,EAAE,WAAU,WAAA,CAAW;AAAA,oBAAA;AAAA,kBAAA;AAAA,gBAC1B,GACF;AAAA,oCACC,OAAA,EAAI,WAAU,0BACb,UAAA,oBAAC,aAAA,EAAY,KAAU,EAAA,CACzB;AAAA,cAAA;AAAA,YAAA;AAAA,YAjBK,IAAI;AAAA,UAAA;AAAA,QAoBf;AAEA,eACE;AAAA,UAAC;AAAA,UAAA;AAAA,YAEC,WAAW,WAAW,kBAAkB;AAAA,YAExC,UAAA,oBAAC,eAAY,IAAA,CAAU;AAAA,UAAA;AAAA,UAHlB,IAAI;AAAA,QAAA;AAAA,MAMf,CAAC;AAAA,IAAA;AAAA,EAAA;AAGP;AAGA,MAAM,SAAa,MAAM;AACvB,QAAM,EAAE,MAAM,aAAa,cAAc,UAAU,SAAA,IACjD,mBAAA;AAEF,MAAI,KAAK,UAAU,KAAK,aAAa,QAAS,QAAO;AAErD,6BACG,OAAA,EAAI,WAAU,yEACZ,UAAA,KAAK,IAAI,CAAC,QAAQ;AACjB,UAAM,SAAS,kBAAkB,IAAI,IAAI;AACzC,UAAM,WAAW,OAAO;AACxB,UAAM,WAAW,IAAI,OAAO;AAE5B,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QAEC,WAAW,uHACT,WACI,8DACA,oEACN;AAAA,QACA,SAAS,MAAM,aAAa,IAAI,EAAE;AAAA,QAElC,UAAA;AAAA,UAAA,oBAAC,UAAA,EAAS,WAAU,kBAAA,CAAkB;AAAA,8BACrC,QAAA,EAAK,WAAU,YAAY,UAAA,IAAI,QAAQ,OAAO,OAAM;AAAA,UACrD;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,SAAS,CAAC,MAAM;AACd,kBAAE,gBAAA;AACF,yBAAS,IAAI,EAAE;AAAA,cACjB;AAAA,cACA,WAAU;AAAA,cAEV,UAAA,oBAAC,GAAA,EAAE,WAAU,WAAA,CAAW;AAAA,YAAA;AAAA,UAAA;AAAA,QAC1B;AAAA,MAAA;AAAA,MAlBK,IAAI;AAAA,IAAA;AAAA,EAqBf,CAAC,EAAA,CACH;AAEJ;AAGA,MAAM,cAA4C,CAAC,EAAE,iBAAiB;AACpE,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,IACE,mBAAA;AACJ,QAAM,YAAY,KAAK,KAAK,CAAC,MAAM,EAAE,OAAO,WAAW;AACvD,QAAM,SAAS,mBAAkB,uCAAW,SAAQ,SAAS;AAC7D,QAAM,WAAW,OAAO;AACxB,QAAM,UAAU,aAAa;AAE7B,SACE,qBAAC,OAAA,EAAI,WAAU,yDACZ,UAAA;AAAA,IAAA,CAAC,WACA,oBAAC,UAAA,EAAS,WAAU,wCAAA,CAAwC;AAAA,IAE9D,oBAAC,KAAA,EAAE,WAAU,yDACV,UAAA,UACG,GAAG,KAAK,MAAM,aACd,uCAAW,SAAQ,OAAO,OAChC;AAAA,IAEC,KAAK,UAAU,KACd;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,SAAS,MAAM,YAAY,UAAU,SAAS,OAAO;AAAA,QACrD,WAAW,wEACT,UACI,+BACA,6DACN;AAAA,QACA,OAAO,UAAU,mBAAmB;AAAA,QAEnC,UAAA,8BACE,QAAA,EAAO,WAAU,YAAW,IAE7B,oBAAC,YAAA,EAAW,WAAU,WAAA,CAAW;AAAA,MAAA;AAAA,IAAA;AAAA,IAKvC;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,SAAS;AAAA,QACT,WAAU;AAAA,QACV,OAAO,aAAa,oBAAoB;AAAA,QAEvC,UAAA,iCACE,WAAA,EAAU,WAAU,YAAW,IAEhC,oBAAC,WAAA,EAAU,WAAU,WAAA,CAAW;AAAA,MAAA;AAAA,IAAA;AAAA,IAGpC;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,SAAS;AAAA,QACT,WAAU;AAAA,QACV,OAAM;AAAA,QAEN,UAAA,oBAAC,GAAA,EAAE,WAAU,SAAA,CAAS;AAAA,MAAA;AAAA,IAAA;AAAA,EACxB,GACF;AAEJ;AAEO,MAAM,aAAkC,CAAC;AAAA,EAC9C,mCAAmC;AACrC,MAAM;AACJ,QAAM,eAAe,mBAAmB,CAAC,MAAM,EAAE,YAAY;AAE7D,QAAM,UACJ,qBAAA,UAAA,EACE,UAAA;AAAA,IAAA,oBAAC,aAAA,EAAY,YAAY,aAAA,CAAc;AAAA,wBACtC,QAAA,EAAO;AAAA,IACR,oBAAC,gBAAa,iCAAA,CAAoE;AAAA,EAAA,GACpF;AAGF,MAAI,cAAc;AAChB,WACE,oBAAC,OAAA,EAAI,WAAU,kDACZ,UAAA,SACH;AAAA,EAEJ;AAEA,SACE,oBAAC,OAAA,EAAI,WAAU,sCAAsC,UAAA,SAAQ;AAEjE;"}
|
|
1
|
+
{"version":3,"file":"AssetPanel.js","sources":["../../src/assets/AssetPanel.tsx"],"sourcesContent":["\"use client\";\n\nimport { type FC, memo, useCallback, useEffect, useMemo, useRef } from \"react\";\nimport {\n X,\n Loader2,\n AlertCircle,\n Maximize2,\n Minimize2,\n FileText,\n FileSpreadsheet,\n Presentation,\n File,\n BookOpen,\n LayoutGrid,\n Layers,\n} from \"lucide-react\";\nimport { appendCitationReferenceParams, useAssetEmbed } from \"./use-asset-embed\";\nimport {\n useAssetPanelStore,\n type AssetTab,\n type AssetType,\n} from \"./asset-panel-store\";\nimport {\n buildAssetIframeSrc,\n buildPresentationNavigationMessage,\n} from \"./asset-panel-url\";\nimport { useAthenaConfig } from \"../provider/AthenaContext\";\n\nconst ASSET_TYPE_CONFIG: Record<\n AssetType,\n { icon: typeof File; label: string }\n> = {\n presentation: { icon: Presentation, label: \"Presentation\" },\n spreadsheet: { icon: FileSpreadsheet, label: \"Spreadsheet\" },\n document: { icon: FileText, label: \"Document\" },\n notebook: { icon: BookOpen, label: \"Notebook\" },\n unknown: { icon: File, label: \"Asset\" },\n};\n\n/** Memoized iframe for a single asset — only re-renders when iframe inputs change. */\nconst AssetIframe = memo<{ tab: AssetTab }>(\n ({ tab }) => {\n const iframeRef = useRef<HTMLIFrameElement | null>(null);\n const { backendUrl, appUrl, apiKey, token, getToken } = useAthenaConfig();\n const { embedUrl, isLoading, error } = useAssetEmbed(tab.id, {\n backendUrl,\n appUrl,\n apiKey,\n token,\n getToken,\n });\n const initialSlideRef = useRef<{\n assetId: string;\n slideNumber?: number;\n }>({\n assetId: tab.id,\n slideNumber: tab.slideNumber,\n });\n if (initialSlideRef.current.assetId !== tab.id) {\n initialSlideRef.current = {\n assetId: tab.id,\n slideNumber: tab.slideNumber,\n };\n }\n const initialSlideNumber = initialSlideRef.current.slideNumber;\n // The citation target rides the embed URL: the embed page parses the\n // anchor params and navigates (sheet range scroll/flash, PDF page). A\n // repeat click with a DIFFERENT target updates the tab's reference ->\n // new src -> the iframe reloads at the new range. Same target twice is a\n // no-op (already there).\n //\n // Deliberately does NOT set `athena_sdk_bridge=1` (buildSdkAssetEmbedUrl\n // is for AthenaAssetEmbed): that flag makes the embed page wait\n // indefinitely for a host config handshake that only AthenaAssetEmbed\n // implements — the panel's bare iframe never replies, so the embed would\n // hang on its loading bar forever.\n const iframeSrc = useMemo(() => {\n if (!embedUrl) return null;\n const base = buildAssetIframeSrc({\n embedUrl,\n assetType: tab.type,\n slideNumber: initialSlideNumber,\n });\n try {\n const url = new URL(base, window.location.href);\n appendCitationReferenceParams({ params: url.searchParams, reference: tab.reference });\n return url.toString();\n } catch {\n return base;\n }\n }, [embedUrl, initialSlideNumber, tab.type, tab.reference]);\n const navigationMessage = useMemo(\n () =>\n buildPresentationNavigationMessage({\n assetId: tab.id,\n assetType: tab.type,\n slideNumber: tab.slideNumber,\n }),\n [tab.id, tab.slideNumber, tab.type],\n );\n const navigationTargetOrigin = useMemo(() => {\n if (!iframeSrc) return null;\n\n try {\n return new URL(iframeSrc, window.location.href).origin;\n } catch {\n return null;\n }\n }, [iframeSrc]);\n const postNavigationMessage = useCallback(() => {\n if (!navigationMessage || !navigationTargetOrigin) return;\n iframeRef.current?.contentWindow?.postMessage(\n navigationMessage,\n navigationTargetOrigin,\n );\n }, [navigationMessage, navigationTargetOrigin]);\n\n useEffect(() => {\n postNavigationMessage();\n }, [postNavigationMessage]);\n\n if (isLoading) {\n return (\n <div className=\"flex h-full items-center justify-center\">\n <div className=\"text-center\">\n <Loader2 className=\"mx-auto size-6 animate-spin text-muted-foreground\" />\n <p className=\"mt-2 text-xs text-muted-foreground\">Loading...</p>\n </div>\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"flex h-full items-center justify-center p-4\">\n <div className=\"max-w-sm text-center\">\n <AlertCircle className=\"mx-auto size-5 text-red-400\" />\n <p className=\"mt-1.5 text-xs font-medium text-foreground\">\n Failed to load\n </p>\n <p className=\"mt-0.5 line-clamp-2 break-words text-[10px] text-muted-foreground\">\n {error}\n </p>\n </div>\n </div>\n );\n }\n\n if (!iframeSrc) return null;\n\n return (\n <iframe\n ref={iframeRef}\n src={iframeSrc}\n width=\"100%\"\n height=\"100%\"\n frameBorder=\"0\"\n allow=\"clipboard-read; clipboard-write; fullscreen\"\n title={tab.name ?? `Asset ${tab.id}`}\n className=\"h-full w-full\"\n onLoad={postNavigationMessage}\n />\n );\n },\n (prev, next) =>\n prev.tab.id === next.tab.id &&\n prev.tab.name === next.tab.name &&\n prev.tab.type === next.tab.type &&\n prev.tab.slideNumber === next.tab.slideNumber,\n);\n\nAssetIframe.displayName = \"AssetIframe\";\n\nexport interface AssetPanelProps {\n /** Register this panel as an asset-panel host when used outside AthenaLayout. */\n manageAssetPanelHostRegistration?: boolean;\n}\n\n/**\n * Content area — renders all iframes in the same tree.\n * Tab mode: only active tab visible via CSS. Tiled mode: CSS grid. Iframes never unmount on switch.\n */\nconst PanelContent: FC<{ manageAssetPanelHostRegistration: boolean }> = ({\n manageAssetPanelHostRegistration,\n}) => {\n const {\n tabs,\n activeTabId,\n viewMode,\n closeTab,\n registerAssetPanelHost,\n unregisterAssetPanelHost,\n } =\n useAssetPanelStore();\n const isTiled = viewMode === \"tiled\" && tabs.length >= 2;\n\n useEffect(() => {\n if (!manageAssetPanelHostRegistration) {\n return;\n }\n\n registerAssetPanelHost();\n return () => {\n unregisterAssetPanelHost();\n };\n }, [manageAssetPanelHostRegistration, registerAssetPanelHost, unregisterAssetPanelHost]);\n\n const gridClass = useMemo(() => {\n const n = tabs.length;\n if (n <= 1) return \"\";\n if (n === 2) return \"grid grid-cols-2 grid-rows-1\";\n if (n <= 4) return \"grid grid-cols-2 grid-rows-2\";\n if (n <= 6) return \"grid grid-cols-3 grid-rows-2\";\n return \"grid grid-cols-3 grid-rows-3\";\n }, [tabs.length]);\n\n return (\n <div\n className={`flex-1 overflow-hidden ${isTiled ? `${gridClass} gap-px bg-border/60` : \"\"}`}\n >\n {tabs.map((tab) => {\n const isActive = tab.id === activeTabId;\n const config = ASSET_TYPE_CONFIG[tab.type];\n const TypeIcon = config.icon;\n\n if (isTiled) {\n return (\n <div\n key={tab.id}\n className=\"flex min-h-0 min-w-0 flex-col bg-background\"\n >\n <div className=\"flex shrink-0 items-center gap-1.5 border-b px-2 py-1\">\n <TypeIcon className=\"size-3 shrink-0 text-muted-foreground\" />\n <span className=\"flex-1 truncate text-[11px] font-medium text-foreground\">\n {tab.name ?? config.label}\n </span>\n <button\n onClick={() => closeTab(tab.id)}\n className=\"shrink-0 rounded p-0.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\"\n >\n <X className=\"size-2.5\" />\n </button>\n </div>\n <div className=\"flex-1 overflow-hidden\">\n <AssetIframe tab={tab} />\n </div>\n </div>\n );\n }\n\n return (\n <div\n key={tab.id}\n className={isActive ? \"h-full w-full\" : \"hidden\"}\n >\n <AssetIframe tab={tab} />\n </div>\n );\n })}\n </div>\n );\n};\n\n/** Tab bar for tab mode — scrollable row of tabs. */\nconst TabBar: FC = () => {\n const { tabs, activeTabId, setActiveTab, closeTab, viewMode } =\n useAssetPanelStore();\n\n if (tabs.length <= 1 || viewMode === \"tiled\") return null;\n\n return (\n <div className=\"flex shrink-0 items-center gap-0 overflow-x-auto border-b bg-muted/30\">\n {tabs.map((tab) => {\n const config = ASSET_TYPE_CONFIG[tab.type];\n const TypeIcon = config.icon;\n const isActive = tab.id === activeTabId;\n\n return (\n <div\n key={tab.id}\n className={`group flex max-w-[180px] shrink-0 cursor-pointer items-center gap-1.5 border-r border-border/40 px-3 py-1.5 text-xs ${\n isActive\n ? \"border-b-2 border-b-primary bg-background text-foreground\"\n : \"text-muted-foreground hover:bg-background/50 hover:text-foreground\"\n }`}\n onClick={() => setActiveTab(tab.id)}\n >\n <TypeIcon className=\"size-3 shrink-0\" />\n <span className=\"truncate\">{tab.name ?? config.label}</span>\n <button\n onClick={(e) => {\n e.stopPropagation();\n closeTab(tab.id);\n }}\n className=\"ml-auto shrink-0 rounded p-0.5 opacity-0 transition-opacity hover:bg-muted group-hover:opacity-100\"\n >\n <X className=\"size-2.5\" />\n </button>\n </div>\n );\n })}\n </div>\n );\n};\n\n/** Header with active tab info + panel controls. */\nconst PanelHeader: FC<{ fullscreen?: boolean }> = ({ fullscreen }) => {\n const {\n closePanel,\n toggleFullscreen,\n tabs,\n activeTabId,\n viewMode,\n setViewMode,\n } = useAssetPanelStore();\n const activeTab = tabs.find((t) => t.id === activeTabId);\n const config = ASSET_TYPE_CONFIG[activeTab?.type ?? \"unknown\"];\n const TypeIcon = config.icon;\n const isTiled = viewMode === \"tiled\";\n\n return (\n <div className=\"flex shrink-0 items-center gap-2 border-b px-4 py-2.5\">\n {!isTiled && (\n <TypeIcon className=\"size-4 shrink-0 text-muted-foreground\" />\n )}\n <p className=\"flex-1 truncate text-sm font-semibold text-foreground\">\n {isTiled\n ? `${tabs.length} Assets`\n : activeTab?.name ?? config.label}\n </p>\n\n {tabs.length >= 2 && (\n <button\n onClick={() => setViewMode(isTiled ? \"tabs\" : \"tiled\")}\n className={`flex size-7 items-center justify-center rounded-lg transition-colors ${\n isTiled\n ? \"bg-primary/10 text-primary\"\n : \"text-muted-foreground hover:bg-accent hover:text-foreground\"\n }`}\n title={isTiled ? \"Switch to tabs\" : \"Tile all assets\"}\n >\n {isTiled ? (\n <Layers className=\"size-3.5\" />\n ) : (\n <LayoutGrid className=\"size-3.5\" />\n )}\n </button>\n )}\n\n <button\n onClick={toggleFullscreen}\n className=\"flex size-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent hover:text-foreground\"\n title={fullscreen ? \"Exit fullscreen\" : \"Fullscreen\"}\n >\n {fullscreen ? (\n <Minimize2 className=\"size-3.5\" />\n ) : (\n <Maximize2 className=\"size-3.5\" />\n )}\n </button>\n <button\n onClick={closePanel}\n className=\"flex size-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent hover:text-foreground\"\n title=\"Close all\"\n >\n <X className=\"size-4\" />\n </button>\n </div>\n );\n};\n\nexport const AssetPanel: FC<AssetPanelProps> = ({\n manageAssetPanelHostRegistration = true,\n}) => {\n const isFullscreen = useAssetPanelStore((s) => s.isFullscreen);\n\n const content = (\n <>\n <PanelHeader fullscreen={isFullscreen} />\n <TabBar />\n <PanelContent manageAssetPanelHostRegistration={manageAssetPanelHostRegistration} />\n </>\n );\n\n if (isFullscreen) {\n return (\n <div className=\"fixed inset-0 z-50 flex flex-col bg-background\">\n {content}\n </div>\n );\n }\n\n return (\n <div className=\"flex h-full flex-col bg-background\">{content}</div>\n );\n};\n"],"names":[],"mappings":";;;;;;;AA6BA,MAAM,oBAGF;AAAA,EACF,cAAc,EAAE,MAAM,cAAc,OAAO,eAAA;AAAA,EAC3C,aAAa,EAAE,MAAM,iBAAiB,OAAO,cAAA;AAAA,EAC7C,UAAU,EAAE,MAAM,UAAU,OAAO,WAAA;AAAA,EACnC,UAAU,EAAE,MAAM,UAAU,OAAO,WAAA;AAAA,EACnC,SAAS,EAAE,MAAM,MAAM,OAAO,QAAA;AAChC;AAGA,MAAM,cAAc;AAAA,EAClB,CAAC,EAAE,IAAA,MAAU;AACX,UAAM,YAAY,OAAiC,IAAI;AACvD,UAAM,EAAE,YAAY,QAAQ,QAAQ,OAAO,SAAA,IAAa,gBAAA;AACxD,UAAM,EAAE,UAAU,WAAW,UAAU,cAAc,IAAI,IAAI;AAAA,MAC3D;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA,CACD;AACD,UAAM,kBAAkB,OAGrB;AAAA,MACD,SAAS,IAAI;AAAA,MACb,aAAa,IAAI;AAAA,IAAA,CAClB;AACD,QAAI,gBAAgB,QAAQ,YAAY,IAAI,IAAI;AAC9C,sBAAgB,UAAU;AAAA,QACxB,SAAS,IAAI;AAAA,QACb,aAAa,IAAI;AAAA,MAAA;AAAA,IAErB;AACA,UAAM,qBAAqB,gBAAgB,QAAQ;AAYnD,UAAM,YAAY,QAAQ,MAAM;AAC9B,UAAI,CAAC,SAAU,QAAO;AACtB,YAAM,OAAO,oBAAoB;AAAA,QAC/B;AAAA,QACA,WAAW,IAAI;AAAA,QACf,aAAa;AAAA,MAAA,CACd;AACD,UAAI;AACF,cAAM,MAAM,IAAI,IAAI,MAAM,OAAO,SAAS,IAAI;AAC9C,sCAA8B,EAAE,QAAQ,IAAI,cAAc,WAAW,IAAI,WAAW;AACpF,eAAO,IAAI,SAAA;AAAA,MACb,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF,GAAG,CAAC,UAAU,oBAAoB,IAAI,MAAM,IAAI,SAAS,CAAC;AAC1D,UAAM,oBAAoB;AAAA,MACxB,MACE,mCAAmC;AAAA,QACjC,SAAS,IAAI;AAAA,QACb,WAAW,IAAI;AAAA,QACf,aAAa,IAAI;AAAA,MAAA,CAClB;AAAA,MACH,CAAC,IAAI,IAAI,IAAI,aAAa,IAAI,IAAI;AAAA,IAAA;AAEpC,UAAM,yBAAyB,QAAQ,MAAM;AAC3C,UAAI,CAAC,UAAW,QAAO;AAEvB,UAAI;AACF,eAAO,IAAI,IAAI,WAAW,OAAO,SAAS,IAAI,EAAE;AAAA,MAClD,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF,GAAG,CAAC,SAAS,CAAC;AACd,UAAM,wBAAwB,YAAY,MAAM;;AAC9C,UAAI,CAAC,qBAAqB,CAAC,uBAAwB;AACnD,4BAAU,YAAV,mBAAmB,kBAAnB,mBAAkC;AAAA,QAChC;AAAA,QACA;AAAA;AAAA,IAEJ,GAAG,CAAC,mBAAmB,sBAAsB,CAAC;AAE9C,cAAU,MAAM;AACd,4BAAA;AAAA,IACF,GAAG,CAAC,qBAAqB,CAAC;AAE1B,QAAI,WAAW;AACb,iCACG,OAAA,EAAI,WAAU,2CACb,UAAA,qBAAC,OAAA,EAAI,WAAU,eACb,UAAA;AAAA,QAAA,oBAAC,SAAA,EAAQ,WAAU,oDAAA,CAAoD;AAAA,QACvE,oBAAC,KAAA,EAAE,WAAU,sCAAqC,UAAA,aAAA,CAAU;AAAA,MAAA,EAAA,CAC9D,EAAA,CACF;AAAA,IAEJ;AAEA,QAAI,OAAO;AACT,iCACG,OAAA,EAAI,WAAU,+CACb,UAAA,qBAAC,OAAA,EAAI,WAAU,wBACb,UAAA;AAAA,QAAA,oBAAC,aAAA,EAAY,WAAU,8BAAA,CAA8B;AAAA,QACrD,oBAAC,KAAA,EAAE,WAAU,8CAA6C,UAAA,kBAE1D;AAAA,QACA,oBAAC,KAAA,EAAE,WAAU,qEACV,UAAA,MAAA,CACH;AAAA,MAAA,EAAA,CACF,EAAA,CACF;AAAA,IAEJ;AAEA,QAAI,CAAC,UAAW,QAAO;AAEvB,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,KAAK;AAAA,QACL,KAAK;AAAA,QACL,OAAM;AAAA,QACN,QAAO;AAAA,QACP,aAAY;AAAA,QACZ,OAAM;AAAA,QACN,OAAO,IAAI,QAAQ,SAAS,IAAI,EAAE;AAAA,QAClC,WAAU;AAAA,QACV,QAAQ;AAAA,MAAA;AAAA,IAAA;AAAA,EAGd;AAAA,EACA,CAAC,MAAM,SACL,KAAK,IAAI,OAAO,KAAK,IAAI,MACzB,KAAK,IAAI,SAAS,KAAK,IAAI,QAC3B,KAAK,IAAI,SAAS,KAAK,IAAI,QAC3B,KAAK,IAAI,gBAAgB,KAAK,IAAI;AACtC;AAEA,YAAY,cAAc;AAW1B,MAAM,eAAkE,CAAC;AAAA,EACvE;AACF,MAAM;AACJ,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,IAEA,mBAAA;AACF,QAAM,UAAU,aAAa,WAAW,KAAK,UAAU;AAEvD,YAAU,MAAM;AACd,QAAI,CAAC,kCAAkC;AACrC;AAAA,IACF;AAEA,2BAAA;AACA,WAAO,MAAM;AACX,+BAAA;AAAA,IACF;AAAA,EACF,GAAG,CAAC,kCAAkC,wBAAwB,wBAAwB,CAAC;AAEvF,QAAM,YAAY,QAAQ,MAAM;AAC9B,UAAM,IAAI,KAAK;AACf,QAAI,KAAK,EAAG,QAAO;AACnB,QAAI,MAAM,EAAG,QAAO;AACpB,QAAI,KAAK,EAAG,QAAO;AACnB,QAAI,KAAK,EAAG,QAAO;AACnB,WAAO;AAAA,EACT,GAAG,CAAC,KAAK,MAAM,CAAC;AAEhB,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW,0BAA0B,UAAU,GAAG,SAAS,yBAAyB,EAAE;AAAA,MAErF,UAAA,KAAK,IAAI,CAAC,QAAQ;AACjB,cAAM,WAAW,IAAI,OAAO;AAC5B,cAAM,SAAS,kBAAkB,IAAI,IAAI;AACzC,cAAM,WAAW,OAAO;AAExB,YAAI,SAAS;AACX,iBACE;AAAA,YAAC;AAAA,YAAA;AAAA,cAEC,WAAU;AAAA,cAEV,UAAA;AAAA,gBAAA,qBAAC,OAAA,EAAI,WAAU,yDACb,UAAA;AAAA,kBAAA,oBAAC,UAAA,EAAS,WAAU,wCAAA,CAAwC;AAAA,sCAC3D,QAAA,EAAK,WAAU,2DACb,UAAA,IAAI,QAAQ,OAAO,OACtB;AAAA,kBACA;AAAA,oBAAC;AAAA,oBAAA;AAAA,sBACC,SAAS,MAAM,SAAS,IAAI,EAAE;AAAA,sBAC9B,WAAU;AAAA,sBAEV,UAAA,oBAAC,GAAA,EAAE,WAAU,WAAA,CAAW;AAAA,oBAAA;AAAA,kBAAA;AAAA,gBAC1B,GACF;AAAA,oCACC,OAAA,EAAI,WAAU,0BACb,UAAA,oBAAC,aAAA,EAAY,KAAU,EAAA,CACzB;AAAA,cAAA;AAAA,YAAA;AAAA,YAjBK,IAAI;AAAA,UAAA;AAAA,QAoBf;AAEA,eACE;AAAA,UAAC;AAAA,UAAA;AAAA,YAEC,WAAW,WAAW,kBAAkB;AAAA,YAExC,UAAA,oBAAC,eAAY,IAAA,CAAU;AAAA,UAAA;AAAA,UAHlB,IAAI;AAAA,QAAA;AAAA,MAMf,CAAC;AAAA,IAAA;AAAA,EAAA;AAGP;AAGA,MAAM,SAAa,MAAM;AACvB,QAAM,EAAE,MAAM,aAAa,cAAc,UAAU,SAAA,IACjD,mBAAA;AAEF,MAAI,KAAK,UAAU,KAAK,aAAa,QAAS,QAAO;AAErD,6BACG,OAAA,EAAI,WAAU,yEACZ,UAAA,KAAK,IAAI,CAAC,QAAQ;AACjB,UAAM,SAAS,kBAAkB,IAAI,IAAI;AACzC,UAAM,WAAW,OAAO;AACxB,UAAM,WAAW,IAAI,OAAO;AAE5B,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QAEC,WAAW,uHACT,WACI,8DACA,oEACN;AAAA,QACA,SAAS,MAAM,aAAa,IAAI,EAAE;AAAA,QAElC,UAAA;AAAA,UAAA,oBAAC,UAAA,EAAS,WAAU,kBAAA,CAAkB;AAAA,8BACrC,QAAA,EAAK,WAAU,YAAY,UAAA,IAAI,QAAQ,OAAO,OAAM;AAAA,UACrD;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,SAAS,CAAC,MAAM;AACd,kBAAE,gBAAA;AACF,yBAAS,IAAI,EAAE;AAAA,cACjB;AAAA,cACA,WAAU;AAAA,cAEV,UAAA,oBAAC,GAAA,EAAE,WAAU,WAAA,CAAW;AAAA,YAAA;AAAA,UAAA;AAAA,QAC1B;AAAA,MAAA;AAAA,MAlBK,IAAI;AAAA,IAAA;AAAA,EAqBf,CAAC,EAAA,CACH;AAEJ;AAGA,MAAM,cAA4C,CAAC,EAAE,iBAAiB;AACpE,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,IACE,mBAAA;AACJ,QAAM,YAAY,KAAK,KAAK,CAAC,MAAM,EAAE,OAAO,WAAW;AACvD,QAAM,SAAS,mBAAkB,uCAAW,SAAQ,SAAS;AAC7D,QAAM,WAAW,OAAO;AACxB,QAAM,UAAU,aAAa;AAE7B,SACE,qBAAC,OAAA,EAAI,WAAU,yDACZ,UAAA;AAAA,IAAA,CAAC,WACA,oBAAC,UAAA,EAAS,WAAU,wCAAA,CAAwC;AAAA,IAE9D,oBAAC,KAAA,EAAE,WAAU,yDACV,UAAA,UACG,GAAG,KAAK,MAAM,aACd,uCAAW,SAAQ,OAAO,OAChC;AAAA,IAEC,KAAK,UAAU,KACd;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,SAAS,MAAM,YAAY,UAAU,SAAS,OAAO;AAAA,QACrD,WAAW,wEACT,UACI,+BACA,6DACN;AAAA,QACA,OAAO,UAAU,mBAAmB;AAAA,QAEnC,UAAA,8BACE,QAAA,EAAO,WAAU,YAAW,IAE7B,oBAAC,YAAA,EAAW,WAAU,WAAA,CAAW;AAAA,MAAA;AAAA,IAAA;AAAA,IAKvC;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,SAAS;AAAA,QACT,WAAU;AAAA,QACV,OAAO,aAAa,oBAAoB;AAAA,QAEvC,UAAA,iCACE,WAAA,EAAU,WAAU,YAAW,IAEhC,oBAAC,WAAA,EAAU,WAAU,WAAA,CAAW;AAAA,MAAA;AAAA,IAAA;AAAA,IAGpC;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,SAAS;AAAA,QACT,WAAU;AAAA,QACV,OAAM;AAAA,QAEN,UAAA,oBAAC,GAAA,EAAE,WAAU,SAAA,CAAS;AAAA,MAAA;AAAA,IAAA;AAAA,EACxB,GACF;AAEJ;AAEO,MAAM,aAAkC,CAAC;AAAA,EAC9C,mCAAmC;AACrC,MAAM;AACJ,QAAM,eAAe,mBAAmB,CAAC,MAAM,EAAE,YAAY;AAE7D,QAAM,UACJ,qBAAA,UAAA,EACE,UAAA;AAAA,IAAA,oBAAC,aAAA,EAAY,YAAY,aAAA,CAAc;AAAA,wBACtC,QAAA,EAAO;AAAA,IACR,oBAAC,gBAAa,iCAAA,CAAoE;AAAA,EAAA,GACpF;AAGF,MAAI,cAAc;AAChB,WACE,oBAAC,OAAA,EAAI,WAAU,kDACZ,UAAA,SACH;AAAA,EAEJ;AAEA,SACE,oBAAC,OAAA,EAAI,WAAU,sCAAsC,UAAA,SAAQ;AAEjE;"}
|
|
@@ -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 resolveAthenaAssetCitationActionBehavior,\n resolveAthenaAssetCitationBehavior,\n} from \"./asset-embed-messages\";\nimport {\n buildSdkAssetEmbedUrl,\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 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 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 expiresInSeconds,\n citationBehavior,\n onCitationClick,\n citationActionBehavior,\n onCitationAction,\n onLoad,\n onError,\n loadingFallback,\n errorFallback,\n className,\n style,\n title,\n allow = \"clipboard-write; fullscreen\",\n ...iframeProps\n}) => {\n const iframeRef = useRef<HTMLIFrameElement | null>(null);\n const athenaConfig = useAthenaConfig();\n const { backendUrl, appUrl, apiKey } = athenaConfig;\n const { embedUrl, isLoading, error } = useAssetEmbed(assetId, {\n backendUrl,\n appUrl,\n apiKey,\n token: athenaConfig.token,\n getToken: athenaConfig.getToken,\n readOnly,\n expiresInSeconds,\n displayMode,\n });\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 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 }, [embedUrl, onCitationAction, onCitationClick, 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","useRef","useAthenaConfig","useAssetEmbed","useMemo","buildSdkAssetEmbedUrl","resolveAthenaAssetCitationBehavior","resolveAthenaAssetCitationActionBehavior","useCallback","ATHENA_ASSET_EMBED_CONFIG_MESSAGE","useEffect","isAthenaAssetEmbedReadyEvent","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 resolveAthenaAssetCitationActionBehavior,\n resolveAthenaAssetCitationBehavior,\n} from \"./asset-embed-messages\";\nimport {\n buildSdkAssetEmbedUrl,\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 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 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 expiresInSeconds,\n citationBehavior,\n onCitationClick,\n citationActionBehavior,\n onCitationAction,\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 const { embedUrl, isLoading, error } = useAssetEmbed(assetId, {\n backendUrl,\n appUrl,\n apiKey,\n token: athenaConfig.token,\n getToken: athenaConfig.getToken,\n readOnly,\n expiresInSeconds,\n displayMode,\n });\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 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 }, [embedUrl, onCitationAction, onCitationClick, 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","useRef","useAthenaConfig","useAssetEmbed","useMemo","buildSdkAssetEmbedUrl","resolveAthenaAssetCitationBehavior","resolveAthenaAssetCitationActionBehavior","useCallback","ATHENA_ASSET_EMBED_CONFIG_MESSAGE","useEffect","isAthenaAssetEmbedReadyEvent","isAthenaAssetCitationClickEvent","isAthenaAssetCitationActionEvent","jsxs","jsx","Loader2","AlertCircle","cn"],"mappings":"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"}
|
|
@@ -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 resolveAthenaAssetCitationActionBehavior,\n resolveAthenaAssetCitationBehavior,\n} from \"./asset-embed-messages\";\nimport {\n buildSdkAssetEmbedUrl,\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 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 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 expiresInSeconds,\n citationBehavior,\n onCitationClick,\n citationActionBehavior,\n onCitationAction,\n onLoad,\n onError,\n loadingFallback,\n errorFallback,\n className,\n style,\n title,\n allow = \"clipboard-write; fullscreen\",\n ...iframeProps\n}) => {\n const iframeRef = useRef<HTMLIFrameElement | null>(null);\n const athenaConfig = useAthenaConfig();\n const { backendUrl, appUrl, apiKey } = athenaConfig;\n const { embedUrl, isLoading, error } = useAssetEmbed(assetId, {\n backendUrl,\n appUrl,\n apiKey,\n token: athenaConfig.token,\n getToken: athenaConfig.getToken,\n readOnly,\n expiresInSeconds,\n displayMode,\n });\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 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 }, [embedUrl, onCitationAction, onCitationClick, 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 resolveAthenaAssetCitationActionBehavior,\n resolveAthenaAssetCitationBehavior,\n} from \"./asset-embed-messages\";\nimport {\n buildSdkAssetEmbedUrl,\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 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 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 expiresInSeconds,\n citationBehavior,\n onCitationClick,\n citationActionBehavior,\n onCitationAction,\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 const { embedUrl, isLoading, error } = useAssetEmbed(assetId, {\n backendUrl,\n appUrl,\n apiKey,\n token: athenaConfig.token,\n getToken: athenaConfig.getToken,\n readOnly,\n expiresInSeconds,\n displayMode,\n });\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 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 }, [embedUrl, onCitationAction, onCitationClick, 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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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
function runInputRulesOnEnter(editor) {
|
|
4
|
+
const { view } = editor;
|
|
5
|
+
const event = new KeyboardEvent("keydown", { key: "Enter" });
|
|
6
|
+
return view.state.plugins.some(
|
|
7
|
+
(plugin) => {
|
|
8
|
+
var _a;
|
|
9
|
+
return plugin.spec.isInputRules === true && ((_a = plugin.props.handleKeyDown) == null ? void 0 : _a.call(plugin, view, event)) === true;
|
|
10
|
+
}
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
exports.runInputRulesOnEnter = runInputRulesOnEnter;
|
|
14
|
+
//# sourceMappingURL=run-input-rules-on-enter.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-input-rules-on-enter.cjs","sources":["../../../src/chat-ui/composer/run-input-rules-on-enter.ts"],"sourcesContent":["import type { Editor } from \"@tiptap/core\";\n\n/**\n * Offers Enter to the editor's input rules and reports whether one applied.\n *\n * Tiptap 2 ran every input rule ahead of all keymaps, so Enter right after a\n * markdown marker (\"1.\", \"-\", \"> \") converted the line before the submit\n * keybind saw the key. Tiptap 3 gives each extension its own input-rule plugin\n * in that extension's priority slot, behind higher-priority keymaps.\n */\nexport function runInputRulesOnEnter(editor: Editor): boolean {\n const { view } = editor;\n const event = new KeyboardEvent(\"keydown\", { key: \"Enter\" });\n return view.state.plugins.some(\n (plugin) =>\n plugin.spec.isInputRules === true &&\n plugin.props.handleKeyDown?.call(plugin, view, event) === true,\n );\n}\n"],"names":[],"mappings":";;AAUO,SAAS,qBAAqB,QAAyB;AAC5D,QAAM,EAAE,SAAS;AACjB,QAAM,QAAQ,IAAI,cAAc,WAAW,EAAE,KAAK,SAAS;AAC3D,SAAO,KAAK,MAAM,QAAQ;AAAA,IACxB,CAAC,WAAA;;AACC,oBAAO,KAAK,iBAAiB,UAC7B,YAAO,MAAM,kBAAb,mBAA4B,KAAK,QAAQ,MAAM,YAAW;AAAA;AAAA,EAAA;AAEhE;;"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Editor } from "@tiptap/core";
|
|
2
|
+
/**
|
|
3
|
+
* Offers Enter to the editor's input rules and reports whether one applied.
|
|
4
|
+
*
|
|
5
|
+
* Tiptap 2 ran every input rule ahead of all keymaps, so Enter right after a
|
|
6
|
+
* markdown marker ("1.", "-", "> ") converted the line before the submit
|
|
7
|
+
* keybind saw the key. Tiptap 3 gives each extension its own input-rule plugin
|
|
8
|
+
* in that extension's priority slot, behind higher-priority keymaps.
|
|
9
|
+
*/
|
|
10
|
+
export declare function runInputRulesOnEnter(editor: Editor): boolean;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
function runInputRulesOnEnter(editor) {
|
|
2
|
+
const { view } = editor;
|
|
3
|
+
const event = new KeyboardEvent("keydown", { key: "Enter" });
|
|
4
|
+
return view.state.plugins.some(
|
|
5
|
+
(plugin) => {
|
|
6
|
+
var _a;
|
|
7
|
+
return plugin.spec.isInputRules === true && ((_a = plugin.props.handleKeyDown) == null ? void 0 : _a.call(plugin, view, event)) === true;
|
|
8
|
+
}
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
runInputRulesOnEnter
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=run-input-rules-on-enter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-input-rules-on-enter.js","sources":["../../../src/chat-ui/composer/run-input-rules-on-enter.ts"],"sourcesContent":["import type { Editor } from \"@tiptap/core\";\n\n/**\n * Offers Enter to the editor's input rules and reports whether one applied.\n *\n * Tiptap 2 ran every input rule ahead of all keymaps, so Enter right after a\n * markdown marker (\"1.\", \"-\", \"> \") converted the line before the submit\n * keybind saw the key. Tiptap 3 gives each extension its own input-rule plugin\n * in that extension's priority slot, behind higher-priority keymaps.\n */\nexport function runInputRulesOnEnter(editor: Editor): boolean {\n const { view } = editor;\n const event = new KeyboardEvent(\"keydown\", { key: \"Enter\" });\n return view.state.plugins.some(\n (plugin) =>\n plugin.spec.isInputRules === true &&\n plugin.props.handleKeyDown?.call(plugin, view, event) === true,\n );\n}\n"],"names":[],"mappings":"AAUO,SAAS,qBAAqB,QAAyB;AAC5D,QAAM,EAAE,SAAS;AACjB,QAAM,QAAQ,IAAI,cAAc,WAAW,EAAE,KAAK,SAAS;AAC3D,SAAO,KAAK,MAAM,QAAQ;AAAA,IACxB,CAAC,WAAA;AAJE;AAKD,oBAAO,KAAK,iBAAiB,UAC7B,YAAO,MAAM,kBAAb,mBAA4B,KAAK,QAAQ,MAAM,YAAW;AAAA;AAAA,EAAA;AAEhE;"}
|
|
@@ -11,11 +11,13 @@ const react$1 = require("@assistant-ui/react");
|
|
|
11
11
|
const composerHandoff = require("../../composer/composer-handoff.cjs");
|
|
12
12
|
const lucideReact = require("lucide-react");
|
|
13
13
|
const chatTextSchema = require("../rich-text/chat-text-schema.cjs");
|
|
14
|
+
const markdownStorage = require("../markdown-storage.cjs");
|
|
14
15
|
const mentionSuggestions_extension = require("../mentions/suggestions/mention-suggestions.extension.cjs");
|
|
15
16
|
const mentionSuggestionPopup = require("../mentions/suggestions/components/mention-suggestion-popup.cjs");
|
|
16
17
|
const mentionSuggestionList = require("../mentions/suggestions/components/mention-suggestion-list.cjs");
|
|
17
18
|
const useMentionSuggestions = require("../mentions/suggestions/data/use-mention-suggestions.cjs");
|
|
18
19
|
const pendingAttachmentUploads = require("./pending-attachment-uploads.cjs");
|
|
20
|
+
const runInputRulesOnEnter = require("./run-input-rules-on-enter.cjs");
|
|
19
21
|
const assetAttachment = require("../../packages/agent-runtime/src/asset-attachment.cjs");
|
|
20
22
|
const cnfast = require("cnfast");
|
|
21
23
|
const SubmitKeybind = core.Extension.create({
|
|
@@ -29,6 +31,7 @@ const SubmitKeybind = core.Extension.create({
|
|
|
29
31
|
return {
|
|
30
32
|
Enter: () => {
|
|
31
33
|
if (this.options.isMentionOpen()) return false;
|
|
34
|
+
if (runInputRulesOnEnter.runInputRulesOnEnter(this.editor)) return true;
|
|
32
35
|
if (["listItem", "taskItem", "codeBlock", "table", "blockquote"].some(
|
|
33
36
|
(node) => this.editor.isActive(node)
|
|
34
37
|
)) return false;
|
|
@@ -73,7 +76,7 @@ const MENTION_STYLES = `
|
|
|
73
76
|
}
|
|
74
77
|
`;
|
|
75
78
|
function serializeDraft(editor) {
|
|
76
|
-
return editor.
|
|
79
|
+
return markdownStorage.getMarkdownStorage(editor).getMarkdown();
|
|
77
80
|
}
|
|
78
81
|
const NO_WEBPAGES = [];
|
|
79
82
|
function revokeImagePreview(src) {
|
|
@@ -229,7 +232,7 @@ const TiptapComposer = react.forwardRef(
|
|
|
229
232
|
if (interceptSubmit) {
|
|
230
233
|
const hasAttachments = (((_a = aui.composer.getState().attachments) == null ? void 0 : _a.length) ?? 0) > 0;
|
|
231
234
|
if (!hasAttachments && await interceptSubmit(text)) {
|
|
232
|
-
ed.commands.clearContent();
|
|
235
|
+
ed.commands.clearContent(false);
|
|
233
236
|
clearImagePreviews();
|
|
234
237
|
onDraftStateChange == null ? void 0 : onDraftStateChange(false);
|
|
235
238
|
onSubmitted == null ? void 0 : onSubmitted();
|
|
@@ -240,7 +243,7 @@ const TiptapComposer = react.forwardRef(
|
|
|
240
243
|
if (!composerInput.canSend) return;
|
|
241
244
|
composerInput.setText(text);
|
|
242
245
|
composerInput.send({ steer: false });
|
|
243
|
-
ed.commands.clearContent();
|
|
246
|
+
ed.commands.clearContent(false);
|
|
244
247
|
clearImagePreviews();
|
|
245
248
|
onDraftStateChange == null ? void 0 : onDraftStateChange(false);
|
|
246
249
|
onSubmitted == null ? void 0 : onSubmitted();
|
|
@@ -418,7 +421,7 @@ const TiptapComposer = react.forwardRef(
|
|
|
418
421
|
if (cancelled || editor.isDestroyed) return;
|
|
419
422
|
applyingRuntimeTextRef.current = true;
|
|
420
423
|
try {
|
|
421
|
-
editor.commands.setContent(value);
|
|
424
|
+
editor.commands.setContent(value, { emitUpdate: false });
|
|
422
425
|
lastEmittedMarkdownRef.current = serializeDraft(editor);
|
|
423
426
|
} finally {
|
|
424
427
|
applyingRuntimeTextRef.current = false;
|