@athenaintel/react 0.40.3 → 0.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +55 -0
  2. package/dist/chat/athena-citation-links.cjs +23 -2
  3. package/dist/chat/athena-citation-links.cjs.map +1 -1
  4. package/dist/chat/athena-citation-links.d.ts +5 -0
  5. package/dist/chat/athena-citation-links.js +23 -2
  6. package/dist/chat/athena-citation-links.js.map +1 -1
  7. package/dist/chat-ui/index.d.ts +1 -1
  8. package/dist/chat-ui/mentions/mention-chip.cjs +118 -7
  9. package/dist/chat-ui/mentions/mention-chip.cjs.map +1 -1
  10. package/dist/chat-ui/mentions/mention-chip.d.ts +29 -0
  11. package/dist/chat-ui/mentions/mention-chip.js +120 -9
  12. package/dist/chat-ui/mentions/mention-chip.js.map +1 -1
  13. package/dist/chat-ui/rich-text/chat-message-body.cjs +2 -1
  14. package/dist/chat-ui/rich-text/chat-message-body.cjs.map +1 -1
  15. package/dist/chat-ui/rich-text/chat-message-body.js +2 -1
  16. package/dist/chat-ui/rich-text/chat-message-body.js.map +1 -1
  17. package/dist/react/package.json.cjs +1 -1
  18. package/dist/react/package.json.js +1 -1
  19. package/dist/references/src/anchors/file-line-range.cjs +70 -0
  20. package/dist/references/src/anchors/file-line-range.cjs.map +1 -0
  21. package/dist/references/src/anchors/file-line-range.js +70 -0
  22. package/dist/references/src/anchors/file-line-range.js.map +1 -0
  23. package/dist/references/src/anchors/format.cjs +219 -0
  24. package/dist/references/src/anchors/format.cjs.map +1 -0
  25. package/dist/references/src/anchors/format.js +219 -0
  26. package/dist/references/src/anchors/format.js.map +1 -0
  27. package/dist/references/src/anchors/parse.cjs +408 -0
  28. package/dist/references/src/anchors/parse.cjs.map +1 -0
  29. package/dist/references/src/anchors/parse.js +408 -0
  30. package/dist/references/src/anchors/parse.js.map +1 -0
  31. package/dist/references/src/anchors/serialize.cjs +185 -0
  32. package/dist/references/src/anchors/serialize.cjs.map +1 -0
  33. package/dist/references/src/anchors/serialize.js +185 -0
  34. package/dist/references/src/anchors/serialize.js.map +1 -0
  35. package/dist/references/src/anchors/types.cjs +50 -0
  36. package/dist/references/src/anchors/types.cjs.map +1 -0
  37. package/dist/references/src/anchors/types.js +50 -0
  38. package/dist/references/src/anchors/types.js.map +1 -0
  39. package/dist/references/src/formats/uri/asset-mention-uri.cjs +29 -0
  40. package/dist/references/src/formats/uri/asset-mention-uri.cjs.map +1 -0
  41. package/dist/references/src/formats/uri/asset-mention-uri.js +29 -0
  42. package/dist/references/src/formats/uri/asset-mention-uri.js.map +1 -0
  43. package/dist/references/src/formats/uri/asset-spaces-url.cjs +64 -0
  44. package/dist/references/src/formats/uri/asset-spaces-url.cjs.map +1 -0
  45. package/dist/references/src/formats/uri/asset-spaces-url.js +64 -0
  46. package/dist/references/src/formats/uri/asset-spaces-url.js.map +1 -0
  47. package/dist/references/src/formats/uri/template-placeholder.cjs +9 -0
  48. package/dist/references/src/formats/uri/template-placeholder.cjs.map +1 -0
  49. package/dist/references/src/formats/uri/template-placeholder.js +9 -0
  50. package/dist/references/src/formats/uri/template-placeholder.js.map +1 -0
  51. package/dist/references/src/sheet-name.cjs +10 -0
  52. package/dist/references/src/sheet-name.cjs.map +1 -0
  53. package/dist/references/src/sheet-name.js +10 -0
  54. package/dist/references/src/sheet-name.js.map +1 -0
  55. package/dist/references/src/version-ref.cjs +61 -0
  56. package/dist/references/src/version-ref.cjs.map +1 -0
  57. package/dist/references/src/version-ref.js +61 -0
  58. package/dist/references/src/version-ref.js.map +1 -0
  59. package/dist/styles.css +1 -1
  60. package/package.json +3 -2
package/README.md CHANGED
@@ -807,6 +807,31 @@ Both use the same bearer / API key as session creation. The full recipe
807
807
  template skill `voice-session-guide.md`. `createAthenaVoiceSession` is also
808
808
  exported if you hold the bridge state yourself.
809
809
 
810
+ ## What's new in 0.41.0
811
+
812
+ **Citation chips say where they point, with an icon, in every app.** An asset chip in a chat
813
+ message now ends with the place it cites, in grey: `p.5`, `Slide 3`, `'Buy List'!A1:B2`,
814
+ `jwt.py:12-25`. The place is read and labelled by `@athenaintel/references`, the parser and
815
+ labels Olympus's chips use. A citation written without a label is named by its place (`Page 5`),
816
+ its tooltip is the excerpt or else the place spelled out, and its accessible name says the place
817
+ (`Open Q4 report at Page 5 in Spaces`). Chips in apps that serve no asset-type SVGs (the computer
818
+ templates and most SDK apps) used to show no icon at all. They now show the asset type's Lucide
819
+ icon, and a repository file's lines show a code file.
820
+
821
+ **A click opens the cited place.** A citation the asset panel shows (a slide, a document block, a
822
+ transcript message, a time, …) now opens at that place instead of at the top: the panel's embed
823
+ gets the anchor as `@athenaintel/references` writes it. A repository file's lines (a GitHub
824
+ catalog) used to open in the asset panel, which can show no catalog, so the panel showed an error.
825
+ Such a citation now opens in Athena on the web, at the file with the lines highlighted. This holds
826
+ even when the link names the wrong type. The host's `linkClicks.onClick` still sees every click; for
827
+ these citations `openInAssetPanel` is undefined.
828
+
829
+ A `mention://asset` citation's chip now links to its place too; it used to open the asset at the
830
+ top.
831
+
832
+ New: `MentionChip` takes `assetType` and `citation` (`MentionChipCitation`), and `parseMentionUrl`
833
+ returns both, plus `sourceHref` for a `mention://asset` citation. Nothing to change in your app.
834
+
810
835
  ## What's new in 0.40.3
811
836
 
812
837
  **A wake from a GitHub watch says what happened to the pull request.** When a `watch` on a pull request or an issue wakes the conversation, its card read "Someone triggered “Github check run completed” on GitHub - owner/repo", which named neither the pull request nor the result. It now says what happened, from the subject agora stamps with the event (the number and the state word, never text): "A check failed on #47506", "Checks passed on #47506", "#123 was merged", "Changes were requested on #123", "New comment on #123". The repository stands in when the event names no pull request, and the line under it still counts a batch ("4 events"). Every other event reads as before.
@@ -1597,6 +1622,36 @@ If you need full control, `ssoUserInfoUrl`, `ssoLoginUrl`, and `ssoLogoutUrl` re
1597
1622
  - `onError` receives the typed `auth_sso_failed` error of a read that gave up.
1598
1623
  - Without `displayOnError`, a person with no session is sent to `ssoLoginUrl`.
1599
1624
 
1625
+ ## Mentions and citations
1626
+
1627
+ An asset mention and an asset citation are one link, written the way Athena writes it everywhere
1628
+ (`@athenaintel/references`, the [Reference Standard](../../docs/ANCHOR_REFERENCE_STANDARD_GUIDE.md#rendering-mentions-and-citations)):
1629
+
1630
+ ```md
1631
+ [@Q4 report](https://app.athenaintel.com/dashboard/spaces/?asset_ids=asset_…&type=pdf)
1632
+ [@Q4 report](https://app.athenaintel.com/dashboard/spaces/?asset_ids=asset_…&type=pdf&anchor=page&page=5&excerpt=Revenue%20grew)
1633
+ ```
1634
+
1635
+ With no `anchor` it is a mention; with one it is a citation of that place (a page, a slide, a
1636
+ sheet range, a time, a repository file's lines, …). The agent writes these links, and so can your
1637
+ app. `AthenaChat` renders them as chips with nothing to configure, and so do `ChatMessageBody`,
1638
+ `TiptapText`, `MentionLink` and `parseMentionsInText` in a renderer of your own
1639
+ (`@athenaintel/react/chat-ui`). A chip shows:
1640
+
1641
+ - **the asset type's icon.** Hosts that serve Athena's asset-type SVGs point the chips at them with
1642
+ `configureAssetIconBasePath({ basePath })`. Every other host gets a Lucide icon for the type,
1643
+ and a repository file's lines read as a code file.
1644
+ - **the label the link was written with**, then **the cited place** in grey (`p.5`, `Slide 3`,
1645
+ `'Buy List'!A1:B2`, `jwt.py:12-25`). The SDK reads and labels the place with the same parser
1646
+ Olympus's chips use. A citation written without a label is named by its place (`Page 5`).
1647
+ - **the excerpt as its tooltip**, or the place spelled out when the link carries no excerpt.
1648
+
1649
+ A click goes through `linkClicks` (below). A citation the asset panel can show opens there at the
1650
+ cited place. A repository file's lines, or any catalog, open in Athena on the web, which shows the
1651
+ file with the lines highlighted: no embed renders a catalog. Agora's own citation anchors
1652
+ (`<a class="mention" data-type="pdf" data-id="asset_…" data-page-number=…>`) render as the same
1653
+ chips.
1654
+
1600
1655
  ## Citation Links
1601
1656
 
1602
1657
  When you render chat inside `<AthenaLayout>`, Athena citation links (`app.athenaintel.com/dashboard/spaces?...`) now open the referenced asset in the SDK asset pane by default instead of navigating away.
@@ -1,5 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const serialize = require("../references/src/anchors/serialize.cjs");
4
+ const assetSpacesUrl = require("../references/src/formats/uri/asset-spaces-url.cjs");
3
5
  const react = require("react");
4
6
  const assetPanelStore = require("../assets/asset-panel-store.cjs");
5
7
  const AthenaContext = require("../provider/AthenaContext.cjs");
@@ -17,6 +19,13 @@ const ASSET_TYPE_ALIASES = {
17
19
  word_document: "document"
18
20
  };
19
21
  const isAthenaSpacesPath = (url) => url.pathname.replace(/\/+$/, "") === "/dashboard/spaces";
22
+ const NON_EMBEDDABLE_ASSET_TYPES = /* @__PURE__ */ new Set(["catalog"]);
23
+ const NON_EMBEDDABLE_ANCHOR_TYPES = /* @__PURE__ */ new Set(["file_line_range"]);
24
+ const isEmbeddableCitation = (url) => {
25
+ const rawType = (url.searchParams.get("asset_type") ?? url.searchParams.get("type") ?? "").trim().replace(/^athena\//, "").toLowerCase();
26
+ const anchorType = url.searchParams.get("anchor") ?? "";
27
+ return !NON_EMBEDDABLE_ASSET_TYPES.has(rawType) && !NON_EMBEDDABLE_ANCHOR_TYPES.has(anchorType);
28
+ };
20
29
  const getFiniteNumber = (url, key) => {
21
30
  const value = url.searchParams.get(key);
22
31
  if (value === null || value.trim() === "") return null;
@@ -27,6 +36,13 @@ const getNonEmptyString = (url, key) => {
27
36
  const value = url.searchParams.get(key);
28
37
  return value === null || value === "" ? null : value;
29
38
  };
39
+ const readSharedAnchor = (url) => {
40
+ try {
41
+ return assetSpacesUrl.decodeFromAssetSpacesUrl(url).anchor ?? null;
42
+ } catch {
43
+ return null;
44
+ }
45
+ };
30
46
  const parseCitationReference = ({
31
47
  url,
32
48
  assetId
@@ -117,6 +133,10 @@ const parseCitationReference = ({
117
133
  break;
118
134
  }
119
135
  }
136
+ if (reference.anchor === void 0) {
137
+ const anchor = readSharedAnchor(url);
138
+ if (anchor) reference.anchor = serialize.serializeAnchorToParams(anchor).toString();
139
+ }
120
140
  const excerpt = url.searchParams.get("excerpt");
121
141
  if (excerpt) reference.meta = { excerpt };
122
142
  return reference;
@@ -210,7 +230,8 @@ const parseAthenaCitationLink = ({
210
230
  assetId,
211
231
  assetIds,
212
232
  assetType: normalizeAssetType(url.searchParams.get("asset_type") ?? url.searchParams.get("type")),
213
- reference: parseCitationReference({ url, assetId })
233
+ reference: parseCitationReference({ url, assetId }),
234
+ embeddable: isEmbeddableCitation(url)
214
235
  };
215
236
  };
216
237
  const isAthenaCitationUrl = ({
@@ -262,7 +283,7 @@ const useAthenaLinkClickHandler = () => {
262
283
  assetIds: citationLink.assetIds,
263
284
  assetType: citationLink.assetType
264
285
  } : null;
265
- const openInAssetPanel = citationLink ? () => openAsset(citationLink.assetId, {
286
+ const openInAssetPanel = (citationLink == null ? void 0 : citationLink.embeddable) ? () => openAsset(citationLink.assetId, {
266
287
  name: trimmedLinkText ?? void 0,
267
288
  type: citationLink.assetType ?? void 0,
268
289
  // Carry the citation target so the panel's iframe navigates to
@@ -1 +1 @@
1
- {"version":3,"file":"athena-citation-links.cjs","sources":["../../src/chat/athena-citation-links.ts"],"sourcesContent":["import { useCallback, useContext } from 'react';\nimport type { AthenaAssetCitationReference } from '../assets/asset-embed-messages';\nimport { type AssetType, useAssetPanelStore } from '../assets/asset-panel-store';\nimport { AthenaContext } from '../provider/AthenaContext';\nimport type {\n AthenaCitationLinkMetadata,\n AthenaCitationLinkClickContext,\n AthenaCitationLinkHandleInput,\n AthenaCitationLinkHandler,\n AthenaLinkClickContext,\n AthenaLinkClickHandler,\n AthenaLinkClickHandleInput,\n AthenaLinkKind,\n} from '../provider/citation-links';\n\nconst ATHENA_APP_HOSTNAMES = new Set(['app.athenaintel.com', 'staging-app.athenaintel.com']);\nconst ATHENA_PREVIEW_HOSTNAME_SUFFIX = '.previews.athenaintel.com';\nconst ATHENA_WORKSPACE_HOSTNAME_SUFFIXES = ['.app.athenaintel.com', '.staging-app.athenaintel.com'];\n\nconst ASSET_TYPE_ALIASES: Record<string, AssetType> = {\n document: 'document',\n notebook: 'notebook',\n pdf: 'document',\n powerpoint: 'presentation',\n presentation: 'presentation',\n sheet: 'spreadsheet',\n spreadsheet: 'spreadsheet',\n word_document: 'document',\n};\n\nconst isAthenaSpacesPath = (url: URL): boolean => url.pathname.replace(/\\/+$/, '') === '/dashboard/spaces';\n\nexport interface ParsedAthenaCitationLink {\n url: URL;\n assetId: string;\n assetIds: string[];\n assetType: AssetType | null;\n /** Complete target that can be passed directly to AthenaAssetEmbed.reference. */\n reference: AthenaAssetCitationReference;\n}\n\nconst getFiniteNumber = (url: URL, key: string): number | null => {\n const value = url.searchParams.get(key);\n if (value === null || value.trim() === '') return null;\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : null;\n};\n\nconst getNonEmptyString = (url: URL, key: string): string | null => {\n const value = url.searchParams.get(key);\n return value === null || value === '' ? null : value;\n};\n\nconst parseCitationReference = ({\n url,\n assetId,\n}: {\n url: URL;\n assetId: string;\n}): AthenaAssetCitationReference => {\n const reference: AthenaAssetCitationReference = { id: assetId };\n const anchorType = url.searchParams.get('anchor');\n const page = getFiniteNumber(url, 'page');\n\n switch (anchorType) {\n case 'page':\n if (page !== null) reference.anchor = { type: anchorType, page };\n break;\n case 'page_range': {\n const startPage = getFiniteNumber(url, 'start_page');\n const endPage = getFiniteNumber(url, 'end_page');\n if (startPage !== null && endPage !== null) {\n reference.anchor = { type: anchorType, startPage, endPage };\n }\n break;\n }\n case 'page_rect': {\n const x = getFiniteNumber(url, 'x');\n const y = getFiniteNumber(url, 'y');\n const width = getFiniteNumber(url, 'width');\n const height = getFiniteNumber(url, 'height');\n if (page !== null && x !== null && y !== null && width !== null && height !== null) {\n const unit = url.searchParams.get('unit');\n reference.anchor = {\n type: anchorType,\n page,\n x,\n y,\n width,\n height,\n ...(unit === 'percent' || unit === 'point' ? { unit } : {}),\n };\n }\n break;\n }\n case 'page_text': {\n const text = url.searchParams.get('text');\n if (page !== null && text) {\n // Optional context that disambiguates a repeated phrase, and the hash\n // of the cited unit's normalized text (Clio C-23).\n const prefix = getNonEmptyString(url, 'prefix');\n const suffix = getNonEmptyString(url, 'suffix');\n const hash = getNonEmptyString(url, 'hash');\n reference.anchor = {\n type: anchorType,\n page,\n text,\n ...(prefix ? { prefix } : {}),\n ...(suffix ? { suffix } : {}),\n ...(hash ? { hash } : {}),\n };\n }\n break;\n }\n // Sheet anchors: the embed's spreadsheet view navigates + flashes the\n // target range (Olympus `useSheetAnchors`). `sheet_id` is the engine's\n // numeric sheet id, NOT a tab index.\n case 'sheet_cell': {\n const sheetId = getFiniteNumber(url, 'sheet_id');\n const cell = url.searchParams.get('cell');\n if (sheetId !== null && cell) reference.anchor = { type: anchorType, sheetId, cell };\n break;\n }\n case 'sheet_range': {\n const sheetId = getFiniteNumber(url, 'sheet_id');\n const range = url.searchParams.get('range');\n if (sheetId !== null && range) reference.anchor = { type: anchorType, sheetId, range };\n break;\n }\n case 'sheet_tab': {\n const sheetId = getFiniteNumber(url, 'sheet_id');\n if (sheetId !== null) reference.anchor = { type: anchorType, sheetId };\n break;\n }\n case 'sheet_table': {\n const sheetId = getFiniteNumber(url, 'sheet_id');\n const tableName = url.searchParams.get('table_name');\n const fallbackRange = url.searchParams.get('fallback_range');\n if (sheetId !== null && tableName) {\n reference.anchor = {\n type: anchorType,\n sheetId,\n tableName,\n ...(fallbackRange ? { fallbackRange } : {}),\n };\n }\n break;\n }\n }\n\n const excerpt = url.searchParams.get('excerpt');\n if (excerpt) reference.meta = { excerpt };\n return reference;\n};\n\nconst normalizeAssetType = (value: string | null): AssetType | null => {\n if (!value) {\n return null;\n }\n\n const normalizedValue = value.trim().replace(/^athena\\//, '').toLowerCase().replace(/-/g, '_');\n return ASSET_TYPE_ALIASES[normalizedValue] ?? null;\n};\n\nconst getOrigin = (value?: string | null): string | null => {\n if (!value) {\n return null;\n }\n\n try {\n return new URL(value).origin;\n } catch {\n return null;\n }\n};\n\nconst getHostname = (value?: string | null): string | null => {\n if (!value) {\n return null;\n }\n\n try {\n return new URL(value).hostname.toLowerCase();\n } catch {\n return null;\n }\n};\n\nconst matchesHostnameOrSubdomain = ({\n hostname,\n candidateHostname,\n}: {\n hostname: string;\n candidateHostname: string;\n}): boolean => hostname === candidateHostname || hostname.endsWith(`.${candidateHostname}`);\n\nconst getCurrentOrigin = (): string | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n\n return window.location.origin;\n};\n\nconst parseUrl = ({\n href,\n appUrl,\n}: {\n href: string;\n appUrl?: string | null;\n}): URL | null => {\n const fallbackBase = appUrl ?? getCurrentOrigin() ?? 'https://app.athenaintel.com';\n\n try {\n return new URL(href, fallbackBase);\n } catch {\n return null;\n }\n};\n\nconst isAthenaAppUrl = ({\n url,\n appUrl,\n}: {\n url: URL;\n appUrl?: string | null;\n}): boolean => {\n const hostname = url.hostname.toLowerCase();\n\n if (\n ATHENA_APP_HOSTNAMES.has(hostname)\n || hostname.endsWith(ATHENA_PREVIEW_HOSTNAME_SUFFIX)\n || ATHENA_WORKSPACE_HOSTNAME_SUFFIXES.some((suffix) => hostname.endsWith(suffix))\n ) {\n return true;\n }\n\n const configuredHostname = getHostname(appUrl);\n if (configuredHostname && matchesHostnameOrSubdomain({ hostname, candidateHostname: configuredHostname })) {\n return true;\n }\n\n const configuredOrigin = getOrigin(appUrl);\n if (configuredOrigin && url.origin === configuredOrigin) {\n return true;\n }\n\n // Fallback: treat same-origin `/dashboard/spaces?asset_ids=...` links as Athena\n // citations so local dev and private-cloud embeds still work without appUrl.\n // Consumers can disable the default interception with\n // linkClicks={{ interceptAthenaCitations: false }} or the legacy citationLinks alias.\n const currentOrigin = getCurrentOrigin();\n return !!currentOrigin && url.origin === currentOrigin;\n};\n\nexport const parseAthenaCitationLink = ({\n href,\n appUrl,\n}: {\n href: string;\n appUrl?: string | null;\n}): ParsedAthenaCitationLink | null => {\n const url = parseUrl({ href, appUrl });\n if (!url || !isAthenaAppUrl({ url, appUrl }) || !isAthenaSpacesPath(url)) {\n return null;\n }\n\n const assetIdsParam = url.searchParams.get('asset_ids');\n if (!assetIdsParam) {\n return null;\n }\n\n const assetIds = assetIdsParam\n .split(',')\n .map((value) => value.trim())\n .filter((value) => value.length > 0);\n const assetId = assetIds[0];\n\n if (!assetId) {\n return null;\n }\n\n return {\n url,\n assetId,\n assetIds,\n assetType: normalizeAssetType(url.searchParams.get('asset_type') ?? url.searchParams.get('type')),\n reference: parseCitationReference({ url, assetId }),\n };\n};\n\nexport const isAthenaCitationUrl = ({\n href,\n appUrl,\n}: {\n href: string;\n appUrl?: string | null;\n}): boolean => parseAthenaCitationLink({ href, appUrl }) !== null;\n\ninterface ResolvedAthenaLink {\n url: URL;\n kind: AthenaLinkKind;\n citation: ParsedAthenaCitationLink | null;\n}\n\nconst resolveAthenaLink = ({\n href,\n appUrl,\n}: AthenaLinkClickHandleInput & {\n appUrl?: string | null;\n}): ResolvedAthenaLink | null => {\n const url = parseUrl({ href, appUrl });\n if (!url) {\n return null;\n }\n\n const citation = parseAthenaCitationLink({ href, appUrl });\n if (citation) {\n return {\n url: citation.url,\n kind: 'athena-citation',\n citation,\n };\n }\n\n return {\n url,\n kind: isAthenaAppUrl({ url, appUrl }) ? 'athena-app' : 'external',\n citation: null,\n };\n};\n\nexport const useAthenaLinkClickHandler = (): AthenaLinkClickHandler => {\n const openAsset = useAssetPanelStore((state) => state.openAsset);\n // A host that opens assets its own way (`AthenaProvider onOpenAsset`) counts as a panel: the\n // citation goes to it instead of to the browser.\n const isAssetPanelAvailable = useAssetPanelStore(\n (state) => state.assetPanelHostCount > 0 || state.assetOpeners.length > 0,\n );\n const athenaConfig = useContext(AthenaContext);\n const appUrl = athenaConfig?.appUrl;\n\n return useCallback<AthenaLinkClickHandler>(\n ({ href, linkText, target, nativeEvent }: AthenaLinkClickHandleInput) => {\n const resolvedLink = resolveAthenaLink({ href, appUrl });\n if (!resolvedLink) {\n return false;\n }\n\n const linkClicks = athenaConfig?.linkClicks;\n const citationLinks = athenaConfig?.citationLinks;\n const trimmedLinkText = linkText?.trim() || null;\n const citationLink = resolvedLink.citation;\n const citationMetadata: AthenaCitationLinkMetadata | null = citationLink\n ? {\n assetId: citationLink.assetId,\n assetIds: citationLink.assetIds,\n assetType: citationLink.assetType,\n }\n : null;\n const openInAssetPanel = citationLink\n ? () =>\n openAsset(citationLink.assetId, {\n name: trimmedLinkText ?? undefined,\n type: citationLink.assetType ?? undefined,\n // Carry the citation target so the panel's iframe navigates to\n // the cited range/page instead of opening at the top.\n reference: citationLink.reference,\n // Reading references is one preview surface: a citation to a\n // different asset swaps the active tab rather than stacking a\n // tab per cited document.\n replaceActiveTab: true,\n // A host's own opener that opens it on the web keeps the link as written.\n href: resolvedLink.url.toString(),\n })\n : undefined;\n const context: AthenaLinkClickContext = {\n href,\n url: resolvedLink.url,\n target: target ?? null,\n linkText: trimmedLinkText,\n isAssetPanelAvailable,\n kind: resolvedLink.kind,\n citation: citationMetadata,\n nativeEvent: nativeEvent ?? null,\n openInAssetPanel,\n openInBrowser: (nextTarget) => {\n window.open(resolvedLink.url.toString(), nextTarget ?? target ?? '_blank')?.focus();\n },\n };\n\n if (linkClicks?.onClick?.(context) === true) {\n nativeEvent?.preventDefault();\n return true;\n }\n\n if (!citationLink || !openInAssetPanel || !citationMetadata) {\n return false;\n }\n\n const citationContext: AthenaCitationLinkClickContext = {\n ...context,\n kind: 'athena-citation',\n citation: citationMetadata,\n assetId: citationMetadata.assetId,\n assetIds: citationMetadata.assetIds,\n assetType: citationMetadata.assetType,\n openInAssetPanel,\n };\n\n if (citationLinks?.onClick?.(citationContext) === true) {\n nativeEvent?.preventDefault();\n return true;\n }\n\n if ((linkClicks?.interceptAthenaCitations ?? citationLinks?.interceptAthenaCitations) === false) {\n return false;\n }\n\n if (!isAssetPanelAvailable) {\n return false;\n }\n\n // Nothing showed it (the host's opener declined and no panel is mounted): the link opens as\n // a link.\n if (!citationContext.openInAssetPanel()) {\n return false;\n }\n nativeEvent?.preventDefault();\n return true;\n },\n [appUrl, athenaConfig, isAssetPanelAvailable, openAsset],\n );\n};\n\nexport const useAthenaCitationLinkHandler = (): AthenaCitationLinkHandler => {\n const athenaConfig = useContext(AthenaContext);\n const appUrl = athenaConfig?.appUrl;\n const handleLinkClick = useAthenaLinkClickHandler();\n\n return useCallback<AthenaCitationLinkHandler>(\n (input: AthenaCitationLinkHandleInput) => {\n if (!parseAthenaCitationLink({ href: input.href, appUrl })) {\n return false;\n }\n\n return handleLinkClick(input);\n },\n [appUrl, handleLinkClick],\n );\n};\n"],"names":["useAssetPanelStore","useContext","AthenaContext","useCallback","_a"],"mappings":"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"}
1
+ {"version":3,"file":"athena-citation-links.cjs","sources":["../../src/chat/athena-citation-links.ts"],"sourcesContent":["import { decodeFromAssetSpacesUrl, serializeAnchorToParams } from '@athenaintel/references/asset-links';\nimport { useCallback, useContext } from 'react';\nimport type { AthenaAssetCitationReference } from '../assets/asset-embed-messages';\nimport { type AssetType, useAssetPanelStore } from '../assets/asset-panel-store';\nimport { AthenaContext } from '../provider/AthenaContext';\nimport type {\n AthenaCitationLinkMetadata,\n AthenaCitationLinkClickContext,\n AthenaCitationLinkHandleInput,\n AthenaCitationLinkHandler,\n AthenaLinkClickContext,\n AthenaLinkClickHandler,\n AthenaLinkClickHandleInput,\n AthenaLinkKind,\n} from '../provider/citation-links';\n\nconst ATHENA_APP_HOSTNAMES = new Set(['app.athenaintel.com', 'staging-app.athenaintel.com']);\nconst ATHENA_PREVIEW_HOSTNAME_SUFFIX = '.previews.athenaintel.com';\nconst ATHENA_WORKSPACE_HOSTNAME_SUFFIXES = ['.app.athenaintel.com', '.staging-app.athenaintel.com'];\n\nconst ASSET_TYPE_ALIASES: Record<string, AssetType> = {\n document: 'document',\n notebook: 'notebook',\n pdf: 'document',\n powerpoint: 'presentation',\n presentation: 'presentation',\n sheet: 'spreadsheet',\n spreadsheet: 'spreadsheet',\n word_document: 'document',\n};\n\nconst isAthenaSpacesPath = (url: URL): boolean => url.pathname.replace(/\\/+$/, '') === '/dashboard/spaces';\n\n/**\n * Asset types no embed viewer renders: agora refuses their embed token (422,\n * `agora/agora/web/api/embed/embed_viewer_asset_types.json`), so the asset\n * panel could only show an error for them.\n */\nconst NON_EMBEDDABLE_ASSET_TYPES: ReadonlySet<string> = new Set(['catalog']);\n\n/**\n * Anchors only a non-embeddable asset holds: a repository file's lines live in\n * a GitHub catalog (`ASSET_ANCHOR_REGISTRY`), which Athena on the web opens at\n * the file with the lines highlighted. Read from the anchor too, so a citation\n * whose `type=` names the wrong type still opens where it can be shown.\n */\nconst NON_EMBEDDABLE_ANCHOR_TYPES: ReadonlySet<string> = new Set(['file_line_range']);\n\n/** Whether the asset panel can show the cited asset, or only Athena on the web can. */\nconst isEmbeddableCitation = (url: URL): boolean => {\n const rawType = (url.searchParams.get('asset_type') ?? url.searchParams.get('type') ?? '')\n .trim()\n .replace(/^athena\\//, '')\n .toLowerCase();\n const anchorType = url.searchParams.get('anchor') ?? '';\n return !NON_EMBEDDABLE_ASSET_TYPES.has(rawType) && !NON_EMBEDDABLE_ANCHOR_TYPES.has(anchorType);\n};\n\nexport interface ParsedAthenaCitationLink {\n url: URL;\n assetId: string;\n assetIds: string[];\n assetType: AssetType | null;\n /** Complete target that can be passed directly to AthenaAssetEmbed.reference. */\n reference: AthenaAssetCitationReference;\n /**\n * Whether the asset panel can show the cited asset. A citation it cannot (a repository file's\n * lines, any catalog) opens as the link it is, in Athena on the web.\n */\n embeddable: boolean;\n}\n\nconst getFiniteNumber = (url: URL, key: string): number | null => {\n const value = url.searchParams.get(key);\n if (value === null || value.trim() === '') return null;\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : null;\n};\n\nconst getNonEmptyString = (url: URL, key: string): string | null => {\n const value = url.searchParams.get(key);\n return value === null || value === '' ? null : value;\n};\n\n/** The link's anchor as the shared reference parser reads it, or null when it names none it can read. */\nconst readSharedAnchor = (url: URL) => {\n try {\n return decodeFromAssetSpacesUrl(url).anchor ?? null;\n } catch {\n return null;\n }\n};\n\nconst parseCitationReference = ({\n url,\n assetId,\n}: {\n url: URL;\n assetId: string;\n}): AthenaAssetCitationReference => {\n const reference: AthenaAssetCitationReference = { id: assetId };\n const anchorType = url.searchParams.get('anchor');\n const page = getFiniteNumber(url, 'page');\n\n switch (anchorType) {\n case 'page':\n if (page !== null) reference.anchor = { type: anchorType, page };\n break;\n case 'page_range': {\n const startPage = getFiniteNumber(url, 'start_page');\n const endPage = getFiniteNumber(url, 'end_page');\n if (startPage !== null && endPage !== null) {\n reference.anchor = { type: anchorType, startPage, endPage };\n }\n break;\n }\n case 'page_rect': {\n const x = getFiniteNumber(url, 'x');\n const y = getFiniteNumber(url, 'y');\n const width = getFiniteNumber(url, 'width');\n const height = getFiniteNumber(url, 'height');\n if (page !== null && x !== null && y !== null && width !== null && height !== null) {\n const unit = url.searchParams.get('unit');\n reference.anchor = {\n type: anchorType,\n page,\n x,\n y,\n width,\n height,\n ...(unit === 'percent' || unit === 'point' ? { unit } : {}),\n };\n }\n break;\n }\n case 'page_text': {\n const text = url.searchParams.get('text');\n if (page !== null && text) {\n // Optional context that disambiguates a repeated phrase, and the hash\n // of the cited unit's normalized text (Clio C-23).\n const prefix = getNonEmptyString(url, 'prefix');\n const suffix = getNonEmptyString(url, 'suffix');\n const hash = getNonEmptyString(url, 'hash');\n reference.anchor = {\n type: anchorType,\n page,\n text,\n ...(prefix ? { prefix } : {}),\n ...(suffix ? { suffix } : {}),\n ...(hash ? { hash } : {}),\n };\n }\n break;\n }\n // Sheet anchors: the embed's spreadsheet view navigates + flashes the\n // target range (Olympus `useSheetAnchors`). `sheet_id` is the engine's\n // numeric sheet id, NOT a tab index.\n case 'sheet_cell': {\n const sheetId = getFiniteNumber(url, 'sheet_id');\n const cell = url.searchParams.get('cell');\n if (sheetId !== null && cell) reference.anchor = { type: anchorType, sheetId, cell };\n break;\n }\n case 'sheet_range': {\n const sheetId = getFiniteNumber(url, 'sheet_id');\n const range = url.searchParams.get('range');\n if (sheetId !== null && range) reference.anchor = { type: anchorType, sheetId, range };\n break;\n }\n case 'sheet_tab': {\n const sheetId = getFiniteNumber(url, 'sheet_id');\n if (sheetId !== null) reference.anchor = { type: anchorType, sheetId };\n break;\n }\n case 'sheet_table': {\n const sheetId = getFiniteNumber(url, 'sheet_id');\n const tableName = url.searchParams.get('table_name');\n const fallbackRange = url.searchParams.get('fallback_range');\n if (sheetId !== null && tableName) {\n reference.anchor = {\n type: anchorType,\n sheetId,\n tableName,\n ...(fallbackRange ? { fallbackRange } : {}),\n };\n }\n break;\n }\n }\n\n // Every other place (a slide, a time, a document block, a transcript message, …) goes to the\n // embed as the canonical query string `@athenaintel/references` writes, which the embed reads\n // back with the same parser, so the panel opens at it instead of at the top.\n if (reference.anchor === undefined) {\n const anchor = readSharedAnchor(url);\n if (anchor) reference.anchor = serializeAnchorToParams(anchor).toString();\n }\n\n const excerpt = url.searchParams.get('excerpt');\n if (excerpt) reference.meta = { excerpt };\n return reference;\n};\n\nconst normalizeAssetType = (value: string | null): AssetType | null => {\n if (!value) {\n return null;\n }\n\n const normalizedValue = value.trim().replace(/^athena\\//, '').toLowerCase().replace(/-/g, '_');\n return ASSET_TYPE_ALIASES[normalizedValue] ?? null;\n};\n\nconst getOrigin = (value?: string | null): string | null => {\n if (!value) {\n return null;\n }\n\n try {\n return new URL(value).origin;\n } catch {\n return null;\n }\n};\n\nconst getHostname = (value?: string | null): string | null => {\n if (!value) {\n return null;\n }\n\n try {\n return new URL(value).hostname.toLowerCase();\n } catch {\n return null;\n }\n};\n\nconst matchesHostnameOrSubdomain = ({\n hostname,\n candidateHostname,\n}: {\n hostname: string;\n candidateHostname: string;\n}): boolean => hostname === candidateHostname || hostname.endsWith(`.${candidateHostname}`);\n\nconst getCurrentOrigin = (): string | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n\n return window.location.origin;\n};\n\nconst parseUrl = ({\n href,\n appUrl,\n}: {\n href: string;\n appUrl?: string | null;\n}): URL | null => {\n const fallbackBase = appUrl ?? getCurrentOrigin() ?? 'https://app.athenaintel.com';\n\n try {\n return new URL(href, fallbackBase);\n } catch {\n return null;\n }\n};\n\nconst isAthenaAppUrl = ({\n url,\n appUrl,\n}: {\n url: URL;\n appUrl?: string | null;\n}): boolean => {\n const hostname = url.hostname.toLowerCase();\n\n if (\n ATHENA_APP_HOSTNAMES.has(hostname)\n || hostname.endsWith(ATHENA_PREVIEW_HOSTNAME_SUFFIX)\n || ATHENA_WORKSPACE_HOSTNAME_SUFFIXES.some((suffix) => hostname.endsWith(suffix))\n ) {\n return true;\n }\n\n const configuredHostname = getHostname(appUrl);\n if (configuredHostname && matchesHostnameOrSubdomain({ hostname, candidateHostname: configuredHostname })) {\n return true;\n }\n\n const configuredOrigin = getOrigin(appUrl);\n if (configuredOrigin && url.origin === configuredOrigin) {\n return true;\n }\n\n // Fallback: treat same-origin `/dashboard/spaces?asset_ids=...` links as Athena\n // citations so local dev and private-cloud embeds still work without appUrl.\n // Consumers can disable the default interception with\n // linkClicks={{ interceptAthenaCitations: false }} or the legacy citationLinks alias.\n const currentOrigin = getCurrentOrigin();\n return !!currentOrigin && url.origin === currentOrigin;\n};\n\nexport const parseAthenaCitationLink = ({\n href,\n appUrl,\n}: {\n href: string;\n appUrl?: string | null;\n}): ParsedAthenaCitationLink | null => {\n const url = parseUrl({ href, appUrl });\n if (!url || !isAthenaAppUrl({ url, appUrl }) || !isAthenaSpacesPath(url)) {\n return null;\n }\n\n const assetIdsParam = url.searchParams.get('asset_ids');\n if (!assetIdsParam) {\n return null;\n }\n\n const assetIds = assetIdsParam\n .split(',')\n .map((value) => value.trim())\n .filter((value) => value.length > 0);\n const assetId = assetIds[0];\n\n if (!assetId) {\n return null;\n }\n\n return {\n url,\n assetId,\n assetIds,\n assetType: normalizeAssetType(url.searchParams.get('asset_type') ?? url.searchParams.get('type')),\n reference: parseCitationReference({ url, assetId }),\n embeddable: isEmbeddableCitation(url),\n };\n};\n\nexport const isAthenaCitationUrl = ({\n href,\n appUrl,\n}: {\n href: string;\n appUrl?: string | null;\n}): boolean => parseAthenaCitationLink({ href, appUrl }) !== null;\n\ninterface ResolvedAthenaLink {\n url: URL;\n kind: AthenaLinkKind;\n citation: ParsedAthenaCitationLink | null;\n}\n\nconst resolveAthenaLink = ({\n href,\n appUrl,\n}: AthenaLinkClickHandleInput & {\n appUrl?: string | null;\n}): ResolvedAthenaLink | null => {\n const url = parseUrl({ href, appUrl });\n if (!url) {\n return null;\n }\n\n const citation = parseAthenaCitationLink({ href, appUrl });\n if (citation) {\n return {\n url: citation.url,\n kind: 'athena-citation',\n citation,\n };\n }\n\n return {\n url,\n kind: isAthenaAppUrl({ url, appUrl }) ? 'athena-app' : 'external',\n citation: null,\n };\n};\n\nexport const useAthenaLinkClickHandler = (): AthenaLinkClickHandler => {\n const openAsset = useAssetPanelStore((state) => state.openAsset);\n // A host that opens assets its own way (`AthenaProvider onOpenAsset`) counts as a panel: the\n // citation goes to it instead of to the browser.\n const isAssetPanelAvailable = useAssetPanelStore(\n (state) => state.assetPanelHostCount > 0 || state.assetOpeners.length > 0,\n );\n const athenaConfig = useContext(AthenaContext);\n const appUrl = athenaConfig?.appUrl;\n\n return useCallback<AthenaLinkClickHandler>(\n ({ href, linkText, target, nativeEvent }: AthenaLinkClickHandleInput) => {\n const resolvedLink = resolveAthenaLink({ href, appUrl });\n if (!resolvedLink) {\n return false;\n }\n\n const linkClicks = athenaConfig?.linkClicks;\n const citationLinks = athenaConfig?.citationLinks;\n const trimmedLinkText = linkText?.trim() || null;\n const citationLink = resolvedLink.citation;\n const citationMetadata: AthenaCitationLinkMetadata | null = citationLink\n ? {\n assetId: citationLink.assetId,\n assetIds: citationLink.assetIds,\n assetType: citationLink.assetType,\n }\n : null;\n // A citation the panel cannot show (a catalog's file lines) is left to the link itself.\n const openInAssetPanel = citationLink?.embeddable\n ? () =>\n openAsset(citationLink.assetId, {\n name: trimmedLinkText ?? undefined,\n type: citationLink.assetType ?? undefined,\n // Carry the citation target so the panel's iframe navigates to\n // the cited range/page instead of opening at the top.\n reference: citationLink.reference,\n // Reading references is one preview surface: a citation to a\n // different asset swaps the active tab rather than stacking a\n // tab per cited document.\n replaceActiveTab: true,\n // A host's own opener that opens it on the web keeps the link as written.\n href: resolvedLink.url.toString(),\n })\n : undefined;\n const context: AthenaLinkClickContext = {\n href,\n url: resolvedLink.url,\n target: target ?? null,\n linkText: trimmedLinkText,\n isAssetPanelAvailable,\n kind: resolvedLink.kind,\n citation: citationMetadata,\n nativeEvent: nativeEvent ?? null,\n openInAssetPanel,\n openInBrowser: (nextTarget) => {\n window.open(resolvedLink.url.toString(), nextTarget ?? target ?? '_blank')?.focus();\n },\n };\n\n if (linkClicks?.onClick?.(context) === true) {\n nativeEvent?.preventDefault();\n return true;\n }\n\n if (!citationLink || !openInAssetPanel || !citationMetadata) {\n return false;\n }\n\n const citationContext: AthenaCitationLinkClickContext = {\n ...context,\n kind: 'athena-citation',\n citation: citationMetadata,\n assetId: citationMetadata.assetId,\n assetIds: citationMetadata.assetIds,\n assetType: citationMetadata.assetType,\n openInAssetPanel,\n };\n\n if (citationLinks?.onClick?.(citationContext) === true) {\n nativeEvent?.preventDefault();\n return true;\n }\n\n if ((linkClicks?.interceptAthenaCitations ?? citationLinks?.interceptAthenaCitations) === false) {\n return false;\n }\n\n if (!isAssetPanelAvailable) {\n return false;\n }\n\n // Nothing showed it (the host's opener declined and no panel is mounted): the link opens as\n // a link.\n if (!citationContext.openInAssetPanel()) {\n return false;\n }\n nativeEvent?.preventDefault();\n return true;\n },\n [appUrl, athenaConfig, isAssetPanelAvailable, openAsset],\n );\n};\n\nexport const useAthenaCitationLinkHandler = (): AthenaCitationLinkHandler => {\n const athenaConfig = useContext(AthenaContext);\n const appUrl = athenaConfig?.appUrl;\n const handleLinkClick = useAthenaLinkClickHandler();\n\n return useCallback<AthenaCitationLinkHandler>(\n (input: AthenaCitationLinkHandleInput) => {\n if (!parseAthenaCitationLink({ href: input.href, appUrl })) {\n return false;\n }\n\n return handleLinkClick(input);\n },\n [appUrl, handleLinkClick],\n );\n};\n"],"names":["decodeFromAssetSpacesUrl","serializeAnchorToParams","useAssetPanelStore","useContext","AthenaContext","useCallback","_a"],"mappings":"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"}
@@ -8,6 +8,11 @@ export interface ParsedAthenaCitationLink {
8
8
  assetType: AssetType | null;
9
9
  /** Complete target that can be passed directly to AthenaAssetEmbed.reference. */
10
10
  reference: AthenaAssetCitationReference;
11
+ /**
12
+ * Whether the asset panel can show the cited asset. A citation it cannot (a repository file's
13
+ * lines, any catalog) opens as the link it is, in Athena on the web.
14
+ */
15
+ embeddable: boolean;
11
16
  }
12
17
  export declare const parseAthenaCitationLink: ({ href, appUrl, }: {
13
18
  href: string;
@@ -1,3 +1,5 @@
1
+ import { serializeAnchorToParams } from "../references/src/anchors/serialize.js";
2
+ import { decodeFromAssetSpacesUrl } from "../references/src/formats/uri/asset-spaces-url.js";
1
3
  import { useContext, useCallback } from "react";
2
4
  import { useAssetPanelStore } from "../assets/asset-panel-store.js";
3
5
  import { AthenaContext } from "../provider/AthenaContext.js";
@@ -15,6 +17,13 @@ const ASSET_TYPE_ALIASES = {
15
17
  word_document: "document"
16
18
  };
17
19
  const isAthenaSpacesPath = (url) => url.pathname.replace(/\/+$/, "") === "/dashboard/spaces";
20
+ const NON_EMBEDDABLE_ASSET_TYPES = /* @__PURE__ */ new Set(["catalog"]);
21
+ const NON_EMBEDDABLE_ANCHOR_TYPES = /* @__PURE__ */ new Set(["file_line_range"]);
22
+ const isEmbeddableCitation = (url) => {
23
+ const rawType = (url.searchParams.get("asset_type") ?? url.searchParams.get("type") ?? "").trim().replace(/^athena\//, "").toLowerCase();
24
+ const anchorType = url.searchParams.get("anchor") ?? "";
25
+ return !NON_EMBEDDABLE_ASSET_TYPES.has(rawType) && !NON_EMBEDDABLE_ANCHOR_TYPES.has(anchorType);
26
+ };
18
27
  const getFiniteNumber = (url, key) => {
19
28
  const value = url.searchParams.get(key);
20
29
  if (value === null || value.trim() === "") return null;
@@ -25,6 +34,13 @@ const getNonEmptyString = (url, key) => {
25
34
  const value = url.searchParams.get(key);
26
35
  return value === null || value === "" ? null : value;
27
36
  };
37
+ const readSharedAnchor = (url) => {
38
+ try {
39
+ return decodeFromAssetSpacesUrl(url).anchor ?? null;
40
+ } catch {
41
+ return null;
42
+ }
43
+ };
28
44
  const parseCitationReference = ({
29
45
  url,
30
46
  assetId
@@ -115,6 +131,10 @@ const parseCitationReference = ({
115
131
  break;
116
132
  }
117
133
  }
134
+ if (reference.anchor === void 0) {
135
+ const anchor = readSharedAnchor(url);
136
+ if (anchor) reference.anchor = serializeAnchorToParams(anchor).toString();
137
+ }
118
138
  const excerpt = url.searchParams.get("excerpt");
119
139
  if (excerpt) reference.meta = { excerpt };
120
140
  return reference;
@@ -208,7 +228,8 @@ const parseAthenaCitationLink = ({
208
228
  assetId,
209
229
  assetIds,
210
230
  assetType: normalizeAssetType(url.searchParams.get("asset_type") ?? url.searchParams.get("type")),
211
- reference: parseCitationReference({ url, assetId })
231
+ reference: parseCitationReference({ url, assetId }),
232
+ embeddable: isEmbeddableCitation(url)
212
233
  };
213
234
  };
214
235
  const isAthenaCitationUrl = ({
@@ -260,7 +281,7 @@ const useAthenaLinkClickHandler = () => {
260
281
  assetIds: citationLink.assetIds,
261
282
  assetType: citationLink.assetType
262
283
  } : null;
263
- const openInAssetPanel = citationLink ? () => openAsset(citationLink.assetId, {
284
+ const openInAssetPanel = (citationLink == null ? void 0 : citationLink.embeddable) ? () => openAsset(citationLink.assetId, {
264
285
  name: trimmedLinkText ?? void 0,
265
286
  type: citationLink.assetType ?? void 0,
266
287
  // Carry the citation target so the panel's iframe navigates to
@@ -1 +1 @@
1
- {"version":3,"file":"athena-citation-links.js","sources":["../../src/chat/athena-citation-links.ts"],"sourcesContent":["import { useCallback, useContext } from 'react';\nimport type { AthenaAssetCitationReference } from '../assets/asset-embed-messages';\nimport { type AssetType, useAssetPanelStore } from '../assets/asset-panel-store';\nimport { AthenaContext } from '../provider/AthenaContext';\nimport type {\n AthenaCitationLinkMetadata,\n AthenaCitationLinkClickContext,\n AthenaCitationLinkHandleInput,\n AthenaCitationLinkHandler,\n AthenaLinkClickContext,\n AthenaLinkClickHandler,\n AthenaLinkClickHandleInput,\n AthenaLinkKind,\n} from '../provider/citation-links';\n\nconst ATHENA_APP_HOSTNAMES = new Set(['app.athenaintel.com', 'staging-app.athenaintel.com']);\nconst ATHENA_PREVIEW_HOSTNAME_SUFFIX = '.previews.athenaintel.com';\nconst ATHENA_WORKSPACE_HOSTNAME_SUFFIXES = ['.app.athenaintel.com', '.staging-app.athenaintel.com'];\n\nconst ASSET_TYPE_ALIASES: Record<string, AssetType> = {\n document: 'document',\n notebook: 'notebook',\n pdf: 'document',\n powerpoint: 'presentation',\n presentation: 'presentation',\n sheet: 'spreadsheet',\n spreadsheet: 'spreadsheet',\n word_document: 'document',\n};\n\nconst isAthenaSpacesPath = (url: URL): boolean => url.pathname.replace(/\\/+$/, '') === '/dashboard/spaces';\n\nexport interface ParsedAthenaCitationLink {\n url: URL;\n assetId: string;\n assetIds: string[];\n assetType: AssetType | null;\n /** Complete target that can be passed directly to AthenaAssetEmbed.reference. */\n reference: AthenaAssetCitationReference;\n}\n\nconst getFiniteNumber = (url: URL, key: string): number | null => {\n const value = url.searchParams.get(key);\n if (value === null || value.trim() === '') return null;\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : null;\n};\n\nconst getNonEmptyString = (url: URL, key: string): string | null => {\n const value = url.searchParams.get(key);\n return value === null || value === '' ? null : value;\n};\n\nconst parseCitationReference = ({\n url,\n assetId,\n}: {\n url: URL;\n assetId: string;\n}): AthenaAssetCitationReference => {\n const reference: AthenaAssetCitationReference = { id: assetId };\n const anchorType = url.searchParams.get('anchor');\n const page = getFiniteNumber(url, 'page');\n\n switch (anchorType) {\n case 'page':\n if (page !== null) reference.anchor = { type: anchorType, page };\n break;\n case 'page_range': {\n const startPage = getFiniteNumber(url, 'start_page');\n const endPage = getFiniteNumber(url, 'end_page');\n if (startPage !== null && endPage !== null) {\n reference.anchor = { type: anchorType, startPage, endPage };\n }\n break;\n }\n case 'page_rect': {\n const x = getFiniteNumber(url, 'x');\n const y = getFiniteNumber(url, 'y');\n const width = getFiniteNumber(url, 'width');\n const height = getFiniteNumber(url, 'height');\n if (page !== null && x !== null && y !== null && width !== null && height !== null) {\n const unit = url.searchParams.get('unit');\n reference.anchor = {\n type: anchorType,\n page,\n x,\n y,\n width,\n height,\n ...(unit === 'percent' || unit === 'point' ? { unit } : {}),\n };\n }\n break;\n }\n case 'page_text': {\n const text = url.searchParams.get('text');\n if (page !== null && text) {\n // Optional context that disambiguates a repeated phrase, and the hash\n // of the cited unit's normalized text (Clio C-23).\n const prefix = getNonEmptyString(url, 'prefix');\n const suffix = getNonEmptyString(url, 'suffix');\n const hash = getNonEmptyString(url, 'hash');\n reference.anchor = {\n type: anchorType,\n page,\n text,\n ...(prefix ? { prefix } : {}),\n ...(suffix ? { suffix } : {}),\n ...(hash ? { hash } : {}),\n };\n }\n break;\n }\n // Sheet anchors: the embed's spreadsheet view navigates + flashes the\n // target range (Olympus `useSheetAnchors`). `sheet_id` is the engine's\n // numeric sheet id, NOT a tab index.\n case 'sheet_cell': {\n const sheetId = getFiniteNumber(url, 'sheet_id');\n const cell = url.searchParams.get('cell');\n if (sheetId !== null && cell) reference.anchor = { type: anchorType, sheetId, cell };\n break;\n }\n case 'sheet_range': {\n const sheetId = getFiniteNumber(url, 'sheet_id');\n const range = url.searchParams.get('range');\n if (sheetId !== null && range) reference.anchor = { type: anchorType, sheetId, range };\n break;\n }\n case 'sheet_tab': {\n const sheetId = getFiniteNumber(url, 'sheet_id');\n if (sheetId !== null) reference.anchor = { type: anchorType, sheetId };\n break;\n }\n case 'sheet_table': {\n const sheetId = getFiniteNumber(url, 'sheet_id');\n const tableName = url.searchParams.get('table_name');\n const fallbackRange = url.searchParams.get('fallback_range');\n if (sheetId !== null && tableName) {\n reference.anchor = {\n type: anchorType,\n sheetId,\n tableName,\n ...(fallbackRange ? { fallbackRange } : {}),\n };\n }\n break;\n }\n }\n\n const excerpt = url.searchParams.get('excerpt');\n if (excerpt) reference.meta = { excerpt };\n return reference;\n};\n\nconst normalizeAssetType = (value: string | null): AssetType | null => {\n if (!value) {\n return null;\n }\n\n const normalizedValue = value.trim().replace(/^athena\\//, '').toLowerCase().replace(/-/g, '_');\n return ASSET_TYPE_ALIASES[normalizedValue] ?? null;\n};\n\nconst getOrigin = (value?: string | null): string | null => {\n if (!value) {\n return null;\n }\n\n try {\n return new URL(value).origin;\n } catch {\n return null;\n }\n};\n\nconst getHostname = (value?: string | null): string | null => {\n if (!value) {\n return null;\n }\n\n try {\n return new URL(value).hostname.toLowerCase();\n } catch {\n return null;\n }\n};\n\nconst matchesHostnameOrSubdomain = ({\n hostname,\n candidateHostname,\n}: {\n hostname: string;\n candidateHostname: string;\n}): boolean => hostname === candidateHostname || hostname.endsWith(`.${candidateHostname}`);\n\nconst getCurrentOrigin = (): string | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n\n return window.location.origin;\n};\n\nconst parseUrl = ({\n href,\n appUrl,\n}: {\n href: string;\n appUrl?: string | null;\n}): URL | null => {\n const fallbackBase = appUrl ?? getCurrentOrigin() ?? 'https://app.athenaintel.com';\n\n try {\n return new URL(href, fallbackBase);\n } catch {\n return null;\n }\n};\n\nconst isAthenaAppUrl = ({\n url,\n appUrl,\n}: {\n url: URL;\n appUrl?: string | null;\n}): boolean => {\n const hostname = url.hostname.toLowerCase();\n\n if (\n ATHENA_APP_HOSTNAMES.has(hostname)\n || hostname.endsWith(ATHENA_PREVIEW_HOSTNAME_SUFFIX)\n || ATHENA_WORKSPACE_HOSTNAME_SUFFIXES.some((suffix) => hostname.endsWith(suffix))\n ) {\n return true;\n }\n\n const configuredHostname = getHostname(appUrl);\n if (configuredHostname && matchesHostnameOrSubdomain({ hostname, candidateHostname: configuredHostname })) {\n return true;\n }\n\n const configuredOrigin = getOrigin(appUrl);\n if (configuredOrigin && url.origin === configuredOrigin) {\n return true;\n }\n\n // Fallback: treat same-origin `/dashboard/spaces?asset_ids=...` links as Athena\n // citations so local dev and private-cloud embeds still work without appUrl.\n // Consumers can disable the default interception with\n // linkClicks={{ interceptAthenaCitations: false }} or the legacy citationLinks alias.\n const currentOrigin = getCurrentOrigin();\n return !!currentOrigin && url.origin === currentOrigin;\n};\n\nexport const parseAthenaCitationLink = ({\n href,\n appUrl,\n}: {\n href: string;\n appUrl?: string | null;\n}): ParsedAthenaCitationLink | null => {\n const url = parseUrl({ href, appUrl });\n if (!url || !isAthenaAppUrl({ url, appUrl }) || !isAthenaSpacesPath(url)) {\n return null;\n }\n\n const assetIdsParam = url.searchParams.get('asset_ids');\n if (!assetIdsParam) {\n return null;\n }\n\n const assetIds = assetIdsParam\n .split(',')\n .map((value) => value.trim())\n .filter((value) => value.length > 0);\n const assetId = assetIds[0];\n\n if (!assetId) {\n return null;\n }\n\n return {\n url,\n assetId,\n assetIds,\n assetType: normalizeAssetType(url.searchParams.get('asset_type') ?? url.searchParams.get('type')),\n reference: parseCitationReference({ url, assetId }),\n };\n};\n\nexport const isAthenaCitationUrl = ({\n href,\n appUrl,\n}: {\n href: string;\n appUrl?: string | null;\n}): boolean => parseAthenaCitationLink({ href, appUrl }) !== null;\n\ninterface ResolvedAthenaLink {\n url: URL;\n kind: AthenaLinkKind;\n citation: ParsedAthenaCitationLink | null;\n}\n\nconst resolveAthenaLink = ({\n href,\n appUrl,\n}: AthenaLinkClickHandleInput & {\n appUrl?: string | null;\n}): ResolvedAthenaLink | null => {\n const url = parseUrl({ href, appUrl });\n if (!url) {\n return null;\n }\n\n const citation = parseAthenaCitationLink({ href, appUrl });\n if (citation) {\n return {\n url: citation.url,\n kind: 'athena-citation',\n citation,\n };\n }\n\n return {\n url,\n kind: isAthenaAppUrl({ url, appUrl }) ? 'athena-app' : 'external',\n citation: null,\n };\n};\n\nexport const useAthenaLinkClickHandler = (): AthenaLinkClickHandler => {\n const openAsset = useAssetPanelStore((state) => state.openAsset);\n // A host that opens assets its own way (`AthenaProvider onOpenAsset`) counts as a panel: the\n // citation goes to it instead of to the browser.\n const isAssetPanelAvailable = useAssetPanelStore(\n (state) => state.assetPanelHostCount > 0 || state.assetOpeners.length > 0,\n );\n const athenaConfig = useContext(AthenaContext);\n const appUrl = athenaConfig?.appUrl;\n\n return useCallback<AthenaLinkClickHandler>(\n ({ href, linkText, target, nativeEvent }: AthenaLinkClickHandleInput) => {\n const resolvedLink = resolveAthenaLink({ href, appUrl });\n if (!resolvedLink) {\n return false;\n }\n\n const linkClicks = athenaConfig?.linkClicks;\n const citationLinks = athenaConfig?.citationLinks;\n const trimmedLinkText = linkText?.trim() || null;\n const citationLink = resolvedLink.citation;\n const citationMetadata: AthenaCitationLinkMetadata | null = citationLink\n ? {\n assetId: citationLink.assetId,\n assetIds: citationLink.assetIds,\n assetType: citationLink.assetType,\n }\n : null;\n const openInAssetPanel = citationLink\n ? () =>\n openAsset(citationLink.assetId, {\n name: trimmedLinkText ?? undefined,\n type: citationLink.assetType ?? undefined,\n // Carry the citation target so the panel's iframe navigates to\n // the cited range/page instead of opening at the top.\n reference: citationLink.reference,\n // Reading references is one preview surface: a citation to a\n // different asset swaps the active tab rather than stacking a\n // tab per cited document.\n replaceActiveTab: true,\n // A host's own opener that opens it on the web keeps the link as written.\n href: resolvedLink.url.toString(),\n })\n : undefined;\n const context: AthenaLinkClickContext = {\n href,\n url: resolvedLink.url,\n target: target ?? null,\n linkText: trimmedLinkText,\n isAssetPanelAvailable,\n kind: resolvedLink.kind,\n citation: citationMetadata,\n nativeEvent: nativeEvent ?? null,\n openInAssetPanel,\n openInBrowser: (nextTarget) => {\n window.open(resolvedLink.url.toString(), nextTarget ?? target ?? '_blank')?.focus();\n },\n };\n\n if (linkClicks?.onClick?.(context) === true) {\n nativeEvent?.preventDefault();\n return true;\n }\n\n if (!citationLink || !openInAssetPanel || !citationMetadata) {\n return false;\n }\n\n const citationContext: AthenaCitationLinkClickContext = {\n ...context,\n kind: 'athena-citation',\n citation: citationMetadata,\n assetId: citationMetadata.assetId,\n assetIds: citationMetadata.assetIds,\n assetType: citationMetadata.assetType,\n openInAssetPanel,\n };\n\n if (citationLinks?.onClick?.(citationContext) === true) {\n nativeEvent?.preventDefault();\n return true;\n }\n\n if ((linkClicks?.interceptAthenaCitations ?? citationLinks?.interceptAthenaCitations) === false) {\n return false;\n }\n\n if (!isAssetPanelAvailable) {\n return false;\n }\n\n // Nothing showed it (the host's opener declined and no panel is mounted): the link opens as\n // a link.\n if (!citationContext.openInAssetPanel()) {\n return false;\n }\n nativeEvent?.preventDefault();\n return true;\n },\n [appUrl, athenaConfig, isAssetPanelAvailable, openAsset],\n );\n};\n\nexport const useAthenaCitationLinkHandler = (): AthenaCitationLinkHandler => {\n const athenaConfig = useContext(AthenaContext);\n const appUrl = athenaConfig?.appUrl;\n const handleLinkClick = useAthenaLinkClickHandler();\n\n return useCallback<AthenaCitationLinkHandler>(\n (input: AthenaCitationLinkHandleInput) => {\n if (!parseAthenaCitationLink({ href: input.href, appUrl })) {\n return false;\n }\n\n return handleLinkClick(input);\n },\n [appUrl, handleLinkClick],\n );\n};\n"],"names":["_a"],"mappings":"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"}
1
+ {"version":3,"file":"athena-citation-links.js","sources":["../../src/chat/athena-citation-links.ts"],"sourcesContent":["import { decodeFromAssetSpacesUrl, serializeAnchorToParams } from '@athenaintel/references/asset-links';\nimport { useCallback, useContext } from 'react';\nimport type { AthenaAssetCitationReference } from '../assets/asset-embed-messages';\nimport { type AssetType, useAssetPanelStore } from '../assets/asset-panel-store';\nimport { AthenaContext } from '../provider/AthenaContext';\nimport type {\n AthenaCitationLinkMetadata,\n AthenaCitationLinkClickContext,\n AthenaCitationLinkHandleInput,\n AthenaCitationLinkHandler,\n AthenaLinkClickContext,\n AthenaLinkClickHandler,\n AthenaLinkClickHandleInput,\n AthenaLinkKind,\n} from '../provider/citation-links';\n\nconst ATHENA_APP_HOSTNAMES = new Set(['app.athenaintel.com', 'staging-app.athenaintel.com']);\nconst ATHENA_PREVIEW_HOSTNAME_SUFFIX = '.previews.athenaintel.com';\nconst ATHENA_WORKSPACE_HOSTNAME_SUFFIXES = ['.app.athenaintel.com', '.staging-app.athenaintel.com'];\n\nconst ASSET_TYPE_ALIASES: Record<string, AssetType> = {\n document: 'document',\n notebook: 'notebook',\n pdf: 'document',\n powerpoint: 'presentation',\n presentation: 'presentation',\n sheet: 'spreadsheet',\n spreadsheet: 'spreadsheet',\n word_document: 'document',\n};\n\nconst isAthenaSpacesPath = (url: URL): boolean => url.pathname.replace(/\\/+$/, '') === '/dashboard/spaces';\n\n/**\n * Asset types no embed viewer renders: agora refuses their embed token (422,\n * `agora/agora/web/api/embed/embed_viewer_asset_types.json`), so the asset\n * panel could only show an error for them.\n */\nconst NON_EMBEDDABLE_ASSET_TYPES: ReadonlySet<string> = new Set(['catalog']);\n\n/**\n * Anchors only a non-embeddable asset holds: a repository file's lines live in\n * a GitHub catalog (`ASSET_ANCHOR_REGISTRY`), which Athena on the web opens at\n * the file with the lines highlighted. Read from the anchor too, so a citation\n * whose `type=` names the wrong type still opens where it can be shown.\n */\nconst NON_EMBEDDABLE_ANCHOR_TYPES: ReadonlySet<string> = new Set(['file_line_range']);\n\n/** Whether the asset panel can show the cited asset, or only Athena on the web can. */\nconst isEmbeddableCitation = (url: URL): boolean => {\n const rawType = (url.searchParams.get('asset_type') ?? url.searchParams.get('type') ?? '')\n .trim()\n .replace(/^athena\\//, '')\n .toLowerCase();\n const anchorType = url.searchParams.get('anchor') ?? '';\n return !NON_EMBEDDABLE_ASSET_TYPES.has(rawType) && !NON_EMBEDDABLE_ANCHOR_TYPES.has(anchorType);\n};\n\nexport interface ParsedAthenaCitationLink {\n url: URL;\n assetId: string;\n assetIds: string[];\n assetType: AssetType | null;\n /** Complete target that can be passed directly to AthenaAssetEmbed.reference. */\n reference: AthenaAssetCitationReference;\n /**\n * Whether the asset panel can show the cited asset. A citation it cannot (a repository file's\n * lines, any catalog) opens as the link it is, in Athena on the web.\n */\n embeddable: boolean;\n}\n\nconst getFiniteNumber = (url: URL, key: string): number | null => {\n const value = url.searchParams.get(key);\n if (value === null || value.trim() === '') return null;\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : null;\n};\n\nconst getNonEmptyString = (url: URL, key: string): string | null => {\n const value = url.searchParams.get(key);\n return value === null || value === '' ? null : value;\n};\n\n/** The link's anchor as the shared reference parser reads it, or null when it names none it can read. */\nconst readSharedAnchor = (url: URL) => {\n try {\n return decodeFromAssetSpacesUrl(url).anchor ?? null;\n } catch {\n return null;\n }\n};\n\nconst parseCitationReference = ({\n url,\n assetId,\n}: {\n url: URL;\n assetId: string;\n}): AthenaAssetCitationReference => {\n const reference: AthenaAssetCitationReference = { id: assetId };\n const anchorType = url.searchParams.get('anchor');\n const page = getFiniteNumber(url, 'page');\n\n switch (anchorType) {\n case 'page':\n if (page !== null) reference.anchor = { type: anchorType, page };\n break;\n case 'page_range': {\n const startPage = getFiniteNumber(url, 'start_page');\n const endPage = getFiniteNumber(url, 'end_page');\n if (startPage !== null && endPage !== null) {\n reference.anchor = { type: anchorType, startPage, endPage };\n }\n break;\n }\n case 'page_rect': {\n const x = getFiniteNumber(url, 'x');\n const y = getFiniteNumber(url, 'y');\n const width = getFiniteNumber(url, 'width');\n const height = getFiniteNumber(url, 'height');\n if (page !== null && x !== null && y !== null && width !== null && height !== null) {\n const unit = url.searchParams.get('unit');\n reference.anchor = {\n type: anchorType,\n page,\n x,\n y,\n width,\n height,\n ...(unit === 'percent' || unit === 'point' ? { unit } : {}),\n };\n }\n break;\n }\n case 'page_text': {\n const text = url.searchParams.get('text');\n if (page !== null && text) {\n // Optional context that disambiguates a repeated phrase, and the hash\n // of the cited unit's normalized text (Clio C-23).\n const prefix = getNonEmptyString(url, 'prefix');\n const suffix = getNonEmptyString(url, 'suffix');\n const hash = getNonEmptyString(url, 'hash');\n reference.anchor = {\n type: anchorType,\n page,\n text,\n ...(prefix ? { prefix } : {}),\n ...(suffix ? { suffix } : {}),\n ...(hash ? { hash } : {}),\n };\n }\n break;\n }\n // Sheet anchors: the embed's spreadsheet view navigates + flashes the\n // target range (Olympus `useSheetAnchors`). `sheet_id` is the engine's\n // numeric sheet id, NOT a tab index.\n case 'sheet_cell': {\n const sheetId = getFiniteNumber(url, 'sheet_id');\n const cell = url.searchParams.get('cell');\n if (sheetId !== null && cell) reference.anchor = { type: anchorType, sheetId, cell };\n break;\n }\n case 'sheet_range': {\n const sheetId = getFiniteNumber(url, 'sheet_id');\n const range = url.searchParams.get('range');\n if (sheetId !== null && range) reference.anchor = { type: anchorType, sheetId, range };\n break;\n }\n case 'sheet_tab': {\n const sheetId = getFiniteNumber(url, 'sheet_id');\n if (sheetId !== null) reference.anchor = { type: anchorType, sheetId };\n break;\n }\n case 'sheet_table': {\n const sheetId = getFiniteNumber(url, 'sheet_id');\n const tableName = url.searchParams.get('table_name');\n const fallbackRange = url.searchParams.get('fallback_range');\n if (sheetId !== null && tableName) {\n reference.anchor = {\n type: anchorType,\n sheetId,\n tableName,\n ...(fallbackRange ? { fallbackRange } : {}),\n };\n }\n break;\n }\n }\n\n // Every other place (a slide, a time, a document block, a transcript message, …) goes to the\n // embed as the canonical query string `@athenaintel/references` writes, which the embed reads\n // back with the same parser, so the panel opens at it instead of at the top.\n if (reference.anchor === undefined) {\n const anchor = readSharedAnchor(url);\n if (anchor) reference.anchor = serializeAnchorToParams(anchor).toString();\n }\n\n const excerpt = url.searchParams.get('excerpt');\n if (excerpt) reference.meta = { excerpt };\n return reference;\n};\n\nconst normalizeAssetType = (value: string | null): AssetType | null => {\n if (!value) {\n return null;\n }\n\n const normalizedValue = value.trim().replace(/^athena\\//, '').toLowerCase().replace(/-/g, '_');\n return ASSET_TYPE_ALIASES[normalizedValue] ?? null;\n};\n\nconst getOrigin = (value?: string | null): string | null => {\n if (!value) {\n return null;\n }\n\n try {\n return new URL(value).origin;\n } catch {\n return null;\n }\n};\n\nconst getHostname = (value?: string | null): string | null => {\n if (!value) {\n return null;\n }\n\n try {\n return new URL(value).hostname.toLowerCase();\n } catch {\n return null;\n }\n};\n\nconst matchesHostnameOrSubdomain = ({\n hostname,\n candidateHostname,\n}: {\n hostname: string;\n candidateHostname: string;\n}): boolean => hostname === candidateHostname || hostname.endsWith(`.${candidateHostname}`);\n\nconst getCurrentOrigin = (): string | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n\n return window.location.origin;\n};\n\nconst parseUrl = ({\n href,\n appUrl,\n}: {\n href: string;\n appUrl?: string | null;\n}): URL | null => {\n const fallbackBase = appUrl ?? getCurrentOrigin() ?? 'https://app.athenaintel.com';\n\n try {\n return new URL(href, fallbackBase);\n } catch {\n return null;\n }\n};\n\nconst isAthenaAppUrl = ({\n url,\n appUrl,\n}: {\n url: URL;\n appUrl?: string | null;\n}): boolean => {\n const hostname = url.hostname.toLowerCase();\n\n if (\n ATHENA_APP_HOSTNAMES.has(hostname)\n || hostname.endsWith(ATHENA_PREVIEW_HOSTNAME_SUFFIX)\n || ATHENA_WORKSPACE_HOSTNAME_SUFFIXES.some((suffix) => hostname.endsWith(suffix))\n ) {\n return true;\n }\n\n const configuredHostname = getHostname(appUrl);\n if (configuredHostname && matchesHostnameOrSubdomain({ hostname, candidateHostname: configuredHostname })) {\n return true;\n }\n\n const configuredOrigin = getOrigin(appUrl);\n if (configuredOrigin && url.origin === configuredOrigin) {\n return true;\n }\n\n // Fallback: treat same-origin `/dashboard/spaces?asset_ids=...` links as Athena\n // citations so local dev and private-cloud embeds still work without appUrl.\n // Consumers can disable the default interception with\n // linkClicks={{ interceptAthenaCitations: false }} or the legacy citationLinks alias.\n const currentOrigin = getCurrentOrigin();\n return !!currentOrigin && url.origin === currentOrigin;\n};\n\nexport const parseAthenaCitationLink = ({\n href,\n appUrl,\n}: {\n href: string;\n appUrl?: string | null;\n}): ParsedAthenaCitationLink | null => {\n const url = parseUrl({ href, appUrl });\n if (!url || !isAthenaAppUrl({ url, appUrl }) || !isAthenaSpacesPath(url)) {\n return null;\n }\n\n const assetIdsParam = url.searchParams.get('asset_ids');\n if (!assetIdsParam) {\n return null;\n }\n\n const assetIds = assetIdsParam\n .split(',')\n .map((value) => value.trim())\n .filter((value) => value.length > 0);\n const assetId = assetIds[0];\n\n if (!assetId) {\n return null;\n }\n\n return {\n url,\n assetId,\n assetIds,\n assetType: normalizeAssetType(url.searchParams.get('asset_type') ?? url.searchParams.get('type')),\n reference: parseCitationReference({ url, assetId }),\n embeddable: isEmbeddableCitation(url),\n };\n};\n\nexport const isAthenaCitationUrl = ({\n href,\n appUrl,\n}: {\n href: string;\n appUrl?: string | null;\n}): boolean => parseAthenaCitationLink({ href, appUrl }) !== null;\n\ninterface ResolvedAthenaLink {\n url: URL;\n kind: AthenaLinkKind;\n citation: ParsedAthenaCitationLink | null;\n}\n\nconst resolveAthenaLink = ({\n href,\n appUrl,\n}: AthenaLinkClickHandleInput & {\n appUrl?: string | null;\n}): ResolvedAthenaLink | null => {\n const url = parseUrl({ href, appUrl });\n if (!url) {\n return null;\n }\n\n const citation = parseAthenaCitationLink({ href, appUrl });\n if (citation) {\n return {\n url: citation.url,\n kind: 'athena-citation',\n citation,\n };\n }\n\n return {\n url,\n kind: isAthenaAppUrl({ url, appUrl }) ? 'athena-app' : 'external',\n citation: null,\n };\n};\n\nexport const useAthenaLinkClickHandler = (): AthenaLinkClickHandler => {\n const openAsset = useAssetPanelStore((state) => state.openAsset);\n // A host that opens assets its own way (`AthenaProvider onOpenAsset`) counts as a panel: the\n // citation goes to it instead of to the browser.\n const isAssetPanelAvailable = useAssetPanelStore(\n (state) => state.assetPanelHostCount > 0 || state.assetOpeners.length > 0,\n );\n const athenaConfig = useContext(AthenaContext);\n const appUrl = athenaConfig?.appUrl;\n\n return useCallback<AthenaLinkClickHandler>(\n ({ href, linkText, target, nativeEvent }: AthenaLinkClickHandleInput) => {\n const resolvedLink = resolveAthenaLink({ href, appUrl });\n if (!resolvedLink) {\n return false;\n }\n\n const linkClicks = athenaConfig?.linkClicks;\n const citationLinks = athenaConfig?.citationLinks;\n const trimmedLinkText = linkText?.trim() || null;\n const citationLink = resolvedLink.citation;\n const citationMetadata: AthenaCitationLinkMetadata | null = citationLink\n ? {\n assetId: citationLink.assetId,\n assetIds: citationLink.assetIds,\n assetType: citationLink.assetType,\n }\n : null;\n // A citation the panel cannot show (a catalog's file lines) is left to the link itself.\n const openInAssetPanel = citationLink?.embeddable\n ? () =>\n openAsset(citationLink.assetId, {\n name: trimmedLinkText ?? undefined,\n type: citationLink.assetType ?? undefined,\n // Carry the citation target so the panel's iframe navigates to\n // the cited range/page instead of opening at the top.\n reference: citationLink.reference,\n // Reading references is one preview surface: a citation to a\n // different asset swaps the active tab rather than stacking a\n // tab per cited document.\n replaceActiveTab: true,\n // A host's own opener that opens it on the web keeps the link as written.\n href: resolvedLink.url.toString(),\n })\n : undefined;\n const context: AthenaLinkClickContext = {\n href,\n url: resolvedLink.url,\n target: target ?? null,\n linkText: trimmedLinkText,\n isAssetPanelAvailable,\n kind: resolvedLink.kind,\n citation: citationMetadata,\n nativeEvent: nativeEvent ?? null,\n openInAssetPanel,\n openInBrowser: (nextTarget) => {\n window.open(resolvedLink.url.toString(), nextTarget ?? target ?? '_blank')?.focus();\n },\n };\n\n if (linkClicks?.onClick?.(context) === true) {\n nativeEvent?.preventDefault();\n return true;\n }\n\n if (!citationLink || !openInAssetPanel || !citationMetadata) {\n return false;\n }\n\n const citationContext: AthenaCitationLinkClickContext = {\n ...context,\n kind: 'athena-citation',\n citation: citationMetadata,\n assetId: citationMetadata.assetId,\n assetIds: citationMetadata.assetIds,\n assetType: citationMetadata.assetType,\n openInAssetPanel,\n };\n\n if (citationLinks?.onClick?.(citationContext) === true) {\n nativeEvent?.preventDefault();\n return true;\n }\n\n if ((linkClicks?.interceptAthenaCitations ?? citationLinks?.interceptAthenaCitations) === false) {\n return false;\n }\n\n if (!isAssetPanelAvailable) {\n return false;\n }\n\n // Nothing showed it (the host's opener declined and no panel is mounted): the link opens as\n // a link.\n if (!citationContext.openInAssetPanel()) {\n return false;\n }\n nativeEvent?.preventDefault();\n return true;\n },\n [appUrl, athenaConfig, isAssetPanelAvailable, openAsset],\n );\n};\n\nexport const useAthenaCitationLinkHandler = (): AthenaCitationLinkHandler => {\n const athenaConfig = useContext(AthenaContext);\n const appUrl = athenaConfig?.appUrl;\n const handleLinkClick = useAthenaLinkClickHandler();\n\n return useCallback<AthenaCitationLinkHandler>(\n (input: AthenaCitationLinkHandleInput) => {\n if (!parseAthenaCitationLink({ href: input.href, appUrl })) {\n return false;\n }\n\n return handleLinkClick(input);\n },\n [appUrl, handleLinkClick],\n );\n};\n"],"names":["_a"],"mappings":"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"}
@@ -23,7 +23,7 @@ export { type ComposerSend, prepareComposerSend } from "./composer/composer-mess
23
23
  export { PendingAttachmentUploads } from "./composer/pending-attachment-uploads";
24
24
  export { resolveComposerAction, type ComposerAction, } from "./composer/composer-action";
25
25
  export { MentionExtension, makeMentionUrl, parseAssetAnchorAttributes, parseMentionAnchorAttributes, serializeMention, } from "./mentions/mention-extension";
26
- export { MentionChip, MentionLink, normalizeAthenaMentionHtml, parseMentionUrl, parseMentionsInText, } from "./mentions/mention-chip";
26
+ export { MentionChip, type MentionChipCitation, MentionLink, normalizeAthenaMentionHtml, parseMentionUrl, parseMentionsInText, } from "./mentions/mention-chip";
27
27
  export { MentionSuggestionsExtension } from "./mentions/suggestions/mention-suggestions.extension";
28
28
  export { MentionSuggestionList } from "./mentions/suggestions/components/mention-suggestion-list";
29
29
  export { MentionSuggestionPopup } from "./mentions/suggestions/components/mention-suggestion-popup";