@athenaintel/react 0.40.3 → 0.41.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -0
- package/dist/chat/athena-citation-links.cjs +23 -2
- package/dist/chat/athena-citation-links.cjs.map +1 -1
- package/dist/chat/athena-citation-links.d.ts +5 -0
- package/dist/chat/athena-citation-links.js +23 -2
- package/dist/chat/athena-citation-links.js.map +1 -1
- package/dist/chat-ui/index.d.ts +1 -1
- package/dist/chat-ui/lib/asset-urls.cjs +6 -1
- package/dist/chat-ui/lib/asset-urls.cjs.map +1 -1
- package/dist/chat-ui/lib/asset-urls.d.ts +6 -1
- package/dist/chat-ui/lib/asset-urls.js +7 -2
- package/dist/chat-ui/lib/asset-urls.js.map +1 -1
- package/dist/chat-ui/mentions/mention-chip.cjs +118 -7
- package/dist/chat-ui/mentions/mention-chip.cjs.map +1 -1
- package/dist/chat-ui/mentions/mention-chip.d.ts +29 -0
- package/dist/chat-ui/mentions/mention-chip.js +120 -9
- package/dist/chat-ui/mentions/mention-chip.js.map +1 -1
- package/dist/chat-ui/rich-text/chat-message-body.cjs +2 -1
- package/dist/chat-ui/rich-text/chat-message-body.cjs.map +1 -1
- package/dist/chat-ui/rich-text/chat-message-body.js +2 -1
- package/dist/chat-ui/rich-text/chat-message-body.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/lib/athena-urls.cjs +17 -0
- package/dist/lib/athena-urls.cjs.map +1 -1
- package/dist/lib/athena-urls.d.ts +14 -0
- package/dist/lib/athena-urls.js +17 -0
- package/dist/lib/athena-urls.js.map +1 -1
- package/dist/react/package.json.cjs +1 -1
- package/dist/react/package.json.js +1 -1
- package/dist/references/src/anchors/file-line-range.cjs +70 -0
- package/dist/references/src/anchors/file-line-range.cjs.map +1 -0
- package/dist/references/src/anchors/file-line-range.js +70 -0
- package/dist/references/src/anchors/file-line-range.js.map +1 -0
- package/dist/references/src/anchors/format.cjs +219 -0
- package/dist/references/src/anchors/format.cjs.map +1 -0
- package/dist/references/src/anchors/format.js +219 -0
- package/dist/references/src/anchors/format.js.map +1 -0
- package/dist/references/src/anchors/parse.cjs +408 -0
- package/dist/references/src/anchors/parse.cjs.map +1 -0
- package/dist/references/src/anchors/parse.js +408 -0
- package/dist/references/src/anchors/parse.js.map +1 -0
- package/dist/references/src/anchors/serialize.cjs +185 -0
- package/dist/references/src/anchors/serialize.cjs.map +1 -0
- package/dist/references/src/anchors/serialize.js +185 -0
- package/dist/references/src/anchors/serialize.js.map +1 -0
- package/dist/references/src/anchors/types.cjs +50 -0
- package/dist/references/src/anchors/types.cjs.map +1 -0
- package/dist/references/src/anchors/types.js +50 -0
- package/dist/references/src/anchors/types.js.map +1 -0
- package/dist/references/src/formats/uri/asset-mention-uri.cjs +29 -0
- package/dist/references/src/formats/uri/asset-mention-uri.cjs.map +1 -0
- package/dist/references/src/formats/uri/asset-mention-uri.js +29 -0
- package/dist/references/src/formats/uri/asset-mention-uri.js.map +1 -0
- package/dist/references/src/formats/uri/asset-spaces-url.cjs +64 -0
- package/dist/references/src/formats/uri/asset-spaces-url.cjs.map +1 -0
- package/dist/references/src/formats/uri/asset-spaces-url.js +64 -0
- package/dist/references/src/formats/uri/asset-spaces-url.js.map +1 -0
- package/dist/references/src/formats/uri/template-placeholder.cjs +9 -0
- package/dist/references/src/formats/uri/template-placeholder.cjs.map +1 -0
- package/dist/references/src/formats/uri/template-placeholder.js +9 -0
- package/dist/references/src/formats/uri/template-placeholder.js.map +1 -0
- package/dist/references/src/sheet-name.cjs +10 -0
- package/dist/references/src/sheet-name.cjs.map +1 -0
- package/dist/references/src/sheet-name.js +10 -0
- package/dist/references/src/sheet-name.js.map +1 -0
- package/dist/references/src/version-ref.cjs +61 -0
- package/dist/references/src/version-ref.cjs.map +1 -0
- package/dist/references/src/version-ref.js +61 -0
- package/dist/references/src/version-ref.js.map +1 -0
- package/dist/styles.css +1 -1
- package/dist/workbench/session-workbench-model.cjs +0 -18
- package/dist/workbench/session-workbench-model.cjs.map +1 -1
- package/dist/workbench/session-workbench-model.d.ts +0 -13
- package/dist/workbench/session-workbench-model.js +0 -18
- package/dist/workbench/session-workbench-model.js.map +1 -1
- package/dist/workbench/session-workbench-panes.cjs +1 -1
- package/dist/workbench/session-workbench-panes.cjs.map +1 -1
- package/dist/workbench/session-workbench-panes.js +2 -2
- package/dist/workbench/session-workbench-panes.js.map +1 -1
- package/package.json +3 -2
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const jsxRuntime = require("react/jsx-runtime");
|
|
4
4
|
const react = require("react");
|
|
5
|
+
const format = require("../../references/src/anchors/format.cjs");
|
|
6
|
+
const assetMentionUri = require("../../references/src/formats/uri/asset-mention-uri.cjs");
|
|
7
|
+
const assetSpacesUrl = require("../../references/src/formats/uri/asset-spaces-url.cjs");
|
|
5
8
|
const lucideReact = require("lucide-react");
|
|
6
9
|
const assetUrls = require("../lib/asset-urls.cjs");
|
|
7
10
|
const apiClient = require("./suggestions/data/api-client.cjs");
|
|
@@ -17,6 +20,79 @@ function getSpacesAssetType(url) {
|
|
|
17
20
|
const assetType = url.searchParams.get("assetType") || url.searchParams.get("asset_type") || url.searchParams.get("type");
|
|
18
21
|
return assetType === "sheet" ? "spreadsheet" : assetType;
|
|
19
22
|
}
|
|
23
|
+
function citationOf(reference) {
|
|
24
|
+
const { anchor, meta } = reference;
|
|
25
|
+
if (!anchor) return void 0;
|
|
26
|
+
const label = format.formatReferenceAnchor(anchor, "short", meta);
|
|
27
|
+
if (!label) return void 0;
|
|
28
|
+
const { excerpt: _excerpt, ...place } = meta ?? {};
|
|
29
|
+
return {
|
|
30
|
+
anchorType: anchor.type,
|
|
31
|
+
label,
|
|
32
|
+
description: format.formatReferenceAnchor(anchor, "long", place) ?? label
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const ASSET_CHIP_ICONS = {
|
|
36
|
+
document: lucideReact.FileTextIcon,
|
|
37
|
+
word_document: lucideReact.FileTextIcon,
|
|
38
|
+
markdown: lucideReact.FileTextIcon,
|
|
39
|
+
txt: lucideReact.FileTextIcon,
|
|
40
|
+
pdf: lucideReact.FileTextIcon,
|
|
41
|
+
spreadsheet: lucideReact.SheetIcon,
|
|
42
|
+
table: lucideReact.SheetIcon,
|
|
43
|
+
presentation: lucideReact.PresentationIcon,
|
|
44
|
+
powerpoint_deck: lucideReact.PresentationIcon,
|
|
45
|
+
presentation_pptx_studio: lucideReact.PresentationIcon,
|
|
46
|
+
pptx: lucideReact.PresentationIcon,
|
|
47
|
+
image: lucideReact.ImageIcon,
|
|
48
|
+
figure: lucideReact.ImageIcon,
|
|
49
|
+
canvas: lucideReact.ImageIcon,
|
|
50
|
+
video: lucideReact.VideoIcon,
|
|
51
|
+
meeting: lucideReact.VideoIcon,
|
|
52
|
+
video_session: lucideReact.VideoIcon,
|
|
53
|
+
audio: lucideReact.HeadphonesIcon,
|
|
54
|
+
voice_memo: lucideReact.HeadphonesIcon,
|
|
55
|
+
phone_call: lucideReact.HeadphonesIcon,
|
|
56
|
+
email: lucideReact.MailIcon,
|
|
57
|
+
email_message: lucideReact.MailIcon,
|
|
58
|
+
live_email_thread: lucideReact.MailIcon,
|
|
59
|
+
live_calendar_event: lucideReact.MailIcon,
|
|
60
|
+
python: lucideReact.FileCodeIcon,
|
|
61
|
+
javascript: lucideReact.FileCodeIcon,
|
|
62
|
+
typescript: lucideReact.FileCodeIcon,
|
|
63
|
+
json: lucideReact.FileCodeIcon,
|
|
64
|
+
yaml: lucideReact.FileCodeIcon,
|
|
65
|
+
database: lucideReact.DatabaseIcon,
|
|
66
|
+
catalog: lucideReact.DatabaseIcon,
|
|
67
|
+
drive: lucideReact.DatabaseIcon,
|
|
68
|
+
dataset: lucideReact.DatabaseIcon,
|
|
69
|
+
semantic_model: lucideReact.DatabaseIcon,
|
|
70
|
+
query_snippet: lucideReact.DatabaseIcon,
|
|
71
|
+
folder: lucideReact.FolderIcon,
|
|
72
|
+
gdrive_folder: lucideReact.FolderIcon,
|
|
73
|
+
project: lucideReact.FolderIcon,
|
|
74
|
+
collection: lucideReact.FolderIcon,
|
|
75
|
+
session: lucideReact.MessageSquareIcon,
|
|
76
|
+
computer: lucideReact.MonitorIcon,
|
|
77
|
+
desktop_session: lucideReact.MonitorIcon,
|
|
78
|
+
user_interface: lucideReact.MonitorIcon,
|
|
79
|
+
application: lucideReact.MonitorIcon,
|
|
80
|
+
html: lucideReact.Globe2Icon,
|
|
81
|
+
web_page: lucideReact.Globe2Icon,
|
|
82
|
+
browse_session: lucideReact.Globe2Icon
|
|
83
|
+
};
|
|
84
|
+
function assetChipIcon(assetType, citation) {
|
|
85
|
+
if ((citation == null ? void 0 : citation.anchorType) === "file_line_range") return lucideReact.FileCodeIcon;
|
|
86
|
+
return assetType && ASSET_CHIP_ICONS[assetType.toLowerCase()] || lucideReact.FileIcon;
|
|
87
|
+
}
|
|
88
|
+
const SPEAKABLE = /[\p{L}\p{N}]/u;
|
|
89
|
+
function decodeAssetLink(url, decode) {
|
|
90
|
+
try {
|
|
91
|
+
return decode(url);
|
|
92
|
+
} catch {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
20
96
|
function personEmailForMention(parsed) {
|
|
21
97
|
if (parsed.type !== "person") return void 0;
|
|
22
98
|
return parsed.email || (parsed.id.includes("@") ? parsed.id : void 0);
|
|
@@ -32,8 +108,10 @@ const MentionLink = ({ href, children, interactive = true }) => {
|
|
|
32
108
|
type: parsed.type,
|
|
33
109
|
personEmail: personEmailForMention(parsed),
|
|
34
110
|
assetIds: parsed.assetIds,
|
|
111
|
+
assetType: parsed.assetType,
|
|
112
|
+
citation: parsed.citation,
|
|
35
113
|
thumbnailUrl: parsed.thumbnailUrl,
|
|
36
|
-
sourceHref: isSpacesMentionUrl(href) ? href :
|
|
114
|
+
sourceHref: isSpacesMentionUrl(href) ? href : parsed.sourceHref,
|
|
37
115
|
interactive
|
|
38
116
|
}
|
|
39
117
|
);
|
|
@@ -75,6 +153,8 @@ const MentionChip = ({
|
|
|
75
153
|
name,
|
|
76
154
|
type,
|
|
77
155
|
assetIds,
|
|
156
|
+
assetType,
|
|
157
|
+
citation,
|
|
78
158
|
personEmail,
|
|
79
159
|
thumbnailUrl,
|
|
80
160
|
sourceHref,
|
|
@@ -84,6 +164,8 @@ const MentionChip = ({
|
|
|
84
164
|
const memberAvatarUrl = useMemberAvatar.useMemberAvatarUrl(personEmail);
|
|
85
165
|
const [erroredAvatarUrl, setErroredAvatarUrl] = react.useState();
|
|
86
166
|
const excerpt = sourceHref ? readSourceExcerpt(sourceHref) : null;
|
|
167
|
+
const tooltip = excerpt ?? (citation == null ? void 0 : citation.description) ?? null;
|
|
168
|
+
const anchorLabel = citation && name !== citation.description && name !== citation.label ? citation.label : null;
|
|
87
169
|
const assetRootHref = type === "asset" && (assetIds == null ? void 0 : assetIds.length) ? assetUrls.createOlympusAssetUrl({
|
|
88
170
|
appUrl: auth == null ? void 0 : auth.appUrl,
|
|
89
171
|
backendUrl: auth == null ? void 0 : auth.backendUrl,
|
|
@@ -105,8 +187,11 @@ const MentionChip = ({
|
|
|
105
187
|
})();
|
|
106
188
|
const className = cnfast.cn(
|
|
107
189
|
"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",
|
|
190
|
+
// The cited place gets its own room after a name already at its cap.
|
|
191
|
+
anchorLabel && "max-w-[340px]",
|
|
108
192
|
href && "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"
|
|
109
193
|
);
|
|
194
|
+
const AssetIcon = assetChipIcon(assetType, citation);
|
|
110
195
|
const contents = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
111
196
|
thumbnailUrl && /* @__PURE__ */ jsxRuntime.jsx(
|
|
112
197
|
"img",
|
|
@@ -117,6 +202,9 @@ const MentionChip = ({
|
|
|
117
202
|
draggable: false
|
|
118
203
|
}
|
|
119
204
|
),
|
|
205
|
+
!thumbnailUrl && (type === "asset" || type === "folder") && // A host that bundles no asset-type SVGs (computer apps, most SDK
|
|
206
|
+
// apps) still gets the type's icon.
|
|
207
|
+
/* @__PURE__ */ jsxRuntime.jsx(AssetIcon, { className: "size-3.5 shrink-0", "aria-hidden": "true" }),
|
|
120
208
|
!thumbnailUrl && type === "webpage" && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Globe2Icon, { className: "size-3.5 shrink-0", "aria-hidden": "true" }),
|
|
121
209
|
!thumbnailUrl && type === "person" && (memberAvatarUrl && erroredAvatarUrl !== memberAvatarUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
122
210
|
"img",
|
|
@@ -128,7 +216,18 @@ const MentionChip = ({
|
|
|
128
216
|
onError: () => setErroredAvatarUrl(memberAvatarUrl)
|
|
129
217
|
}
|
|
130
218
|
) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.UserIcon, { className: "size-3.5 shrink-0", "aria-hidden": "true" })),
|
|
131
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "max-w-[180px] overflow-hidden text-ellipsis", children: name })
|
|
219
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "max-w-[180px] overflow-hidden text-ellipsis", children: name }),
|
|
220
|
+
citation && anchorLabel && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
221
|
+
" ",
|
|
222
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
223
|
+
"span",
|
|
224
|
+
{
|
|
225
|
+
className: "max-w-[140px] shrink-0 overflow-hidden text-ellipsis font-normal text-slate-400",
|
|
226
|
+
"data-citation-anchor": citation.anchorType,
|
|
227
|
+
children: anchorLabel
|
|
228
|
+
}
|
|
229
|
+
)
|
|
230
|
+
] })
|
|
132
231
|
] });
|
|
133
232
|
if (href && interactive) {
|
|
134
233
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -138,9 +237,10 @@ const MentionChip = ({
|
|
|
138
237
|
target: "_blank",
|
|
139
238
|
rel: "noopener noreferrer",
|
|
140
239
|
className,
|
|
141
|
-
"aria-label": `Open ${name} in Spaces`,
|
|
142
|
-
title:
|
|
240
|
+
"aria-label": anchorLabel && citation && SPEAKABLE.test(citation.description) ? `Open ${name} at ${citation.description} in Spaces` : `Open ${name} in Spaces`,
|
|
241
|
+
title: tooltip ?? `Open ${name} in Spaces`,
|
|
143
242
|
"data-excerpt": excerpt ?? void 0,
|
|
243
|
+
"data-mention-name": name,
|
|
144
244
|
children: contents
|
|
145
245
|
}
|
|
146
246
|
);
|
|
@@ -149,7 +249,7 @@ const MentionChip = ({
|
|
|
149
249
|
"span",
|
|
150
250
|
{
|
|
151
251
|
className,
|
|
152
|
-
title:
|
|
252
|
+
title: tooltip ?? void 0,
|
|
153
253
|
"data-excerpt": excerpt ?? void 0,
|
|
154
254
|
children: contents
|
|
155
255
|
}
|
|
@@ -172,17 +272,22 @@ function citationFallbackLabel(params) {
|
|
|
172
272
|
return "Asset";
|
|
173
273
|
}
|
|
174
274
|
function parseMentionUrl(url) {
|
|
275
|
+
var _a, _b;
|
|
175
276
|
try {
|
|
176
277
|
if (isSpacesMentionUrl(url)) {
|
|
177
278
|
const parsed = new URL(url, "https://app.athenaintel.com");
|
|
178
279
|
const assetIds = (parsed.searchParams.get("asset_ids") ?? "").split(",").map((id3) => id3.trim()).filter(Boolean);
|
|
179
280
|
const id2 = assetIds[0] ?? "";
|
|
180
281
|
const assetType = getSpacesAssetType(parsed);
|
|
282
|
+
const reference2 = assetIds.length === 1 ? decodeAssetLink(parsed, assetSpacesUrl.decodeFromAssetSpacesUrl) : null;
|
|
283
|
+
const citation = reference2 ? citationOf(reference2) : void 0;
|
|
181
284
|
return {
|
|
182
285
|
type: "asset",
|
|
183
|
-
name: assetIds.length > 1 ? `${assetIds.length} assets` : citationFallbackLabel(parsed.searchParams),
|
|
286
|
+
name: assetIds.length > 1 ? `${assetIds.length} assets` : (citation == null ? void 0 : citation.description) ?? citationFallbackLabel(parsed.searchParams),
|
|
184
287
|
id: id2,
|
|
185
288
|
assetIds,
|
|
289
|
+
assetType: assetType ?? void 0,
|
|
290
|
+
citation,
|
|
186
291
|
thumbnailUrl: assetTypeIcons.assetTypeToThumb(assetType)
|
|
187
292
|
};
|
|
188
293
|
}
|
|
@@ -200,12 +305,16 @@ function parseMentionUrl(url) {
|
|
|
200
305
|
thumbnailUrl = assetTypeIcons.assetTypeToThumb(type === "folder" ? "folder" : null);
|
|
201
306
|
}
|
|
202
307
|
}
|
|
308
|
+
const reference = type === "asset" ? decodeAssetLink(u, assetMentionUri.decodeFromAssetMentionUri) : null;
|
|
203
309
|
return {
|
|
204
310
|
type,
|
|
205
311
|
name,
|
|
206
312
|
id,
|
|
207
313
|
email: type === "person" ? u.searchParams.get("email") || void 0 : void 0,
|
|
208
314
|
assetIds: type === "asset" && id ? [id] : void 0,
|
|
315
|
+
assetType: type === "asset" || type === "folder" ? u.searchParams.get("assetType") || ((_a = reference == null ? void 0 : reference.meta) == null ? void 0 : _a.assetType) || (type === "folder" ? "folder" : void 0) : void 0,
|
|
316
|
+
citation: reference ? citationOf(reference) : void 0,
|
|
317
|
+
sourceHref: reference && (reference.anchor || ((_b = reference.meta) == null ? void 0 : _b.excerpt)) ? assetSpacesUrl.encodeToAssetSpacesUrl(reference) : void 0,
|
|
209
318
|
thumbnailUrl
|
|
210
319
|
};
|
|
211
320
|
} catch {
|
|
@@ -271,8 +380,10 @@ function parseMentionsInText(text) {
|
|
|
271
380
|
type: parsed.type,
|
|
272
381
|
personEmail: personEmailForMention(parsed),
|
|
273
382
|
assetIds: parsed.assetIds,
|
|
383
|
+
assetType: parsed.assetType,
|
|
384
|
+
citation: parsed.citation,
|
|
274
385
|
thumbnailUrl: parsed.thumbnailUrl,
|
|
275
|
-
sourceHref: isSpacesMentionUrl(url) ? url :
|
|
386
|
+
sourceHref: isSpacesMentionUrl(url) ? url : parsed.sourceHref
|
|
276
387
|
},
|
|
277
388
|
`mention-${match.index}`
|
|
278
389
|
)
|
|
@@ -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 {\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(/&/gi, \"&\")\n .replace(/"/gi, '\"')\n .replace(/'|'/gi, \"'\")\n .replace(/</gi, \"<\")\n .replace(/>/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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"}
|
|
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 {\n type AssetReference,\n decodeFromAssetMentionUri,\n decodeFromAssetSpacesUrl,\n encodeToAssetSpacesUrl,\n formatReferenceAnchor,\n} from \"@athenaintel/references/asset-links\";\nimport {\n CrosshairIcon,\n DatabaseIcon,\n FileCodeIcon,\n FileIcon,\n FileTextIcon,\n FolderIcon,\n Globe2Icon,\n HeadphonesIcon,\n ImageIcon,\n type LucideIcon,\n MailIcon,\n MessageSquareIcon,\n MonitorIcon,\n PresentationIcon,\n SheetIcon,\n UserIcon,\n VideoIcon,\n} 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/**\n * Where in the asset a citation points, as its chip shows it. The anchor is\n * read and labelled by `@athenaintel/references` — the same parser and labels\n * Olympus's mention chips use — so a chip reads `jwt.py:12-25`, `p.5` or\n * `Slide 3` in every Athena chat.\n */\nexport interface MentionChipCitation {\n /** The anchor type (`page`, `file_line_range`, `slide`, …). */\n anchorType: string;\n /** The short label drawn after the name (`p.5`, `jwt.py:12-25`). */\n label: string;\n /**\n * The place spelled out (`Page 5`, `agora/…/jwt.py:12-25`), without the\n * excerpt: the tooltip when the link carries no excerpt, and part of the\n * chip's accessible name when it holds a word.\n */\n description: string;\n}\n\n/** The citation an asset link's anchor names, or undefined for a plain mention or an anchor nobody can read. */\nfunction citationOf(reference: AssetReference): MentionChipCitation | undefined {\n const { anchor, meta } = reference;\n if (!anchor) return undefined;\n const label = formatReferenceAnchor(anchor, \"short\", meta);\n if (!label) return undefined;\n const { excerpt: _excerpt, ...place } = meta ?? {};\n return {\n anchorType: anchor.type,\n label,\n description: formatReferenceAnchor(anchor, \"long\", place) ?? label,\n };\n}\n\n/**\n * The chip's icon for an asset type when the host serves no asset-type SVGs.\n * A compact set on purpose: the chip renders in every message (and Olympus's\n * main layout reaches it through the queued-message tray), so it carries a\n * dozen icons rather than the `@` menu's whole map.\n */\nconst ASSET_CHIP_ICONS: Record<string, LucideIcon> = {\n document: FileTextIcon,\n word_document: FileTextIcon,\n markdown: FileTextIcon,\n txt: FileTextIcon,\n pdf: FileTextIcon,\n spreadsheet: SheetIcon,\n table: SheetIcon,\n presentation: PresentationIcon,\n powerpoint_deck: PresentationIcon,\n presentation_pptx_studio: PresentationIcon,\n pptx: PresentationIcon,\n image: ImageIcon,\n figure: ImageIcon,\n canvas: ImageIcon,\n video: VideoIcon,\n meeting: VideoIcon,\n video_session: VideoIcon,\n audio: HeadphonesIcon,\n voice_memo: HeadphonesIcon,\n phone_call: HeadphonesIcon,\n email: MailIcon,\n email_message: MailIcon,\n live_email_thread: MailIcon,\n live_calendar_event: MailIcon,\n python: FileCodeIcon,\n javascript: FileCodeIcon,\n typescript: FileCodeIcon,\n json: FileCodeIcon,\n yaml: FileCodeIcon,\n database: DatabaseIcon,\n catalog: DatabaseIcon,\n drive: DatabaseIcon,\n dataset: DatabaseIcon,\n semantic_model: DatabaseIcon,\n query_snippet: DatabaseIcon,\n folder: FolderIcon,\n gdrive_folder: FolderIcon,\n project: FolderIcon,\n collection: FolderIcon,\n session: MessageSquareIcon,\n computer: MonitorIcon,\n desktop_session: MonitorIcon,\n user_interface: MonitorIcon,\n application: MonitorIcon,\n html: Globe2Icon,\n web_page: Globe2Icon,\n browse_session: Globe2Icon,\n};\n\n/** A repository file's lines read as a code file, whatever the catalog holding them. */\nfunction assetChipIcon(\n assetType: string | undefined,\n citation: MentionChipCitation | undefined,\n): LucideIcon {\n if (citation?.anchorType === \"file_line_range\") return FileCodeIcon;\n return (assetType && ASSET_CHIP_ICONS[assetType.toLowerCase()]) || FileIcon;\n}\n\n/** A label with a letter or digit in it; `§` or `•` alone says nothing read aloud. */\nconst SPEAKABLE = /[\\p{L}\\p{N}]/u;\n\n/** The asset reference an asset link decodes to, or null when it does not parse. */\nfunction decodeAssetLink(\n url: URL,\n decode: (url: URL) => AssetReference,\n): AssetReference | null {\n try {\n return decode(url);\n } catch {\n return null;\n }\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 assetType={parsed.assetType}\n citation={parsed.citation}\n thumbnailUrl={parsed.thumbnailUrl}\n sourceHref={isSpacesMentionUrl(href) ? href : parsed.sourceHref}\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 /** The asset's type (`document`, `catalog`, …): picks the icon when the host bundles no SVG thumbnails. */\n assetType?: string;\n /** Where in the asset the link points: its label follows the name. */\n citation?: MentionChipCitation;\n personEmail?: string;\n thumbnailUrl?: string;\n sourceHref?: string;\n interactive?: boolean;\n}> = ({\n name,\n type,\n assetIds,\n assetType,\n citation,\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, else\n // the cited place spelled out; the accessible name stays the action.\n const excerpt = sourceHref ? readSourceExcerpt(sourceHref) : null;\n const tooltip = excerpt ?? citation?.description ?? null;\n // A chip the model left unlabelled is already named by its place.\n const anchorLabel =\n citation && name !== citation.description && name !== citation.label\n ? citation.label\n : 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 // The cited place gets its own room after a name already at its cap.\n anchorLabel && \"max-w-[340px]\",\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 AssetIcon = assetChipIcon(assetType, citation);\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 === \"asset\" || type === \"folder\") && (\n // A host that bundles no asset-type SVGs (computer apps, most SDK\n // apps) still gets the type's icon.\n <AssetIcon className=\"size-3.5 shrink-0\" aria-hidden=\"true\" />\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 {citation && anchorLabel && (\n <>\n {/* Copied or read as text, the place stays a word apart from the name; a flex row draws no space. */}{\" \"}\n <span\n className=\"max-w-[140px] shrink-0 overflow-hidden text-ellipsis font-normal text-slate-400\"\n data-citation-anchor={citation.anchorType}\n >\n {anchorLabel}\n </span>\n </>\n )}\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={\n anchorLabel && citation && SPEAKABLE.test(citation.description)\n ? `Open ${name} at ${citation.description} in Spaces`\n : `Open ${name} in Spaces`\n }\n title={tooltip ?? `Open ${name} in Spaces`}\n data-excerpt={excerpt ?? undefined}\n // The link's text without its place: what a click names the opened asset.\n data-mention-name={name}\n >\n {contents}\n </a>\n );\n }\n\n return (\n <span\n className={className}\n title={tooltip ?? 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 assetType?: string;\n citation?: MentionChipCitation;\n /**\n * For a `mention://asset` citation, the Spaces link to its place (anchor and\n * excerpt), so the chip opens there; a Spaces mention is its own link.\n */\n sourceHref?: 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 // A link to several assets names no one place in any of them.\n const reference =\n assetIds.length === 1\n ? decodeAssetLink(parsed, decodeFromAssetSpacesUrl)\n : null;\n const citation = reference ? citationOf(reference) : undefined;\n return {\n type: \"asset\",\n name:\n assetIds.length > 1\n ? `${assetIds.length} assets`\n : (citation?.description ??\n citationFallbackLabel(parsed.searchParams)),\n id,\n assetIds,\n assetType: assetType ?? undefined,\n citation,\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 const reference =\n type === \"asset\" ? decodeAssetLink(u, decodeFromAssetMentionUri) : null;\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 assetType:\n type === \"asset\" || type === \"folder\"\n ? u.searchParams.get(\"assetType\") ||\n reference?.meta?.assetType ||\n (type === \"folder\" ? \"folder\" : undefined)\n : undefined,\n citation: reference ? citationOf(reference) : undefined,\n sourceHref:\n reference && (reference.anchor || reference.meta?.excerpt)\n ? encodeToAssetSpacesUrl(reference)\n : 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(/&/gi, \"&\")\n .replace(/"/gi, '\"')\n .replace(/'|'/gi, \"'\")\n .replace(/</gi, \"<\")\n .replace(/>/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 assetType={parsed.assetType}\n citation={parsed.citation}\n thumbnailUrl={parsed.thumbnailUrl}\n sourceHref={isSpacesMentionUrl(url) ? url : parsed.sourceHref}\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":["formatReferenceAnchor","FileTextIcon","SheetIcon","PresentationIcon","ImageIcon","VideoIcon","HeadphonesIcon","MailIcon","FileCodeIcon","DatabaseIcon","FolderIcon","MessageSquareIcon","MonitorIcon","Globe2Icon","FileIcon","jsx","isScreenPointHref","parseScreenPointHref","formatScreenPoint","jsxs","CrosshairIcon","useContext","MentionAuthCtx","useMemberAvatarUrl","useState","createOlympusAssetUrl","cn","Fragment","UserIcon","id","reference","decodeFromAssetSpacesUrl","assetTypeToThumb","decodeFromAssetMentionUri","encodeToAssetSpacesUrl"],"mappings":"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"}
|
|
@@ -6,6 +6,24 @@
|
|
|
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
|
+
* Where in the asset a citation points, as its chip shows it. The anchor is
|
|
11
|
+
* read and labelled by `@athenaintel/references` — the same parser and labels
|
|
12
|
+
* Olympus's mention chips use — so a chip reads `jwt.py:12-25`, `p.5` or
|
|
13
|
+
* `Slide 3` in every Athena chat.
|
|
14
|
+
*/
|
|
15
|
+
export interface MentionChipCitation {
|
|
16
|
+
/** The anchor type (`page`, `file_line_range`, `slide`, …). */
|
|
17
|
+
anchorType: string;
|
|
18
|
+
/** The short label drawn after the name (`p.5`, `jwt.py:12-25`). */
|
|
19
|
+
label: string;
|
|
20
|
+
/**
|
|
21
|
+
* The place spelled out (`Page 5`, `agora/…/jwt.py:12-25`), without the
|
|
22
|
+
* excerpt: the tooltip when the link carries no excerpt, and part of the
|
|
23
|
+
* chip's accessible name when it holds a word.
|
|
24
|
+
*/
|
|
25
|
+
description: string;
|
|
26
|
+
}
|
|
9
27
|
export declare const MentionLink: FC<{
|
|
10
28
|
href?: string;
|
|
11
29
|
children?: ReactNode;
|
|
@@ -24,6 +42,10 @@ export declare const MentionChip: FC<{
|
|
|
24
42
|
name: string;
|
|
25
43
|
type?: string;
|
|
26
44
|
assetIds?: string[];
|
|
45
|
+
/** The asset's type (`document`, `catalog`, …): picks the icon when the host bundles no SVG thumbnails. */
|
|
46
|
+
assetType?: string;
|
|
47
|
+
/** Where in the asset the link points: its label follows the name. */
|
|
48
|
+
citation?: MentionChipCitation;
|
|
27
49
|
personEmail?: string;
|
|
28
50
|
thumbnailUrl?: string;
|
|
29
51
|
sourceHref?: string;
|
|
@@ -35,6 +57,13 @@ export declare function parseMentionUrl(url: string): {
|
|
|
35
57
|
id: string;
|
|
36
58
|
email?: string;
|
|
37
59
|
assetIds?: string[];
|
|
60
|
+
assetType?: string;
|
|
61
|
+
citation?: MentionChipCitation;
|
|
62
|
+
/**
|
|
63
|
+
* For a `mention://asset` citation, the Spaces link to its place (anchor and
|
|
64
|
+
* excerpt), so the chip opens there; a Spaces mention is its own link.
|
|
65
|
+
*/
|
|
66
|
+
sourceHref?: string;
|
|
38
67
|
thumbnailUrl?: string;
|
|
39
68
|
};
|
|
40
69
|
/** Turn complete model-emitted URL citation anchors into safe Markdown links. */
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsxs, Fragment, jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useContext, useState } from "react";
|
|
3
|
-
import {
|
|
3
|
+
import { formatReferenceAnchor } from "../../references/src/anchors/format.js";
|
|
4
|
+
import { decodeFromAssetMentionUri } from "../../references/src/formats/uri/asset-mention-uri.js";
|
|
5
|
+
import { decodeFromAssetSpacesUrl, encodeToAssetSpacesUrl } from "../../references/src/formats/uri/asset-spaces-url.js";
|
|
6
|
+
import { Globe2Icon, UserIcon, FileCodeIcon, MonitorIcon, MessageSquareIcon, FolderIcon, DatabaseIcon, MailIcon, HeadphonesIcon, VideoIcon, ImageIcon, PresentationIcon, SheetIcon, FileTextIcon, FileIcon, CrosshairIcon } from "lucide-react";
|
|
4
7
|
import { createOlympusAssetUrl } from "../lib/asset-urls.js";
|
|
5
8
|
import { MentionAuthCtx } from "./suggestions/data/api-client.js";
|
|
6
9
|
import { assetTypeToThumb } from "./suggestions/data/sources/asset-type-icons.js";
|
|
@@ -15,6 +18,79 @@ function getSpacesAssetType(url) {
|
|
|
15
18
|
const assetType = url.searchParams.get("assetType") || url.searchParams.get("asset_type") || url.searchParams.get("type");
|
|
16
19
|
return assetType === "sheet" ? "spreadsheet" : assetType;
|
|
17
20
|
}
|
|
21
|
+
function citationOf(reference) {
|
|
22
|
+
const { anchor, meta } = reference;
|
|
23
|
+
if (!anchor) return void 0;
|
|
24
|
+
const label = formatReferenceAnchor(anchor, "short", meta);
|
|
25
|
+
if (!label) return void 0;
|
|
26
|
+
const { excerpt: _excerpt, ...place } = meta ?? {};
|
|
27
|
+
return {
|
|
28
|
+
anchorType: anchor.type,
|
|
29
|
+
label,
|
|
30
|
+
description: formatReferenceAnchor(anchor, "long", place) ?? label
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const ASSET_CHIP_ICONS = {
|
|
34
|
+
document: FileTextIcon,
|
|
35
|
+
word_document: FileTextIcon,
|
|
36
|
+
markdown: FileTextIcon,
|
|
37
|
+
txt: FileTextIcon,
|
|
38
|
+
pdf: FileTextIcon,
|
|
39
|
+
spreadsheet: SheetIcon,
|
|
40
|
+
table: SheetIcon,
|
|
41
|
+
presentation: PresentationIcon,
|
|
42
|
+
powerpoint_deck: PresentationIcon,
|
|
43
|
+
presentation_pptx_studio: PresentationIcon,
|
|
44
|
+
pptx: PresentationIcon,
|
|
45
|
+
image: ImageIcon,
|
|
46
|
+
figure: ImageIcon,
|
|
47
|
+
canvas: ImageIcon,
|
|
48
|
+
video: VideoIcon,
|
|
49
|
+
meeting: VideoIcon,
|
|
50
|
+
video_session: VideoIcon,
|
|
51
|
+
audio: HeadphonesIcon,
|
|
52
|
+
voice_memo: HeadphonesIcon,
|
|
53
|
+
phone_call: HeadphonesIcon,
|
|
54
|
+
email: MailIcon,
|
|
55
|
+
email_message: MailIcon,
|
|
56
|
+
live_email_thread: MailIcon,
|
|
57
|
+
live_calendar_event: MailIcon,
|
|
58
|
+
python: FileCodeIcon,
|
|
59
|
+
javascript: FileCodeIcon,
|
|
60
|
+
typescript: FileCodeIcon,
|
|
61
|
+
json: FileCodeIcon,
|
|
62
|
+
yaml: FileCodeIcon,
|
|
63
|
+
database: DatabaseIcon,
|
|
64
|
+
catalog: DatabaseIcon,
|
|
65
|
+
drive: DatabaseIcon,
|
|
66
|
+
dataset: DatabaseIcon,
|
|
67
|
+
semantic_model: DatabaseIcon,
|
|
68
|
+
query_snippet: DatabaseIcon,
|
|
69
|
+
folder: FolderIcon,
|
|
70
|
+
gdrive_folder: FolderIcon,
|
|
71
|
+
project: FolderIcon,
|
|
72
|
+
collection: FolderIcon,
|
|
73
|
+
session: MessageSquareIcon,
|
|
74
|
+
computer: MonitorIcon,
|
|
75
|
+
desktop_session: MonitorIcon,
|
|
76
|
+
user_interface: MonitorIcon,
|
|
77
|
+
application: MonitorIcon,
|
|
78
|
+
html: Globe2Icon,
|
|
79
|
+
web_page: Globe2Icon,
|
|
80
|
+
browse_session: Globe2Icon
|
|
81
|
+
};
|
|
82
|
+
function assetChipIcon(assetType, citation) {
|
|
83
|
+
if ((citation == null ? void 0 : citation.anchorType) === "file_line_range") return FileCodeIcon;
|
|
84
|
+
return assetType && ASSET_CHIP_ICONS[assetType.toLowerCase()] || FileIcon;
|
|
85
|
+
}
|
|
86
|
+
const SPEAKABLE = /[\p{L}\p{N}]/u;
|
|
87
|
+
function decodeAssetLink(url, decode) {
|
|
88
|
+
try {
|
|
89
|
+
return decode(url);
|
|
90
|
+
} catch {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
18
94
|
function personEmailForMention(parsed) {
|
|
19
95
|
if (parsed.type !== "person") return void 0;
|
|
20
96
|
return parsed.email || (parsed.id.includes("@") ? parsed.id : void 0);
|
|
@@ -30,8 +106,10 @@ const MentionLink = ({ href, children, interactive = true }) => {
|
|
|
30
106
|
type: parsed.type,
|
|
31
107
|
personEmail: personEmailForMention(parsed),
|
|
32
108
|
assetIds: parsed.assetIds,
|
|
109
|
+
assetType: parsed.assetType,
|
|
110
|
+
citation: parsed.citation,
|
|
33
111
|
thumbnailUrl: parsed.thumbnailUrl,
|
|
34
|
-
sourceHref: isSpacesMentionUrl(href) ? href :
|
|
112
|
+
sourceHref: isSpacesMentionUrl(href) ? href : parsed.sourceHref,
|
|
35
113
|
interactive
|
|
36
114
|
}
|
|
37
115
|
);
|
|
@@ -73,6 +151,8 @@ const MentionChip = ({
|
|
|
73
151
|
name,
|
|
74
152
|
type,
|
|
75
153
|
assetIds,
|
|
154
|
+
assetType,
|
|
155
|
+
citation,
|
|
76
156
|
personEmail,
|
|
77
157
|
thumbnailUrl,
|
|
78
158
|
sourceHref,
|
|
@@ -82,6 +162,8 @@ const MentionChip = ({
|
|
|
82
162
|
const memberAvatarUrl = useMemberAvatarUrl(personEmail);
|
|
83
163
|
const [erroredAvatarUrl, setErroredAvatarUrl] = useState();
|
|
84
164
|
const excerpt = sourceHref ? readSourceExcerpt(sourceHref) : null;
|
|
165
|
+
const tooltip = excerpt ?? (citation == null ? void 0 : citation.description) ?? null;
|
|
166
|
+
const anchorLabel = citation && name !== citation.description && name !== citation.label ? citation.label : null;
|
|
85
167
|
const assetRootHref = type === "asset" && (assetIds == null ? void 0 : assetIds.length) ? createOlympusAssetUrl({
|
|
86
168
|
appUrl: auth == null ? void 0 : auth.appUrl,
|
|
87
169
|
backendUrl: auth == null ? void 0 : auth.backendUrl,
|
|
@@ -103,8 +185,11 @@ const MentionChip = ({
|
|
|
103
185
|
})();
|
|
104
186
|
const className = cn(
|
|
105
187
|
"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",
|
|
188
|
+
// The cited place gets its own room after a name already at its cap.
|
|
189
|
+
anchorLabel && "max-w-[340px]",
|
|
106
190
|
href && "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"
|
|
107
191
|
);
|
|
192
|
+
const AssetIcon = assetChipIcon(assetType, citation);
|
|
108
193
|
const contents = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
109
194
|
thumbnailUrl && /* @__PURE__ */ jsx(
|
|
110
195
|
"img",
|
|
@@ -115,6 +200,9 @@ const MentionChip = ({
|
|
|
115
200
|
draggable: false
|
|
116
201
|
}
|
|
117
202
|
),
|
|
203
|
+
!thumbnailUrl && (type === "asset" || type === "folder") && // A host that bundles no asset-type SVGs (computer apps, most SDK
|
|
204
|
+
// apps) still gets the type's icon.
|
|
205
|
+
/* @__PURE__ */ jsx(AssetIcon, { className: "size-3.5 shrink-0", "aria-hidden": "true" }),
|
|
118
206
|
!thumbnailUrl && type === "webpage" && /* @__PURE__ */ jsx(Globe2Icon, { className: "size-3.5 shrink-0", "aria-hidden": "true" }),
|
|
119
207
|
!thumbnailUrl && type === "person" && (memberAvatarUrl && erroredAvatarUrl !== memberAvatarUrl ? /* @__PURE__ */ jsx(
|
|
120
208
|
"img",
|
|
@@ -126,7 +214,18 @@ const MentionChip = ({
|
|
|
126
214
|
onError: () => setErroredAvatarUrl(memberAvatarUrl)
|
|
127
215
|
}
|
|
128
216
|
) : /* @__PURE__ */ jsx(UserIcon, { className: "size-3.5 shrink-0", "aria-hidden": "true" })),
|
|
129
|
-
/* @__PURE__ */ jsx("span", { className: "max-w-[180px] overflow-hidden text-ellipsis", children: name })
|
|
217
|
+
/* @__PURE__ */ jsx("span", { className: "max-w-[180px] overflow-hidden text-ellipsis", children: name }),
|
|
218
|
+
citation && anchorLabel && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
219
|
+
" ",
|
|
220
|
+
/* @__PURE__ */ jsx(
|
|
221
|
+
"span",
|
|
222
|
+
{
|
|
223
|
+
className: "max-w-[140px] shrink-0 overflow-hidden text-ellipsis font-normal text-slate-400",
|
|
224
|
+
"data-citation-anchor": citation.anchorType,
|
|
225
|
+
children: anchorLabel
|
|
226
|
+
}
|
|
227
|
+
)
|
|
228
|
+
] })
|
|
130
229
|
] });
|
|
131
230
|
if (href && interactive) {
|
|
132
231
|
return /* @__PURE__ */ jsx(
|
|
@@ -136,9 +235,10 @@ const MentionChip = ({
|
|
|
136
235
|
target: "_blank",
|
|
137
236
|
rel: "noopener noreferrer",
|
|
138
237
|
className,
|
|
139
|
-
"aria-label": `Open ${name} in Spaces`,
|
|
140
|
-
title:
|
|
238
|
+
"aria-label": anchorLabel && citation && SPEAKABLE.test(citation.description) ? `Open ${name} at ${citation.description} in Spaces` : `Open ${name} in Spaces`,
|
|
239
|
+
title: tooltip ?? `Open ${name} in Spaces`,
|
|
141
240
|
"data-excerpt": excerpt ?? void 0,
|
|
241
|
+
"data-mention-name": name,
|
|
142
242
|
children: contents
|
|
143
243
|
}
|
|
144
244
|
);
|
|
@@ -147,7 +247,7 @@ const MentionChip = ({
|
|
|
147
247
|
"span",
|
|
148
248
|
{
|
|
149
249
|
className,
|
|
150
|
-
title:
|
|
250
|
+
title: tooltip ?? void 0,
|
|
151
251
|
"data-excerpt": excerpt ?? void 0,
|
|
152
252
|
children: contents
|
|
153
253
|
}
|
|
@@ -170,17 +270,22 @@ function citationFallbackLabel(params) {
|
|
|
170
270
|
return "Asset";
|
|
171
271
|
}
|
|
172
272
|
function parseMentionUrl(url) {
|
|
273
|
+
var _a, _b;
|
|
173
274
|
try {
|
|
174
275
|
if (isSpacesMentionUrl(url)) {
|
|
175
276
|
const parsed = new URL(url, "https://app.athenaintel.com");
|
|
176
277
|
const assetIds = (parsed.searchParams.get("asset_ids") ?? "").split(",").map((id3) => id3.trim()).filter(Boolean);
|
|
177
278
|
const id2 = assetIds[0] ?? "";
|
|
178
279
|
const assetType = getSpacesAssetType(parsed);
|
|
280
|
+
const reference2 = assetIds.length === 1 ? decodeAssetLink(parsed, decodeFromAssetSpacesUrl) : null;
|
|
281
|
+
const citation = reference2 ? citationOf(reference2) : void 0;
|
|
179
282
|
return {
|
|
180
283
|
type: "asset",
|
|
181
|
-
name: assetIds.length > 1 ? `${assetIds.length} assets` : citationFallbackLabel(parsed.searchParams),
|
|
284
|
+
name: assetIds.length > 1 ? `${assetIds.length} assets` : (citation == null ? void 0 : citation.description) ?? citationFallbackLabel(parsed.searchParams),
|
|
182
285
|
id: id2,
|
|
183
286
|
assetIds,
|
|
287
|
+
assetType: assetType ?? void 0,
|
|
288
|
+
citation,
|
|
184
289
|
thumbnailUrl: assetTypeToThumb(assetType)
|
|
185
290
|
};
|
|
186
291
|
}
|
|
@@ -198,12 +303,16 @@ function parseMentionUrl(url) {
|
|
|
198
303
|
thumbnailUrl = assetTypeToThumb(type === "folder" ? "folder" : null);
|
|
199
304
|
}
|
|
200
305
|
}
|
|
306
|
+
const reference = type === "asset" ? decodeAssetLink(u, decodeFromAssetMentionUri) : null;
|
|
201
307
|
return {
|
|
202
308
|
type,
|
|
203
309
|
name,
|
|
204
310
|
id,
|
|
205
311
|
email: type === "person" ? u.searchParams.get("email") || void 0 : void 0,
|
|
206
312
|
assetIds: type === "asset" && id ? [id] : void 0,
|
|
313
|
+
assetType: type === "asset" || type === "folder" ? u.searchParams.get("assetType") || ((_a = reference == null ? void 0 : reference.meta) == null ? void 0 : _a.assetType) || (type === "folder" ? "folder" : void 0) : void 0,
|
|
314
|
+
citation: reference ? citationOf(reference) : void 0,
|
|
315
|
+
sourceHref: reference && (reference.anchor || ((_b = reference.meta) == null ? void 0 : _b.excerpt)) ? encodeToAssetSpacesUrl(reference) : void 0,
|
|
207
316
|
thumbnailUrl
|
|
208
317
|
};
|
|
209
318
|
} catch {
|
|
@@ -269,8 +378,10 @@ function parseMentionsInText(text) {
|
|
|
269
378
|
type: parsed.type,
|
|
270
379
|
personEmail: personEmailForMention(parsed),
|
|
271
380
|
assetIds: parsed.assetIds,
|
|
381
|
+
assetType: parsed.assetType,
|
|
382
|
+
citation: parsed.citation,
|
|
272
383
|
thumbnailUrl: parsed.thumbnailUrl,
|
|
273
|
-
sourceHref: isSpacesMentionUrl(url) ? url :
|
|
384
|
+
sourceHref: isSpacesMentionUrl(url) ? url : parsed.sourceHref
|
|
274
385
|
},
|
|
275
386
|
`mention-${match.index}`
|
|
276
387
|
)
|