@athenaintel/react 0.42.2 → 0.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/agent-runtime/src/conversation-list-groups.d.ts +74 -0
- package/dist/agent-runtime/src/index.d.ts +1 -0
- package/dist/agent-runtime/src/tool-metadata.cjs +0 -8
- package/dist/agent-runtime/src/tool-metadata.cjs.map +1 -1
- package/dist/agent-runtime/src/tool-metadata.js +0 -8
- package/dist/agent-runtime/src/tool-metadata.js.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-voice.cjs +30 -15
- package/dist/agent-runtime/src/use-deep-agent-voice.cjs.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-voice.d.ts +8 -0
- package/dist/agent-runtime/src/use-deep-agent-voice.js +30 -15
- package/dist/agent-runtime/src/use-deep-agent-voice.js.map +1 -1
- package/dist/chat/super-grouping/SuperGroupingCard.cjs +8 -2
- package/dist/chat/super-grouping/SuperGroupingCard.cjs.map +1 -1
- package/dist/chat/super-grouping/SuperGroupingCard.d.ts +6 -1
- package/dist/chat/super-grouping/SuperGroupingCard.js +8 -2
- package/dist/chat/super-grouping/SuperGroupingCard.js.map +1 -1
- package/dist/chat/super-grouping/ToolTimeline.cjs +27 -0
- package/dist/chat/super-grouping/ToolTimeline.cjs.map +1 -1
- package/dist/chat/super-grouping/ToolTimeline.d.ts +5 -1
- package/dist/chat/super-grouping/ToolTimeline.js +27 -0
- package/dist/chat/super-grouping/ToolTimeline.js.map +1 -1
- package/dist/chat/super-grouping/index.d.ts +1 -0
- package/dist/chat/super-grouping/timeline-step.cjs +61 -2
- package/dist/chat/super-grouping/timeline-step.cjs.map +1 -1
- package/dist/chat/super-grouping/timeline-step.d.ts +13 -1
- package/dist/chat/super-grouping/timeline-step.js +61 -2
- package/dist/chat/super-grouping/timeline-step.js.map +1 -1
- package/dist/chat-ui/mentions/mention-chip.cjs +144 -35
- package/dist/chat-ui/mentions/mention-chip.cjs.map +1 -1
- package/dist/chat-ui/mentions/mention-chip.d.ts +17 -1
- package/dist/chat-ui/mentions/mention-chip.js +146 -37
- package/dist/chat-ui/mentions/mention-chip.js.map +1 -1
- package/dist/chat-ui/mentions/use-mention-asset.cjs +140 -0
- package/dist/chat-ui/mentions/use-mention-asset.cjs.map +1 -0
- package/dist/chat-ui/mentions/use-mention-asset.d.ts +16 -0
- package/dist/chat-ui/mentions/use-mention-asset.js +140 -0
- package/dist/chat-ui/mentions/use-mention-asset.js.map +1 -0
- package/dist/chat-ui/rich-text/chat-link.cjs +5 -4
- package/dist/chat-ui/rich-text/chat-link.cjs.map +1 -1
- package/dist/chat-ui/rich-text/chat-link.d.ts +2 -3
- package/dist/chat-ui/rich-text/chat-link.js +6 -5
- package/dist/chat-ui/rich-text/chat-link.js.map +1 -1
- package/dist/chat-ui/rich-text/chat-node-views.cjs +1 -0
- package/dist/chat-ui/rich-text/chat-node-views.cjs.map +1 -1
- package/dist/chat-ui/rich-text/chat-node-views.js +1 -0
- package/dist/chat-ui/rich-text/chat-node-views.js.map +1 -1
- package/dist/react/package.json.cjs +1 -1
- package/dist/react/package.json.js +1 -1
- package/dist/references/src/tool-option-schemas.d.ts +0 -34
- package/dist/styles.css +1 -1
- package/dist/threads/statewire-thread-list.cjs +6 -2
- package/dist/threads/statewire-thread-list.cjs.map +1 -1
- package/dist/threads/statewire-thread-list.js +6 -2
- package/dist/threads/statewire-thread-list.js.map +1 -1
- package/dist/tools/tool-summary.cjs +0 -2
- package/dist/tools/tool-summary.cjs.map +1 -1
- package/dist/tools/tool-summary.js +0 -2
- package/dist/tools/tool-summary.js.map +1 -1
- package/package.json +1 -1
|
@@ -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 {\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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"}
|
|
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 isValidElement,\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\";\nimport { useMentionAsset } from \"./use-mention-asset\";\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\nfunction assetChipIcon(assetType: string | undefined): LucideIcon {\n return (assetType && ASSET_CHIP_ICONS[assetType.toLowerCase()]) || FileIcon;\n}\n\n/**\n * GitHub's mark (Octicons `mark-github`), drawn by a repository file's\n * citation. Lucide carries no brand icons and an SDK app serves none of\n * Olympus's, so the chip brings its own. Only a GitHub repository catalog\n * serves `file_line_range` anchors (`ASSET_ANCHOR_REGISTRY`; agora's\n * `_CATALOG_ANCHORS_BY_PROVIDER`), so the anchor alone names the provider.\n */\nfunction GitHubMarkIcon({ className }: { className?: string }) {\n return (\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n className={className}\n data-chip-icon=\"github\"\n >\n <path d=\"M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z\" />\n </svg>\n );\n}\n\n/** Anchors only a session holds: its messages, blocks and tool calls. */\nconst SESSION_ANCHORS: ReadonlySet<string> = new Set([\n \"msg\",\n \"msg_range\",\n \"msg_block\",\n \"msg_section\",\n \"msg_text\",\n \"tool_call\",\n]);\n\n/** Whether a link names a session (its type, or an anchor only a session holds). */\nfunction isSessionLink(\n assetType: string | undefined,\n citation: MentionChipCitation | undefined,\n): boolean {\n return (\n assetType?.toLowerCase() === \"session\" ||\n (citation !== undefined && SESSION_ANCHORS.has(citation.anchorType))\n );\n}\n\n/** Past this many characters a place may not show whole in the chip, so the tooltip spells it out. */\nconst PLACE_FITS_CHARS = 20;\n\n/** `route.ts:18-24` as the file's name and its lines, so a long name shortens while its lines stay. */\nfunction splitFileLines(label: string): { file: string; lines: string } {\n const colon = label.lastIndexOf(\":\");\n return colon > 0\n ? { file: label.slice(0, colon), lines: label.slice(colon) }\n : { file: label, lines: \"\" };\n}\n\n/**\n * Whether a name already says where its citation points: the place's label or\n * its spelled-out form, or a name ending in the label (`Q4 report p.5`,\n * `olympus/…/route.ts:18-24`). The chip then draws the place once.\n */\nfunction restatesPlace(name: string, citation: MentionChipCitation): boolean {\n const said = name.trim().toLowerCase();\n const label = citation.label.toLowerCase();\n return said === label || said === citation.description.toLowerCase() || said.endsWith(label);\n}\n\n/**\n * The link's own label when it tells the reader something the chip does not\n * already show: not the place again (a path, `file:lines`), not the asset's\n * name. The tooltip keeps it, since the chip shows the asset and the place.\n */\nfunction informativeLabel({\n label,\n shown,\n citation,\n}: {\n label: string;\n shown: readonly (string | null | undefined)[];\n citation: MentionChipCitation | undefined;\n}): string | null {\n const trimmed = label.trim();\n if (!trimmed || !SPEAKABLE.test(trimmed)) return null;\n if (PLACEHOLDER_LABELS.has(trimmed.toLowerCase())) return null;\n if (citation && restatesPlace(trimmed, citation)) return null;\n // Every word of it already shown (`Tesla Q3 report` beside `Tesla Q3\n // Earnings Report`, `demo-app-monorepo` beside the repository's title).\n const shownWords = new Set(\n [...shown, citation?.description].flatMap((text) => (text ? wordsOf(text) : [])),\n );\n return wordsOf(trimmed).every((word) => shownWords.has(word)) ? null : trimmed;\n}\n\nfunction wordsOf(text: string): string[] {\n return text.toLowerCase().match(/[\\p{L}\\p{N}]+/gu) ?? [];\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/** Labels that name nothing: agora's read_asset citations all read `Citation`. */\nconst PLACEHOLDER_LABELS: ReadonlySet<string> = new Set([\"citation\", \"source\", \"link\", \"here\"]);\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\n/**\n * The text a link's children read as. Markdown can hand a link several children, some of them\n * elements (an escaped `_` or a `*` in a URL splits its text), and stringifying an element gives\n * `[object Object]`.\n */\nfunction textOf(node: ReactNode): string {\n if (typeof node === \"string\" || typeof node === \"number\") return String(node);\n if (Array.isArray(node)) return node.map(textOf).join(\"\");\n if (isValidElement<{ children?: ReactNode }>(node)) return textOf(node.props.children);\n return \"\";\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 = textOf(children).trim().replace(/^@/, \"\");\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// ── The chip pill ──\n\n/**\n * Olympus's mention pill (`mentionPillClassName`: 20px high, 16px icon, the\n * place in `text-xs`) in the provider's theme tokens, so a dark theme keeps it\n * legible, and `box-border` so its height holds without a host's CSS reset.\n * Every chat chip draws it: an asset or citation chip, a web source, a screen\n * point. Only stock Tailwind sizes: Olympus renders these chips (the queued\n * message tray) with a stylesheet that does not scan this package, so an\n * arbitrary value would reach it unstyled. `not-prose` matters: assistant\n * messages render inside Tailwind Typography's `prose`, whose\n * zero-specificity `img` margins (~1.7em top/bottom) would otherwise inflate\n * the chip's icon into a tall card that breaks the sentence flow around it.\n */\nexport const CHIP_PILL_CLASS =\n \"not-prose box-border inline-flex h-5 items-center gap-1.5 whitespace-nowrap rounded-md border border-border bg-muted px-1 py-0.5 align-middle font-normal leading-none text-foreground no-underline\";\n/** A chip that opens something. */\nexport const CHIP_LINK_CLASS =\n \"cursor-pointer transition-colors hover:bg-accent hover:text-accent-foreground hover:no-underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1\";\nexport const CHIP_ICON_CLASS = \"size-4 shrink-0\";\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={cn(CHIP_PILL_CLASS, \"max-w-[260px]\")}\n title={description}\n aria-label={description}\n data-screen-point=\"valid\"\n >\n <CrosshairIcon className={CHIP_ICON_CLASS} aria-hidden=\"true\" />\n <span className=\"min-w-0 max-w-[180px] overflow-hidden text-ellipsis\">\n {children}\n </span>\n <span className=\"shrink-0 text-xs text-muted-foreground\">\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 until (or unless) the asset is looked up. */\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 const excerpt = sourceHref ? readSourceExcerpt(sourceHref) : null;\n // A repository file's lines read as the file (`route.ts:18-24`): the chip\n // opens that file, and every citation in a reply names the same repository,\n // whatever label the model wrote.\n const repositoryFile =\n citation?.anchorType === \"file_line_range\"\n ? { ...splitFileLines(citation.label), label: citation.label }\n : null;\n // The asset as agora has it, read the way Olympus's chips read it: its own\n // title and type. Until it answers, and for an asset the viewer cannot read,\n // the link's label and `type=` stand in. Asked only where it changes the\n // chip: a repository file reads as its file whatever the catalog is called,\n // and a session's row is too heavy to read for a name.\n const asset = useMentionAsset(\n type === \"asset\" &&\n assetIds?.length === 1 &&\n !repositoryFile &&\n !isSessionLink(assetType, citation)\n ? assetIds[0]\n : undefined,\n );\n const assetTitle = asset?.title || null;\n const chipName = repositoryFile?.label ?? assetTitle ?? name;\n // The place follows a name that does not already say it, as `[p.5]`.\n const placeLabel =\n citation && !repositoryFile && !restatesPlace(chipName, citation)\n ? citation.label\n : null;\n // The tooltip: what the model called it (when the chip does not show it),\n // the repository a file is in and the file's whole path, the cited passage.\n // The place spelled out stands in for a passage the citation does not carry,\n // and for a place too long to show whole (a long sheet name).\n const tooltip =\n citation || excerpt\n ? tooltipText({\n lines: [\n informativeLabel({ label: name, shown: [chipName, assetTitle], citation }),\n repositoryFile ? assetTitle : null,\n repositoryFile || !excerpt || (placeLabel?.length ?? 0) > PLACE_FITS_CHARS\n ? citation?.description\n : null,\n ],\n excerpt,\n })\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 const className = cn(CHIP_PILL_CLASS, \"max-w-[275px]\", href && CHIP_LINK_CLASS);\n // A host's asset-type SVG follows the looked-up type, when it serves them.\n const chipThumbnailUrl = repositoryFile\n ? undefined\n : (asset?.assetType && assetTypeToThumb(asset.assetType)) || thumbnailUrl;\n const AssetIcon = assetChipIcon(asset?.assetType || assetType);\n const contents = (\n <>\n {repositoryFile ? (\n <GitHubMarkIcon className={CHIP_ICON_CLASS} />\n ) : chipThumbnailUrl ? (\n <img\n src={chipThumbnailUrl}\n alt=\"\"\n className={CHIP_ICON_CLASS}\n draggable={false}\n />\n ) : 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={CHIP_ICON_CLASS} aria-hidden=\"true\" />\n ) : type === \"webpage\" ? (\n <Globe2Icon className={CHIP_ICON_CLASS} aria-hidden=\"true\" />\n ) : type === \"person\" ? (\n memberAvatarUrl && erroredAvatarUrl !== memberAvatarUrl ? (\n <img\n src={memberAvatarUrl}\n alt=\"\"\n className={cn(CHIP_ICON_CLASS, \"rounded-full object-cover\")}\n draggable={false}\n onError={() => setErroredAvatarUrl(memberAvatarUrl)}\n />\n ) : (\n <UserIcon className={CHIP_ICON_CLASS} aria-hidden=\"true\" />\n )\n ) : null}\n {repositoryFile ? (\n <span\n className=\"flex min-w-0 items-center\"\n data-citation-anchor=\"file_line_range\"\n >\n <span className=\"min-w-0 overflow-hidden text-ellipsis\">\n {repositoryFile.file}\n </span>\n <span className=\"shrink-0 text-muted-foreground\">{repositoryFile.lines}</span>\n </span>\n ) : (\n <span\n className={cn(\n \"max-w-[180px] overflow-hidden text-ellipsis\",\n // A long place may shorten the name, never make it vanish.\n placeLabel && \"min-w-8\",\n )}\n >\n {chipName}\n </span>\n )}\n {citation && placeLabel && (\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=\"flex min-w-0 items-center text-xs text-muted-foreground\"\n data-citation-anchor={citation.anchorType}\n >\n [<span className=\"min-w-0 overflow-hidden text-ellipsis\">{placeLabel}</span>]\n </span>\n </>\n )}\n </>\n );\n\n if (href && interactive) {\n const speakablePlace = citation && SPEAKABLE.test(citation.description);\n return (\n <a\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={className}\n aria-label={\n repositoryFile && citation\n ? `Open ${citation.description} in Spaces`\n : placeLabel && citation && speakablePlace\n ? `Open ${chipName} at ${citation.description} in Spaces`\n : `Open ${chipName} in Spaces`\n }\n title={tooltip ?? `Open ${chipName} in Spaces`}\n data-excerpt={excerpt ?? undefined}\n // The asset without its place: what a click names the opened asset.\n data-mention-name={repositoryFile ? (assetTitle ?? name) : chipName}\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/**\n * A chip's tooltip: the lines that say something, each once, then the cited\n * passage, quoted when it shares the tooltip with other lines.\n */\nfunction tooltipText({\n lines,\n excerpt,\n}: {\n lines: readonly (string | null | undefined)[];\n excerpt: string | null;\n}): string | null {\n const kept: string[] = [];\n for (const line of lines) {\n const trimmed = line?.trim();\n if (trimmed && !kept.includes(trimmed)) kept.push(trimmed);\n }\n if (excerpt) kept.push(kept.length ? `“${excerpt}”` : excerpt);\n return kept.length ? kept.join(\"\\n\") : null;\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","isValidElement","isScreenPointHref","parseScreenPointHref","formatScreenPoint","jsxs","cn","CrosshairIcon","useContext","MentionAuthCtx","useMemberAvatarUrl","useState","useMentionAsset","createOlympusAssetUrl","assetTypeToThumb","Fragment","UserIcon","id","reference","decodeFromAssetSpacesUrl","decodeFromAssetMentionUri","encodeToAssetSpacesUrl"],"mappings":"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"}
|
|
@@ -29,6 +29,22 @@ export declare const MentionLink: FC<{
|
|
|
29
29
|
children?: ReactNode;
|
|
30
30
|
interactive?: boolean;
|
|
31
31
|
}>;
|
|
32
|
+
/**
|
|
33
|
+
* Olympus's mention pill (`mentionPillClassName`: 20px high, 16px icon, the
|
|
34
|
+
* place in `text-xs`) in the provider's theme tokens, so a dark theme keeps it
|
|
35
|
+
* legible, and `box-border` so its height holds without a host's CSS reset.
|
|
36
|
+
* Every chat chip draws it: an asset or citation chip, a web source, a screen
|
|
37
|
+
* point. Only stock Tailwind sizes: Olympus renders these chips (the queued
|
|
38
|
+
* message tray) with a stylesheet that does not scan this package, so an
|
|
39
|
+
* arbitrary value would reach it unstyled. `not-prose` matters: assistant
|
|
40
|
+
* messages render inside Tailwind Typography's `prose`, whose
|
|
41
|
+
* zero-specificity `img` margins (~1.7em top/bottom) would otherwise inflate
|
|
42
|
+
* the chip's icon into a tall card that breaks the sentence flow around it.
|
|
43
|
+
*/
|
|
44
|
+
export declare const CHIP_PILL_CLASS = "not-prose box-border inline-flex h-5 items-center gap-1.5 whitespace-nowrap rounded-md border border-border bg-muted px-1 py-0.5 align-middle font-normal leading-none text-foreground no-underline";
|
|
45
|
+
/** A chip that opens something. */
|
|
46
|
+
export declare const CHIP_LINK_CLASS = "cursor-pointer transition-colors hover:bg-accent hover:text-accent-foreground hover:no-underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1";
|
|
47
|
+
export declare const CHIP_ICON_CLASS = "size-4 shrink-0";
|
|
32
48
|
/**
|
|
33
49
|
* Renders an `athena-point://` link as a non-navigating coordinate chip.
|
|
34
50
|
* Surfaces without screenshot context (extension side panel, SDK consumers)
|
|
@@ -42,7 +58,7 @@ export declare const MentionChip: FC<{
|
|
|
42
58
|
name: string;
|
|
43
59
|
type?: string;
|
|
44
60
|
assetIds?: string[];
|
|
45
|
-
/** The asset's type (`document`, `catalog`, …): picks the icon
|
|
61
|
+
/** The asset's type (`document`, `catalog`, …): picks the icon until (or unless) the asset is looked up. */
|
|
46
62
|
assetType?: string;
|
|
47
63
|
/** Where in the asset the link points: its label follows the name. */
|
|
48
64
|
citation?: MentionChipCitation;
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { jsxs, Fragment, jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useContext, useState } from "react";
|
|
2
|
+
import { useContext, useState, isValidElement } from "react";
|
|
3
3
|
import { formatReferenceAnchor } from "../../references/src/anchors/format.js";
|
|
4
4
|
import { decodeFromAssetMentionUri } from "../../references/src/formats/uri/asset-mention-uri.js";
|
|
5
5
|
import { decodeFromAssetSpacesUrl, encodeToAssetSpacesUrl } from "../../references/src/formats/uri/asset-spaces-url.js";
|
|
6
|
-
import { Globe2Icon, UserIcon,
|
|
6
|
+
import { Globe2Icon, UserIcon, MonitorIcon, MessageSquareIcon, FolderIcon, DatabaseIcon, FileCodeIcon, MailIcon, HeadphonesIcon, VideoIcon, ImageIcon, PresentationIcon, SheetIcon, FileTextIcon, FileIcon, CrosshairIcon } from "lucide-react";
|
|
7
7
|
import { createOlympusAssetUrl } from "../lib/asset-urls.js";
|
|
8
8
|
import { MentionAuthCtx } from "./suggestions/data/api-client.js";
|
|
9
9
|
import { assetTypeToThumb } from "./suggestions/data/sources/asset-type-icons.js";
|
|
10
10
|
import { useMemberAvatarUrl } from "./use-member-avatar.js";
|
|
11
|
+
import { useMentionAsset } from "./use-mention-asset.js";
|
|
11
12
|
import { isScreenPointHref, parseScreenPointHref, formatScreenPoint } from "../../agent-runtime/src/screen-point.js";
|
|
12
13
|
import { cn } from "cnfast";
|
|
13
14
|
const SPACES_MENTION_PATH = /\/dashboard\/spaces\/?\?/;
|
|
@@ -79,11 +80,62 @@ const ASSET_CHIP_ICONS = {
|
|
|
79
80
|
web_page: Globe2Icon,
|
|
80
81
|
browse_session: Globe2Icon
|
|
81
82
|
};
|
|
82
|
-
function assetChipIcon(assetType
|
|
83
|
-
if ((citation == null ? void 0 : citation.anchorType) === "file_line_range") return FileCodeIcon;
|
|
83
|
+
function assetChipIcon(assetType) {
|
|
84
84
|
return assetType && ASSET_CHIP_ICONS[assetType.toLowerCase()] || FileIcon;
|
|
85
85
|
}
|
|
86
|
+
function GitHubMarkIcon({ className }) {
|
|
87
|
+
return /* @__PURE__ */ jsx(
|
|
88
|
+
"svg",
|
|
89
|
+
{
|
|
90
|
+
viewBox: "0 0 16 16",
|
|
91
|
+
fill: "currentColor",
|
|
92
|
+
"aria-hidden": "true",
|
|
93
|
+
className,
|
|
94
|
+
"data-chip-icon": "github",
|
|
95
|
+
children: /* @__PURE__ */ jsx("path", { d: "M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z" })
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
const SESSION_ANCHORS = /* @__PURE__ */ new Set([
|
|
100
|
+
"msg",
|
|
101
|
+
"msg_range",
|
|
102
|
+
"msg_block",
|
|
103
|
+
"msg_section",
|
|
104
|
+
"msg_text",
|
|
105
|
+
"tool_call"
|
|
106
|
+
]);
|
|
107
|
+
function isSessionLink(assetType, citation) {
|
|
108
|
+
return (assetType == null ? void 0 : assetType.toLowerCase()) === "session" || citation !== void 0 && SESSION_ANCHORS.has(citation.anchorType);
|
|
109
|
+
}
|
|
110
|
+
const PLACE_FITS_CHARS = 20;
|
|
111
|
+
function splitFileLines(label) {
|
|
112
|
+
const colon = label.lastIndexOf(":");
|
|
113
|
+
return colon > 0 ? { file: label.slice(0, colon), lines: label.slice(colon) } : { file: label, lines: "" };
|
|
114
|
+
}
|
|
115
|
+
function restatesPlace(name, citation) {
|
|
116
|
+
const said = name.trim().toLowerCase();
|
|
117
|
+
const label = citation.label.toLowerCase();
|
|
118
|
+
return said === label || said === citation.description.toLowerCase() || said.endsWith(label);
|
|
119
|
+
}
|
|
120
|
+
function informativeLabel({
|
|
121
|
+
label,
|
|
122
|
+
shown,
|
|
123
|
+
citation
|
|
124
|
+
}) {
|
|
125
|
+
const trimmed = label.trim();
|
|
126
|
+
if (!trimmed || !SPEAKABLE.test(trimmed)) return null;
|
|
127
|
+
if (PLACEHOLDER_LABELS.has(trimmed.toLowerCase())) return null;
|
|
128
|
+
if (citation && restatesPlace(trimmed, citation)) return null;
|
|
129
|
+
const shownWords = new Set(
|
|
130
|
+
[...shown, citation == null ? void 0 : citation.description].flatMap((text) => text ? wordsOf(text) : [])
|
|
131
|
+
);
|
|
132
|
+
return wordsOf(trimmed).every((word) => shownWords.has(word)) ? null : trimmed;
|
|
133
|
+
}
|
|
134
|
+
function wordsOf(text) {
|
|
135
|
+
return text.toLowerCase().match(/[\p{L}\p{N}]+/gu) ?? [];
|
|
136
|
+
}
|
|
86
137
|
const SPEAKABLE = /[\p{L}\p{N}]/u;
|
|
138
|
+
const PLACEHOLDER_LABELS = /* @__PURE__ */ new Set(["citation", "source", "link", "here"]);
|
|
87
139
|
function decodeAssetLink(url, decode) {
|
|
88
140
|
try {
|
|
89
141
|
return decode(url);
|
|
@@ -95,9 +147,15 @@ function personEmailForMention(parsed) {
|
|
|
95
147
|
if (parsed.type !== "person") return void 0;
|
|
96
148
|
return parsed.email || (parsed.id.includes("@") ? parsed.id : void 0);
|
|
97
149
|
}
|
|
150
|
+
function textOf(node) {
|
|
151
|
+
if (typeof node === "string" || typeof node === "number") return String(node);
|
|
152
|
+
if (Array.isArray(node)) return node.map(textOf).join("");
|
|
153
|
+
if (isValidElement(node)) return textOf(node.props.children);
|
|
154
|
+
return "";
|
|
155
|
+
}
|
|
98
156
|
const MentionLink = ({ href, children, interactive = true }) => {
|
|
99
157
|
if (href && (href.startsWith("mention://") || isSpacesMentionUrl(href))) {
|
|
100
|
-
const name =
|
|
158
|
+
const name = textOf(children).trim().replace(/^@/, "");
|
|
101
159
|
const parsed = parseMentionUrl(href);
|
|
102
160
|
return /* @__PURE__ */ jsx(
|
|
103
161
|
MentionChip,
|
|
@@ -122,6 +180,9 @@ const MentionLink = ({ href, children, interactive = true }) => {
|
|
|
122
180
|
}
|
|
123
181
|
return /* @__PURE__ */ jsx("a", { href, target: "_blank", rel: "noopener noreferrer", children });
|
|
124
182
|
};
|
|
183
|
+
const CHIP_PILL_CLASS = "not-prose box-border inline-flex h-5 items-center gap-1.5 whitespace-nowrap rounded-md border border-border bg-muted px-1 py-0.5 align-middle font-normal leading-none text-foreground no-underline";
|
|
184
|
+
const CHIP_LINK_CLASS = "cursor-pointer transition-colors hover:bg-accent hover:text-accent-foreground hover:no-underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1";
|
|
185
|
+
const CHIP_ICON_CLASS = "size-4 shrink-0";
|
|
125
186
|
const ScreenPointChip = ({ href, children }) => {
|
|
126
187
|
const parsed = parseScreenPointHref(href);
|
|
127
188
|
if (!parsed.ok) {
|
|
@@ -131,14 +192,14 @@ const ScreenPointChip = ({ href, children }) => {
|
|
|
131
192
|
return /* @__PURE__ */ jsxs(
|
|
132
193
|
"span",
|
|
133
194
|
{
|
|
134
|
-
className: "
|
|
195
|
+
className: cn(CHIP_PILL_CLASS, "max-w-[260px]"),
|
|
135
196
|
title: description,
|
|
136
197
|
"aria-label": description,
|
|
137
198
|
"data-screen-point": "valid",
|
|
138
199
|
children: [
|
|
139
|
-
/* @__PURE__ */ jsx(CrosshairIcon, { className:
|
|
140
|
-
/* @__PURE__ */ jsx("span", { className: "max-w-[180px] overflow-hidden text-ellipsis", children }),
|
|
141
|
-
/* @__PURE__ */ jsxs("span", { className: "
|
|
200
|
+
/* @__PURE__ */ jsx(CrosshairIcon, { className: CHIP_ICON_CLASS, "aria-hidden": "true" }),
|
|
201
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 max-w-[180px] overflow-hidden text-ellipsis", children }),
|
|
202
|
+
/* @__PURE__ */ jsxs("span", { className: "shrink-0 text-xs text-muted-foreground", children: [
|
|
142
203
|
parsed.point.x,
|
|
143
204
|
", ",
|
|
144
205
|
parsed.point.y
|
|
@@ -162,8 +223,21 @@ const MentionChip = ({
|
|
|
162
223
|
const memberAvatarUrl = useMemberAvatarUrl(personEmail);
|
|
163
224
|
const [erroredAvatarUrl, setErroredAvatarUrl] = useState();
|
|
164
225
|
const excerpt = sourceHref ? readSourceExcerpt(sourceHref) : null;
|
|
165
|
-
const
|
|
166
|
-
const
|
|
226
|
+
const repositoryFile = (citation == null ? void 0 : citation.anchorType) === "file_line_range" ? { ...splitFileLines(citation.label), label: citation.label } : null;
|
|
227
|
+
const asset = useMentionAsset(
|
|
228
|
+
type === "asset" && (assetIds == null ? void 0 : assetIds.length) === 1 && !repositoryFile && !isSessionLink(assetType, citation) ? assetIds[0] : void 0
|
|
229
|
+
);
|
|
230
|
+
const assetTitle = (asset == null ? void 0 : asset.title) || null;
|
|
231
|
+
const chipName = (repositoryFile == null ? void 0 : repositoryFile.label) ?? assetTitle ?? name;
|
|
232
|
+
const placeLabel = citation && !repositoryFile && !restatesPlace(chipName, citation) ? citation.label : null;
|
|
233
|
+
const tooltip = citation || excerpt ? tooltipText({
|
|
234
|
+
lines: [
|
|
235
|
+
informativeLabel({ label: name, shown: [chipName, assetTitle], citation }),
|
|
236
|
+
repositoryFile ? assetTitle : null,
|
|
237
|
+
repositoryFile || !excerpt || ((placeLabel == null ? void 0 : placeLabel.length) ?? 0) > PLACE_FITS_CHARS ? citation == null ? void 0 : citation.description : null
|
|
238
|
+
],
|
|
239
|
+
excerpt
|
|
240
|
+
}) : null;
|
|
167
241
|
const assetRootHref = type === "asset" && (assetIds == null ? void 0 : assetIds.length) ? createOlympusAssetUrl({
|
|
168
242
|
appUrl: auth == null ? void 0 : auth.appUrl,
|
|
169
243
|
backendUrl: auth == null ? void 0 : auth.backendUrl,
|
|
@@ -183,51 +257,71 @@ const MentionChip = ({
|
|
|
183
257
|
return assetRootHref;
|
|
184
258
|
}
|
|
185
259
|
})();
|
|
186
|
-
const className = cn(
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
anchorLabel && "max-w-[340px]",
|
|
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"
|
|
191
|
-
);
|
|
192
|
-
const AssetIcon = assetChipIcon(assetType, citation);
|
|
260
|
+
const className = cn(CHIP_PILL_CLASS, "max-w-[275px]", href && CHIP_LINK_CLASS);
|
|
261
|
+
const chipThumbnailUrl = repositoryFile ? void 0 : (asset == null ? void 0 : asset.assetType) && assetTypeToThumb(asset.assetType) || thumbnailUrl;
|
|
262
|
+
const AssetIcon = assetChipIcon((asset == null ? void 0 : asset.assetType) || assetType);
|
|
193
263
|
const contents = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
194
|
-
|
|
264
|
+
repositoryFile ? /* @__PURE__ */ jsx(GitHubMarkIcon, { className: CHIP_ICON_CLASS }) : chipThumbnailUrl ? /* @__PURE__ */ jsx(
|
|
195
265
|
"img",
|
|
196
266
|
{
|
|
197
|
-
src:
|
|
267
|
+
src: chipThumbnailUrl,
|
|
198
268
|
alt: "",
|
|
199
|
-
className:
|
|
269
|
+
className: CHIP_ICON_CLASS,
|
|
200
270
|
draggable: false
|
|
201
271
|
}
|
|
202
|
-
)
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
!thumbnailUrl && type === "person" && (memberAvatarUrl && erroredAvatarUrl !== memberAvatarUrl ? /* @__PURE__ */ jsx(
|
|
272
|
+
) : type === "asset" || type === "folder" ? (
|
|
273
|
+
// A host that bundles no asset-type SVGs (computer apps, most SDK
|
|
274
|
+
// apps) still gets the type's icon.
|
|
275
|
+
/* @__PURE__ */ jsx(AssetIcon, { className: CHIP_ICON_CLASS, "aria-hidden": "true" })
|
|
276
|
+
) : type === "webpage" ? /* @__PURE__ */ jsx(Globe2Icon, { className: CHIP_ICON_CLASS, "aria-hidden": "true" }) : type === "person" ? memberAvatarUrl && erroredAvatarUrl !== memberAvatarUrl ? /* @__PURE__ */ jsx(
|
|
208
277
|
"img",
|
|
209
278
|
{
|
|
210
279
|
src: memberAvatarUrl,
|
|
211
280
|
alt: "",
|
|
212
|
-
className: "
|
|
281
|
+
className: cn(CHIP_ICON_CLASS, "rounded-full object-cover"),
|
|
213
282
|
draggable: false,
|
|
214
283
|
onError: () => setErroredAvatarUrl(memberAvatarUrl)
|
|
215
284
|
}
|
|
216
|
-
) : /* @__PURE__ */ jsx(UserIcon, { className:
|
|
217
|
-
/* @__PURE__ */
|
|
218
|
-
|
|
285
|
+
) : /* @__PURE__ */ jsx(UserIcon, { className: CHIP_ICON_CLASS, "aria-hidden": "true" }) : null,
|
|
286
|
+
repositoryFile ? /* @__PURE__ */ jsxs(
|
|
287
|
+
"span",
|
|
288
|
+
{
|
|
289
|
+
className: "flex min-w-0 items-center",
|
|
290
|
+
"data-citation-anchor": "file_line_range",
|
|
291
|
+
children: [
|
|
292
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 overflow-hidden text-ellipsis", children: repositoryFile.file }),
|
|
293
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-muted-foreground", children: repositoryFile.lines })
|
|
294
|
+
]
|
|
295
|
+
}
|
|
296
|
+
) : /* @__PURE__ */ jsx(
|
|
297
|
+
"span",
|
|
298
|
+
{
|
|
299
|
+
className: cn(
|
|
300
|
+
"max-w-[180px] overflow-hidden text-ellipsis",
|
|
301
|
+
// A long place may shorten the name, never make it vanish.
|
|
302
|
+
placeLabel && "min-w-8"
|
|
303
|
+
),
|
|
304
|
+
children: chipName
|
|
305
|
+
}
|
|
306
|
+
),
|
|
307
|
+
citation && placeLabel && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
219
308
|
" ",
|
|
220
|
-
/* @__PURE__ */
|
|
309
|
+
/* @__PURE__ */ jsxs(
|
|
221
310
|
"span",
|
|
222
311
|
{
|
|
223
|
-
className: "
|
|
312
|
+
className: "flex min-w-0 items-center text-xs text-muted-foreground",
|
|
224
313
|
"data-citation-anchor": citation.anchorType,
|
|
225
|
-
children:
|
|
314
|
+
children: [
|
|
315
|
+
"[",
|
|
316
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 overflow-hidden text-ellipsis", children: placeLabel }),
|
|
317
|
+
"]"
|
|
318
|
+
]
|
|
226
319
|
}
|
|
227
320
|
)
|
|
228
321
|
] })
|
|
229
322
|
] });
|
|
230
323
|
if (href && interactive) {
|
|
324
|
+
const speakablePlace = citation && SPEAKABLE.test(citation.description);
|
|
231
325
|
return /* @__PURE__ */ jsx(
|
|
232
326
|
"a",
|
|
233
327
|
{
|
|
@@ -235,10 +329,10 @@ const MentionChip = ({
|
|
|
235
329
|
target: "_blank",
|
|
236
330
|
rel: "noopener noreferrer",
|
|
237
331
|
className,
|
|
238
|
-
"aria-label":
|
|
239
|
-
title: tooltip ?? `Open ${
|
|
332
|
+
"aria-label": repositoryFile && citation ? `Open ${citation.description} in Spaces` : placeLabel && citation && speakablePlace ? `Open ${chipName} at ${citation.description} in Spaces` : `Open ${chipName} in Spaces`,
|
|
333
|
+
title: tooltip ?? `Open ${chipName} in Spaces`,
|
|
240
334
|
"data-excerpt": excerpt ?? void 0,
|
|
241
|
-
"data-mention-name": name,
|
|
335
|
+
"data-mention-name": repositoryFile ? assetTitle ?? name : chipName,
|
|
242
336
|
children: contents
|
|
243
337
|
}
|
|
244
338
|
);
|
|
@@ -253,6 +347,18 @@ const MentionChip = ({
|
|
|
253
347
|
}
|
|
254
348
|
);
|
|
255
349
|
};
|
|
350
|
+
function tooltipText({
|
|
351
|
+
lines,
|
|
352
|
+
excerpt
|
|
353
|
+
}) {
|
|
354
|
+
const kept = [];
|
|
355
|
+
for (const line of lines) {
|
|
356
|
+
const trimmed = line == null ? void 0 : line.trim();
|
|
357
|
+
if (trimmed && !kept.includes(trimmed)) kept.push(trimmed);
|
|
358
|
+
}
|
|
359
|
+
if (excerpt) kept.push(kept.length ? `“${excerpt}”` : excerpt);
|
|
360
|
+
return kept.length ? kept.join("\n") : null;
|
|
361
|
+
}
|
|
256
362
|
function readSourceExcerpt(href) {
|
|
257
363
|
var _a;
|
|
258
364
|
try {
|
|
@@ -399,6 +505,9 @@ function mentionsToPlainText(text) {
|
|
|
399
505
|
});
|
|
400
506
|
}
|
|
401
507
|
export {
|
|
508
|
+
CHIP_ICON_CLASS,
|
|
509
|
+
CHIP_LINK_CLASS,
|
|
510
|
+
CHIP_PILL_CLASS,
|
|
402
511
|
MentionChip,
|
|
403
512
|
MentionLink,
|
|
404
513
|
ScreenPointChip,
|