@athenaintel/react 0.30.0 → 0.32.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 (147) hide show
  1. package/README.md +61 -0
  2. package/dist/agent-runtime/src/athena-asset-drag.cjs +44 -0
  3. package/dist/agent-runtime/src/athena-asset-drag.cjs.map +1 -0
  4. package/dist/agent-runtime/src/athena-asset-drag.js +44 -0
  5. package/dist/agent-runtime/src/athena-asset-drag.js.map +1 -0
  6. package/dist/agent-runtime/src/config-mutations.cjs +59 -0
  7. package/dist/agent-runtime/src/config-mutations.cjs.map +1 -0
  8. package/dist/agent-runtime/src/config-mutations.js +59 -0
  9. package/dist/agent-runtime/src/config-mutations.js.map +1 -0
  10. package/dist/agent-runtime/src/generated/runconfig-contract.cjs +6 -0
  11. package/dist/agent-runtime/src/generated/runconfig-contract.cjs.map +1 -1
  12. package/dist/agent-runtime/src/generated/runconfig-contract.js +6 -0
  13. package/dist/agent-runtime/src/generated/runconfig-contract.js.map +1 -1
  14. package/dist/agent-runtime/src/quota-safe-local-storage-session.cjs +48 -0
  15. package/dist/agent-runtime/src/quota-safe-local-storage-session.cjs.map +1 -1
  16. package/dist/agent-runtime/src/quota-safe-local-storage-session.js +49 -1
  17. package/dist/agent-runtime/src/quota-safe-local-storage-session.js.map +1 -1
  18. package/dist/agent-runtime/src/tool-metadata.cjs +25 -6
  19. package/dist/agent-runtime/src/tool-metadata.cjs.map +1 -1
  20. package/dist/agent-runtime/src/tool-metadata.js +25 -6
  21. package/dist/agent-runtime/src/tool-metadata.js.map +1 -1
  22. package/dist/agent-runtime/src/use-deep-agent-config.cjs +117 -1
  23. package/dist/agent-runtime/src/use-deep-agent-config.cjs.map +1 -1
  24. package/dist/agent-runtime/src/use-deep-agent-config.js +118 -2
  25. package/dist/agent-runtime/src/use-deep-agent-config.js.map +1 -1
  26. package/dist/assets/AthenaAssetEmbed.cjs +6 -1
  27. package/dist/assets/AthenaAssetEmbed.cjs.map +1 -1
  28. package/dist/assets/AthenaAssetEmbed.d.ts +2 -0
  29. package/dist/assets/AthenaAssetEmbed.js +7 -2
  30. package/dist/assets/AthenaAssetEmbed.js.map +1 -1
  31. package/dist/assets/asset-embed-messages.cjs +6 -0
  32. package/dist/assets/asset-embed-messages.cjs.map +1 -1
  33. package/dist/assets/asset-embed-messages.d.ts +7 -0
  34. package/dist/assets/asset-embed-messages.js +6 -0
  35. package/dist/assets/asset-embed-messages.js.map +1 -1
  36. package/dist/chat/AthenaChat.cjs +41 -26
  37. package/dist/chat/AthenaChat.cjs.map +1 -1
  38. package/dist/chat/AthenaChat.js +41 -26
  39. package/dist/chat/AthenaChat.js.map +1 -1
  40. package/dist/chat/super-grouping/SuperGroupingCard.cjs +31 -11
  41. package/dist/chat/super-grouping/SuperGroupingCard.cjs.map +1 -1
  42. package/dist/chat/super-grouping/SuperGroupingCard.d.ts +14 -3
  43. package/dist/chat/super-grouping/SuperGroupingCard.js +32 -12
  44. package/dist/chat/super-grouping/SuperGroupingCard.js.map +1 -1
  45. package/dist/chat/super-grouping/SuperGroupingTranscript.cjs +80 -53
  46. package/dist/chat/super-grouping/SuperGroupingTranscript.cjs.map +1 -1
  47. package/dist/chat/super-grouping/SuperGroupingTranscript.d.ts +2 -2
  48. package/dist/chat/super-grouping/SuperGroupingTranscript.js +81 -54
  49. package/dist/chat/super-grouping/SuperGroupingTranscript.js.map +1 -1
  50. package/dist/chat/super-grouping/activity-timeline.cjs +19 -4
  51. package/dist/chat/super-grouping/activity-timeline.cjs.map +1 -1
  52. package/dist/chat/super-grouping/activity-timeline.d.ts +20 -1
  53. package/dist/chat/super-grouping/activity-timeline.js +19 -4
  54. package/dist/chat/super-grouping/activity-timeline.js.map +1 -1
  55. package/dist/chat/super-grouping/index.cjs +2 -0
  56. package/dist/chat/super-grouping/index.cjs.map +1 -1
  57. package/dist/chat/super-grouping/index.d.ts +1 -1
  58. package/dist/chat/super-grouping/index.js +3 -1
  59. package/dist/chat/super-grouping/toolkit-map.cjs +1 -0
  60. package/dist/chat/super-grouping/toolkit-map.cjs.map +1 -1
  61. package/dist/chat/super-grouping/toolkit-map.js +1 -0
  62. package/dist/chat/super-grouping/toolkit-map.js.map +1 -1
  63. package/dist/chat-ui/composer/tiptap-composer.cjs +40 -4
  64. package/dist/chat-ui/composer/tiptap-composer.cjs.map +1 -1
  65. package/dist/chat-ui/composer/tiptap-composer.js +40 -4
  66. package/dist/chat-ui/composer/tiptap-composer.js.map +1 -1
  67. package/dist/chat-ui/mentions/mention-chip.cjs +29 -2
  68. package/dist/chat-ui/mentions/mention-chip.cjs.map +1 -1
  69. package/dist/chat-ui/mentions/mention-chip.d.ts +2 -5
  70. package/dist/chat-ui/mentions/mention-chip.js +30 -3
  71. package/dist/chat-ui/mentions/mention-chip.js.map +1 -1
  72. package/dist/chat-ui/mentions/mention-extension.cjs +1 -1
  73. package/dist/chat-ui/mentions/mention-extension.cjs.map +1 -1
  74. package/dist/chat-ui/mentions/mention-extension.js +1 -1
  75. package/dist/chat-ui/mentions/mention-extension.js.map +1 -1
  76. package/dist/chat-ui/mentions/use-account-workspace-id.cjs +61 -0
  77. package/dist/chat-ui/mentions/use-account-workspace-id.cjs.map +1 -0
  78. package/dist/chat-ui/mentions/use-account-workspace-id.d.ts +3 -0
  79. package/dist/chat-ui/mentions/use-account-workspace-id.js +61 -0
  80. package/dist/chat-ui/mentions/use-account-workspace-id.js.map +1 -0
  81. package/dist/chat-ui/mentions/use-member-avatar.cjs +75 -0
  82. package/dist/chat-ui/mentions/use-member-avatar.cjs.map +1 -0
  83. package/dist/chat-ui/mentions/use-member-avatar.d.ts +2 -0
  84. package/dist/chat-ui/mentions/use-member-avatar.js +75 -0
  85. package/dist/chat-ui/mentions/use-member-avatar.js.map +1 -0
  86. package/dist/collab/client.cjs +80 -19
  87. package/dist/collab/client.cjs.map +1 -1
  88. package/dist/collab/client.d.ts +22 -0
  89. package/dist/collab/client.js +80 -19
  90. package/dist/collab/client.js.map +1 -1
  91. package/dist/diagnostics/athena-diagnostics.cjs.map +1 -1
  92. package/dist/diagnostics/athena-diagnostics.d.ts +3 -1
  93. package/dist/diagnostics/athena-diagnostics.js.map +1 -1
  94. package/dist/index.cjs +4 -0
  95. package/dist/index.cjs.map +1 -1
  96. package/dist/index.d.ts +3 -1
  97. package/dist/index.js +5 -1
  98. package/dist/index.js.map +1 -1
  99. package/dist/provider/AthenaProvider.cjs +34 -4
  100. package/dist/provider/AthenaProvider.cjs.map +1 -1
  101. package/dist/provider/AthenaProvider.d.ts +7 -1
  102. package/dist/provider/AthenaProvider.js +36 -6
  103. package/dist/provider/AthenaProvider.js.map +1 -1
  104. package/dist/react/package.json.cjs +1 -1
  105. package/dist/react/package.json.js +1 -1
  106. package/dist/runtime/StatewireModelLaneSync.cjs +96 -0
  107. package/dist/runtime/StatewireModelLaneSync.cjs.map +1 -0
  108. package/dist/runtime/StatewireModelLaneSync.d.ts +45 -0
  109. package/dist/runtime/StatewireModelLaneSync.js +96 -0
  110. package/dist/runtime/StatewireModelLaneSync.js.map +1 -0
  111. package/dist/runtime/statewire-lifecycle.cjs +5 -2
  112. package/dist/runtime/statewire-lifecycle.cjs.map +1 -1
  113. package/dist/runtime/statewire-lifecycle.d.ts +5 -0
  114. package/dist/runtime/statewire-lifecycle.js +5 -2
  115. package/dist/runtime/statewire-lifecycle.js.map +1 -1
  116. package/dist/runtime/statewire-model-lane.cjs +28 -0
  117. package/dist/runtime/statewire-model-lane.cjs.map +1 -0
  118. package/dist/runtime/statewire-model-lane.d.ts +64 -0
  119. package/dist/runtime/statewire-model-lane.js +28 -0
  120. package/dist/runtime/statewire-model-lane.js.map +1 -0
  121. package/dist/runtime/statewire-run-config.cjs +16 -2
  122. package/dist/runtime/statewire-run-config.cjs.map +1 -1
  123. package/dist/runtime/statewire-run-config.d.ts +10 -0
  124. package/dist/runtime/statewire-run-config.js +16 -2
  125. package/dist/runtime/statewire-run-config.js.map +1 -1
  126. package/dist/runtime/statewire-server-lane-context.cjs +24 -0
  127. package/dist/runtime/statewire-server-lane-context.cjs.map +1 -0
  128. package/dist/runtime/statewire-server-lane-context.d.ts +30 -0
  129. package/dist/runtime/statewire-server-lane-context.js +24 -0
  130. package/dist/runtime/statewire-server-lane-context.js.map +1 -0
  131. package/dist/runtime/useAthenaStatewireRuntime.cjs +38 -18
  132. package/dist/runtime/useAthenaStatewireRuntime.cjs.map +1 -1
  133. package/dist/runtime/useAthenaStatewireRuntime.d.ts +8 -1
  134. package/dist/runtime/useAthenaStatewireRuntime.js +39 -19
  135. package/dist/runtime/useAthenaStatewireRuntime.js.map +1 -1
  136. package/dist/styles.css +1 -1
  137. package/dist/threads/api.cjs +2 -1
  138. package/dist/threads/api.cjs.map +1 -1
  139. package/dist/threads/api.d.ts +1 -0
  140. package/dist/threads/api.js +2 -1
  141. package/dist/threads/api.js.map +1 -1
  142. package/dist/threads/use-athena-thread-model.cjs +14 -0
  143. package/dist/threads/use-athena-thread-model.cjs.map +1 -0
  144. package/dist/threads/use-athena-thread-model.d.ts +22 -0
  145. package/dist/threads/use-athena-thread-model.js +14 -0
  146. package/dist/threads/use-athena-thread-model.js.map +1 -0
  147. package/package.json +2 -2
@@ -6,6 +6,7 @@ const lucideReact = require("lucide-react");
6
6
  const assetUrls = require("../lib/asset-urls.cjs");
7
7
  const apiClient = require("./suggestions/data/api-client.cjs");
8
8
  const assetTypeIcons = require("./suggestions/data/sources/asset-type-icons.cjs");
9
+ const useMemberAvatar = require("./use-member-avatar.cjs");
9
10
  const screenPoint = require("../../agent-runtime/src/screen-point.cjs");
10
11
  const cnfast = require("cnfast");
11
12
  const SPACES_MENTION_PATH = /\/dashboard\/spaces\/?\?/;
@@ -16,6 +17,10 @@ function getSpacesAssetType(url) {
16
17
  const assetType = url.searchParams.get("assetType") || url.searchParams.get("asset_type") || url.searchParams.get("type");
17
18
  return assetType === "sheet" ? "spreadsheet" : assetType;
18
19
  }
20
+ function personEmailForMention(parsed) {
21
+ if (parsed.type !== "person") return void 0;
22
+ return parsed.email || (parsed.id.includes("@") ? parsed.id : void 0);
23
+ }
19
24
  const MentionLink = ({ href, children, interactive = true }) => {
20
25
  if (href && (href.startsWith("mention://") || isSpacesMentionUrl(href))) {
21
26
  const name = typeof children === "string" ? children.replace(/^@/, "") : Array.isArray(children) ? String(children[0] ?? "").replace(/^@/, "") : "";
@@ -25,6 +30,7 @@ const MentionLink = ({ href, children, interactive = true }) => {
25
30
  {
26
31
  name: name || parsed.name,
27
32
  type: parsed.type,
33
+ personEmail: personEmailForMention(parsed),
28
34
  assetIds: parsed.assetIds,
29
35
  thumbnailUrl: parsed.thumbnailUrl,
30
36
  sourceHref: isSpacesMentionUrl(href) ? href : void 0,
@@ -65,8 +71,18 @@ const ScreenPointChip = ({ href, children }) => {
65
71
  }
66
72
  );
67
73
  };
68
- const MentionChip = ({ name, type, assetIds, thumbnailUrl, sourceHref, interactive = true }) => {
74
+ const MentionChip = ({
75
+ name,
76
+ type,
77
+ assetIds,
78
+ personEmail,
79
+ thumbnailUrl,
80
+ sourceHref,
81
+ interactive = true
82
+ }) => {
69
83
  const auth = react.useContext(apiClient.MentionAuthCtx);
84
+ const memberAvatarUrl = useMemberAvatar.useMemberAvatarUrl(personEmail);
85
+ const [erroredAvatarUrl, setErroredAvatarUrl] = react.useState();
70
86
  const excerpt = sourceHref ? readSourceExcerpt(sourceHref) : null;
71
87
  const assetRootHref = type === "asset" && (assetIds == null ? void 0 : assetIds.length) ? assetUrls.createOlympusAssetUrl({
72
88
  appUrl: auth == null ? void 0 : auth.appUrl,
@@ -102,7 +118,16 @@ const MentionChip = ({ name, type, assetIds, thumbnailUrl, sourceHref, interacti
102
118
  }
103
119
  ),
104
120
  !thumbnailUrl && type === "webpage" && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Globe2Icon, { className: "size-3.5 shrink-0", "aria-hidden": "true" }),
105
- !thumbnailUrl && type === "person" && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.UserIcon, { className: "size-3.5 shrink-0", "aria-hidden": "true" }),
121
+ !thumbnailUrl && type === "person" && (memberAvatarUrl && erroredAvatarUrl !== memberAvatarUrl ? /* @__PURE__ */ jsxRuntime.jsx(
122
+ "img",
123
+ {
124
+ src: memberAvatarUrl,
125
+ alt: "",
126
+ className: "size-3.5 shrink-0 rounded-full object-cover",
127
+ draggable: false,
128
+ onError: () => setErroredAvatarUrl(memberAvatarUrl)
129
+ }
130
+ ) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.UserIcon, { className: "size-3.5 shrink-0", "aria-hidden": "true" })),
106
131
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "max-w-[180px] overflow-hidden text-ellipsis", children: name })
107
132
  ] });
108
133
  if (href && interactive) {
@@ -179,6 +204,7 @@ function parseMentionUrl(url) {
179
204
  type,
180
205
  name,
181
206
  id,
207
+ email: type === "person" ? u.searchParams.get("email") || void 0 : void 0,
182
208
  assetIds: type === "asset" && id ? [id] : void 0,
183
209
  thumbnailUrl
184
210
  };
@@ -243,6 +269,7 @@ function parseMentionsInText(text) {
243
269
  {
244
270
  name: name || parsed.name,
245
271
  type: parsed.type,
272
+ personEmail: personEmailForMention(parsed),
246
273
  assetIds: parsed.assetIds,
247
274
  thumbnailUrl: parsed.thumbnailUrl,
248
275
  sourceHref: isSpacesMentionUrl(url) ? url : void 0
@@ -1 +1 @@
1
- {"version":3,"file":"mention-chip.cjs","sources":["../../../src/chat-ui/mentions/mention-chip.tsx"],"sourcesContent":["/**\n * Mention chip rendering for message display.\n *\n * Parses [@name](mention://type?...) markdown links and renders them\n * as styled inline chips matching the Olympus mention style.\n * Used in both user messages (inline text) and assistant messages (markdown links).\n */\n\nimport { useContext, type FC, type ReactElement, type ReactNode } from \"react\";\nimport {\n formatScreenPoint,\n isScreenPointHref,\n parseScreenPointHref,\n} from \"@athenaintel/agent-runtime\";\nimport { CrosshairIcon, Globe2Icon, UserIcon } from \"lucide-react\";\nimport { cn } from \"../../lib/utils\";\nimport { createOlympusAssetUrl } from \"../lib/asset-urls\";\nimport { MentionAuthCtx } from \"./suggestions/data/api-client\";\nimport { assetTypeToThumb } from \"./suggestions/data/sources/asset-type-icons\";\n\nconst SPACES_MENTION_PATH = /\\/dashboard\\/spaces\\/?\\?/;\n\nfunction isSpacesMentionUrl(url: string): boolean {\n return SPACES_MENTION_PATH.test(url);\n}\n\nfunction getSpacesAssetType(url: URL): string | null {\n const assetType =\n url.searchParams.get(\"assetType\") ||\n url.searchParams.get(\"asset_type\") ||\n url.searchParams.get(\"type\");\n return assetType === \"sheet\" ? \"spreadsheet\" : assetType;\n}\n\n// ── Mention link component for markdown renderer ──\n\n/**\n * Custom <a> renderer for MarkdownTextPrimitive.\n * Intercepts mention:// links and renders them as chips.\n * Falls through to a normal link for non-mention URLs.\n */\nexport const MentionLink: FC<{\n href?: string;\n children?: ReactNode;\n interactive?: boolean;\n}> = ({ href, children, interactive = true }) => {\n if (\n href &&\n (href.startsWith(\"mention://\") || isSpacesMentionUrl(href))\n ) {\n const name = typeof children === \"string\"\n ? children.replace(/^@/, \"\")\n : Array.isArray(children)\n ? String(children[0] ?? \"\").replace(/^@/, \"\")\n : \"\";\n\n const parsed = parseMentionUrl(href);\n\n return (\n <MentionChip\n name={name || parsed.name}\n type={parsed.type}\n assetIds={parsed.assetIds}\n thumbnailUrl={parsed.thumbnailUrl}\n sourceHref={isSpacesMentionUrl(href) ? href : undefined}\n interactive={interactive}\n />\n );\n }\n\n if (href?.startsWith(\"chrome-extension://\")) {\n return <span>{children}</span>;\n }\n\n if (isScreenPointHref(href)) {\n return <ScreenPointChip href={href}>{children}</ScreenPointChip>;\n }\n\n // Normal link — render as-is\n return (\n <a href={href} target=\"_blank\" rel=\"noopener noreferrer\">\n {children}\n </a>\n );\n};\n\n// ── Inline screen-point chip ──\n\n/**\n * Renders an `athena-point://` link as a non-navigating coordinate chip.\n * Surfaces without screenshot context (extension side panel, SDK consumers)\n * get the label plus the coordinates; malformed points fall back to text.\n */\nexport const ScreenPointChip: FC<{\n href: string;\n children?: ReactNode;\n}> = ({ href, children }) => {\n const parsed = parseScreenPointHref(href);\n if (!parsed.ok) {\n return <span>{children}</span>;\n }\n const description = formatScreenPoint(parsed.point);\n return (\n <span\n className=\"not-prose inline-flex max-w-[260px] items-center gap-1 whitespace-nowrap rounded-md border border-slate-300 bg-white px-1.5 py-px align-middle text-[0.85em] font-medium leading-[1.4] text-slate-600\"\n title={description}\n aria-label={description}\n data-screen-point=\"valid\"\n >\n <CrosshairIcon className=\"size-3.5 shrink-0\" aria-hidden=\"true\" />\n <span className=\"max-w-[180px] overflow-hidden text-ellipsis\">\n {children}\n </span>\n <span className=\"font-normal text-slate-400\">\n {parsed.point.x}, {parsed.point.y}\n </span>\n </span>\n );\n};\n\n// ── Inline mention chip ──\n\nexport const MentionChip: FC<{\n name: string;\n type?: string;\n assetIds?: string[];\n thumbnailUrl?: string;\n sourceHref?: string;\n interactive?: boolean;\n}> = ({ name, type, assetIds, thumbnailUrl, sourceHref, interactive = true }) => {\n const auth = useContext(MentionAuthCtx);\n // A citation's excerpt (the passage the chip points at) is the tooltip; the\n // accessible name stays the action.\n const excerpt = sourceHref ? readSourceExcerpt(sourceHref) : null;\n const assetRootHref =\n type === \"asset\" && assetIds?.length\n ? createOlympusAssetUrl({\n appUrl: auth?.appUrl,\n backendUrl: auth?.backendUrl,\n assetIds,\n })\n : null;\n const href = (() => {\n if (!assetRootHref || !sourceHref) return assetRootHref;\n try {\n const source = new URL(sourceHref, \"https://app.athenaintel.com\");\n const destination = new URL(assetRootHref);\n for (const [key, value] of source.searchParams) {\n destination.searchParams.set(key, value);\n }\n destination.searchParams.set(\"asset_ids\", assetIds?.join(\",\") ?? \"\");\n return destination.toString();\n } catch {\n return assetRootHref;\n }\n })();\n // `not-prose` matters: assistant messages render inside Tailwind\n // Typography's `prose`, whose zero-specificity `img` margins (~1.7em\n // top/bottom) would otherwise inflate the chip's icon into a tall card\n // that breaks the sentence flow around it.\n const className = cn(\n \"not-prose inline-flex max-w-[220px] items-center gap-1 whitespace-nowrap rounded-md border border-slate-300 bg-white px-1.5 py-px align-middle text-[0.85em] font-medium leading-[1.4] text-slate-600 no-underline\",\n href &&\n \"cursor-pointer transition-colors hover:border-slate-400 hover:bg-slate-50 hover:text-slate-800 hover:no-underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1\",\n );\n const contents = (\n <>\n {thumbnailUrl && (\n <img\n src={thumbnailUrl}\n alt=\"\"\n className=\"size-3.5 shrink-0\"\n draggable={false}\n />\n )}\n {!thumbnailUrl && type === \"webpage\" && (\n <Globe2Icon className=\"size-3.5 shrink-0\" aria-hidden=\"true\" />\n )}\n {!thumbnailUrl && type === \"person\" && (\n <UserIcon className=\"size-3.5 shrink-0\" aria-hidden=\"true\" />\n )}\n <span className=\"max-w-[180px] overflow-hidden text-ellipsis\">\n {name}\n </span>\n </>\n );\n\n if (href && interactive) {\n return (\n <a\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={className}\n aria-label={`Open ${name} in Spaces`}\n title={excerpt ?? `Open ${name} in Spaces`}\n data-excerpt={excerpt ?? undefined}\n >\n {contents}\n </a>\n );\n }\n\n return (\n <span\n className={className}\n title={excerpt ?? undefined}\n data-excerpt={excerpt ?? undefined}\n >\n {contents}\n </span>\n );\n};\n\n/** The `excerpt` a Spaces citation URL carries, or null. */\nfunction readSourceExcerpt(href: string): string | null {\n try {\n const excerpt = new URL(href, \"https://app.athenaintel.com\").searchParams\n .get(\"excerpt\")\n ?.trim();\n return excerpt ? excerpt : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The label an asset citation chip falls back to when the model wrote none\n * (`[](…/spaces/?asset_ids=…&page=5)`): the cited location, else \"Asset\" — a\n * chip must never render as an empty box.\n */\nfunction citationFallbackLabel(params: URLSearchParams): string {\n const start = params.get(\"start_page\") ?? params.get(\"page\");\n const end = params.get(\"end_page\");\n if (start && end && end !== start) return `Pages ${start}–${end}`;\n if (start) return `Page ${start}`;\n return \"Asset\";\n}\n\n// ── Parse mention:// URLs ──\n\nexport function parseMentionUrl(url: string): {\n type: string;\n name: string;\n id: string;\n assetIds?: string[];\n thumbnailUrl?: string;\n} {\n try {\n if (isSpacesMentionUrl(url)) {\n const parsed = new URL(url, \"https://app.athenaintel.com\");\n const assetIds = (parsed.searchParams.get(\"asset_ids\") ?? \"\")\n .split(\",\")\n .map((id) => id.trim())\n .filter(Boolean);\n const id = assetIds[0] ?? \"\";\n const assetType = getSpacesAssetType(parsed);\n return {\n type: \"asset\",\n name:\n assetIds.length > 1\n ? `${assetIds.length} assets`\n : citationFallbackLabel(parsed.searchParams),\n id,\n assetIds,\n thumbnailUrl: assetTypeToThumb(assetType),\n };\n }\n // mention://tool?name=...&description=...\n // mention://asset?id=...&assetType=project&thumb=../icons/files/ic-project.svg\n const u = new URL(url);\n const type = u.hostname || u.pathname.replace(/^\\/\\//, \"\").split(\"?\")[0];\n const isWebpage = type === \"webpage\";\n const id = isWebpage\n ? u.searchParams.get(\"tab_id\") || \"\"\n : u.searchParams.get(\"id\") || u.searchParams.get(\"name\") || \"\";\n const name = isWebpage\n ? u.searchParams.get(\"title\") || \"This webpage\"\n : u.searchParams.get(\"name\") || id;\n\n // Get thumbnail: prefer explicit thumb param, then derive from assetType\n let thumbnailUrl = u.searchParams.get(\"thumb\") || undefined;\n if (!thumbnailUrl) {\n const assetType = u.searchParams.get(\"assetType\");\n if (assetType) {\n thumbnailUrl = assetTypeToThumb(assetType);\n } else if (type === \"asset\" || type === \"folder\") {\n thumbnailUrl = assetTypeToThumb(type === \"folder\" ? \"folder\" : null);\n }\n }\n\n return {\n type,\n name,\n id,\n assetIds: type === \"asset\" && id ? [id] : undefined,\n thumbnailUrl,\n };\n } catch {\n return { type: \"unknown\", name: \"\", id: \"\" };\n }\n}\n\nfunction htmlAttribute(attributes: string, name: string): string | null {\n const escapedName = name.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const match = new RegExp(\n `(?:^|\\\\s)${escapedName}\\\\s*=\\\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\\\s>]+))`,\n \"i\",\n ).exec(attributes);\n return match?.[1] ?? match?.[2] ?? match?.[3] ?? null;\n}\n\nfunction decodeHtmlAttribute(value: string): string {\n return value\n .replace(/&amp;/gi, \"&\")\n .replace(/&quot;/gi, '\"')\n .replace(/&#39;|&apos;/gi, \"'\")\n .replace(/&lt;/gi, \"<\")\n .replace(/&gt;/gi, \">\");\n}\n\n/** Turn complete model-emitted URL citation anchors into safe Markdown links. */\nexport function normalizeAthenaMentionHtml(text: string): string {\n return text.replace(\n /<a\\b([^>]*)>([\\s\\S]*?)<\\/a>/gi,\n (original, attributes: string, rawLabel: string) => {\n const className = htmlAttribute(attributes, \"class\") ?? \"\";\n const type = htmlAttribute(attributes, \"data-type\") ?? \"\";\n if (!className.split(/\\s+/).includes(\"mention\") || type !== \"url\") {\n return original;\n }\n\n const rawUrl =\n htmlAttribute(attributes, \"data-url\") ??\n htmlAttribute(attributes, \"href\");\n if (!rawUrl) return original;\n const url = decodeHtmlAttribute(rawUrl);\n let normalizedUrl: string;\n try {\n const parsed = new URL(url);\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n return original;\n }\n normalizedUrl = parsed.toString();\n } catch {\n return original;\n }\n\n const label = decodeHtmlAttribute(rawLabel.replace(/<[^>]*>/g, \"\"))\n .trim()\n .replace(/([\\\\[\\]])/g, \"\\\\$1\");\n return label ? `[${label}](<${normalizedUrl}>)` : original;\n },\n );\n}\n// ── Parse mention markdown in plain text ──\n\n// The label may hold balanced brackets one level deep (`[@Plan [Q3]](…)`) or a lone `[`.\nconst MENTION_RE =\n /\\[@((?:\\[[^[\\]]*\\](?!\\((?:[a-z][a-z0-9+.-]*:\\/\\/|\\/))|[^\\]])*)\\]\\(((?:mention:\\/\\/|(?:https?:\\/\\/[^)\\s]+)?\\/dashboard\\/spaces\\/?\\?)[^)]+)\\)/g;\n\n/**\n * Parse text containing [@name](mention://...) patterns into\n * an array of string segments and MentionChip elements.\n */\nexport function parseMentionsInText(text: string): (string | ReactElement)[] {\n const parts: (string | ReactElement)[] = [];\n let lastIndex = 0;\n let match: RegExpExecArray | null;\n\n MENTION_RE.lastIndex = 0;\n\n while ((match = MENTION_RE.exec(text)) !== null) {\n if (match.index > lastIndex) {\n parts.push(text.slice(lastIndex, match.index));\n }\n\n const name = match[1];\n const url = match[2];\n const parsed = parseMentionUrl(url);\n\n parts.push(\n <MentionChip\n key={`mention-${match.index}`}\n name={name || parsed.name}\n type={parsed.type}\n assetIds={parsed.assetIds}\n thumbnailUrl={parsed.thumbnailUrl}\n sourceHref={isSpacesMentionUrl(url) ? url : undefined}\n />\n );\n\n lastIndex = match.index + match[0].length;\n }\n\n if (lastIndex < text.length) {\n parts.push(text.slice(lastIndex));\n }\n\n return parts.length > 0 ? parts : [text];\n}\n\n/** The text as a reader sees it: each `[@name](…)` mention becomes `@name`. */\nexport function mentionsToPlainText(text: string): string {\n return text.replace(MENTION_RE, (_match, name: string, url: string) => {\n return `@${name || parseMentionUrl(url).name}`;\n });\n}\n"],"names":["jsx","isScreenPointHref","parseScreenPointHref","formatScreenPoint","jsxs","CrosshairIcon","useContext","MentionAuthCtx","createOlympusAssetUrl","cn","Fragment","Globe2Icon","UserIcon","id","assetTypeToThumb"],"mappings":"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"}
1
+ {"version":3,"file":"mention-chip.cjs","sources":["../../../src/chat-ui/mentions/mention-chip.tsx"],"sourcesContent":["/**\n * Mention chip rendering for message display.\n *\n * Parses [@name](mention://type?...) markdown links and renders them\n * as styled inline chips matching the Olympus mention style.\n * Used in both user messages (inline text) and assistant messages (markdown links).\n */\n\nimport {\n useContext,\n useState,\n type FC,\n type ReactElement,\n type ReactNode,\n} from \"react\";\nimport {\n formatScreenPoint,\n isScreenPointHref,\n parseScreenPointHref,\n} from \"@athenaintel/agent-runtime\";\nimport { CrosshairIcon, Globe2Icon, UserIcon } from \"lucide-react\";\nimport { cn } from \"../../lib/utils\";\nimport { createOlympusAssetUrl } from \"../lib/asset-urls\";\nimport { MentionAuthCtx } from \"./suggestions/data/api-client\";\nimport { assetTypeToThumb } from \"./suggestions/data/sources/asset-type-icons\";\nimport { useMemberAvatarUrl } from \"./use-member-avatar\";\n\nconst SPACES_MENTION_PATH = /\\/dashboard\\/spaces\\/?\\?/;\n\nfunction isSpacesMentionUrl(url: string): boolean {\n return SPACES_MENTION_PATH.test(url);\n}\n\nfunction getSpacesAssetType(url: URL): string | null {\n const assetType =\n url.searchParams.get(\"assetType\") ||\n url.searchParams.get(\"asset_type\") ||\n url.searchParams.get(\"type\");\n return assetType === \"sheet\" ? \"spreadsheet\" : assetType;\n}\n\n// ── Mention link component for markdown renderer ──\n\n/**\n * Custom <a> renderer for MarkdownTextPrimitive.\n * Intercepts mention:// links and renders them as chips.\n * Falls through to a normal link for non-mention URLs.\n */\nfunction personEmailForMention(\n parsed: ReturnType<typeof parseMentionUrl>,\n): string | undefined {\n if (parsed.type !== \"person\") return undefined;\n return parsed.email || (parsed.id.includes(\"@\") ? parsed.id : undefined);\n}\n\nexport const MentionLink: FC<{\n href?: string;\n children?: ReactNode;\n interactive?: boolean;\n}> = ({ href, children, interactive = true }) => {\n if (\n href &&\n (href.startsWith(\"mention://\") || isSpacesMentionUrl(href))\n ) {\n const name = typeof children === \"string\"\n ? children.replace(/^@/, \"\")\n : Array.isArray(children)\n ? String(children[0] ?? \"\").replace(/^@/, \"\")\n : \"\";\n\n const parsed = parseMentionUrl(href);\n\n return (\n <MentionChip\n name={name || parsed.name}\n type={parsed.type}\n personEmail={personEmailForMention(parsed)}\n assetIds={parsed.assetIds}\n thumbnailUrl={parsed.thumbnailUrl}\n sourceHref={isSpacesMentionUrl(href) ? href : undefined}\n interactive={interactive}\n />\n );\n }\n\n if (href?.startsWith(\"chrome-extension://\")) {\n return <span>{children}</span>;\n }\n\n if (isScreenPointHref(href)) {\n return <ScreenPointChip href={href}>{children}</ScreenPointChip>;\n }\n\n // Normal link — render as-is\n return (\n <a href={href} target=\"_blank\" rel=\"noopener noreferrer\">\n {children}\n </a>\n );\n};\n\n// ── Inline screen-point chip ──\n\n/**\n * Renders an `athena-point://` link as a non-navigating coordinate chip.\n * Surfaces without screenshot context (extension side panel, SDK consumers)\n * get the label plus the coordinates; malformed points fall back to text.\n */\nexport const ScreenPointChip: FC<{\n href: string;\n children?: ReactNode;\n}> = ({ href, children }) => {\n const parsed = parseScreenPointHref(href);\n if (!parsed.ok) {\n return <span>{children}</span>;\n }\n const description = formatScreenPoint(parsed.point);\n return (\n <span\n className=\"not-prose inline-flex max-w-[260px] items-center gap-1 whitespace-nowrap rounded-md border border-slate-300 bg-white px-1.5 py-px align-middle text-[0.85em] font-medium leading-[1.4] text-slate-600\"\n title={description}\n aria-label={description}\n data-screen-point=\"valid\"\n >\n <CrosshairIcon className=\"size-3.5 shrink-0\" aria-hidden=\"true\" />\n <span className=\"max-w-[180px] overflow-hidden text-ellipsis\">\n {children}\n </span>\n <span className=\"font-normal text-slate-400\">\n {parsed.point.x}, {parsed.point.y}\n </span>\n </span>\n );\n};\n\n// ── Inline mention chip ──\n\nexport const MentionChip: FC<{\n name: string;\n type?: string;\n assetIds?: string[];\n personEmail?: string;\n thumbnailUrl?: string;\n sourceHref?: string;\n interactive?: boolean;\n}> = ({\n name,\n type,\n assetIds,\n personEmail,\n thumbnailUrl,\n sourceHref,\n interactive = true,\n}) => {\n const auth = useContext(MentionAuthCtx);\n const memberAvatarUrl = useMemberAvatarUrl(personEmail);\n const [erroredAvatarUrl, setErroredAvatarUrl] = useState<string>();\n // A citation's excerpt (the passage the chip points at) is the tooltip; the\n // accessible name stays the action.\n const excerpt = sourceHref ? readSourceExcerpt(sourceHref) : null;\n const assetRootHref =\n type === \"asset\" && assetIds?.length\n ? createOlympusAssetUrl({\n appUrl: auth?.appUrl,\n backendUrl: auth?.backendUrl,\n assetIds,\n })\n : null;\n const href = (() => {\n if (!assetRootHref || !sourceHref) return assetRootHref;\n try {\n const source = new URL(sourceHref, \"https://app.athenaintel.com\");\n const destination = new URL(assetRootHref);\n for (const [key, value] of source.searchParams) {\n destination.searchParams.set(key, value);\n }\n destination.searchParams.set(\"asset_ids\", assetIds?.join(\",\") ?? \"\");\n return destination.toString();\n } catch {\n return assetRootHref;\n }\n })();\n // `not-prose` matters: assistant messages render inside Tailwind\n // Typography's `prose`, whose zero-specificity `img` margins (~1.7em\n // top/bottom) would otherwise inflate the chip's icon into a tall card\n // that breaks the sentence flow around it.\n const className = cn(\n \"not-prose inline-flex max-w-[220px] items-center gap-1 whitespace-nowrap rounded-md border border-slate-300 bg-white px-1.5 py-px align-middle text-[0.85em] font-medium leading-[1.4] text-slate-600 no-underline\",\n href &&\n \"cursor-pointer transition-colors hover:border-slate-400 hover:bg-slate-50 hover:text-slate-800 hover:no-underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1\",\n );\n const contents = (\n <>\n {thumbnailUrl && (\n <img\n src={thumbnailUrl}\n alt=\"\"\n className=\"size-3.5 shrink-0\"\n draggable={false}\n />\n )}\n {!thumbnailUrl && type === \"webpage\" && (\n <Globe2Icon className=\"size-3.5 shrink-0\" aria-hidden=\"true\" />\n )}\n {!thumbnailUrl && type === \"person\" && (\n memberAvatarUrl && erroredAvatarUrl !== memberAvatarUrl ? (\n <img\n src={memberAvatarUrl}\n alt=\"\"\n className=\"size-3.5 shrink-0 rounded-full object-cover\"\n draggable={false}\n onError={() => setErroredAvatarUrl(memberAvatarUrl)}\n />\n ) : (\n <UserIcon className=\"size-3.5 shrink-0\" aria-hidden=\"true\" />\n )\n )}\n <span className=\"max-w-[180px] overflow-hidden text-ellipsis\">\n {name}\n </span>\n </>\n );\n\n if (href && interactive) {\n return (\n <a\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={className}\n aria-label={`Open ${name} in Spaces`}\n title={excerpt ?? `Open ${name} in Spaces`}\n data-excerpt={excerpt ?? undefined}\n >\n {contents}\n </a>\n );\n }\n\n return (\n <span\n className={className}\n title={excerpt ?? undefined}\n data-excerpt={excerpt ?? undefined}\n >\n {contents}\n </span>\n );\n};\n\n/** The `excerpt` a Spaces citation URL carries, or null. */\nfunction readSourceExcerpt(href: string): string | null {\n try {\n const excerpt = new URL(href, \"https://app.athenaintel.com\").searchParams\n .get(\"excerpt\")\n ?.trim();\n return excerpt ? excerpt : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The label an asset citation chip falls back to when the model wrote none\n * (`[](…/spaces/?asset_ids=…&page=5)`): the cited location, else \"Asset\" — a\n * chip must never render as an empty box.\n */\nfunction citationFallbackLabel(params: URLSearchParams): string {\n const start = params.get(\"start_page\") ?? params.get(\"page\");\n const end = params.get(\"end_page\");\n if (start && end && end !== start) return `Pages ${start}–${end}`;\n if (start) return `Page ${start}`;\n return \"Asset\";\n}\n\n// ── Parse mention:// URLs ──\n\nexport function parseMentionUrl(url: string): {\n type: string;\n name: string;\n id: string;\n email?: string;\n assetIds?: string[];\n thumbnailUrl?: string;\n} {\n try {\n if (isSpacesMentionUrl(url)) {\n const parsed = new URL(url, \"https://app.athenaintel.com\");\n const assetIds = (parsed.searchParams.get(\"asset_ids\") ?? \"\")\n .split(\",\")\n .map((id) => id.trim())\n .filter(Boolean);\n const id = assetIds[0] ?? \"\";\n const assetType = getSpacesAssetType(parsed);\n return {\n type: \"asset\",\n name:\n assetIds.length > 1\n ? `${assetIds.length} assets`\n : citationFallbackLabel(parsed.searchParams),\n id,\n assetIds,\n thumbnailUrl: assetTypeToThumb(assetType),\n };\n }\n // mention://tool?name=...&description=...\n // mention://asset?id=...&assetType=project&thumb=../icons/files/ic-project.svg\n const u = new URL(url);\n const type = u.hostname || u.pathname.replace(/^\\/\\//, \"\").split(\"?\")[0];\n const isWebpage = type === \"webpage\";\n const id = isWebpage\n ? u.searchParams.get(\"tab_id\") || \"\"\n : u.searchParams.get(\"id\") || u.searchParams.get(\"name\") || \"\";\n const name = isWebpage\n ? u.searchParams.get(\"title\") || \"This webpage\"\n : u.searchParams.get(\"name\") || id;\n\n // Get thumbnail: prefer explicit thumb param, then derive from assetType\n let thumbnailUrl = u.searchParams.get(\"thumb\") || undefined;\n if (!thumbnailUrl) {\n const assetType = u.searchParams.get(\"assetType\");\n if (assetType) {\n thumbnailUrl = assetTypeToThumb(assetType);\n } else if (type === \"asset\" || type === \"folder\") {\n thumbnailUrl = assetTypeToThumb(type === \"folder\" ? \"folder\" : null);\n }\n }\n\n return {\n type,\n name,\n id,\n email: type === \"person\" ? u.searchParams.get(\"email\") || undefined : undefined,\n assetIds: type === \"asset\" && id ? [id] : undefined,\n thumbnailUrl,\n };\n } catch {\n return { type: \"unknown\", name: \"\", id: \"\" };\n }\n}\n\nfunction htmlAttribute(attributes: string, name: string): string | null {\n const escapedName = name.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const match = new RegExp(\n `(?:^|\\\\s)${escapedName}\\\\s*=\\\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\\\s>]+))`,\n \"i\",\n ).exec(attributes);\n return match?.[1] ?? match?.[2] ?? match?.[3] ?? null;\n}\n\nfunction decodeHtmlAttribute(value: string): string {\n return value\n .replace(/&amp;/gi, \"&\")\n .replace(/&quot;/gi, '\"')\n .replace(/&#39;|&apos;/gi, \"'\")\n .replace(/&lt;/gi, \"<\")\n .replace(/&gt;/gi, \">\");\n}\n\n/** Turn complete model-emitted URL citation anchors into safe Markdown links. */\nexport function normalizeAthenaMentionHtml(text: string): string {\n return text.replace(\n /<a\\b([^>]*)>([\\s\\S]*?)<\\/a>/gi,\n (original, attributes: string, rawLabel: string) => {\n const className = htmlAttribute(attributes, \"class\") ?? \"\";\n const type = htmlAttribute(attributes, \"data-type\") ?? \"\";\n if (!className.split(/\\s+/).includes(\"mention\") || type !== \"url\") {\n return original;\n }\n\n const rawUrl =\n htmlAttribute(attributes, \"data-url\") ??\n htmlAttribute(attributes, \"href\");\n if (!rawUrl) return original;\n const url = decodeHtmlAttribute(rawUrl);\n let normalizedUrl: string;\n try {\n const parsed = new URL(url);\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n return original;\n }\n normalizedUrl = parsed.toString();\n } catch {\n return original;\n }\n\n const label = decodeHtmlAttribute(rawLabel.replace(/<[^>]*>/g, \"\"))\n .trim()\n .replace(/([\\\\[\\]])/g, \"\\\\$1\");\n return label ? `[${label}](<${normalizedUrl}>)` : original;\n },\n );\n}\n// ── Parse mention markdown in plain text ──\n\n// The label may hold balanced brackets one level deep (`[@Plan [Q3]](…)`) or a lone `[`.\nconst MENTION_RE =\n /\\[@((?:\\[[^[\\]]*\\](?!\\((?:[a-z][a-z0-9+.-]*:\\/\\/|\\/))|[^\\]])*)\\]\\(((?:mention:\\/\\/|(?:https?:\\/\\/[^)\\s]+)?\\/dashboard\\/spaces\\/?\\?)[^)]+)\\)/g;\n\n/**\n * Parse text containing [@name](mention://...) patterns into\n * an array of string segments and MentionChip elements.\n */\nexport function parseMentionsInText(text: string): (string | ReactElement)[] {\n const parts: (string | ReactElement)[] = [];\n let lastIndex = 0;\n let match: RegExpExecArray | null;\n\n MENTION_RE.lastIndex = 0;\n\n while ((match = MENTION_RE.exec(text)) !== null) {\n if (match.index > lastIndex) {\n parts.push(text.slice(lastIndex, match.index));\n }\n\n const name = match[1];\n const url = match[2];\n const parsed = parseMentionUrl(url);\n\n parts.push(\n <MentionChip\n key={`mention-${match.index}`}\n name={name || parsed.name}\n type={parsed.type}\n personEmail={personEmailForMention(parsed)}\n assetIds={parsed.assetIds}\n thumbnailUrl={parsed.thumbnailUrl}\n sourceHref={isSpacesMentionUrl(url) ? url : undefined}\n />\n );\n\n lastIndex = match.index + match[0].length;\n }\n\n if (lastIndex < text.length) {\n parts.push(text.slice(lastIndex));\n }\n\n return parts.length > 0 ? parts : [text];\n}\n\n/** The text as a reader sees it: each `[@name](…)` mention becomes `@name`. */\nexport function mentionsToPlainText(text: string): string {\n return text.replace(MENTION_RE, (_match, name: string, url: string) => {\n return `@${name || parseMentionUrl(url).name}`;\n });\n}\n"],"names":["jsx","isScreenPointHref","parseScreenPointHref","formatScreenPoint","jsxs","CrosshairIcon","useContext","MentionAuthCtx","useMemberAvatarUrl","useState","createOlympusAssetUrl","cn","Fragment","Globe2Icon","UserIcon","id","assetTypeToThumb"],"mappings":"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"}
@@ -6,11 +6,6 @@
6
6
  * Used in both user messages (inline text) and assistant messages (markdown links).
7
7
  */
8
8
  import { type FC, type ReactElement, type ReactNode } from "react";
9
- /**
10
- * Custom <a> renderer for MarkdownTextPrimitive.
11
- * Intercepts mention:// links and renders them as chips.
12
- * Falls through to a normal link for non-mention URLs.
13
- */
14
9
  export declare const MentionLink: FC<{
15
10
  href?: string;
16
11
  children?: ReactNode;
@@ -29,6 +24,7 @@ export declare const MentionChip: FC<{
29
24
  name: string;
30
25
  type?: string;
31
26
  assetIds?: string[];
27
+ personEmail?: string;
32
28
  thumbnailUrl?: string;
33
29
  sourceHref?: string;
34
30
  interactive?: boolean;
@@ -37,6 +33,7 @@ export declare function parseMentionUrl(url: string): {
37
33
  type: string;
38
34
  name: string;
39
35
  id: string;
36
+ email?: string;
40
37
  assetIds?: string[];
41
38
  thumbnailUrl?: string;
42
39
  };
@@ -1,9 +1,10 @@
1
1
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
- import { useContext } from "react";
2
+ import { useContext, useState } from "react";
3
3
  import { Globe2Icon, UserIcon, CrosshairIcon } from "lucide-react";
4
4
  import { createOlympusAssetUrl } from "../lib/asset-urls.js";
5
5
  import { MentionAuthCtx } from "./suggestions/data/api-client.js";
6
6
  import { assetTypeToThumb } from "./suggestions/data/sources/asset-type-icons.js";
7
+ import { useMemberAvatarUrl } from "./use-member-avatar.js";
7
8
  import { isScreenPointHref, parseScreenPointHref, formatScreenPoint } from "../../agent-runtime/src/screen-point.js";
8
9
  import { cn } from "cnfast";
9
10
  const SPACES_MENTION_PATH = /\/dashboard\/spaces\/?\?/;
@@ -14,6 +15,10 @@ function getSpacesAssetType(url) {
14
15
  const assetType = url.searchParams.get("assetType") || url.searchParams.get("asset_type") || url.searchParams.get("type");
15
16
  return assetType === "sheet" ? "spreadsheet" : assetType;
16
17
  }
18
+ function personEmailForMention(parsed) {
19
+ if (parsed.type !== "person") return void 0;
20
+ return parsed.email || (parsed.id.includes("@") ? parsed.id : void 0);
21
+ }
17
22
  const MentionLink = ({ href, children, interactive = true }) => {
18
23
  if (href && (href.startsWith("mention://") || isSpacesMentionUrl(href))) {
19
24
  const name = typeof children === "string" ? children.replace(/^@/, "") : Array.isArray(children) ? String(children[0] ?? "").replace(/^@/, "") : "";
@@ -23,6 +28,7 @@ const MentionLink = ({ href, children, interactive = true }) => {
23
28
  {
24
29
  name: name || parsed.name,
25
30
  type: parsed.type,
31
+ personEmail: personEmailForMention(parsed),
26
32
  assetIds: parsed.assetIds,
27
33
  thumbnailUrl: parsed.thumbnailUrl,
28
34
  sourceHref: isSpacesMentionUrl(href) ? href : void 0,
@@ -63,8 +69,18 @@ const ScreenPointChip = ({ href, children }) => {
63
69
  }
64
70
  );
65
71
  };
66
- const MentionChip = ({ name, type, assetIds, thumbnailUrl, sourceHref, interactive = true }) => {
72
+ const MentionChip = ({
73
+ name,
74
+ type,
75
+ assetIds,
76
+ personEmail,
77
+ thumbnailUrl,
78
+ sourceHref,
79
+ interactive = true
80
+ }) => {
67
81
  const auth = useContext(MentionAuthCtx);
82
+ const memberAvatarUrl = useMemberAvatarUrl(personEmail);
83
+ const [erroredAvatarUrl, setErroredAvatarUrl] = useState();
68
84
  const excerpt = sourceHref ? readSourceExcerpt(sourceHref) : null;
69
85
  const assetRootHref = type === "asset" && (assetIds == null ? void 0 : assetIds.length) ? createOlympusAssetUrl({
70
86
  appUrl: auth == null ? void 0 : auth.appUrl,
@@ -100,7 +116,16 @@ const MentionChip = ({ name, type, assetIds, thumbnailUrl, sourceHref, interacti
100
116
  }
101
117
  ),
102
118
  !thumbnailUrl && type === "webpage" && /* @__PURE__ */ jsx(Globe2Icon, { className: "size-3.5 shrink-0", "aria-hidden": "true" }),
103
- !thumbnailUrl && type === "person" && /* @__PURE__ */ jsx(UserIcon, { className: "size-3.5 shrink-0", "aria-hidden": "true" }),
119
+ !thumbnailUrl && type === "person" && (memberAvatarUrl && erroredAvatarUrl !== memberAvatarUrl ? /* @__PURE__ */ jsx(
120
+ "img",
121
+ {
122
+ src: memberAvatarUrl,
123
+ alt: "",
124
+ className: "size-3.5 shrink-0 rounded-full object-cover",
125
+ draggable: false,
126
+ onError: () => setErroredAvatarUrl(memberAvatarUrl)
127
+ }
128
+ ) : /* @__PURE__ */ jsx(UserIcon, { className: "size-3.5 shrink-0", "aria-hidden": "true" })),
104
129
  /* @__PURE__ */ jsx("span", { className: "max-w-[180px] overflow-hidden text-ellipsis", children: name })
105
130
  ] });
106
131
  if (href && interactive) {
@@ -177,6 +202,7 @@ function parseMentionUrl(url) {
177
202
  type,
178
203
  name,
179
204
  id,
205
+ email: type === "person" ? u.searchParams.get("email") || void 0 : void 0,
180
206
  assetIds: type === "asset" && id ? [id] : void 0,
181
207
  thumbnailUrl
182
208
  };
@@ -241,6 +267,7 @@ function parseMentionsInText(text) {
241
267
  {
242
268
  name: name || parsed.name,
243
269
  type: parsed.type,
270
+ personEmail: personEmailForMention(parsed),
244
271
  assetIds: parsed.assetIds,
245
272
  thumbnailUrl: parsed.thumbnailUrl,
246
273
  sourceHref: isSpacesMentionUrl(url) ? url : void 0
@@ -1 +1 @@
1
- {"version":3,"file":"mention-chip.js","sources":["../../../src/chat-ui/mentions/mention-chip.tsx"],"sourcesContent":["/**\n * Mention chip rendering for message display.\n *\n * Parses [@name](mention://type?...) markdown links and renders them\n * as styled inline chips matching the Olympus mention style.\n * Used in both user messages (inline text) and assistant messages (markdown links).\n */\n\nimport { useContext, type FC, type ReactElement, type ReactNode } from \"react\";\nimport {\n formatScreenPoint,\n isScreenPointHref,\n parseScreenPointHref,\n} from \"@athenaintel/agent-runtime\";\nimport { CrosshairIcon, Globe2Icon, UserIcon } from \"lucide-react\";\nimport { cn } from \"../../lib/utils\";\nimport { createOlympusAssetUrl } from \"../lib/asset-urls\";\nimport { MentionAuthCtx } from \"./suggestions/data/api-client\";\nimport { assetTypeToThumb } from \"./suggestions/data/sources/asset-type-icons\";\n\nconst SPACES_MENTION_PATH = /\\/dashboard\\/spaces\\/?\\?/;\n\nfunction isSpacesMentionUrl(url: string): boolean {\n return SPACES_MENTION_PATH.test(url);\n}\n\nfunction getSpacesAssetType(url: URL): string | null {\n const assetType =\n url.searchParams.get(\"assetType\") ||\n url.searchParams.get(\"asset_type\") ||\n url.searchParams.get(\"type\");\n return assetType === \"sheet\" ? \"spreadsheet\" : assetType;\n}\n\n// ── Mention link component for markdown renderer ──\n\n/**\n * Custom <a> renderer for MarkdownTextPrimitive.\n * Intercepts mention:// links and renders them as chips.\n * Falls through to a normal link for non-mention URLs.\n */\nexport const MentionLink: FC<{\n href?: string;\n children?: ReactNode;\n interactive?: boolean;\n}> = ({ href, children, interactive = true }) => {\n if (\n href &&\n (href.startsWith(\"mention://\") || isSpacesMentionUrl(href))\n ) {\n const name = typeof children === \"string\"\n ? children.replace(/^@/, \"\")\n : Array.isArray(children)\n ? String(children[0] ?? \"\").replace(/^@/, \"\")\n : \"\";\n\n const parsed = parseMentionUrl(href);\n\n return (\n <MentionChip\n name={name || parsed.name}\n type={parsed.type}\n assetIds={parsed.assetIds}\n thumbnailUrl={parsed.thumbnailUrl}\n sourceHref={isSpacesMentionUrl(href) ? href : undefined}\n interactive={interactive}\n />\n );\n }\n\n if (href?.startsWith(\"chrome-extension://\")) {\n return <span>{children}</span>;\n }\n\n if (isScreenPointHref(href)) {\n return <ScreenPointChip href={href}>{children}</ScreenPointChip>;\n }\n\n // Normal link — render as-is\n return (\n <a href={href} target=\"_blank\" rel=\"noopener noreferrer\">\n {children}\n </a>\n );\n};\n\n// ── Inline screen-point chip ──\n\n/**\n * Renders an `athena-point://` link as a non-navigating coordinate chip.\n * Surfaces without screenshot context (extension side panel, SDK consumers)\n * get the label plus the coordinates; malformed points fall back to text.\n */\nexport const ScreenPointChip: FC<{\n href: string;\n children?: ReactNode;\n}> = ({ href, children }) => {\n const parsed = parseScreenPointHref(href);\n if (!parsed.ok) {\n return <span>{children}</span>;\n }\n const description = formatScreenPoint(parsed.point);\n return (\n <span\n className=\"not-prose inline-flex max-w-[260px] items-center gap-1 whitespace-nowrap rounded-md border border-slate-300 bg-white px-1.5 py-px align-middle text-[0.85em] font-medium leading-[1.4] text-slate-600\"\n title={description}\n aria-label={description}\n data-screen-point=\"valid\"\n >\n <CrosshairIcon className=\"size-3.5 shrink-0\" aria-hidden=\"true\" />\n <span className=\"max-w-[180px] overflow-hidden text-ellipsis\">\n {children}\n </span>\n <span className=\"font-normal text-slate-400\">\n {parsed.point.x}, {parsed.point.y}\n </span>\n </span>\n );\n};\n\n// ── Inline mention chip ──\n\nexport const MentionChip: FC<{\n name: string;\n type?: string;\n assetIds?: string[];\n thumbnailUrl?: string;\n sourceHref?: string;\n interactive?: boolean;\n}> = ({ name, type, assetIds, thumbnailUrl, sourceHref, interactive = true }) => {\n const auth = useContext(MentionAuthCtx);\n // A citation's excerpt (the passage the chip points at) is the tooltip; the\n // accessible name stays the action.\n const excerpt = sourceHref ? readSourceExcerpt(sourceHref) : null;\n const assetRootHref =\n type === \"asset\" && assetIds?.length\n ? createOlympusAssetUrl({\n appUrl: auth?.appUrl,\n backendUrl: auth?.backendUrl,\n assetIds,\n })\n : null;\n const href = (() => {\n if (!assetRootHref || !sourceHref) return assetRootHref;\n try {\n const source = new URL(sourceHref, \"https://app.athenaintel.com\");\n const destination = new URL(assetRootHref);\n for (const [key, value] of source.searchParams) {\n destination.searchParams.set(key, value);\n }\n destination.searchParams.set(\"asset_ids\", assetIds?.join(\",\") ?? \"\");\n return destination.toString();\n } catch {\n return assetRootHref;\n }\n })();\n // `not-prose` matters: assistant messages render inside Tailwind\n // Typography's `prose`, whose zero-specificity `img` margins (~1.7em\n // top/bottom) would otherwise inflate the chip's icon into a tall card\n // that breaks the sentence flow around it.\n const className = cn(\n \"not-prose inline-flex max-w-[220px] items-center gap-1 whitespace-nowrap rounded-md border border-slate-300 bg-white px-1.5 py-px align-middle text-[0.85em] font-medium leading-[1.4] text-slate-600 no-underline\",\n href &&\n \"cursor-pointer transition-colors hover:border-slate-400 hover:bg-slate-50 hover:text-slate-800 hover:no-underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1\",\n );\n const contents = (\n <>\n {thumbnailUrl && (\n <img\n src={thumbnailUrl}\n alt=\"\"\n className=\"size-3.5 shrink-0\"\n draggable={false}\n />\n )}\n {!thumbnailUrl && type === \"webpage\" && (\n <Globe2Icon className=\"size-3.5 shrink-0\" aria-hidden=\"true\" />\n )}\n {!thumbnailUrl && type === \"person\" && (\n <UserIcon className=\"size-3.5 shrink-0\" aria-hidden=\"true\" />\n )}\n <span className=\"max-w-[180px] overflow-hidden text-ellipsis\">\n {name}\n </span>\n </>\n );\n\n if (href && interactive) {\n return (\n <a\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={className}\n aria-label={`Open ${name} in Spaces`}\n title={excerpt ?? `Open ${name} in Spaces`}\n data-excerpt={excerpt ?? undefined}\n >\n {contents}\n </a>\n );\n }\n\n return (\n <span\n className={className}\n title={excerpt ?? undefined}\n data-excerpt={excerpt ?? undefined}\n >\n {contents}\n </span>\n );\n};\n\n/** The `excerpt` a Spaces citation URL carries, or null. */\nfunction readSourceExcerpt(href: string): string | null {\n try {\n const excerpt = new URL(href, \"https://app.athenaintel.com\").searchParams\n .get(\"excerpt\")\n ?.trim();\n return excerpt ? excerpt : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The label an asset citation chip falls back to when the model wrote none\n * (`[](…/spaces/?asset_ids=…&page=5)`): the cited location, else \"Asset\" — a\n * chip must never render as an empty box.\n */\nfunction citationFallbackLabel(params: URLSearchParams): string {\n const start = params.get(\"start_page\") ?? params.get(\"page\");\n const end = params.get(\"end_page\");\n if (start && end && end !== start) return `Pages ${start}–${end}`;\n if (start) return `Page ${start}`;\n return \"Asset\";\n}\n\n// ── Parse mention:// URLs ──\n\nexport function parseMentionUrl(url: string): {\n type: string;\n name: string;\n id: string;\n assetIds?: string[];\n thumbnailUrl?: string;\n} {\n try {\n if (isSpacesMentionUrl(url)) {\n const parsed = new URL(url, \"https://app.athenaintel.com\");\n const assetIds = (parsed.searchParams.get(\"asset_ids\") ?? \"\")\n .split(\",\")\n .map((id) => id.trim())\n .filter(Boolean);\n const id = assetIds[0] ?? \"\";\n const assetType = getSpacesAssetType(parsed);\n return {\n type: \"asset\",\n name:\n assetIds.length > 1\n ? `${assetIds.length} assets`\n : citationFallbackLabel(parsed.searchParams),\n id,\n assetIds,\n thumbnailUrl: assetTypeToThumb(assetType),\n };\n }\n // mention://tool?name=...&description=...\n // mention://asset?id=...&assetType=project&thumb=../icons/files/ic-project.svg\n const u = new URL(url);\n const type = u.hostname || u.pathname.replace(/^\\/\\//, \"\").split(\"?\")[0];\n const isWebpage = type === \"webpage\";\n const id = isWebpage\n ? u.searchParams.get(\"tab_id\") || \"\"\n : u.searchParams.get(\"id\") || u.searchParams.get(\"name\") || \"\";\n const name = isWebpage\n ? u.searchParams.get(\"title\") || \"This webpage\"\n : u.searchParams.get(\"name\") || id;\n\n // Get thumbnail: prefer explicit thumb param, then derive from assetType\n let thumbnailUrl = u.searchParams.get(\"thumb\") || undefined;\n if (!thumbnailUrl) {\n const assetType = u.searchParams.get(\"assetType\");\n if (assetType) {\n thumbnailUrl = assetTypeToThumb(assetType);\n } else if (type === \"asset\" || type === \"folder\") {\n thumbnailUrl = assetTypeToThumb(type === \"folder\" ? \"folder\" : null);\n }\n }\n\n return {\n type,\n name,\n id,\n assetIds: type === \"asset\" && id ? [id] : undefined,\n thumbnailUrl,\n };\n } catch {\n return { type: \"unknown\", name: \"\", id: \"\" };\n }\n}\n\nfunction htmlAttribute(attributes: string, name: string): string | null {\n const escapedName = name.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const match = new RegExp(\n `(?:^|\\\\s)${escapedName}\\\\s*=\\\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\\\s>]+))`,\n \"i\",\n ).exec(attributes);\n return match?.[1] ?? match?.[2] ?? match?.[3] ?? null;\n}\n\nfunction decodeHtmlAttribute(value: string): string {\n return value\n .replace(/&amp;/gi, \"&\")\n .replace(/&quot;/gi, '\"')\n .replace(/&#39;|&apos;/gi, \"'\")\n .replace(/&lt;/gi, \"<\")\n .replace(/&gt;/gi, \">\");\n}\n\n/** Turn complete model-emitted URL citation anchors into safe Markdown links. */\nexport function normalizeAthenaMentionHtml(text: string): string {\n return text.replace(\n /<a\\b([^>]*)>([\\s\\S]*?)<\\/a>/gi,\n (original, attributes: string, rawLabel: string) => {\n const className = htmlAttribute(attributes, \"class\") ?? \"\";\n const type = htmlAttribute(attributes, \"data-type\") ?? \"\";\n if (!className.split(/\\s+/).includes(\"mention\") || type !== \"url\") {\n return original;\n }\n\n const rawUrl =\n htmlAttribute(attributes, \"data-url\") ??\n htmlAttribute(attributes, \"href\");\n if (!rawUrl) return original;\n const url = decodeHtmlAttribute(rawUrl);\n let normalizedUrl: string;\n try {\n const parsed = new URL(url);\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n return original;\n }\n normalizedUrl = parsed.toString();\n } catch {\n return original;\n }\n\n const label = decodeHtmlAttribute(rawLabel.replace(/<[^>]*>/g, \"\"))\n .trim()\n .replace(/([\\\\[\\]])/g, \"\\\\$1\");\n return label ? `[${label}](<${normalizedUrl}>)` : original;\n },\n );\n}\n// ── Parse mention markdown in plain text ──\n\n// The label may hold balanced brackets one level deep (`[@Plan [Q3]](…)`) or a lone `[`.\nconst MENTION_RE =\n /\\[@((?:\\[[^[\\]]*\\](?!\\((?:[a-z][a-z0-9+.-]*:\\/\\/|\\/))|[^\\]])*)\\]\\(((?:mention:\\/\\/|(?:https?:\\/\\/[^)\\s]+)?\\/dashboard\\/spaces\\/?\\?)[^)]+)\\)/g;\n\n/**\n * Parse text containing [@name](mention://...) patterns into\n * an array of string segments and MentionChip elements.\n */\nexport function parseMentionsInText(text: string): (string | ReactElement)[] {\n const parts: (string | ReactElement)[] = [];\n let lastIndex = 0;\n let match: RegExpExecArray | null;\n\n MENTION_RE.lastIndex = 0;\n\n while ((match = MENTION_RE.exec(text)) !== null) {\n if (match.index > lastIndex) {\n parts.push(text.slice(lastIndex, match.index));\n }\n\n const name = match[1];\n const url = match[2];\n const parsed = parseMentionUrl(url);\n\n parts.push(\n <MentionChip\n key={`mention-${match.index}`}\n name={name || parsed.name}\n type={parsed.type}\n assetIds={parsed.assetIds}\n thumbnailUrl={parsed.thumbnailUrl}\n sourceHref={isSpacesMentionUrl(url) ? url : undefined}\n />\n );\n\n lastIndex = match.index + match[0].length;\n }\n\n if (lastIndex < text.length) {\n parts.push(text.slice(lastIndex));\n }\n\n return parts.length > 0 ? parts : [text];\n}\n\n/** The text as a reader sees it: each `[@name](…)` mention becomes `@name`. */\nexport function mentionsToPlainText(text: string): string {\n return text.replace(MENTION_RE, (_match, name: string, url: string) => {\n return `@${name || parseMentionUrl(url).name}`;\n });\n}\n"],"names":["id"],"mappings":"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"}
1
+ {"version":3,"file":"mention-chip.js","sources":["../../../src/chat-ui/mentions/mention-chip.tsx"],"sourcesContent":["/**\n * Mention chip rendering for message display.\n *\n * Parses [@name](mention://type?...) markdown links and renders them\n * as styled inline chips matching the Olympus mention style.\n * Used in both user messages (inline text) and assistant messages (markdown links).\n */\n\nimport {\n useContext,\n useState,\n type FC,\n type ReactElement,\n type ReactNode,\n} from \"react\";\nimport {\n formatScreenPoint,\n isScreenPointHref,\n parseScreenPointHref,\n} from \"@athenaintel/agent-runtime\";\nimport { CrosshairIcon, Globe2Icon, UserIcon } from \"lucide-react\";\nimport { cn } from \"../../lib/utils\";\nimport { createOlympusAssetUrl } from \"../lib/asset-urls\";\nimport { MentionAuthCtx } from \"./suggestions/data/api-client\";\nimport { assetTypeToThumb } from \"./suggestions/data/sources/asset-type-icons\";\nimport { useMemberAvatarUrl } from \"./use-member-avatar\";\n\nconst SPACES_MENTION_PATH = /\\/dashboard\\/spaces\\/?\\?/;\n\nfunction isSpacesMentionUrl(url: string): boolean {\n return SPACES_MENTION_PATH.test(url);\n}\n\nfunction getSpacesAssetType(url: URL): string | null {\n const assetType =\n url.searchParams.get(\"assetType\") ||\n url.searchParams.get(\"asset_type\") ||\n url.searchParams.get(\"type\");\n return assetType === \"sheet\" ? \"spreadsheet\" : assetType;\n}\n\n// ── Mention link component for markdown renderer ──\n\n/**\n * Custom <a> renderer for MarkdownTextPrimitive.\n * Intercepts mention:// links and renders them as chips.\n * Falls through to a normal link for non-mention URLs.\n */\nfunction personEmailForMention(\n parsed: ReturnType<typeof parseMentionUrl>,\n): string | undefined {\n if (parsed.type !== \"person\") return undefined;\n return parsed.email || (parsed.id.includes(\"@\") ? parsed.id : undefined);\n}\n\nexport const MentionLink: FC<{\n href?: string;\n children?: ReactNode;\n interactive?: boolean;\n}> = ({ href, children, interactive = true }) => {\n if (\n href &&\n (href.startsWith(\"mention://\") || isSpacesMentionUrl(href))\n ) {\n const name = typeof children === \"string\"\n ? children.replace(/^@/, \"\")\n : Array.isArray(children)\n ? String(children[0] ?? \"\").replace(/^@/, \"\")\n : \"\";\n\n const parsed = parseMentionUrl(href);\n\n return (\n <MentionChip\n name={name || parsed.name}\n type={parsed.type}\n personEmail={personEmailForMention(parsed)}\n assetIds={parsed.assetIds}\n thumbnailUrl={parsed.thumbnailUrl}\n sourceHref={isSpacesMentionUrl(href) ? href : undefined}\n interactive={interactive}\n />\n );\n }\n\n if (href?.startsWith(\"chrome-extension://\")) {\n return <span>{children}</span>;\n }\n\n if (isScreenPointHref(href)) {\n return <ScreenPointChip href={href}>{children}</ScreenPointChip>;\n }\n\n // Normal link — render as-is\n return (\n <a href={href} target=\"_blank\" rel=\"noopener noreferrer\">\n {children}\n </a>\n );\n};\n\n// ── Inline screen-point chip ──\n\n/**\n * Renders an `athena-point://` link as a non-navigating coordinate chip.\n * Surfaces without screenshot context (extension side panel, SDK consumers)\n * get the label plus the coordinates; malformed points fall back to text.\n */\nexport const ScreenPointChip: FC<{\n href: string;\n children?: ReactNode;\n}> = ({ href, children }) => {\n const parsed = parseScreenPointHref(href);\n if (!parsed.ok) {\n return <span>{children}</span>;\n }\n const description = formatScreenPoint(parsed.point);\n return (\n <span\n className=\"not-prose inline-flex max-w-[260px] items-center gap-1 whitespace-nowrap rounded-md border border-slate-300 bg-white px-1.5 py-px align-middle text-[0.85em] font-medium leading-[1.4] text-slate-600\"\n title={description}\n aria-label={description}\n data-screen-point=\"valid\"\n >\n <CrosshairIcon className=\"size-3.5 shrink-0\" aria-hidden=\"true\" />\n <span className=\"max-w-[180px] overflow-hidden text-ellipsis\">\n {children}\n </span>\n <span className=\"font-normal text-slate-400\">\n {parsed.point.x}, {parsed.point.y}\n </span>\n </span>\n );\n};\n\n// ── Inline mention chip ──\n\nexport const MentionChip: FC<{\n name: string;\n type?: string;\n assetIds?: string[];\n personEmail?: string;\n thumbnailUrl?: string;\n sourceHref?: string;\n interactive?: boolean;\n}> = ({\n name,\n type,\n assetIds,\n personEmail,\n thumbnailUrl,\n sourceHref,\n interactive = true,\n}) => {\n const auth = useContext(MentionAuthCtx);\n const memberAvatarUrl = useMemberAvatarUrl(personEmail);\n const [erroredAvatarUrl, setErroredAvatarUrl] = useState<string>();\n // A citation's excerpt (the passage the chip points at) is the tooltip; the\n // accessible name stays the action.\n const excerpt = sourceHref ? readSourceExcerpt(sourceHref) : null;\n const assetRootHref =\n type === \"asset\" && assetIds?.length\n ? createOlympusAssetUrl({\n appUrl: auth?.appUrl,\n backendUrl: auth?.backendUrl,\n assetIds,\n })\n : null;\n const href = (() => {\n if (!assetRootHref || !sourceHref) return assetRootHref;\n try {\n const source = new URL(sourceHref, \"https://app.athenaintel.com\");\n const destination = new URL(assetRootHref);\n for (const [key, value] of source.searchParams) {\n destination.searchParams.set(key, value);\n }\n destination.searchParams.set(\"asset_ids\", assetIds?.join(\",\") ?? \"\");\n return destination.toString();\n } catch {\n return assetRootHref;\n }\n })();\n // `not-prose` matters: assistant messages render inside Tailwind\n // Typography's `prose`, whose zero-specificity `img` margins (~1.7em\n // top/bottom) would otherwise inflate the chip's icon into a tall card\n // that breaks the sentence flow around it.\n const className = cn(\n \"not-prose inline-flex max-w-[220px] items-center gap-1 whitespace-nowrap rounded-md border border-slate-300 bg-white px-1.5 py-px align-middle text-[0.85em] font-medium leading-[1.4] text-slate-600 no-underline\",\n href &&\n \"cursor-pointer transition-colors hover:border-slate-400 hover:bg-slate-50 hover:text-slate-800 hover:no-underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1\",\n );\n const contents = (\n <>\n {thumbnailUrl && (\n <img\n src={thumbnailUrl}\n alt=\"\"\n className=\"size-3.5 shrink-0\"\n draggable={false}\n />\n )}\n {!thumbnailUrl && type === \"webpage\" && (\n <Globe2Icon className=\"size-3.5 shrink-0\" aria-hidden=\"true\" />\n )}\n {!thumbnailUrl && type === \"person\" && (\n memberAvatarUrl && erroredAvatarUrl !== memberAvatarUrl ? (\n <img\n src={memberAvatarUrl}\n alt=\"\"\n className=\"size-3.5 shrink-0 rounded-full object-cover\"\n draggable={false}\n onError={() => setErroredAvatarUrl(memberAvatarUrl)}\n />\n ) : (\n <UserIcon className=\"size-3.5 shrink-0\" aria-hidden=\"true\" />\n )\n )}\n <span className=\"max-w-[180px] overflow-hidden text-ellipsis\">\n {name}\n </span>\n </>\n );\n\n if (href && interactive) {\n return (\n <a\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={className}\n aria-label={`Open ${name} in Spaces`}\n title={excerpt ?? `Open ${name} in Spaces`}\n data-excerpt={excerpt ?? undefined}\n >\n {contents}\n </a>\n );\n }\n\n return (\n <span\n className={className}\n title={excerpt ?? undefined}\n data-excerpt={excerpt ?? undefined}\n >\n {contents}\n </span>\n );\n};\n\n/** The `excerpt` a Spaces citation URL carries, or null. */\nfunction readSourceExcerpt(href: string): string | null {\n try {\n const excerpt = new URL(href, \"https://app.athenaintel.com\").searchParams\n .get(\"excerpt\")\n ?.trim();\n return excerpt ? excerpt : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The label an asset citation chip falls back to when the model wrote none\n * (`[](…/spaces/?asset_ids=…&page=5)`): the cited location, else \"Asset\" — a\n * chip must never render as an empty box.\n */\nfunction citationFallbackLabel(params: URLSearchParams): string {\n const start = params.get(\"start_page\") ?? params.get(\"page\");\n const end = params.get(\"end_page\");\n if (start && end && end !== start) return `Pages ${start}–${end}`;\n if (start) return `Page ${start}`;\n return \"Asset\";\n}\n\n// ── Parse mention:// URLs ──\n\nexport function parseMentionUrl(url: string): {\n type: string;\n name: string;\n id: string;\n email?: string;\n assetIds?: string[];\n thumbnailUrl?: string;\n} {\n try {\n if (isSpacesMentionUrl(url)) {\n const parsed = new URL(url, \"https://app.athenaintel.com\");\n const assetIds = (parsed.searchParams.get(\"asset_ids\") ?? \"\")\n .split(\",\")\n .map((id) => id.trim())\n .filter(Boolean);\n const id = assetIds[0] ?? \"\";\n const assetType = getSpacesAssetType(parsed);\n return {\n type: \"asset\",\n name:\n assetIds.length > 1\n ? `${assetIds.length} assets`\n : citationFallbackLabel(parsed.searchParams),\n id,\n assetIds,\n thumbnailUrl: assetTypeToThumb(assetType),\n };\n }\n // mention://tool?name=...&description=...\n // mention://asset?id=...&assetType=project&thumb=../icons/files/ic-project.svg\n const u = new URL(url);\n const type = u.hostname || u.pathname.replace(/^\\/\\//, \"\").split(\"?\")[0];\n const isWebpage = type === \"webpage\";\n const id = isWebpage\n ? u.searchParams.get(\"tab_id\") || \"\"\n : u.searchParams.get(\"id\") || u.searchParams.get(\"name\") || \"\";\n const name = isWebpage\n ? u.searchParams.get(\"title\") || \"This webpage\"\n : u.searchParams.get(\"name\") || id;\n\n // Get thumbnail: prefer explicit thumb param, then derive from assetType\n let thumbnailUrl = u.searchParams.get(\"thumb\") || undefined;\n if (!thumbnailUrl) {\n const assetType = u.searchParams.get(\"assetType\");\n if (assetType) {\n thumbnailUrl = assetTypeToThumb(assetType);\n } else if (type === \"asset\" || type === \"folder\") {\n thumbnailUrl = assetTypeToThumb(type === \"folder\" ? \"folder\" : null);\n }\n }\n\n return {\n type,\n name,\n id,\n email: type === \"person\" ? u.searchParams.get(\"email\") || undefined : undefined,\n assetIds: type === \"asset\" && id ? [id] : undefined,\n thumbnailUrl,\n };\n } catch {\n return { type: \"unknown\", name: \"\", id: \"\" };\n }\n}\n\nfunction htmlAttribute(attributes: string, name: string): string | null {\n const escapedName = name.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const match = new RegExp(\n `(?:^|\\\\s)${escapedName}\\\\s*=\\\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\\\s>]+))`,\n \"i\",\n ).exec(attributes);\n return match?.[1] ?? match?.[2] ?? match?.[3] ?? null;\n}\n\nfunction decodeHtmlAttribute(value: string): string {\n return value\n .replace(/&amp;/gi, \"&\")\n .replace(/&quot;/gi, '\"')\n .replace(/&#39;|&apos;/gi, \"'\")\n .replace(/&lt;/gi, \"<\")\n .replace(/&gt;/gi, \">\");\n}\n\n/** Turn complete model-emitted URL citation anchors into safe Markdown links. */\nexport function normalizeAthenaMentionHtml(text: string): string {\n return text.replace(\n /<a\\b([^>]*)>([\\s\\S]*?)<\\/a>/gi,\n (original, attributes: string, rawLabel: string) => {\n const className = htmlAttribute(attributes, \"class\") ?? \"\";\n const type = htmlAttribute(attributes, \"data-type\") ?? \"\";\n if (!className.split(/\\s+/).includes(\"mention\") || type !== \"url\") {\n return original;\n }\n\n const rawUrl =\n htmlAttribute(attributes, \"data-url\") ??\n htmlAttribute(attributes, \"href\");\n if (!rawUrl) return original;\n const url = decodeHtmlAttribute(rawUrl);\n let normalizedUrl: string;\n try {\n const parsed = new URL(url);\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n return original;\n }\n normalizedUrl = parsed.toString();\n } catch {\n return original;\n }\n\n const label = decodeHtmlAttribute(rawLabel.replace(/<[^>]*>/g, \"\"))\n .trim()\n .replace(/([\\\\[\\]])/g, \"\\\\$1\");\n return label ? `[${label}](<${normalizedUrl}>)` : original;\n },\n );\n}\n// ── Parse mention markdown in plain text ──\n\n// The label may hold balanced brackets one level deep (`[@Plan [Q3]](…)`) or a lone `[`.\nconst MENTION_RE =\n /\\[@((?:\\[[^[\\]]*\\](?!\\((?:[a-z][a-z0-9+.-]*:\\/\\/|\\/))|[^\\]])*)\\]\\(((?:mention:\\/\\/|(?:https?:\\/\\/[^)\\s]+)?\\/dashboard\\/spaces\\/?\\?)[^)]+)\\)/g;\n\n/**\n * Parse text containing [@name](mention://...) patterns into\n * an array of string segments and MentionChip elements.\n */\nexport function parseMentionsInText(text: string): (string | ReactElement)[] {\n const parts: (string | ReactElement)[] = [];\n let lastIndex = 0;\n let match: RegExpExecArray | null;\n\n MENTION_RE.lastIndex = 0;\n\n while ((match = MENTION_RE.exec(text)) !== null) {\n if (match.index > lastIndex) {\n parts.push(text.slice(lastIndex, match.index));\n }\n\n const name = match[1];\n const url = match[2];\n const parsed = parseMentionUrl(url);\n\n parts.push(\n <MentionChip\n key={`mention-${match.index}`}\n name={name || parsed.name}\n type={parsed.type}\n personEmail={personEmailForMention(parsed)}\n assetIds={parsed.assetIds}\n thumbnailUrl={parsed.thumbnailUrl}\n sourceHref={isSpacesMentionUrl(url) ? url : undefined}\n />\n );\n\n lastIndex = match.index + match[0].length;\n }\n\n if (lastIndex < text.length) {\n parts.push(text.slice(lastIndex));\n }\n\n return parts.length > 0 ? parts : [text];\n}\n\n/** The text as a reader sees it: each `[@name](…)` mention becomes `@name`. */\nexport function mentionsToPlainText(text: string): string {\n return text.replace(MENTION_RE, (_match, name: string, url: string) => {\n return `@${name || parseMentionUrl(url).name}`;\n });\n}\n"],"names":["id"],"mappings":"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"}
@@ -166,7 +166,7 @@ const MentionExtension = core.Node.create({
166
166
  {
167
167
  src: thumbnailUrl,
168
168
  alt: "",
169
- class: "mention-chip-icon",
169
+ class: node.attrs.type === "user" ? "mention-chip-icon mention-chip-avatar" : "mention-chip-icon",
170
170
  draggable: "false"
171
171
  }
172
172
  ]);
@@ -1 +1 @@
1
- {"version":3,"file":"mention-extension.cjs","sources":["../../../src/chat-ui/mentions/mention-extension.ts"],"sourcesContent":["/**\n * Tiptap Mention Node Extension\n *\n * Defines the mention node type used in the editor. When a mention is\n * inserted, it creates a non-editable inline node with metadata.\n *\n * Renders as a chip with: [icon] Name. Serializes — to plain text and to\n * Markdown alike — as `[@Name](url)`, and parses that link back into a chip.\n */\n\nimport { mergeAttributes, Node } from \"@tiptap/core\";\nimport type { Node as ProseMirrorNode } from \"@tiptap/pm/model\";\n\nexport function makeMentionUrl(attrs: { id: string; type: string; name: string; title?: string; url?: string; description?: string; thumbnailUrl?: string; assetType?: string; href?: string }): string {\n if (attrs.href && (attrs.href.startsWith(\"mention://\") || ASSET_MENTION_HREF.test(attrs.href))) {\n return attrs.href;\n }\n if (attrs.type === \"tool\" || attrs.type === \"toolkit\") {\n return `mention://${attrs.type}?name=${encodeURIComponent(attrs.id)}&description=${encodeURIComponent(attrs.description ?? \"\")}`;\n }\n if (attrs.type === \"asset\") {\n const params = new URLSearchParams({ asset_ids: attrs.id });\n if (attrs.assetType) params.set(\"assetType\", attrs.assetType);\n return `/dashboard/spaces?${params.toString()}`;\n }\n if (attrs.type === \"user\") {\n // The canonical person reference Olympus writes (packages/references\n // `encodeToPersonMentionUri`); members are keyed on their email.\n const params = new URLSearchParams({ id: attrs.id });\n if (attrs.id.includes(\"@\")) params.set(\"email\", attrs.id);\n return `mention://person?${params.toString()}`;\n }\n if (attrs.type === \"tab\") {\n const params = new URLSearchParams({\n tab_id: attrs.id,\n url: attrs.url ?? \"\",\n title: attrs.title ?? attrs.name,\n });\n return `mention://webpage?${params.toString()}`;\n }\n // Include athenaOriginalType and thumbnailUrl so the chip can be reconstructed\n const params = new URLSearchParams();\n params.set(\"id\", attrs.id);\n if (attrs.assetType) params.set(\"assetType\", attrs.assetType);\n if (attrs.thumbnailUrl) params.set(\"thumb\", attrs.thumbnailUrl);\n return `mention://${encodeURIComponent(attrs.type)}?${params.toString()}`;\n}\n\nexport interface MentionNodeAttrs {\n type: string;\n name: string;\n id: string;\n title?: string;\n url?: string;\n description?: string;\n thumbnailUrl?: string;\n fallbackIcon?: string;\n assetType?: string;\n displayAsPreview?: boolean;\n href?: string;\n}\n\n/** The `[@Name](url)` form a mention takes in the outgoing text. */\nexport function serializeMention(attrs: MentionNodeAttrs): string {\n return `[@${attrs.name}](${makeMentionUrl(attrs)})`;\n}\n\nexport function parseMentionAnchorAttributes({\n href,\n textContent,\n}: {\n href: string;\n textContent: string | null;\n}): MentionNodeAttrs {\n const url = new URL(href);\n const isWebpage = url.hostname === \"webpage\";\n const isPerson = url.hostname === \"person\";\n return {\n href,\n type: isWebpage ? \"tab\" : isPerson ? \"user\" : url.hostname || \"tool\",\n name: textContent?.replace(/^@/, \"\") || \"\",\n id: isWebpage\n ? url.searchParams.get(\"tab_id\") || \"\"\n : isPerson\n ? url.searchParams.get(\"id\") || url.searchParams.get(\"email\") || \"\"\n : url.searchParams.get(\"name\") || url.searchParams.get(\"id\") || \"\",\n title: url.searchParams.get(\"title\") || \"\",\n url: url.searchParams.get(\"url\") || \"\",\n description: url.searchParams.get(\"description\") || \"\",\n thumbnailUrl: url.searchParams.get(\"thumb\") || \"\",\n fallbackIcon: \"\",\n assetType: url.searchParams.get(\"assetType\") || \"\",\n };\n}\n\n/** The asset link `makeMentionUrl` writes (`/dashboard/spaces?asset_ids=…`). */\nconst ASSET_MENTION_HREF = /^(?:https?:\\/\\/[^/]+)?\\/dashboard\\/spaces\\/?\\?/;\n\n/**\n * Attributes of an asset mention from its `/dashboard/spaces?asset_ids=…`\n * link, or null for a spaces link that names no asset.\n */\nexport function parseAssetAnchorAttributes({\n href,\n textContent,\n}: {\n href: string;\n textContent: string | null;\n}): MentionNodeAttrs | null {\n if (!ASSET_MENTION_HREF.test(href)) return null;\n const params = new URLSearchParams(href.slice(href.indexOf(\"?\") + 1));\n const id = params.get(\"asset_ids\");\n if (!id) return null;\n return {\n type: \"asset\",\n href,\n name: textContent?.replace(/^@/, \"\") || \"\",\n id,\n assetType: params.get(\"assetType\") || \"\",\n };\n}\n\ninterface MarkdownSerializerState {\n write(text: string): void;\n}\n\n/**\n * ProseMirror tries parse rules by rule priority (default 50), mark rules\n * before node rules at a tie: a serialized mention is an anchor, so its rules\n * must outrank the Link mark's `a[href]` rule or the chip comes back as a\n * plain link.\n */\nconst ANCHOR_RULE_PRIORITY = 100;\n\nexport const MentionExtension = Node.create({\n name: \"mention\",\n group: \"inline\",\n inline: true,\n selectable: false,\n atom: true,\n\n addAttributes() {\n return {\n href: { default: null },\n type: { default: \"tool\" },\n name: { default: \"\" },\n id: { default: \"\" },\n title: { default: \"\" },\n url: { default: \"\" },\n description: { default: \"\" },\n thumbnailUrl: { default: \"\" },\n fallbackIcon: { default: \"\" },\n assetType: { default: \"\" },\n displayAsPreview: { default: false },\n };\n },\n\n /** tiptap-markdown reads this spec so `getMarkdown()` writes the mention, not HTML. */\n addStorage() {\n return {\n markdown: {\n serialize(state: MarkdownSerializerState, node: ProseMirrorNode) {\n state.write(serializeMention(node.attrs as MentionNodeAttrs));\n },\n parse: {\n // The Markdown parser renders the link to an anchor; parseHTML takes it from there.\n },\n },\n };\n },\n\n parseHTML() {\n return [\n {\n tag: \"span[data-mention]\",\n getAttrs: (dom) => {\n const el = dom as HTMLElement;\n return {\n href: el.getAttribute(\"data-href\"),\n type: el.getAttribute(\"data-type\") || \"tool\",\n name: el.getAttribute(\"data-name\") || el.textContent?.replace(/^@/, \"\") || \"\",\n id: el.getAttribute(\"data-id\") || \"\",\n title: el.getAttribute(\"data-title\") || \"\",\n url: el.getAttribute(\"data-url\") || \"\",\n description: el.getAttribute(\"data-description\") || \"\",\n thumbnailUrl: el.getAttribute(\"data-thumbnail-url\") || \"\",\n fallbackIcon: el.getAttribute(\"data-fallback-icon\") || \"\",\n assetType: el.getAttribute(\"data-asset-type\") || \"\",\n displayAsPreview:\n el.getAttribute(\"data-display-as-preview\") === \"true\",\n };\n },\n },\n {\n tag: \"a[href^='mention://']\",\n priority: ANCHOR_RULE_PRIORITY,\n getAttrs: (dom) => {\n const el = dom as HTMLAnchorElement;\n try {\n return parseMentionAnchorAttributes({\n href: el.getAttribute(\"href\") ?? el.href,\n textContent: el.textContent,\n });\n } catch {\n return false;\n }\n },\n },\n {\n tag: \"a[href]\",\n priority: ANCHOR_RULE_PRIORITY,\n getAttrs: (dom) => {\n const el = dom as HTMLAnchorElement;\n return (\n parseAssetAnchorAttributes({\n href: el.getAttribute(\"href\") ?? \"\",\n textContent: el.textContent,\n }) ?? false\n );\n },\n },\n ];\n },\n\n renderHTML({ node, HTMLAttributes }) {\n const thumbnailUrl = node.attrs.thumbnailUrl;\n\n // Build child elements: [icon] name\n const children: any[] = [];\n\n if (thumbnailUrl) {\n // Render SVG thumbnail as an img element inside the chip\n children.push([\n \"img\",\n {\n src: thumbnailUrl,\n alt: \"\",\n class: \"mention-chip-icon\",\n draggable: \"false\",\n },\n ]);\n }\n\n children.push([\"span\", { class: \"mention-chip-text\" }, node.attrs.name]);\n\n return [\n \"span\",\n mergeAttributes(HTMLAttributes, {\n \"data-mention\": \"\",\n \"data-href\": node.attrs.href || \"\",\n \"data-type\": node.attrs.type,\n \"data-name\": node.attrs.name,\n \"data-id\": node.attrs.id,\n \"data-title\": node.attrs.title || \"\",\n \"data-url\": node.attrs.url || \"\",\n \"data-thumbnail-url\": node.attrs.thumbnailUrl || \"\",\n \"data-asset-type\": node.attrs.assetType || \"\",\n \"data-display-as-preview\": String(node.attrs.displayAsPreview === true),\n class: \"mention-chip\",\n }),\n ...children,\n ];\n },\n\n renderText({ node }) {\n return serializeMention(node.attrs as MentionNodeAttrs);\n },\n});\n"],"names":["params","Node","mergeAttributes"],"mappings":"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"}
1
+ {"version":3,"file":"mention-extension.cjs","sources":["../../../src/chat-ui/mentions/mention-extension.ts"],"sourcesContent":["/**\n * Tiptap Mention Node Extension\n *\n * Defines the mention node type used in the editor. When a mention is\n * inserted, it creates a non-editable inline node with metadata.\n *\n * Renders as a chip with: [icon] Name. Serializes — to plain text and to\n * Markdown alike — as `[@Name](url)`, and parses that link back into a chip.\n */\n\nimport { mergeAttributes, Node } from \"@tiptap/core\";\nimport type { Node as ProseMirrorNode } from \"@tiptap/pm/model\";\n\nexport function makeMentionUrl(attrs: { id: string; type: string; name: string; title?: string; url?: string; description?: string; thumbnailUrl?: string; assetType?: string; href?: string }): string {\n if (attrs.href && (attrs.href.startsWith(\"mention://\") || ASSET_MENTION_HREF.test(attrs.href))) {\n return attrs.href;\n }\n if (attrs.type === \"tool\" || attrs.type === \"toolkit\") {\n return `mention://${attrs.type}?name=${encodeURIComponent(attrs.id)}&description=${encodeURIComponent(attrs.description ?? \"\")}`;\n }\n if (attrs.type === \"asset\") {\n const params = new URLSearchParams({ asset_ids: attrs.id });\n if (attrs.assetType) params.set(\"assetType\", attrs.assetType);\n return `/dashboard/spaces?${params.toString()}`;\n }\n if (attrs.type === \"user\") {\n // The canonical person reference Olympus writes (packages/references\n // `encodeToPersonMentionUri`); members are keyed on their email.\n const params = new URLSearchParams({ id: attrs.id });\n if (attrs.id.includes(\"@\")) params.set(\"email\", attrs.id);\n return `mention://person?${params.toString()}`;\n }\n if (attrs.type === \"tab\") {\n const params = new URLSearchParams({\n tab_id: attrs.id,\n url: attrs.url ?? \"\",\n title: attrs.title ?? attrs.name,\n });\n return `mention://webpage?${params.toString()}`;\n }\n // Include athenaOriginalType and thumbnailUrl so the chip can be reconstructed\n const params = new URLSearchParams();\n params.set(\"id\", attrs.id);\n if (attrs.assetType) params.set(\"assetType\", attrs.assetType);\n if (attrs.thumbnailUrl) params.set(\"thumb\", attrs.thumbnailUrl);\n return `mention://${encodeURIComponent(attrs.type)}?${params.toString()}`;\n}\n\nexport interface MentionNodeAttrs {\n type: string;\n name: string;\n id: string;\n title?: string;\n url?: string;\n description?: string;\n thumbnailUrl?: string;\n fallbackIcon?: string;\n assetType?: string;\n displayAsPreview?: boolean;\n href?: string;\n}\n\n/** The `[@Name](url)` form a mention takes in the outgoing text. */\nexport function serializeMention(attrs: MentionNodeAttrs): string {\n return `[@${attrs.name}](${makeMentionUrl(attrs)})`;\n}\n\nexport function parseMentionAnchorAttributes({\n href,\n textContent,\n}: {\n href: string;\n textContent: string | null;\n}): MentionNodeAttrs {\n const url = new URL(href);\n const isWebpage = url.hostname === \"webpage\";\n const isPerson = url.hostname === \"person\";\n return {\n href,\n type: isWebpage ? \"tab\" : isPerson ? \"user\" : url.hostname || \"tool\",\n name: textContent?.replace(/^@/, \"\") || \"\",\n id: isWebpage\n ? url.searchParams.get(\"tab_id\") || \"\"\n : isPerson\n ? url.searchParams.get(\"id\") || url.searchParams.get(\"email\") || \"\"\n : url.searchParams.get(\"name\") || url.searchParams.get(\"id\") || \"\",\n title: url.searchParams.get(\"title\") || \"\",\n url: url.searchParams.get(\"url\") || \"\",\n description: url.searchParams.get(\"description\") || \"\",\n thumbnailUrl: url.searchParams.get(\"thumb\") || \"\",\n fallbackIcon: \"\",\n assetType: url.searchParams.get(\"assetType\") || \"\",\n };\n}\n\n/** The asset link `makeMentionUrl` writes (`/dashboard/spaces?asset_ids=…`). */\nconst ASSET_MENTION_HREF = /^(?:https?:\\/\\/[^/]+)?\\/dashboard\\/spaces\\/?\\?/;\n\n/**\n * Attributes of an asset mention from its `/dashboard/spaces?asset_ids=…`\n * link, or null for a spaces link that names no asset.\n */\nexport function parseAssetAnchorAttributes({\n href,\n textContent,\n}: {\n href: string;\n textContent: string | null;\n}): MentionNodeAttrs | null {\n if (!ASSET_MENTION_HREF.test(href)) return null;\n const params = new URLSearchParams(href.slice(href.indexOf(\"?\") + 1));\n const id = params.get(\"asset_ids\");\n if (!id) return null;\n return {\n type: \"asset\",\n href,\n name: textContent?.replace(/^@/, \"\") || \"\",\n id,\n assetType: params.get(\"assetType\") || \"\",\n };\n}\n\ninterface MarkdownSerializerState {\n write(text: string): void;\n}\n\n/**\n * ProseMirror tries parse rules by rule priority (default 50), mark rules\n * before node rules at a tie: a serialized mention is an anchor, so its rules\n * must outrank the Link mark's `a[href]` rule or the chip comes back as a\n * plain link.\n */\nconst ANCHOR_RULE_PRIORITY = 100;\n\nexport const MentionExtension = Node.create({\n name: \"mention\",\n group: \"inline\",\n inline: true,\n selectable: false,\n atom: true,\n\n addAttributes() {\n return {\n href: { default: null },\n type: { default: \"tool\" },\n name: { default: \"\" },\n id: { default: \"\" },\n title: { default: \"\" },\n url: { default: \"\" },\n description: { default: \"\" },\n thumbnailUrl: { default: \"\" },\n fallbackIcon: { default: \"\" },\n assetType: { default: \"\" },\n displayAsPreview: { default: false },\n };\n },\n\n /** tiptap-markdown reads this spec so `getMarkdown()` writes the mention, not HTML. */\n addStorage() {\n return {\n markdown: {\n serialize(state: MarkdownSerializerState, node: ProseMirrorNode) {\n state.write(serializeMention(node.attrs as MentionNodeAttrs));\n },\n parse: {\n // The Markdown parser renders the link to an anchor; parseHTML takes it from there.\n },\n },\n };\n },\n\n parseHTML() {\n return [\n {\n tag: \"span[data-mention]\",\n getAttrs: (dom) => {\n const el = dom as HTMLElement;\n return {\n href: el.getAttribute(\"data-href\"),\n type: el.getAttribute(\"data-type\") || \"tool\",\n name: el.getAttribute(\"data-name\") || el.textContent?.replace(/^@/, \"\") || \"\",\n id: el.getAttribute(\"data-id\") || \"\",\n title: el.getAttribute(\"data-title\") || \"\",\n url: el.getAttribute(\"data-url\") || \"\",\n description: el.getAttribute(\"data-description\") || \"\",\n thumbnailUrl: el.getAttribute(\"data-thumbnail-url\") || \"\",\n fallbackIcon: el.getAttribute(\"data-fallback-icon\") || \"\",\n assetType: el.getAttribute(\"data-asset-type\") || \"\",\n displayAsPreview:\n el.getAttribute(\"data-display-as-preview\") === \"true\",\n };\n },\n },\n {\n tag: \"a[href^='mention://']\",\n priority: ANCHOR_RULE_PRIORITY,\n getAttrs: (dom) => {\n const el = dom as HTMLAnchorElement;\n try {\n return parseMentionAnchorAttributes({\n href: el.getAttribute(\"href\") ?? el.href,\n textContent: el.textContent,\n });\n } catch {\n return false;\n }\n },\n },\n {\n tag: \"a[href]\",\n priority: ANCHOR_RULE_PRIORITY,\n getAttrs: (dom) => {\n const el = dom as HTMLAnchorElement;\n return (\n parseAssetAnchorAttributes({\n href: el.getAttribute(\"href\") ?? \"\",\n textContent: el.textContent,\n }) ?? false\n );\n },\n },\n ];\n },\n\n renderHTML({ node, HTMLAttributes }) {\n const thumbnailUrl = node.attrs.thumbnailUrl;\n\n // Build child elements: [icon] name\n const children: any[] = [];\n\n if (thumbnailUrl) {\n // Render SVG thumbnail as an img element inside the chip\n children.push([\n \"img\",\n {\n src: thumbnailUrl,\n alt: \"\",\n class:\n node.attrs.type === \"user\"\n ? \"mention-chip-icon mention-chip-avatar\"\n : \"mention-chip-icon\",\n draggable: \"false\",\n },\n ]);\n }\n\n children.push([\"span\", { class: \"mention-chip-text\" }, node.attrs.name]);\n\n return [\n \"span\",\n mergeAttributes(HTMLAttributes, {\n \"data-mention\": \"\",\n \"data-href\": node.attrs.href || \"\",\n \"data-type\": node.attrs.type,\n \"data-name\": node.attrs.name,\n \"data-id\": node.attrs.id,\n \"data-title\": node.attrs.title || \"\",\n \"data-url\": node.attrs.url || \"\",\n \"data-thumbnail-url\": node.attrs.thumbnailUrl || \"\",\n \"data-asset-type\": node.attrs.assetType || \"\",\n \"data-display-as-preview\": String(node.attrs.displayAsPreview === true),\n class: \"mention-chip\",\n }),\n ...children,\n ];\n },\n\n renderText({ node }) {\n return serializeMention(node.attrs as MentionNodeAttrs);\n },\n});\n"],"names":["params","Node","mergeAttributes"],"mappings":"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"}
@@ -164,7 +164,7 @@ const MentionExtension = Node.create({
164
164
  {
165
165
  src: thumbnailUrl,
166
166
  alt: "",
167
- class: "mention-chip-icon",
167
+ class: node.attrs.type === "user" ? "mention-chip-icon mention-chip-avatar" : "mention-chip-icon",
168
168
  draggable: "false"
169
169
  }
170
170
  ]);
@@ -1 +1 @@
1
- {"version":3,"file":"mention-extension.js","sources":["../../../src/chat-ui/mentions/mention-extension.ts"],"sourcesContent":["/**\n * Tiptap Mention Node Extension\n *\n * Defines the mention node type used in the editor. When a mention is\n * inserted, it creates a non-editable inline node with metadata.\n *\n * Renders as a chip with: [icon] Name. Serializes — to plain text and to\n * Markdown alike — as `[@Name](url)`, and parses that link back into a chip.\n */\n\nimport { mergeAttributes, Node } from \"@tiptap/core\";\nimport type { Node as ProseMirrorNode } from \"@tiptap/pm/model\";\n\nexport function makeMentionUrl(attrs: { id: string; type: string; name: string; title?: string; url?: string; description?: string; thumbnailUrl?: string; assetType?: string; href?: string }): string {\n if (attrs.href && (attrs.href.startsWith(\"mention://\") || ASSET_MENTION_HREF.test(attrs.href))) {\n return attrs.href;\n }\n if (attrs.type === \"tool\" || attrs.type === \"toolkit\") {\n return `mention://${attrs.type}?name=${encodeURIComponent(attrs.id)}&description=${encodeURIComponent(attrs.description ?? \"\")}`;\n }\n if (attrs.type === \"asset\") {\n const params = new URLSearchParams({ asset_ids: attrs.id });\n if (attrs.assetType) params.set(\"assetType\", attrs.assetType);\n return `/dashboard/spaces?${params.toString()}`;\n }\n if (attrs.type === \"user\") {\n // The canonical person reference Olympus writes (packages/references\n // `encodeToPersonMentionUri`); members are keyed on their email.\n const params = new URLSearchParams({ id: attrs.id });\n if (attrs.id.includes(\"@\")) params.set(\"email\", attrs.id);\n return `mention://person?${params.toString()}`;\n }\n if (attrs.type === \"tab\") {\n const params = new URLSearchParams({\n tab_id: attrs.id,\n url: attrs.url ?? \"\",\n title: attrs.title ?? attrs.name,\n });\n return `mention://webpage?${params.toString()}`;\n }\n // Include athenaOriginalType and thumbnailUrl so the chip can be reconstructed\n const params = new URLSearchParams();\n params.set(\"id\", attrs.id);\n if (attrs.assetType) params.set(\"assetType\", attrs.assetType);\n if (attrs.thumbnailUrl) params.set(\"thumb\", attrs.thumbnailUrl);\n return `mention://${encodeURIComponent(attrs.type)}?${params.toString()}`;\n}\n\nexport interface MentionNodeAttrs {\n type: string;\n name: string;\n id: string;\n title?: string;\n url?: string;\n description?: string;\n thumbnailUrl?: string;\n fallbackIcon?: string;\n assetType?: string;\n displayAsPreview?: boolean;\n href?: string;\n}\n\n/** The `[@Name](url)` form a mention takes in the outgoing text. */\nexport function serializeMention(attrs: MentionNodeAttrs): string {\n return `[@${attrs.name}](${makeMentionUrl(attrs)})`;\n}\n\nexport function parseMentionAnchorAttributes({\n href,\n textContent,\n}: {\n href: string;\n textContent: string | null;\n}): MentionNodeAttrs {\n const url = new URL(href);\n const isWebpage = url.hostname === \"webpage\";\n const isPerson = url.hostname === \"person\";\n return {\n href,\n type: isWebpage ? \"tab\" : isPerson ? \"user\" : url.hostname || \"tool\",\n name: textContent?.replace(/^@/, \"\") || \"\",\n id: isWebpage\n ? url.searchParams.get(\"tab_id\") || \"\"\n : isPerson\n ? url.searchParams.get(\"id\") || url.searchParams.get(\"email\") || \"\"\n : url.searchParams.get(\"name\") || url.searchParams.get(\"id\") || \"\",\n title: url.searchParams.get(\"title\") || \"\",\n url: url.searchParams.get(\"url\") || \"\",\n description: url.searchParams.get(\"description\") || \"\",\n thumbnailUrl: url.searchParams.get(\"thumb\") || \"\",\n fallbackIcon: \"\",\n assetType: url.searchParams.get(\"assetType\") || \"\",\n };\n}\n\n/** The asset link `makeMentionUrl` writes (`/dashboard/spaces?asset_ids=…`). */\nconst ASSET_MENTION_HREF = /^(?:https?:\\/\\/[^/]+)?\\/dashboard\\/spaces\\/?\\?/;\n\n/**\n * Attributes of an asset mention from its `/dashboard/spaces?asset_ids=…`\n * link, or null for a spaces link that names no asset.\n */\nexport function parseAssetAnchorAttributes({\n href,\n textContent,\n}: {\n href: string;\n textContent: string | null;\n}): MentionNodeAttrs | null {\n if (!ASSET_MENTION_HREF.test(href)) return null;\n const params = new URLSearchParams(href.slice(href.indexOf(\"?\") + 1));\n const id = params.get(\"asset_ids\");\n if (!id) return null;\n return {\n type: \"asset\",\n href,\n name: textContent?.replace(/^@/, \"\") || \"\",\n id,\n assetType: params.get(\"assetType\") || \"\",\n };\n}\n\ninterface MarkdownSerializerState {\n write(text: string): void;\n}\n\n/**\n * ProseMirror tries parse rules by rule priority (default 50), mark rules\n * before node rules at a tie: a serialized mention is an anchor, so its rules\n * must outrank the Link mark's `a[href]` rule or the chip comes back as a\n * plain link.\n */\nconst ANCHOR_RULE_PRIORITY = 100;\n\nexport const MentionExtension = Node.create({\n name: \"mention\",\n group: \"inline\",\n inline: true,\n selectable: false,\n atom: true,\n\n addAttributes() {\n return {\n href: { default: null },\n type: { default: \"tool\" },\n name: { default: \"\" },\n id: { default: \"\" },\n title: { default: \"\" },\n url: { default: \"\" },\n description: { default: \"\" },\n thumbnailUrl: { default: \"\" },\n fallbackIcon: { default: \"\" },\n assetType: { default: \"\" },\n displayAsPreview: { default: false },\n };\n },\n\n /** tiptap-markdown reads this spec so `getMarkdown()` writes the mention, not HTML. */\n addStorage() {\n return {\n markdown: {\n serialize(state: MarkdownSerializerState, node: ProseMirrorNode) {\n state.write(serializeMention(node.attrs as MentionNodeAttrs));\n },\n parse: {\n // The Markdown parser renders the link to an anchor; parseHTML takes it from there.\n },\n },\n };\n },\n\n parseHTML() {\n return [\n {\n tag: \"span[data-mention]\",\n getAttrs: (dom) => {\n const el = dom as HTMLElement;\n return {\n href: el.getAttribute(\"data-href\"),\n type: el.getAttribute(\"data-type\") || \"tool\",\n name: el.getAttribute(\"data-name\") || el.textContent?.replace(/^@/, \"\") || \"\",\n id: el.getAttribute(\"data-id\") || \"\",\n title: el.getAttribute(\"data-title\") || \"\",\n url: el.getAttribute(\"data-url\") || \"\",\n description: el.getAttribute(\"data-description\") || \"\",\n thumbnailUrl: el.getAttribute(\"data-thumbnail-url\") || \"\",\n fallbackIcon: el.getAttribute(\"data-fallback-icon\") || \"\",\n assetType: el.getAttribute(\"data-asset-type\") || \"\",\n displayAsPreview:\n el.getAttribute(\"data-display-as-preview\") === \"true\",\n };\n },\n },\n {\n tag: \"a[href^='mention://']\",\n priority: ANCHOR_RULE_PRIORITY,\n getAttrs: (dom) => {\n const el = dom as HTMLAnchorElement;\n try {\n return parseMentionAnchorAttributes({\n href: el.getAttribute(\"href\") ?? el.href,\n textContent: el.textContent,\n });\n } catch {\n return false;\n }\n },\n },\n {\n tag: \"a[href]\",\n priority: ANCHOR_RULE_PRIORITY,\n getAttrs: (dom) => {\n const el = dom as HTMLAnchorElement;\n return (\n parseAssetAnchorAttributes({\n href: el.getAttribute(\"href\") ?? \"\",\n textContent: el.textContent,\n }) ?? false\n );\n },\n },\n ];\n },\n\n renderHTML({ node, HTMLAttributes }) {\n const thumbnailUrl = node.attrs.thumbnailUrl;\n\n // Build child elements: [icon] name\n const children: any[] = [];\n\n if (thumbnailUrl) {\n // Render SVG thumbnail as an img element inside the chip\n children.push([\n \"img\",\n {\n src: thumbnailUrl,\n alt: \"\",\n class: \"mention-chip-icon\",\n draggable: \"false\",\n },\n ]);\n }\n\n children.push([\"span\", { class: \"mention-chip-text\" }, node.attrs.name]);\n\n return [\n \"span\",\n mergeAttributes(HTMLAttributes, {\n \"data-mention\": \"\",\n \"data-href\": node.attrs.href || \"\",\n \"data-type\": node.attrs.type,\n \"data-name\": node.attrs.name,\n \"data-id\": node.attrs.id,\n \"data-title\": node.attrs.title || \"\",\n \"data-url\": node.attrs.url || \"\",\n \"data-thumbnail-url\": node.attrs.thumbnailUrl || \"\",\n \"data-asset-type\": node.attrs.assetType || \"\",\n \"data-display-as-preview\": String(node.attrs.displayAsPreview === true),\n class: \"mention-chip\",\n }),\n ...children,\n ];\n },\n\n renderText({ node }) {\n return serializeMention(node.attrs as MentionNodeAttrs);\n },\n});\n"],"names":["params"],"mappings":"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"}
1
+ {"version":3,"file":"mention-extension.js","sources":["../../../src/chat-ui/mentions/mention-extension.ts"],"sourcesContent":["/**\n * Tiptap Mention Node Extension\n *\n * Defines the mention node type used in the editor. When a mention is\n * inserted, it creates a non-editable inline node with metadata.\n *\n * Renders as a chip with: [icon] Name. Serializes — to plain text and to\n * Markdown alike — as `[@Name](url)`, and parses that link back into a chip.\n */\n\nimport { mergeAttributes, Node } from \"@tiptap/core\";\nimport type { Node as ProseMirrorNode } from \"@tiptap/pm/model\";\n\nexport function makeMentionUrl(attrs: { id: string; type: string; name: string; title?: string; url?: string; description?: string; thumbnailUrl?: string; assetType?: string; href?: string }): string {\n if (attrs.href && (attrs.href.startsWith(\"mention://\") || ASSET_MENTION_HREF.test(attrs.href))) {\n return attrs.href;\n }\n if (attrs.type === \"tool\" || attrs.type === \"toolkit\") {\n return `mention://${attrs.type}?name=${encodeURIComponent(attrs.id)}&description=${encodeURIComponent(attrs.description ?? \"\")}`;\n }\n if (attrs.type === \"asset\") {\n const params = new URLSearchParams({ asset_ids: attrs.id });\n if (attrs.assetType) params.set(\"assetType\", attrs.assetType);\n return `/dashboard/spaces?${params.toString()}`;\n }\n if (attrs.type === \"user\") {\n // The canonical person reference Olympus writes (packages/references\n // `encodeToPersonMentionUri`); members are keyed on their email.\n const params = new URLSearchParams({ id: attrs.id });\n if (attrs.id.includes(\"@\")) params.set(\"email\", attrs.id);\n return `mention://person?${params.toString()}`;\n }\n if (attrs.type === \"tab\") {\n const params = new URLSearchParams({\n tab_id: attrs.id,\n url: attrs.url ?? \"\",\n title: attrs.title ?? attrs.name,\n });\n return `mention://webpage?${params.toString()}`;\n }\n // Include athenaOriginalType and thumbnailUrl so the chip can be reconstructed\n const params = new URLSearchParams();\n params.set(\"id\", attrs.id);\n if (attrs.assetType) params.set(\"assetType\", attrs.assetType);\n if (attrs.thumbnailUrl) params.set(\"thumb\", attrs.thumbnailUrl);\n return `mention://${encodeURIComponent(attrs.type)}?${params.toString()}`;\n}\n\nexport interface MentionNodeAttrs {\n type: string;\n name: string;\n id: string;\n title?: string;\n url?: string;\n description?: string;\n thumbnailUrl?: string;\n fallbackIcon?: string;\n assetType?: string;\n displayAsPreview?: boolean;\n href?: string;\n}\n\n/** The `[@Name](url)` form a mention takes in the outgoing text. */\nexport function serializeMention(attrs: MentionNodeAttrs): string {\n return `[@${attrs.name}](${makeMentionUrl(attrs)})`;\n}\n\nexport function parseMentionAnchorAttributes({\n href,\n textContent,\n}: {\n href: string;\n textContent: string | null;\n}): MentionNodeAttrs {\n const url = new URL(href);\n const isWebpage = url.hostname === \"webpage\";\n const isPerson = url.hostname === \"person\";\n return {\n href,\n type: isWebpage ? \"tab\" : isPerson ? \"user\" : url.hostname || \"tool\",\n name: textContent?.replace(/^@/, \"\") || \"\",\n id: isWebpage\n ? url.searchParams.get(\"tab_id\") || \"\"\n : isPerson\n ? url.searchParams.get(\"id\") || url.searchParams.get(\"email\") || \"\"\n : url.searchParams.get(\"name\") || url.searchParams.get(\"id\") || \"\",\n title: url.searchParams.get(\"title\") || \"\",\n url: url.searchParams.get(\"url\") || \"\",\n description: url.searchParams.get(\"description\") || \"\",\n thumbnailUrl: url.searchParams.get(\"thumb\") || \"\",\n fallbackIcon: \"\",\n assetType: url.searchParams.get(\"assetType\") || \"\",\n };\n}\n\n/** The asset link `makeMentionUrl` writes (`/dashboard/spaces?asset_ids=…`). */\nconst ASSET_MENTION_HREF = /^(?:https?:\\/\\/[^/]+)?\\/dashboard\\/spaces\\/?\\?/;\n\n/**\n * Attributes of an asset mention from its `/dashboard/spaces?asset_ids=…`\n * link, or null for a spaces link that names no asset.\n */\nexport function parseAssetAnchorAttributes({\n href,\n textContent,\n}: {\n href: string;\n textContent: string | null;\n}): MentionNodeAttrs | null {\n if (!ASSET_MENTION_HREF.test(href)) return null;\n const params = new URLSearchParams(href.slice(href.indexOf(\"?\") + 1));\n const id = params.get(\"asset_ids\");\n if (!id) return null;\n return {\n type: \"asset\",\n href,\n name: textContent?.replace(/^@/, \"\") || \"\",\n id,\n assetType: params.get(\"assetType\") || \"\",\n };\n}\n\ninterface MarkdownSerializerState {\n write(text: string): void;\n}\n\n/**\n * ProseMirror tries parse rules by rule priority (default 50), mark rules\n * before node rules at a tie: a serialized mention is an anchor, so its rules\n * must outrank the Link mark's `a[href]` rule or the chip comes back as a\n * plain link.\n */\nconst ANCHOR_RULE_PRIORITY = 100;\n\nexport const MentionExtension = Node.create({\n name: \"mention\",\n group: \"inline\",\n inline: true,\n selectable: false,\n atom: true,\n\n addAttributes() {\n return {\n href: { default: null },\n type: { default: \"tool\" },\n name: { default: \"\" },\n id: { default: \"\" },\n title: { default: \"\" },\n url: { default: \"\" },\n description: { default: \"\" },\n thumbnailUrl: { default: \"\" },\n fallbackIcon: { default: \"\" },\n assetType: { default: \"\" },\n displayAsPreview: { default: false },\n };\n },\n\n /** tiptap-markdown reads this spec so `getMarkdown()` writes the mention, not HTML. */\n addStorage() {\n return {\n markdown: {\n serialize(state: MarkdownSerializerState, node: ProseMirrorNode) {\n state.write(serializeMention(node.attrs as MentionNodeAttrs));\n },\n parse: {\n // The Markdown parser renders the link to an anchor; parseHTML takes it from there.\n },\n },\n };\n },\n\n parseHTML() {\n return [\n {\n tag: \"span[data-mention]\",\n getAttrs: (dom) => {\n const el = dom as HTMLElement;\n return {\n href: el.getAttribute(\"data-href\"),\n type: el.getAttribute(\"data-type\") || \"tool\",\n name: el.getAttribute(\"data-name\") || el.textContent?.replace(/^@/, \"\") || \"\",\n id: el.getAttribute(\"data-id\") || \"\",\n title: el.getAttribute(\"data-title\") || \"\",\n url: el.getAttribute(\"data-url\") || \"\",\n description: el.getAttribute(\"data-description\") || \"\",\n thumbnailUrl: el.getAttribute(\"data-thumbnail-url\") || \"\",\n fallbackIcon: el.getAttribute(\"data-fallback-icon\") || \"\",\n assetType: el.getAttribute(\"data-asset-type\") || \"\",\n displayAsPreview:\n el.getAttribute(\"data-display-as-preview\") === \"true\",\n };\n },\n },\n {\n tag: \"a[href^='mention://']\",\n priority: ANCHOR_RULE_PRIORITY,\n getAttrs: (dom) => {\n const el = dom as HTMLAnchorElement;\n try {\n return parseMentionAnchorAttributes({\n href: el.getAttribute(\"href\") ?? el.href,\n textContent: el.textContent,\n });\n } catch {\n return false;\n }\n },\n },\n {\n tag: \"a[href]\",\n priority: ANCHOR_RULE_PRIORITY,\n getAttrs: (dom) => {\n const el = dom as HTMLAnchorElement;\n return (\n parseAssetAnchorAttributes({\n href: el.getAttribute(\"href\") ?? \"\",\n textContent: el.textContent,\n }) ?? false\n );\n },\n },\n ];\n },\n\n renderHTML({ node, HTMLAttributes }) {\n const thumbnailUrl = node.attrs.thumbnailUrl;\n\n // Build child elements: [icon] name\n const children: any[] = [];\n\n if (thumbnailUrl) {\n // Render SVG thumbnail as an img element inside the chip\n children.push([\n \"img\",\n {\n src: thumbnailUrl,\n alt: \"\",\n class:\n node.attrs.type === \"user\"\n ? \"mention-chip-icon mention-chip-avatar\"\n : \"mention-chip-icon\",\n draggable: \"false\",\n },\n ]);\n }\n\n children.push([\"span\", { class: \"mention-chip-text\" }, node.attrs.name]);\n\n return [\n \"span\",\n mergeAttributes(HTMLAttributes, {\n \"data-mention\": \"\",\n \"data-href\": node.attrs.href || \"\",\n \"data-type\": node.attrs.type,\n \"data-name\": node.attrs.name,\n \"data-id\": node.attrs.id,\n \"data-title\": node.attrs.title || \"\",\n \"data-url\": node.attrs.url || \"\",\n \"data-thumbnail-url\": node.attrs.thumbnailUrl || \"\",\n \"data-asset-type\": node.attrs.assetType || \"\",\n \"data-display-as-preview\": String(node.attrs.displayAsPreview === true),\n class: \"mention-chip\",\n }),\n ...children,\n ];\n },\n\n renderText({ node }) {\n return serializeMention(node.attrs as MentionNodeAttrs);\n },\n});\n"],"names":["params"],"mappings":"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"}