@flamingo-stack/openframe-frontend-core 0.0.290 → 0.0.291-snapshot.20260618233000
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/dist/{chunk-EL5GCMPU.cjs → chunk-2BMVBPC7.cjs} +9 -9
- package/dist/{chunk-EL5GCMPU.cjs.map → chunk-2BMVBPC7.cjs.map} +1 -1
- package/dist/{chunk-OYXZIPNM.cjs → chunk-2NJ44RTT.cjs} +27 -27
- package/dist/{chunk-OYXZIPNM.cjs.map → chunk-2NJ44RTT.cjs.map} +1 -1
- package/dist/{chunk-R4CLIWAU.js → chunk-5FK7X3EE.js} +270 -172
- package/dist/chunk-5FK7X3EE.js.map +1 -0
- package/dist/{chunk-3SDBXXDP.cjs → chunk-5PELVUFT.cjs} +26 -26
- package/dist/{chunk-3SDBXXDP.cjs.map → chunk-5PELVUFT.cjs.map} +1 -1
- package/dist/{chunk-2V6RCQ5M.cjs → chunk-5R5OODNE.cjs} +40 -40
- package/dist/{chunk-2V6RCQ5M.cjs.map → chunk-5R5OODNE.cjs.map} +1 -1
- package/dist/{chunk-ODR6A6FC.js → chunk-6FHO73AP.js} +22 -10
- package/dist/{chunk-ODR6A6FC.js.map → chunk-6FHO73AP.js.map} +1 -1
- package/dist/{chunk-KJF7SRKH.js → chunk-B2U6INNO.js} +3 -3
- package/dist/{chunk-4F3X2AOB.js → chunk-C667P6LZ.js} +5 -5
- package/dist/{chunk-UC5GB255.cjs → chunk-CDJOKNCS.cjs} +17 -17
- package/dist/{chunk-UC5GB255.cjs.map → chunk-CDJOKNCS.cjs.map} +1 -1
- package/dist/{chunk-7NM7DEUK.js → chunk-CUQH4SHH.js} +2 -2
- package/dist/{chunk-ZLN6SM2U.js → chunk-DUIWR7RQ.js} +3 -3
- package/dist/{chunk-4XMYOZFO.js → chunk-E2YXRSDG.js} +5 -5
- package/dist/{chunk-AAK6IY6Y.cjs → chunk-FFP2A77V.cjs} +10 -10
- package/dist/{chunk-AAK6IY6Y.cjs.map → chunk-FFP2A77V.cjs.map} +1 -1
- package/dist/{chunk-Z5QIVHJW.js → chunk-HTYUZXQP.js} +5 -5
- package/dist/{chunk-LVOBI2M5.js → chunk-IXDTNQF4.js} +3 -3
- package/dist/{chunk-I6ZPGKZ2.cjs → chunk-JC5RN7ZS.cjs} +6 -6
- package/dist/{chunk-I6ZPGKZ2.cjs.map → chunk-JC5RN7ZS.cjs.map} +1 -1
- package/dist/{chunk-VJ4ZWD5G.cjs → chunk-MDLWEJAV.cjs} +1072 -974
- package/dist/chunk-MDLWEJAV.cjs.map +1 -0
- package/dist/{chunk-R2KT5GDD.js → chunk-N45M3TK3.js} +14 -4
- package/dist/chunk-N45M3TK3.js.map +1 -0
- package/dist/{chunk-EI4WALN2.cjs → chunk-OXOTKEYY.cjs} +39 -29
- package/dist/chunk-OXOTKEYY.cjs.map +1 -0
- package/dist/{chunk-7L22MF3U.cjs → chunk-PZZGDS5I.cjs} +17 -17
- package/dist/{chunk-7L22MF3U.cjs.map → chunk-PZZGDS5I.cjs.map} +1 -1
- package/dist/{chunk-VRSXJ5QJ.js → chunk-SLP4KXP6.js} +3 -2
- package/dist/chunk-SLP4KXP6.js.map +1 -0
- package/dist/{chunk-7EYWERFT.js → chunk-VK4B6UGU.js} +4 -4
- package/dist/{chunk-D6RK5YXX.cjs → chunk-Z6BK4XHH.cjs} +22 -10
- package/dist/chunk-Z6BK4XHH.cjs.map +1 -0
- package/dist/{chunk-Y4JNA4W6.cjs → chunk-ZHNL2IPK.cjs} +3 -2
- package/dist/chunk-ZHNL2IPK.cjs.map +1 -0
- package/dist/components/chat/chat-message-enhanced.d.ts.map +1 -1
- package/dist/components/chat/chat-message-list.d.ts.map +1 -1
- package/dist/components/chat/embeddable-chat.d.ts +15 -0
- package/dist/components/chat/embeddable-chat.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-realtime-chunk-processor.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +7 -5
- package/dist/components/chat/index.cjs.map +1 -1
- package/dist/components/chat/index.d.ts +1 -0
- package/dist/components/chat/index.d.ts.map +1 -1
- package/dist/components/chat/index.js +6 -4
- package/dist/components/chat/remark-mention-chips.d.ts +30 -0
- package/dist/components/chat/remark-mention-chips.d.ts.map +1 -0
- package/dist/components/chat/types/api.types.d.ts +4 -0
- package/dist/components/chat/types/api.types.d.ts.map +1 -1
- package/dist/components/chat/types/component.types.d.ts +24 -0
- package/dist/components/chat/types/component.types.d.ts.map +1 -1
- package/dist/components/chat/types/context-item.types.d.ts +5 -0
- package/dist/components/chat/types/context-item.types.d.ts.map +1 -1
- package/dist/components/chat/types/processing.types.d.ts +4 -0
- package/dist/components/chat/types/processing.types.d.ts.map +1 -1
- package/dist/components/chat/utils/chunk-parser.d.ts.map +1 -1
- package/dist/components/chat/utils/nav-anchor-props.d.ts +8 -3
- package/dist/components/chat/utils/nav-anchor-props.d.ts.map +1 -1
- package/dist/components/chat/utils/process-historical-messages.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +6 -6
- package/dist/components/contact/index.js +5 -5
- package/dist/components/docs/index.cjs +5 -5
- package/dist/components/docs/index.js +4 -4
- package/dist/components/embeds/index.cjs +6 -6
- package/dist/components/embeds/index.js +5 -5
- package/dist/components/faq/index.cjs +6 -6
- package/dist/components/faq/index.js +5 -5
- package/dist/components/features/index.cjs +5 -5
- package/dist/components/features/index.js +4 -4
- package/dist/components/features/paths-display.d.ts +1 -1
- package/dist/components/features/paths-display.d.ts.map +1 -1
- package/dist/components/index.cjs +178 -176
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +13 -11
- package/dist/components/index.js.map +1 -1
- package/dist/components/navigation/index.cjs +5 -5
- package/dist/components/navigation/index.js +4 -4
- package/dist/components/onboarding-guides/index.cjs +24 -24
- package/dist/components/onboarding-guides/index.js +4 -4
- package/dist/components/related-content/index.cjs +6 -6
- package/dist/components/related-content/index.js +5 -5
- package/dist/components/tickets/index.cjs +63 -63
- package/dist/components/tickets/index.js +6 -6
- package/dist/components/ui/index.cjs +7 -5
- package/dist/components/ui/index.cjs.map +1 -1
- package/dist/components/ui/index.js +6 -4
- package/dist/components/ui/simple-markdown-renderer.d.ts.map +1 -1
- package/dist/components/ui/tag.d.ts +10 -1
- package/dist/components/ui/tag.d.ts.map +1 -1
- package/dist/index.cjs +7 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +6 -4
- package/dist/utils/index.cjs +21 -9
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.js +21 -9
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/scroll-into-view.d.ts +12 -0
- package/dist/utils/scroll-into-view.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/chat/chat-message-enhanced.tsx +71 -9
- package/src/components/chat/chat-message-list.tsx +2 -0
- package/src/components/chat/embeddable-chat.tsx +50 -6
- package/src/components/chat/hooks/use-realtime-chunk-processor.ts +1 -0
- package/src/components/chat/index.ts +1 -0
- package/src/components/chat/remark-mention-chips.ts +72 -0
- package/src/components/chat/types/api.types.ts +1 -1
- package/src/components/chat/types/component.types.ts +18 -0
- package/src/components/chat/types/context-item.types.ts +5 -0
- package/src/components/chat/types/processing.types.ts +8 -1
- package/src/components/chat/utils/chunk-parser.ts +11 -0
- package/src/components/chat/utils/nav-anchor-props.ts +22 -4
- package/src/components/chat/utils/process-historical-messages.ts +22 -0
- package/src/components/features/.paths-display.md +1 -1
- package/src/components/features/command-box.tsx +1 -1
- package/src/components/features/paths-display.tsx +13 -14
- package/src/components/ui/simple-markdown-renderer.tsx +14 -11
- package/src/components/ui/tag.tsx +12 -2
- package/src/utils/scroll-into-view.ts +51 -9
- package/dist/chunk-D6RK5YXX.cjs.map +0 -1
- package/dist/chunk-EI4WALN2.cjs.map +0 -1
- package/dist/chunk-R2KT5GDD.js.map +0 -1
- package/dist/chunk-R4CLIWAU.js.map +0 -1
- package/dist/chunk-VJ4ZWD5G.cjs.map +0 -1
- package/dist/chunk-VRSXJ5QJ.js.map +0 -1
- package/dist/chunk-Y4JNA4W6.cjs.map +0 -1
- /package/dist/{chunk-KJF7SRKH.js.map → chunk-B2U6INNO.js.map} +0 -0
- /package/dist/{chunk-4F3X2AOB.js.map → chunk-C667P6LZ.js.map} +0 -0
- /package/dist/{chunk-7NM7DEUK.js.map → chunk-CUQH4SHH.js.map} +0 -0
- /package/dist/{chunk-ZLN6SM2U.js.map → chunk-DUIWR7RQ.js.map} +0 -0
- /package/dist/{chunk-4XMYOZFO.js.map → chunk-E2YXRSDG.js.map} +0 -0
- /package/dist/{chunk-Z5QIVHJW.js.map → chunk-HTYUZXQP.js.map} +0 -0
- /package/dist/{chunk-LVOBI2M5.js.map → chunk-IXDTNQF4.js.map} +0 -0
- /package/dist/{chunk-7EYWERFT.js.map → chunk-VK4B6UGU.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/ui/hidden-tags-popup.tsx","../src/components/ui/tag.tsx","../src/utils/format.ts","../src/utils/image-proxy.ts","../src/components/chat/utils/is-cross-origin-url.ts","../src/components/chat/utils/decide-new-tab.ts","../src/components/chat/utils/chat-nav-resolution.ts","../src/utils/fetch-priority.ts","../src/components/layout/page-heading.tsx","../src/components/chat/utils/compact-card-classes.ts","../src/utils/source-icons.ts","../src/components/chat/utils/icon-registry.ts","../src/components/chat/utils/source-row-cta.ts","../src/utils/content-href.ts","../src/components/ui/search-input.tsx"],"sourcesContent":["\"use client\"\n\nimport { type CSSProperties, type ForwardedRef, type ReactNode, forwardRef } from \"react\"\nimport { XmarkCircleIcon } from \"../icons-v2-generated/signs-and-symbols/xmark-circle-icon\"\nimport { cn } from \"../../utils/cn\"\n\nexport interface HiddenTagItem {\n label: ReactNode\n value: unknown\n}\n\nexport interface HiddenTagsPopupProps {\n items: HiddenTagItem[]\n onRemove?: (value: unknown) => void\n disabled?: boolean\n className?: string\n style?: CSSProperties\n}\n\nexport const HiddenTagsPopup = forwardRef(function HiddenTagsPopup(\n { items, onRemove, disabled, className, style }: HiddenTagsPopupProps,\n ref: ForwardedRef<HTMLDivElement>,\n) {\n return (\n <div\n ref={ref}\n style={style}\n className={cn(\n // Base positioning is neutral (left-anchored); consumers override via\n // `style.left` (search-input, tag-search-input) or `className` (autocomplete).\n \"absolute top-full left-0 mt-1 z-50 min-w-[200px]\",\n \"bg-ods-card border border-ods-border rounded-[6px] shadow-lg\",\n \"animate-in fade-in-0 zoom-in-95 duration-150\",\n className,\n )}\n >\n {items.map((item) => (\n <div\n key={String(item.value)}\n className={cn(\n \"flex items-center justify-between gap-3 px-3 h-11 md:h-12\",\n \"border-b border-ods-border last:border-b-0\",\n )}\n >\n <span className=\"min-w-0 flex-1 text-h5 truncate uppercase text-ods-text-primary\" title={typeof item.label === 'string' ? item.label : undefined}>\n {item.label}\n </span>\n {!disabled && onRemove && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n onRemove(item.value)\n }}\n className=\"shrink-0 text-ods-text-secondary hover:text-ods-text-primary transition-colors\"\n aria-label={`Remove ${String(item.label)}`}\n >\n <XmarkCircleIcon size={20} />\n </button>\n )}\n </div>\n ))}\n </div>\n )\n})\n","\"use client\"\n\nimport React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { XmarkCircleIcon } from \"../icons-v2-generated/signs-and-symbols/xmark-circle-icon\"\nimport { cn } from \"../../utils/cn\"\n\nconst tagVariants = cva(\n [\n \"text-h5 h-8 inline-flex items-center justify-center gap-[var(--spacing-system-xxs)] p-[var(--spacing-system-xsf)] rounded-md\",\n \"transition-colors duration-150\",\n ],\n {\n variants: {\n variant: {\n primary: [\n \"bg-[var(--ods-open-yellow-base)] text-[var(--ods-system-greys-black)]\",\n \"hover:bg-[var(--ods-open-yellow-hover)] active:bg-[var(--ods-open-yellow-action)]\",\n ],\n outline: [\n \"bg-[var(--ods-system-greys-black)] text-[var(--ods-system-greys-white)] border border-[var(--ods-system-greys-soft-grey)]\",\n \"hover:bg-[var(--ods-system-greys-black-hover)] hover:border-[var(--ods-system-greys-soft-grey-hover)]\",\n \"active:bg-[var(--ods-system-greys-black-action)] active:border-[var(--ods-system-greys-soft-grey-action)]\",\n ],\n success: [\n \"bg-[var(--ods-attention-green-success-secondary)] text-[var(--ods-attention-green-success)]\",\n \"hover:bg-[#385029] active:bg-[#425a33]\",\n ],\n warning: [\n \"bg-[var(--ods-attention-yellow-warning-secondary)] text-[var(--ods-attention-yellow-warning)]\",\n \"hover:bg-[#544729] active:bg-[#5e5133]\",\n ],\n error: [\n \"bg-[var(--ods-attention-red-error-secondary)] text-[var(--ods-attention-red-error)]\",\n \"hover:bg-[#542b2b] active:bg-[#5e3535]\",\n ],\n critical: [\n \"bg-[var(--ods-attention-red-error)] text-[var(--ods-attention-red-error-secondary)]\",\n \"hover:bg-[var(--ods-attention-red-error-hover)] active:bg-[var(--ods-attention-red-error-action)]\",\n ],\n grey: [\n \"bg-[var(--ods-system-greys-soft-grey)] text-[var(--ods-system-greys-grey)]\",\n \"hover:bg-[var(--ods-system-greys-soft-grey-hover)] active:bg-[var(--ods-system-greys-soft-grey-action)]\",\n ],\n // Matches the EntityTagBadges / StatusBadge tag skin (ods-card + ods-border,\n // mono uppercase) so the tag-editor chips render identically to the public\n // tag badges. Used for FilterChipData variant 'tag' (see search-input).\n badge: [\n \"bg-ods-card text-ods-text-primary border border-ods-border font-mono uppercase tracking-wide\",\n \"hover:border-ods-accent transition-colors\",\n ],\n },\n },\n defaultVariants: {\n variant: \"primary\",\n },\n }\n)\n\nconst disabledTagClasses = [\n \"bg-[var(--ods-system-greys-soft-grey)] text-[var(--ods-system-greys-grey)]\",\n \"border-transparent\",\n \"cursor-not-allowed\",\n \"pointer-events-none\",\n]\n\nexport interface TagProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'>,\n VariantProps<typeof tagVariants> {\n label: React.ReactNode\n labelClassName?: string\n icon?: React.ReactNode\n onClose?: () => void\n disabled?: boolean\n}\n\nfunction Tag({\n label,\n variant,\n icon,\n onClose,\n className,\n labelClassName,\n disabled,\n ...props\n}: TagProps) {\n return (\n <div\n className={cn(\n tagVariants({ variant }),\n disabled && disabledTagClasses,\n className\n )}\n aria-disabled={disabled || undefined}\n {...props}\n >\n {icon && (\n <span className=\"flex items-center justify-center size-5 shrink-0\">\n {icon}\n </span>\n )}\n <span className={cn(\"truncate\", labelClassName)} title={typeof label === 'string' ? label : undefined}>{label}</span>\n {onClose && (\n <button\n type=\"button\"\n disabled={disabled}\n onClick={(e) => {\n e.stopPropagation()\n onClose()\n }}\n className={cn(\n \"flex items-center justify-center size-5 shrink-0 rounded-full opacity-70 transition-opacity\",\n disabled ? \"cursor-not-allowed\" : \"hover:opacity-100\"\n )}\n aria-label=\"Remove\"\n >\n <XmarkCircleIcon className=\"size-4\" />\n </button>\n )}\n </div>\n )\n}\n\nexport { Tag, tagVariants }\n","/**\n * Utility functions for formatting data\n */\n\n/**\n * Format a date to a human-readable string\n * @param date - The date to format (Date object or ISO string)\n * @param options - Formatting options\n * @returns Formatted date string\n */\nexport function formatDate(\n date: Date | string,\n options: Intl.DateTimeFormatOptions = {\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\",\n },\n): string {\n const dateObj = typeof date === \"string\" ? new Date(date) : date\n \n // Check if the date is valid\n if (isNaN(dateObj.getTime())) {\n console.warn(\"Invalid date provided to formatDate:\", date)\n return \"Invalid Date\"\n }\n \n return dateObj.toLocaleDateString(\"en-US\", options)\n}\n\n/**\n * Format a number with thousands separators\n * @param num - The number to format\n * @returns Formatted number string\n */\nexport function formatNumber(num: number): string {\n return num.toLocaleString()\n}\n\n/**\n * Format a price with currency symbol\n * @param price - The price to format\n * @param currency - The currency code\n * @returns Formatted price string\n */\nexport function formatPrice(price: number, currency = \"USD\"): string {\n return new Intl.NumberFormat(\"en-US\", {\n style: \"currency\",\n currency,\n }).format(price)\n}\n\n/**\n * Format bytes to a human-readable string (KB, MB, GB, etc.)\n * @param bytes - The number of bytes\n * @param decimals - Number of decimal places\n * @returns Formatted bytes string\n */\nexport function formatBytes(bytes: number, decimals = 2): string {\n if (bytes === 0) return \"0 Bytes\"\n\n const k = 1024\n const dm = decimals < 0 ? 0 : decimals\n const sizes = [\"Bytes\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\", \"EB\", \"ZB\", \"YB\"]\n\n const i = Math.floor(Math.log(bytes) / Math.log(k))\n\n return Number.parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + \" \" + sizes[i]\n}\n\n/**\n * Compact bytes formatter using the single-letter unit `'B'` (not\n * `'Bytes'`). Used by upload-progress UIs where horizontal space is\n * tight and the longer \"Bytes\" string wraps. Tops out at `TB`.\n * @example formatBytesShort(0) → \"0 B\"; formatBytesShort(1536) → \"1.5 KB\"\n */\nexport function formatBytesShort(bytes: number): string {\n if (bytes === 0) return '0 B'\n const k = 1024\n const sizes = ['B', 'KB', 'MB', 'GB', 'TB']\n const i = Math.floor(Math.log(bytes) / Math.log(k))\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`\n}\n\n/**\n * File-size formatter (1 decimal place, `'B'` unit, caps at `GB`).\n * Hub historical semantics — distinct from `formatBytesShort` (2dp) and\n * `formatBytes` (2dp / `'Bytes'`). Lifted from the hub during the doc-viewer\n * unification so all upload UIs, publication cards, and data-room file-size\n * displays render the same way.\n * @example formatFileSize(0) → \"0 B\"; formatFileSize(1500) → \"1.5 KB\"\n */\nexport function formatFileSize(bytes: number): string {\n if (bytes === 0) return '0 B'\n const k = 1024\n const sizes = ['B', 'KB', 'MB', 'GB']\n const i = Math.floor(Math.log(bytes) / Math.log(k))\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`\n}\n\n/**\n * Format large numbers to abbreviated form (K, M, B) with no decimal points\n * @param num - The number to format\n * @returns Formatted number string (e.g., \"1K\", \"2M\", \"3B\")\n */\nexport function formatLargeNumber(num: number): string {\n if (num === 0) return \"0\"\n \n // Handle negative numbers\n const isNegative = num < 0\n const absNum = Math.abs(num)\n \n let result: string\n \n if (absNum >= 1_000_000_000) {\n // Billions\n result = `${Math.floor(absNum / 1_000_000_000)}B`\n } else if (absNum >= 1_000_000) {\n // Millions\n result = `${Math.floor(absNum / 1_000_000)}M`\n } else if (absNum >= 1_000) {\n // Thousands\n result = `${Math.floor(absNum / 1_000)}K`\n } else {\n // Less than 1000, show as-is\n result = Math.floor(absNum).toString()\n }\n \n return isNegative ? `-${result}` : result\n}\n\n/**\n * Abbreviate large numbers for compact display.\n * 1 200 → 1.2K , 15 000 → 15K , 2 000 000 → 2M\n * @param n Number to format\n */\nexport function formatAbbreviatedNumber(n: number): string {\n if (n >= 1_000_000_000) {\n const value = n / 1_000_000_000;\n return `${Number.isInteger(value) ? value.toFixed(0) : value.toFixed(1)}B`;\n }\n if (n >= 1_000_000) {\n const value = n / 1_000_000;\n return `${Number.isInteger(value) ? value.toFixed(0) : value.toFixed(1)}M`;\n }\n if (n >= 1_000) {\n const value = n / 1_000;\n return `${Number.isInteger(value) ? value.toFixed(0) : value.toFixed(1)}K`;\n }\n return n.toLocaleString();\n}\n\n/**\n * Two-letter uppercase initials from the FIRST + LAST word of a name.\n * Used by avatar-style fallbacks (SquareAvatar, EntityImage) where\n * \"John Michael Doe\" → \"JD\" reads better than \"JM\".\n *\n * Returns `''` for empty / whitespace-only input. Single-word names\n * return a single uppercase letter. Pure — same input always produces\n * the same output, no locale or timezone surface.\n */\nexport function getFirstLastInitials(name?: string | null): string {\n if (!name) return ''\n const words = name.trim().split(/\\s+/)\n if (words.length === 0 || !words[0]) return ''\n if (words.length === 1) return words[0].charAt(0).toUpperCase()\n return (words[0].charAt(0) + words[words.length - 1].charAt(0)).toUpperCase()\n}\n\n/**\n * Two-letter uppercase initials from the FIRST + SECOND word of a name.\n * Used as SquareAvatar / EntityImage / EntityAuthorCard fallback across\n * admin and public pages in both the lib and the hub. Handles empty\n * strings, all-whitespace input, and single-word names cleanly — always\n * returns at least one character so the fallback slot is never empty.\n *\n * Single source of truth: every \"first-letter of each word, uppercase,\n * max 2 chars\" computation across hub + lib MUST come through here.\n */\nexport function nameInitials(\n name: string | null | undefined,\n fallback: string = 'E',\n): string {\n const source = typeof name === 'string' ? name.trim() : ''\n const words = source.length > 0 ? source.split(/\\s+/) : []\n const letters = words\n .map((w) => w[0])\n .filter(Boolean)\n .slice(0, 2)\n .join('')\n return (letters || fallback).toUpperCase()\n}\n\n/**\n * Format seconds to MM:SS or HH:MM:SS format\n * Used for media durations (podcasts, videos)\n * Returns: \"MM:SS\" or \"HH:MM:SS\" if hours > 0\n */\nexport function formatDurationMMSS(seconds: number | null | undefined): string {\n if (!seconds) return '';\n const hours = Math.floor(seconds / 3600);\n const minutes = Math.floor((seconds % 3600) / 60);\n const secs = Math.floor(seconds % 60);\n\n if (hours > 0) {\n return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;\n }\n return `${minutes}:${secs.toString().padStart(2, '0')}`;\n}\n\n/**\n * Format seconds to compact human-readable duration\n * Used for displaying duration in cards and headers\n * Returns: \"Xh Xm\" or \"X min\"\n */\nexport function formatDurationCompact(seconds: number | null | undefined): string {\n if (!seconds) return '';\n const hours = Math.floor(seconds / 3600);\n const minutes = Math.floor((seconds % 3600) / 60);\n if (hours > 0) {\n return `${hours}h ${minutes}m`;\n }\n return `${minutes} min`;\n}\n\n/**\n * Format time with optional timezone\n * Used for webinar and event times\n * Returns: \"10:30 AM EST\" or \"10:30 AM\"\n */\nexport function formatTimeWithTimezone(\n date: Date | string | null | undefined,\n timezone?: string | null\n): string {\n if (!date) return '';\n\n const dateObj = typeof date === 'string' ? new Date(date) : date;\n const timeStr = dateObj.toLocaleTimeString('en-US', {\n hour: 'numeric',\n minute: '2-digit',\n hour12: true,\n });\n\n return timezone ? `${timeStr} ${timezone}` : timeStr;\n}\n\n/**\n * Calculate and format duration between two timestamps\n * Used for webinar durations\n * Returns: \"1h 30m\" or \"45m\"\n */\nexport function formatDurationFromRange(\n startAt: string | Date | null | undefined,\n endAt: string | Date | null | undefined\n): string {\n if (!startAt || !endAt) return '';\n\n const start = typeof startAt === 'string' ? new Date(startAt) : startAt;\n const end = typeof endAt === 'string' ? new Date(endAt) : endAt;\n const durationMs = end.getTime() - start.getTime();\n const minutes = Math.round(durationMs / 60000);\n\n if (minutes >= 60) {\n const hours = Math.floor(minutes / 60);\n const mins = minutes % 60;\n return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`;\n }\n return `${minutes}m`;\n}\n\n/**\n * UTC-anchored date formatter — distinct from the local-rendering\n * `formatDate` above. RAG mappers + audit-style consumers want UTC anchor\n * so date-only strings (`'2024-08-15'`) don't drift across timezones, AND\n * a `'N/A'` (or caller-supplied) fallback for missing values so the LLM\n * never sees a blank.\n *\n * Accepts:\n * - ISO date or timestamp string (`'2024-08-15'` or `'2024-08-15T10:30:00Z'`)\n * - Numeric epoch milliseconds\n * - Numeric string (treated as epoch ms — Slack and GitHub timestamps\n * sometimes arrive that way)\n * - `null` / `undefined` / empty / unparseable → returns `options.fallback`\n *\n * Date-only strings (no `'T'`) get `T00:00:00Z` appended so they anchor to\n * UTC midnight rather than midnight in the runtime's local timezone.\n */\nexport interface FormatDateUTCOptions {\n /** Returned for null/undefined/empty/unparseable input. Defaults to 'N/A'. */\n fallback?: string\n /** 'UTC' (default) or 'local' — switch off the UTC anchor when audit\n * stability matters less than local relevance (chat-card timestamps). */\n timezone?: 'UTC' | 'local'\n}\n\nexport function formatDateUTC(\n value: string | number | null | undefined,\n options: FormatDateUTCOptions = {},\n): string {\n const { fallback = 'N/A', timezone = 'UTC' } = options\n\n if (value === null || value === undefined || value === '') return fallback\n\n let ms: number\n if (typeof value === 'number') {\n ms = value\n } else {\n // String input — first try to interpret as a numeric epoch (Slack/GitHub\n // sometimes arrive that way). Number('') is 0, but we already ruled out\n // empty strings above.\n const n = Number(value)\n if (Number.isFinite(n) && n > 0 && /^-?\\d+(\\.\\d+)?$/.test(value.trim())) {\n ms = n\n } else {\n // ISO string — for date-only forms, anchor to UTC midnight to avoid\n // timezone-offset drift (preserves the original RAG-mapper contract).\n ms = Date.parse(value.includes('T') ? value : value + 'T00:00:00Z')\n }\n }\n\n if (!Number.isFinite(ms)) return fallback\n\n return new Date(ms).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n timeZone: timezone === 'local' ? undefined : 'UTC',\n })\n}\n\n/**\n * Format a reporting-month value (`entry_month`) as \"Mon YYYY\" / \"Month YYYY\",\n * always anchored to UTC. THE single home for the \"What I Shipped\" month label —\n * both the lib card (`'short'`) and the hub detail page (`'long'`) call this, so\n * the React #418 UTC-pin convention lives in exactly one place. Returns `null`\n * for empty input (callers omit the label entirely).\n */\nexport function formatEntryMonthUTC(\n entryMonth: string | null | undefined,\n style: 'short' | 'long' = 'short',\n): string | null {\n if (!entryMonth) return null\n return new Date(entryMonth).toLocaleDateString('en-US', { month: style, year: 'numeric', timeZone: 'UTC' })\n}\n\n/**\n * Format a date string as `MM/DD/YYYY` for legal-document display\n * (privacy policy, terms of service). Locale-stable: always en-US.\n */\nexport function formatLegalDate(dateInput: string): string {\n return new Intl.DateTimeFormat('en-US', {\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n }).format(new Date(dateInput))\n}\n\n/**\n * Format a currency value as `$1,234`. Returns `'N/A'` for null/undefined.\n * USD-rounded (no cents). Used on KPI cards + investor pages.\n */\nexport function formatCurrency(value: number | null | undefined): string {\n if (value == null) return 'N/A'\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: 'USD',\n maximumFractionDigits: 0,\n }).format(value)\n}\n\n/**\n * Format a percent value as `12.50%`. Returns `'N/A'` for null/undefined.\n */\nexport function formatPercent(value: number | null | undefined): string {\n if (value == null) return 'N/A'\n return `${value.toFixed(2)}%`\n}\n\n/**\n * Whole-dollar price (no cents) — `$1,234`. Configurable currency code.\n */\nexport function formatWholeDollars(price: number, currency = 'USD'): string {\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency,\n maximumFractionDigits: 0,\n }).format(price)\n}\n\n// =============================================================================\n// Metric Formatting (for KPI cards / investor updates)\n// =============================================================================\n\nexport type MetricFormat = 'number' | 'currency' | 'percentage' | 'months'\n\n/**\n * Polarity determines whether an increase is good or bad.\n * - 'positive': higher is better (revenue, users, MRR) → up = green, down = red\n * - 'negative': higher is worse (burn rate, churn, CAC) → up = red, down = green\n * - 'neutral': no judgment (headcount, runway) → always gray\n */\nexport type TrendPolarity = 'positive' | 'negative' | 'neutral'\n\n/**\n * Format a metric value with compact notation ($1.2M, 150K, 12 months).\n */\nexport function formatCompactMetric(\n value: number,\n format: MetricFormat = 'number',\n options?: { prefix?: string; suffix?: string },\n): string {\n if (value === 0 || value === null || value === undefined) {\n if (format === 'currency') return `${options?.prefix || '$'}0`\n if (format === 'percentage') return '0%'\n if (format === 'months') return `0 ${options?.suffix || 'months'}`\n return '0'\n }\n\n const absValue = Math.abs(value)\n const sign = value < 0 ? '-' : ''\n\n if (format === 'currency') {\n const prefix = options?.prefix || '$'\n const compact = (val: number, divisor: number, suffix: string) => {\n const divided = val / divisor\n const formatted = divided % 1 === 0 ? divided.toFixed(0) : divided.toFixed(1)\n return `${sign}${prefix}${formatted}${suffix}`\n }\n if (absValue >= 1_000_000_000) return compact(absValue, 1_000_000_000, 'B')\n if (absValue >= 1_000_000) return compact(absValue, 1_000_000, 'M')\n if (absValue >= 1_000) return `${sign}${prefix}${(absValue / 1_000).toFixed(0)}K`\n return `${sign}${prefix}${absValue.toLocaleString()}`\n }\n\n if (format === 'percentage') {\n return `${sign}${absValue}%`\n }\n\n if (format === 'months') {\n const rounded = Math.round(value * 10) / 10\n return `${rounded} ${options?.suffix || 'months'}`\n }\n\n if (absValue >= 1_000_000) return `${sign}${(absValue / 1_000_000).toFixed(1)}M`\n if (absValue >= 1_000) return `${sign}${(absValue / 1_000).toFixed(1)}K`\n return value.toLocaleString()\n}\n\n/**\n * Get ODS trend colors based on direction AND polarity. Single source\n * of truth for trend coloring across hub + lib.\n */\nexport function getTrendColors(\n direction: 'up' | 'down' | 'neutral',\n polarity: TrendPolarity = 'positive',\n): { textClass: string; badgeClass: string } {\n if (direction === 'neutral' || polarity === 'neutral') {\n return {\n textClass: 'text-ods-text-secondary',\n badgeClass: 'bg-ods-border text-ods-text-secondary',\n }\n }\n\n const isPositiveOutcome =\n (direction === 'up' && polarity === 'positive') ||\n (direction === 'down' && polarity === 'negative')\n\n if (isPositiveOutcome) {\n return {\n textClass: 'text-ods-success',\n badgeClass: 'bg-ods-success-secondary text-ods-success',\n }\n }\n\n return {\n textClass: 'text-ods-error',\n badgeClass: 'bg-ods-error-secondary text-ods-error',\n }\n}\n\n/**\n * Format a date range as `Apr 20, 2026 — Jul 20, 2026`. Used on review\n * cycle list/detail rows and summary headers.\n *\n * Accepts either full ISO timestamps or bare `YYYY-MM-DD` dates. Bare\n * dates are interpreted in the viewer's LOCAL timezone — otherwise\n * `\"2026-04-20\"` renders as `\"Apr 19\"` west of UTC. DB values for cycle\n * period are stored as plain dates, so local-tz parsing is correct.\n */\nexport function formatDateRange(\n start: string | null | undefined,\n end: string | null | undefined,\n): string {\n if (!start || !end) return ''\n const fmt = (s: string): string => {\n const bareMatch = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(s)\n const d = bareMatch\n ? new Date(Number(bareMatch[1]), Number(bareMatch[2]) - 1, Number(bareMatch[3]))\n : new Date(s)\n if (Number.isNaN(d.getTime())) return s\n return d.toLocaleDateString(undefined, {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n })\n }\n return `${fmt(start)} — ${fmt(end)}`\n}\n\n/**\n * Format an ISO date string as `\"Jan 5, 2025 at 10:30 AM\"`. Used by\n * admin podcast/webinar cards where the display needs both the short\n * date AND wall-clock time in a single readable phrase.\n *\n * Uses `new Date(...).toLocale*` (NOT the TZ-safe split) because the\n * source columns store full timestamps + the wall-clock half MUST\n * render in the viewer's local timezone (a podcast scheduled \"10:30 AM\n * EST\" should display \"10:30 AM\" for the Eastern admin, \"7:30 AM\" for\n * the Pacific admin — viewer-local is the right semantics here, unlike\n * date-only fields).\n */\nexport function formatDateTimeAt(dateString: string): string {\n const date = new Date(dateString)\n const dateStr = date.toLocaleDateString('en-US', {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n })\n const timeStr = date.toLocaleTimeString('en-US', {\n hour: 'numeric',\n minute: '2-digit',\n hour12: true,\n })\n return `${dateStr} at ${timeStr}`\n}\n\n/**\n * Format a duration measured in MILLISECONDS as a compact human-readable\n * string. Returns `\"0ms\"` for null/NaN/negative, then `Xms` → `X.Xs` →\n * `X.Xm` as the input grows. Used by job-runs / orchestrator dashboards\n * where elapsed milliseconds is the natural unit.\n *\n * Distinct from `formatDuration(seconds)` (verbose `\"X hours Y minutes\"`)\n * and `formatDurationMMSS(seconds)` (media timecode `MM:SS`).\n */\nexport function formatDurationFromMs(ms: number | null | undefined): string {\n if (!ms || isNaN(ms) || ms < 0) return '0ms'\n if (ms < 1000) return `${ms}ms`\n if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`\n return `${(ms / 60000).toFixed(1)}m`\n}\n\n/**\n * Format seconds to verbose human-readable duration: `\"X seconds\"`,\n * `\"X minutes\"`, `\"X hours Y minutes\"`. Use this for human-readable\n * spans; for media timecodes use `formatDurationMMSS`; for compact\n * media labels use `formatDurationCompact`; for elapsed milliseconds\n * use `formatDurationFromMs`.\n */\nexport function formatDuration(seconds: number): string {\n if (seconds < 60) return `${seconds} seconds`\n if (seconds < 3600) return `${Math.floor(seconds / 60)} minutes`\n\n const hours = Math.floor(seconds / 3600)\n const minutes = Math.floor((seconds % 3600) / 60)\n\n if (minutes === 0) return `${hours} hour${hours > 1 ? 's' : ''}`\n return `${hours} hour${hours > 1 ? 's' : ''} ${minutes} minute${minutes > 1 ? 's' : ''}`\n}\n\n// =============================================================================\n// Text formatting helpers (proper-case, HTML strip, bio cleanup)\n// =============================================================================\n\n/**\n * Format underscore-separated text into proper case.\n * `\"self_hosted\"` → `\"Self Hosted\"`\n * `\"open_source\"` → `\"Open Source\"`\n */\nexport function formatUnderscoreText(text: string): string {\n return text\n .split('_')\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(' ')\n}\n\n/**\n * Strip HTML tags and decode common HTML entities from a string.\n * Useful for cleaning API responses that contain HTML content.\n *\n * @example\n * stripHtml('<p>Hello <strong>World</strong></p>') // \"Hello World\"\n * stripHtml('Test & Example') // \"Test & Example\"\n */\nexport function stripHtml(html: string): string {\n // Iterative tag-strip — a single pass is bypass-able by inputs like\n // `<scr<script>ipt>`: stripping the inner `<script>` leaves the outer\n // `<script>` reassembled. Loop until the string is stable so multi-\n // character bypasses cannot survive (CodeQL\n // `js/incomplete-multi-character-sanitization`). `<[^<>]*>` rejects\n // `<` inside the tag body so each pass is itself ReDoS-safe (no\n // backtracking on `<<<<<...<>` inputs).\n let noTags = html\n let prev: string\n do {\n prev = noTags\n noTags = noTags.replace(/<[^<>]*>/g, '')\n } while (noTags !== prev)\n\n // Decode entities. `&` MUST come LAST so we don't double-decode\n // sequences like `&lt;` (which should render as the LITERAL text\n // `<`, not as `<`). All other named/numeric entities decode first;\n // only after those have been replaced do we collapse `&` → `&`.\n return noTags\n .replace(/ /g, ' ')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n .replace(/'/g, \"'\")\n .replace(/'/g, \"'\")\n .replace(/&/g, '&')\n .replace(/\\s+/g, ' ')\n .trim()\n}\n\n/**\n * Display label for the openmsp `vendors.classification` enum.\n * Falls back to proper-cased underscore split for values not in the\n * curated mapping.\n */\nexport function formatClassification(classification: string): string {\n const customMappings: Record<string, string> = {\n openframe_selected: 'OpenFrame Selected',\n }\n return customMappings[classification] || formatUnderscoreText(classification)\n}\n\n/**\n * Display label for the openmsp `vendors.pricing_model` enum.\n * Falls back to proper-cased underscore split for values not in the\n * curated mapping.\n */\nexport function formatPricingModel(pricingModel: string): string {\n const customMappings: Record<string, string> = {\n one_time: 'One-time Purchase',\n self_hosted: 'Self-hosted',\n }\n return customMappings[pricingModel] || formatUnderscoreText(pricingModel)\n}\n\n/**\n * Format a bio/about text from the profiles table for display.\n * Handles HTML content (e.g. `<p>` tags from rich text editors),\n * plain text passthrough, and null/undefined values.\n */\nexport function formatBioText(\n aboutHtml: string | null | undefined,\n fallback: string = '',\n): string {\n if (!aboutHtml || !aboutHtml.trim()) return fallback\n\n if (aboutHtml.includes('<p')) {\n // `<p[^<>]*>` rejects `<` inside the tag so the automaton can't\n // backtrack on `<<<<<...<p>` inputs (ReDoS class fix vs. `<p[^>]*>`).\n const paragraphs = aboutHtml\n .split(/<p[^<>]*>/)\n .slice(1)\n .map((part) => part.split('</p>')[0])\n .map((text) => stripHtml(text).trim())\n .filter((text) => text.length > 0)\n\n if (paragraphs.length > 0) return paragraphs.join(' ')\n }\n\n return stripHtml(aboutHtml).trim() || fallback\n}\n","/**\n * Pure image-proxy URL builder + helpers.\n *\n * Lib-side replacement for the hub's `lib/utils/image-proxy.ts`. The\n * hub used to hardcode `/api/image-proxy` + an `openmsp.ai` skip-domain;\n * this version takes BOTH as parameters so embedded apps (and other\n * platforms that host the lib) can wire their own proxy prefix + skip\n * list at the runtime layer.\n *\n * Pure function — no side effects, no env reads. Callers thread the\n * proxy config through (typically from `ChatRuntime.endpoints.imageProxyUrlPrefix`).\n */\n\nexport type GetProxiedImageUrlOptions = {\n /**\n * URL prefix for the image proxy (`<prefix>?url=<encoded>`). When unset,\n * `getProxiedImageUrl` returns the original URL unchanged — relative\n * URLs always pass through. Hub default: `/api/image-proxy`.\n */\n proxyPrefix?: string;\n /**\n * Domains that should bypass the proxy (e.g. own-CDN hosts whose\n * `Content-Type` is reliable and that already serve CORS-permitting\n * headers). Matched as `imageUrl.includes(domain)` so subdomains\n * inherit. Default: `[]`.\n */\n skipDomains?: string[];\n /**\n * Return the original `https://` URL so the browser loads it directly\n * (no proxy). Use when upstream `Content-Type` breaks the proxy (common\n * with SVG on some CDNs) or you want the origin to see the client\n * request. HTTP stays proxied (mixed content / legacy).\n */\n directHttps?: boolean;\n};\n\n/**\n * Resolve an external image URL through (or around) the image proxy.\n *\n * Resolution order:\n * 1. `imageUrl` already contains `proxyPrefix` → return unchanged\n * (self-skip — prevents double-wrap).\n * 2. `directHttps` set AND `imageUrl` starts with `https://` → return\n * unchanged.\n * 3. `imageUrl` doesn't start with `http://` or `https://` → return\n * unchanged (relative URLs pass through).\n * 4. `proxyPrefix` unset → return unchanged.\n * 5. `skipDomains` matches the URL's host → return unchanged.\n * 6. Else return `<proxyPrefix>?url=<encoded>`.\n */\nexport function getProxiedImageUrl(\n imageUrl: string | null,\n options?: GetProxiedImageUrlOptions,\n): string | null {\n if (!imageUrl) return null;\n\n const proxyPrefix = options?.proxyPrefix;\n const skipDomains = options?.skipDomains ?? [];\n const directHttps = options?.directHttps ?? false;\n\n // (1) Self-skip — already proxied. Check this BEFORE the http/https\n // gate so an absolute proxy URL passed in (e.g. `https://hub.example/api/image-proxy?url=…`)\n // is treated as already-proxied even though it starts with https://.\n if (proxyPrefix && imageUrl.includes(proxyPrefix)) {\n return imageUrl;\n }\n\n // (3) Relative URLs / data: / blob: — return as-is.\n if (!imageUrl.startsWith('http://') && !imageUrl.startsWith('https://')) {\n return imageUrl;\n }\n\n // (2) Direct-https opt-out — only applies to https (http stays proxied).\n if (directHttps && imageUrl.startsWith('https://')) {\n return imageUrl;\n }\n\n // (4) No proxy configured — return as-is.\n if (!proxyPrefix) {\n return imageUrl;\n }\n\n // (5) Skip-list match.\n for (const domain of skipDomains) {\n if (imageUrl.includes(domain)) {\n return imageUrl;\n }\n }\n\n // (6) Proxy.\n return `${proxyPrefix}?url=${encodeURIComponent(imageUrl)}`;\n}\n\n/**\n * Heuristic: URL path looks like an SVG. Useful with `{ directHttps: true }`\n * when only SVGs misbehave through the proxy; raster images can stay\n * proxied if you prefer.\n */\nexport function urlPathLooksLikeSvg(imageUrl: string): boolean {\n try {\n return /\\.svg$/i.test(new URL(imageUrl).pathname);\n } catch {\n return /\\.svg(\\?|#|$)/i.test(imageUrl);\n }\n}\n\n/**\n * Check if an image URL needs to be proxied. `proxyPrefix` is the same\n * value passed to `getProxiedImageUrl` — used to short-circuit the\n * self-skip case.\n */\nexport function shouldProxyImage(\n imageUrl: string | null,\n proxyPrefix?: string,\n): boolean {\n if (!imageUrl) return false;\n if (!imageUrl.startsWith('http://') && !imageUrl.startsWith('https://')) return false;\n if (proxyPrefix && imageUrl.includes(proxyPrefix)) return false;\n return true;\n}\n\n/**\n * Generate a responsive `sizes` attribute for `<img>` / `<Image>` tags.\n * Pure utility — kept here for backwards-compatibility with the previous\n * stub.\n */\nexport function generateImageSizes(_url: string): string {\n return `(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw`;\n}\n","/**\n * Pure utility — true iff `url` has an explicit host (i.e. would resolve\n * to a different origin than where the consumer is running). Used by\n * chat nav decisions and the extracted `decideNewTab` helper.\n *\n * SSR-deterministic: any URL with an explicit host (http://, https://,\n * or protocol-relative //) is treated as cross-origin. By convention\n * same-origin same-app navigation uses RELATIVE paths; an absolute URL\n * with a host is external.\n *\n * Why deterministic: an earlier `window`-dependent implementation\n * returned different values during SSR (false — no window) vs client\n * hydration (true — origin differs), causing React hydration mismatches\n * on every anchor that fell into the no-targetPlatform fallback (e.g.\n * footer \"Support\" links to flamingo.run/support). Same-platform\n * absolute URLs (rare — they only happen when a composer like\n * `buildContentURL` returns an absolute URL for a non-current platform)\n * flow through `decideNewTab`'s PLATFORM branch first (which uses\n * `targetPlatform !== currentSource`) and never reach this fallback.\n */\nexport function isCrossOriginUrl(url: string | null | undefined): boolean {\n if (!url) return false\n // Relative URL — by definition same origin.\n if (url.startsWith('/') && !url.startsWith('//')) return false\n // Anything else — absolute URL (http(s)://) or protocol-relative (//)\n // — is treated as cross-origin.\n return true\n}\n","/**\n * Pure new-tab decision helper.\n *\n * Same logic as the hub's prior inline `decideNewTab`, but parameterized:\n * - `currentSource` is passed by caller (was `currentPlatform()` reads\n * inside the body). Hub callers pass `currentPlatform()`; lib chip\n * code passes `runtime.source ?? ''`.\n *\n * BRANCH PRIORITY:\n * 1. `runtimeMode === 'embed'` → always new-tab (embed-mode short-circuit)\n * 2. `openIn === 'new-tab'` or `'same-tab'` — explicit caller override\n * 3. `alwaysNewTab === true` (legacy synonym for `openIn: 'new-tab'`)\n * 4. `targetPlatform` defined AND `currentSource` non-empty →\n * platform comparison (cross-app → new tab, same-app → same tab)\n * 5. Legacy fallback: `isCrossOriginUrl(href)` — origin compare. Works\n * in prod (distinct hosts) but degrades to \"always same-tab\" in dev.\n *\n * No call site implements its own variant of the rule.\n */\n\nimport { isCrossOriginUrl } from './is-cross-origin-url'\n\nexport type NavSurface = 'useNavLink' | 'useUnifiedNav'\nexport type RuntimeMode = 'host' | 'embed'\n\nexport interface DecideNewTabInput {\n href: string | null | undefined\n targetPlatform?: string | null\n openIn?: 'new-tab' | 'same-tab'\n alwaysNewTab?: boolean\n /** Caller tag — labels the decision in any external trace logging. */\n surface?: NavSurface\n /** Optional chat-runtime mode. When omitted OR set to `'host'`,\n * behavior is byte-equivalent to the legacy logic. Only `'embed'`\n * triggers the new priority-1 branch that forces new-tab. */\n runtimeMode?: RuntimeMode\n /** Current chat source / platform identifier — caller-threaded.\n * Empty string disables the platform-comparison branch (falls\n * through to the origin-compare fallback). */\n currentSource: string\n}\n\nexport function decideNewTab(input: DecideNewTabInput): boolean {\n const { href, targetPlatform, openIn, alwaysNewTab, runtimeMode, currentSource } = input\n\n // Priority #1: embed-mode trumps every other branch.\n if (runtimeMode === 'embed') return true\n if (openIn === 'new-tab') return true\n if (alwaysNewTab === true) return true\n if (openIn === 'same-tab') return false\n // `currentSource` falsy guard: if empty, fall through to origin check.\n if (targetPlatform !== undefined && targetPlatform !== null && currentSource) {\n return targetPlatform !== currentSource\n }\n if (!href) return false\n return isCrossOriginUrl(href)\n}\n","'use client'\n\n/**\n * Embed-mode navigation resolver + shared click-handling primitives.\n *\n * Used by the lib chat panel's chip + card click handlers to force\n * new-tab + absolutize against the embedder-supplied content origin.\n *\n * Refactor from hub origin (D5): inferTargetPlatformFromHref +\n * getPlatformDomain dropped. Caller threads `targetPlatform` directly.\n * Relative URLs without a defaultContentOrigin emit a dev warning\n * and resolve against window.location.origin (likely wrong in embed\n * mode; embedder should set defaultContentOrigin).\n */\n\nimport type { ChatRuntime } from '../../../contexts/chat-runtime-context'\n\n/** Arguments accepted by `window.open` for a new tab — keep `noopener`\n * + `noreferrer` to prevent the opened page from accessing\n * window.opener (reverse-tabnabbing class). */\nexport const NEW_TAB_FEATURES = 'noopener,noreferrer'\n\n/** Modifier-click predicate — matches the existing useNavLink branch\n * (cmd/ctrl/shift/alt OR non-primary mouse button). */\nexport function isModifierClick(e: {\n button?: number\n metaKey?: boolean\n ctrlKey?: boolean\n shiftKey?: boolean\n altKey?: boolean\n}): boolean {\n return (\n e.button !== 0 ||\n !!e.metaKey ||\n !!e.ctrlKey ||\n !!e.shiftKey ||\n !!e.altKey\n )\n}\n\n/** Strip the origin from a same-origin absolute URL, returning a relative\n * path the host's router will treat as in-app navigation. Pass-through\n * for already-relative URLs and for cross-origin URLs. */\nexport function stripSameOriginToPath(href: string): string {\n if (!href.startsWith('http')) return href\n try {\n const u = new URL(href)\n return u.pathname + u.search + u.hash\n } catch {\n return href\n }\n}\n\nexport interface ExternalNavResolution {\n /** Absolutized href — relative paths are joined with\n * `runtime.navigation.defaultContentOrigin` (embed contract). */\n href: string\n /** Synchronous opener. MUST be invoked inline from a user gesture for\n * popup-blocker compatibility (Safari/Firefox). Honors\n * runtime.navigation.openExternal override, else falls back to\n * window.open(href, '_blank', 'noopener,noreferrer'). */\n open: () => void\n}\n\n/**\n * Pre-resolve an href against the embed-mode `defaultContentOrigin`.\n *\n * In `embed` mode every chat-rendered anchor MUST point at an absolute\n * URL on the hub origin (not the embedder origin). Without\n * pre-resolution, relative paths like `/knowledge-base/foo.md` render\n * as `<a href=\"/knowledge-base/...\">` — the click handler intercepts\n * primary-button clicks correctly, but modifier-click (cmd/ctrl/middle/\n * right) bypasses the handler and opens `<embedder-origin>/...` → 404.\n * Pre-resolution at render time makes every browser-native navigation\n * path (hover preview, status bar, copy-link, new-tab via modifier)\n * land on the hub correctly.\n *\n * In `host` mode (the hub itself) the href is returned unchanged —\n * relative paths resolve naturally against the hub's own origin.\n *\n * Absolute URLs and protocol-relative URLs are returned unchanged in\n * both modes.\n */\nexport function resolveHrefForRuntime(\n href: string,\n runtime: ChatRuntime,\n): string {\n if (runtime.navigation.mode !== 'embed') return href\n if (href.startsWith('http')) return href\n if (href.startsWith('//')) return href\n const origin = runtime.navigation.defaultContentOrigin\n if (!origin) return href\n return origin.replace(/\\/+$/, '') + (href.startsWith('/') ? href : '/' + href)\n}\n\n/**\n * Resolve external navigation in embed mode.\n *\n * Browser-only: called from React event handlers (onClick, imperative\n * click handlers) after mount. No SSR-reachable paths.\n */\nexport function resolveExternalNavigation(args: {\n href: string\n targetPlatform: string | null | undefined\n runtime: ChatRuntime\n}): ExternalNavResolution {\n let abs = args.href\n\n // Defense: protocol-relative URLs (`//evil.com/x`) are NOT same-origin —\n // browsers treat them as cross-origin absolute. Without this guard,\n // a malicious href like `//evil.com/x` would skip the `startsWith('http')`\n // check and reach the origin-concat branch below, producing a malformed\n // URL that some browsers normalize back to `https://evil.com/x`\n // (open-redirect class).\n if (abs.startsWith('//')) abs = window.location.protocol + abs\n\n if (!abs.startsWith('http')) {\n if (args.runtime.navigation.defaultContentOrigin) {\n // Strip trailing slashes via a loop, not `/\\/+$/` — CodeQL flags\n // the greedy `+` as polynomial-redos even with the `$` anchor.\n // The loop is unambiguously O(n) in slash count.\n let origin = args.runtime.navigation.defaultContentOrigin\n while (origin.endsWith('/')) origin = origin.slice(0, -1)\n abs = origin + (abs.startsWith('/') ? abs : '/' + abs)\n } else if (\n args.runtime.navigation.mode === 'embed' &&\n process.env.NODE_ENV !== 'production'\n ) {\n console.warn(\n '[chat-nav-resolution] relative href in embed mode with no ' +\n 'defaultContentOrigin set on runtime.navigation — link will resolve ' +\n 'against the embedder origin.',\n { href: abs, targetPlatform: args.targetPlatform },\n )\n }\n }\n\n const open = () => {\n // Explicit if/else (NOT `??`) — `??` only short-circuits on\n // null/undefined LHS, but `openExternal?.(href)` returns void on a\n // successful call, which `??` treats as undefined and re-fires\n // window.open — double-open bug.\n if (args.runtime.navigation.openExternal) {\n args.runtime.navigation.openExternal(abs)\n } else {\n window.open(abs, '_blank', NEW_TAB_FEATURES)\n }\n }\n\n return { href: abs, open }\n}\n","import React from 'react';\n\n/**\n * `fetchPriorityProp(priority)` — build the right shape for the `<img>` /\n * `<iframe>` fetch-priority hint based on the React major version detected\n * at module load.\n *\n * React 18 only recognizes the lowercase HTML attribute `fetchpriority`\n * and emits a console warning when it sees the camelCase prop. React 19+\n * recognizes the camelCase prop `fetchPriority` (matching the DOM's IDL\n * property) and warns when it sees the lowercase form.\n *\n * Spread the returned object into the element so the rendered DOM ends\n * up with the canonical lowercase `fetchpriority` attribute either way,\n * with zero console warnings on both React versions.\n *\n * Accepts either a boolean (for the simple \"high vs low\" hint at LCP\n * candidates) or an explicit HTML `fetchpriority` value (`'high' | 'low'\n * | 'auto'`) for call sites that always want one specific value.\n */\nconst REACT_MAJOR = parseInt((React.version || '0').split('.')[0], 10);\nconst USE_CAMEL_CASE_FETCH_PRIORITY = REACT_MAJOR >= 19;\n\nexport type FetchPriorityValue = 'high' | 'low' | 'auto';\n\nexport function fetchPriorityProp(\n priority: boolean | FetchPriorityValue | undefined,\n): Record<string, string> {\n // `undefined` → `'auto'` so callers can spread `{...fetchPriorityProp(maybeBool)}`\n // without a separate null-check at every callsite. `'auto'` is the\n // DOM default and emits no warning on either React major.\n const value: FetchPriorityValue =\n priority === undefined\n ? 'auto'\n : typeof priority === 'boolean'\n ? priority ? 'high' : 'low'\n : priority;\n return USE_CAMEL_CASE_FETCH_PRIORITY\n ? { fetchPriority: value }\n : { fetchpriority: value };\n}\n","import type { ReactNode } from 'react'\n\n/**\n * THE page-title style — the ODS `text-h1` token (`--font-size-h1-title` =\n * 40 / 48 / 56px, Azeret Mono semibold), which already owns the responsive\n * letter-spacing (-0.02em). Mirrors the global `h1 { @apply text-h1 }` base, so\n * every page heading is the SAME size across the lib and every consuming app.\n *\n * Single source of truth — do NOT hardcode a px size or re-assert the token's\n * tracking; render `<PageHeading>` (or, for the rare non-h1 caller, apply\n * `PAGE_HEADING_CLASS`).\n */\nexport const PAGE_HEADING_CLASS = 'text-h1 text-ods-text-primary'\n\n/**\n * THE section-heading style — the ODS `text-h2` sub-title token\n * (`--font-size-h2-sub-title` = 24 / 32 / 32px), one step below the page\n * title so a section `<h2>`/`<h3>` is always visually distinguishable from\n * the page's `<h1>`. Same single-source rule as PAGE_HEADING_CLASS: never\n * hardcode a px ramp or re-assert the token's tracking on a section heading.\n */\nexport const SECTION_HEADING_CLASS = 'text-h2 text-ods-text-primary'\n\nconst DESCRIPTION_CLASS =\n \"mt-6 max-w-[640px] font-['DM_Sans'] text-[16px] md:text-[18px] leading-[24px] md:leading-[28px] text-ods-text-secondary\"\n\nexport interface PageHeadingProps {\n /** Heading content — plain text or nodes (e.g. an accent <span>). */\n children: ReactNode\n /**\n * Heading level. Defaults to 'h1' (exactly one per page). Pass 'h2' where the\n * page already renders an <h1> above (e.g. a hero/featured item).\n */\n as?: 'h1' | 'h2'\n /** Optional supporting copy rendered as a <p> beneath the heading. */\n description?: ReactNode\n /** Extra classes merged onto the heading (margins, width, truncate, etc.). */\n className?: string\n /** Extra classes merged onto the description <p>. */\n descriptionClassName?: string\n}\n\n/**\n * Unified page heading. Renders the canonical page-title <h1> (or <h2>) plus an\n * optional description, so every page shares one consistent style instead of\n * duplicating the class string. Layout (PageContainer, margins, surrounding\n * sections) stays with the caller — this owns only the heading + description.\n */\nexport function PageHeading({\n children,\n as: Tag = 'h1',\n description,\n className,\n descriptionClassName,\n}: PageHeadingProps) {\n const headingClass = className ? `${PAGE_HEADING_CLASS} ${className}` : PAGE_HEADING_CLASS\n const descClass = descriptionClassName ? `${DESCRIPTION_CLASS} ${descriptionClassName}` : DESCRIPTION_CLASS\n // `description` is a ReactNode, so `description={cond && '...'}` can pass a\n // boolean `false` — exclude it (and empty string) so we never render an empty\n // <p> that adds phantom vertical gap beneath the heading.\n const hasDescription =\n description != null && description !== '' && typeof description !== 'boolean'\n return (\n <>\n <Tag className={headingClass}>{children}</Tag>\n {hasDescription && <p className={descClass}>{description}</p>}\n </>\n )\n}\n","/**\n * Compact-card class constants — single source of truth for the chat-inline\n * `size='sm'` card frame.\n *\n * Lifted from the hub's `components/shared/compact-card/compact-card-classes.ts`\n * so the pure-presentation cards in `entity-cards/` can stop importing from\n * the hub tree. Every compact card across the chat shell shares the SAME\n * outer frame, skeleton frame, image slot, icon slot, text column, and\n * per-line typography.\n *\n * Card-specific variation (Play overlay on podcasts, #N pill on investor\n * updates, status badge on roadmap) lives INSIDE the card next to its\n * other type-specific JSX; the frame stays uniform across types.\n */\n\n/** Loaded compact card outer frame for INTERACTIVE state — anchor wrap\n * with hover/transition affordance. Use ONLY when the card resolves\n * to a real, safe href; the user expects a clickable feel. */\nexport const COMPACT_CARD_OUTER =\n 'my-1.5 flex items-start gap-3 w-full p-2 rounded-lg border border-ods-border bg-ods-card no-underline transition-colors hover:border-ods-text-secondary/40'\n\n/** Loaded compact card outer frame for NON-INTERACTIVE state — span wrap\n * used when `safeHref()` rejects the chat ref's url. */\nexport const COMPACT_CARD_OUTER_STATIC =\n 'my-1.5 flex items-start gap-3 w-full p-2 rounded-lg border border-ods-border bg-ods-card no-underline cursor-default'\n\n/** Skeleton outer frame — same dimensions as loaded, plus `animate-pulse`. */\nexport const COMPACT_CARD_SKELETON_OUTER =\n 'my-1.5 flex w-full animate-pulse items-start gap-3 rounded-lg border border-ods-border bg-ods-card p-2'\n\n/** 56×56 image slot — for cards with a cover image. */\nexport const COMPACT_CARD_IMAGE_SLOT =\n 'relative block shrink-0 self-start w-14 h-14 aspect-square overflow-hidden rounded-md bg-ods-bg'\n\n/** 56×56 placeholder slot for skeletons. */\nexport const COMPACT_CARD_SKELETON_IMAGE_SLOT =\n 'block h-14 w-14 aspect-square shrink-0 self-start rounded-md bg-ods-bg'\n\n/** 56×56 icon slot — for cards without a cover image. */\nexport const COMPACT_CARD_ICON_SLOT =\n 'flex h-14 w-14 aspect-square shrink-0 self-start items-center justify-center rounded-md bg-ods-bg text-ods-accent'\n\n/** Text column wrapper — explicit 56px height with `flex flex-col`. */\nexport const COMPACT_CARD_TEXT_COL =\n 'flex min-w-0 flex-1 flex-col gap-0.5 min-h-14'\n\n/** Title row container — fixed 20px (h-5) to match `text-sm leading-5`. */\nexport const COMPACT_CARD_TITLE_ROW = 'flex items-center min-w-0 h-5'\n\n/** Subtitle / summary row container — fixed 16px (h-4). */\nexport const COMPACT_CARD_META_ROW_BOX = 'flex items-center min-w-0 h-4'\n\n/** Title text — bold 14px on a 20px line. */\nexport const COMPACT_CARD_TITLE =\n 'truncate text-sm font-semibold leading-5 text-ods-text-primary'\n\n/** Subtitle text — 11px on a 16px line. */\nexport const COMPACT_CARD_SUBTITLE =\n 'truncate text-[11px] leading-4 text-ods-text-secondary'\n\n/** Summary text — same metrics as subtitle, dropped to 80% opacity. */\nexport const COMPACT_CARD_SUMMARY =\n 'truncate text-[11px] leading-4 text-ods-text-secondary/80'\n\n/** Meta-row variant — multi-part 11px metadata. */\nexport const COMPACT_CARD_META_ROW =\n 'flex items-center gap-1.5 min-w-0 text-[11px] leading-4 text-ods-text-secondary'\n\n/** Non-breaking space — used as a fallback child so the row's height stays 16px. */\nexport const COMPACT_CARD_ROW_FILLER = ' '\n\n/** Schemes allowed in the outer `<a href>` of a compact card. */\nconst SAFE_URL_SCHEMES = ['http:', 'https:', 'mailto:']\n\n/** Return the input url ONLY if it's safe to drop into an `<a href>`:\n * the scheme must be http / https / mailto, or the URL must be a\n * same-origin path (`/blog/foo`). Otherwise returns `null`. */\nexport function safeHref(url: string | null | undefined): string | null {\n if (!url) return null\n // Defense in depth: control chars + zero-width / line-separator chars.\n if (/[\\u0000-\\u001f\\u007f\\u200b-\\u200d\\u2028\\u2029\\ufeff]/.test(url)) return null\n const trimmed = url.trim()\n if (!trimmed) return null\n // Pure same-origin path.\n if (trimmed.startsWith('/') && !trimmed.startsWith('//')) return trimmed\n if (trimmed.startsWith('#')) return trimmed\n // Reject bare scheme-only inputs.\n if (/^[a-z][a-z0-9+.-]*:$/i.test(trimmed)) return null\n try {\n const parsed = new URL(trimmed, 'https://_safehref_base.invalid')\n if (!SAFE_URL_SCHEMES.includes(parsed.protocol)) return null\n if ((parsed.protocol === 'https:' || parsed.protocol === 'http:') && !parsed.hostname) return null\n return trimmed\n } catch {\n return null\n }\n}\n","/**\n * Per-source icon-name + label lookup. Keyed by `RagTableConfig.id` (the same\n * id used in `chat_source_rag_tables.rag_table_id` and on each\n * `ChatRef.sourceRepo`).\n *\n * Server-safe — lives in `src/utils/` (no `'use client'` banner) so\n * hub server-side rag-mappers + chat route handlers can call\n * `getSourceLabel()` / `getSourceIconName()` directly. The client-side\n * chat barrel re-exports from here for ergonomic client imports.\n *\n * Used by SOURCE-LEVEL chip surfaces:\n * - The chip strip below an assistant message.\n * - The tracking-row source glyph on inline entity cards.\n *\n * Why a small map here instead of a column on RAG_TABLE_CONFIGS:\n * - The icon NAME is a small string; the resolver (icon-name → React\n * component) lives in `src/components/chat/utils/icon-registry.ts`\n * where it can stay client-only. Server-side consumers stay\n * icon-component-free.\n * - The mapping is intrinsic to the TABLE, not per-platform — same\n * entry serves every (platform, source) binding.\n *\n * Per-table admin-UI bucket id (`SOURCE_CATEGORIES_BY_TABLE`) stays\n * hub-side because it depends on the hub's `RagSourceCategoryId` enum.\n */\n\n/** Map RagTableConfig.id → icon_name (resolvable via `getIconComponent`). */\nexport const SOURCE_ICON_NAMES: Record<string, string> = {\n // Doc tables\n 'openframe-docs': 'openframe',\n 'data-room-docs': 'shield',\n\n // CMS / programs\n 'blog-posts': 'newspaper',\n 'product-releases': 'rocket',\n 'case-studies': 'briefcase',\n 'onboarding-guides': 'graduation-cap',\n webinars: 'video',\n events: 'calendar',\n podcasts: 'headphones',\n 'customer-interviews': 'users',\n\n // Financials\n 'investor-updates': 'mail',\n 'financial-kpis': 'activity',\n 'financial-cap-table': 'table',\n 'financial-pnl': 'trending-up',\n 'financial-balance-sheet': 'dollar-sign',\n 'financial-cash-flow': 'banknote',\n\n // ClickUp\n 'clickup-roadmap': 'clickup',\n 'clickup-delivery': 'clickup',\n 'clickup-tasks-internal': 'clickup',\n\n // GitHub\n 'github-commits': 'github',\n 'github-pull-requests': 'github',\n 'github-pr-reviews': 'github',\n 'github-commits-public': 'github',\n 'github-pull-requests-public': 'github',\n 'github-pr-reviews-public': 'github',\n\n // HubSpot\n 'hubspot-tickets': 'hubspot',\n 'hubspot-tickets-anon': 'hubspot',\n 'hubspot-tickets-self': 'hubspot',\n\n // Communications\n 'slack-messages': 'slack',\n}\n\n/** Lookup an icon name by RagTableConfig.id. Returns undefined when\n * unknown so callers can decide whether to fall back to documentType\n * or to the generic FileText glyph in the icon registry. */\nexport function getSourceIconName(tableId: string | null | undefined): string | undefined {\n if (!tableId) return undefined\n return SOURCE_ICON_NAMES[tableId]\n}\n\n/**\n * Per-table display LABEL — used by SERVER-SIDE chip-strip rendering\n * (the row's \"Customer Interviews (8 records)\" chip below an assistant\n * message).\n *\n * The chip GRID + autocomplete DROPDOWN read their command label\n * from the DB (per-source `chat_admin_slash_commands.label`) — those\n * surfaces can carry per-source labels (e.g. \"OpenFrame Commits\" vs\n * \"GitHub Commits\" for the same table). This map is the TABLE-LEVEL\n * label used by sub-message chips where a SINGLE label per table is\n * sufficient regardless of which source's chat is rendering.\n */\nexport const SOURCE_LABELS_BY_TABLE: Record<string, string> = {\n // Doc tables\n 'openframe-docs': 'OpenFrame Docs',\n 'data-room-docs': 'Data Room',\n\n // CMS / programs\n 'blog-posts': 'Blog Posts',\n 'product-releases': 'Product Releases',\n 'case-studies': 'Case Studies',\n 'onboarding-guides': 'Onboarding Guides',\n webinars: 'Webinars',\n events: 'Events',\n podcasts: 'Podcasts',\n 'customer-interviews': 'Customer Interviews',\n\n // Financials\n 'investor-updates': 'Investor Updates',\n 'financial-kpis': 'Financial KPIs',\n 'financial-cap-table': 'Cap Table',\n 'financial-pnl': 'Profit & Loss',\n 'financial-balance-sheet': 'Balance Sheet',\n 'financial-cash-flow': 'Cash Flow',\n\n // ClickUp\n 'clickup-roadmap': 'ClickUp Roadmap',\n 'clickup-delivery': 'ClickUp Delivery',\n 'clickup-tasks-internal': 'ClickUp Tasks',\n\n // GitHub\n 'github-commits': 'GitHub Commits',\n 'github-pull-requests': 'GitHub Pull Requests',\n 'github-pr-reviews': 'GitHub PR Reviews',\n 'github-commits-public': 'OpenFrame Commits',\n 'github-pull-requests-public': 'OpenFrame Pull Requests',\n 'github-pr-reviews-public': 'OpenFrame PR Reviews',\n\n // HubSpot\n 'hubspot-tickets': 'HubSpot Tickets',\n 'hubspot-tickets-anon': 'Known Issues',\n 'hubspot-tickets-self': 'My Tickets',\n\n // Communications\n 'slack-messages': 'OpenMSP Community',\n}\n\n/** Lookup a human-readable label by RagTableConfig.id. Falls back\n * to the raw id (chip text becomes the table slug — visible bug\n * that prompts an entry to land in `SOURCE_LABELS_BY_TABLE`). */\nexport function getSourceLabel(tableId: string): string {\n return SOURCE_LABELS_BY_TABLE[tableId] ?? tableId\n}\n\n/**\n * Default `documentType → RagTableConfig.id` reverse map.\n *\n * Mirrors the canonical lookup the hub registers via\n * `lib/config/rag-table-config.ts:tableIdForDocumentType()` — same vocabulary\n * the LLM emits inside `[card://<type>:<id>]` markers, same tableIds the\n * retrieval layer routes by, and same set of keys covered by\n * `SOURCE_ICON_NAMES` / `SOURCE_LABELS_BY_TABLE` above.\n *\n * Server-safe (no `'use client'` banner), keyed by string for forward\n * compatibility — adding a new RAG table is a single-line edit here so\n * lib-based embedders pick it up without re-publishing.\n *\n * Embedders that need a CUSTOM mapping (e.g. polymorphic types whose\n * tableId depends on per-tenant config) still pass their own\n * `tableIdForDocumentType` callback to `useEmbeddedChat`; this default\n * only applies when no callback is supplied.\n *\n * Inverse mapping audited 2026-05-24 against the hub registry — every\n * entry in `RAG_TABLE_CONFIGS` with a `documentType` is represented.\n */\nexport const DEFAULT_DOCUMENT_TYPE_TO_TABLE_ID: Record<string, string> = {\n // Doc tables\n markdown: 'openframe-docs',\n data_room_doc: 'data-room-docs',\n\n // CMS / programs\n blog_post: 'blog-posts',\n product_release: 'product-releases',\n case_study: 'case-studies',\n onboarding_guide: 'onboarding-guides',\n webinar: 'webinars',\n event: 'events',\n podcast: 'podcasts',\n customer_interview: 'customer-interviews',\n\n // Financials\n investor_update: 'investor-updates',\n financial_kpi: 'financial-kpis',\n cap_table: 'financial-cap-table',\n profit_loss: 'financial-pnl',\n balance_sheet: 'financial-balance-sheet',\n cash_flow: 'financial-cash-flow',\n\n // ClickUp\n roadmap_item: 'clickup-roadmap',\n delivery_item: 'clickup-delivery',\n internal_task: 'clickup-tasks-internal',\n\n // GitHub\n github_commit: 'github-commits',\n github_pull_request: 'github-pull-requests',\n github_pr_review: 'github-pr-reviews',\n github_commit_public: 'github-commits-public',\n github_pull_request_public: 'github-pull-requests-public',\n github_pr_review_public: 'github-pr-reviews-public',\n\n // HubSpot\n hubspot_ticket: 'hubspot-tickets',\n hubspot_ticket_anon: 'hubspot-tickets-anon',\n hubspot_ticket_self: 'hubspot-tickets-self',\n\n // Communications\n slack_message: 'slack-messages',\n}\n\n/**\n * Default `tableIdForDocumentType` resolver used by `useEmbeddedChat` when\n * the caller didn't pass an explicit callback. Returns `null` for\n * unrecognized document types so downstream `discussRef` / `displayRef`\n * still short-circuits gracefully on stale or custom types.\n */\nexport function defaultTableIdForDocumentType(documentType: string): string | null {\n return DEFAULT_DOCUMENT_TYPE_TO_TABLE_ID[documentType] ?? null\n}\n","/**\n * Icon registry — string `icon_name` → React component.\n *\n * SINGLE SOURCE OF TRUTH (no, really this time) for every icon-name →\n * React-component lookup across the lib + the hub. Two DB columns feed\n * in with DIFFERENT conventions:\n *\n * - `chat_admin_slash_commands.icon_name` — kebab-case (`'rocket'`,\n * `'hubspot'`, `'github'`, `'dollar-sign'`).\n * - `social_platforms.icon_name` — PascalCase component names\n * (`'LinkedInIcon'`, `'XLogo'`, `'YouTubeIcon'`, `'Github'`).\n *\n * The registry stores entries under kebab-case keys (URL/DB-friendly,\n * shell-safe, matches `lucide-react`'s own file-naming convention).\n * The PascalCase variants come in via `normalizeIconKey()`, which maps\n * known PascalCase aliases → kebab-case before the lookup runs.\n *\n * Adding a new icon: import the component below, add a `'name': Component`\n * entry in `ICON_REGISTRY`, and (if the same icon also flows in from a DB\n * column that uses PascalCase) add a `PascalName → 'name'` row to\n * `PASCAL_TO_KEBAB_ALIASES`.\n *\n * Fallback: an unknown / null icon_name resolves to `FileText` so a chip\n * never renders without an icon.\n */\n\nimport { createElement, type ComponentType, type ReactNode } from 'react'\nimport {\n Activity,\n Banknote,\n Bell,\n BookOpen,\n Box,\n Briefcase,\n Calendar,\n CheckSquare,\n DollarSign,\n Facebook,\n FileText,\n Github,\n Globe,\n GraduationCap,\n Headphones,\n Info,\n Instagram,\n Mail,\n Megaphone,\n MessageCircle,\n MessageSquare,\n Newspaper,\n Package,\n PenSquare,\n Rocket,\n Search,\n Send,\n Shield,\n Star,\n TableProperties,\n TrendingUp,\n Twitter,\n Users,\n Video,\n Youtube,\n} from 'lucide-react'\n// Brand icons — lib-local exports.\nimport {\n SlackIcon,\n GitHubIcon,\n ClickUpIcon,\n HubspotIcon,\n LinkedInIcon,\n FacebookIcon,\n InstagramIcon,\n YouTubeIcon,\n XLogo,\n OpenFrameLogo as RawOpenFrameLogo,\n} from '../../icons'\n\n// Wrapper so the OpenFrame logo's outer frame inherits the parent's text\n// color instead of its hardcoded default — that default is invisible on\n// dark chat surfaces. The white squares stay white (visible on dark).\n// With this, the parent's `text-ods-text-primary` propagates via\n// `currentColor` and the frame renders in the same primary text color\n// as the lucide icons next to it. Uses `createElement` (not JSX) so this\n// stays a `.ts` file. Accepts `color` for shape-compat with the registry's\n// IconComponent type, but ignores it — the logo's outer frame is wired\n// to `currentColor` via the upperPathColor pass-through.\nconst OpenFrameLogoIcon: ComponentType<{ className?: string; color?: string }> = ({ className }) =>\n createElement(RawOpenFrameLogo, { className, upperPathColor: 'currentColor' })\n\n/**\n * Loose icon-component shape: every entry accepts `className`; `color` is\n * optional (lucide + most brand icons accept it; OpenFrameLogo doesn't,\n * but `color` is optional so passing `undefined` is a no-op). Loose enough\n * to accept both lucide's `LucideIcon` and our brand SVG components.\n */\ntype IconComponent = ComponentType<{ className?: string; color?: string }>\n\n/**\n * The kebab-case registry. ALL lookups go through here; PascalCase\n * aliases route through `normalizeIconKey()` first.\n */\nexport const ICON_REGISTRY: Record<string, IconComponent> = {\n // lucide-react (kebab-case)\n activity: Activity,\n banknote: Banknote,\n bell: Bell,\n 'book-open': BookOpen,\n box: Box,\n briefcase: Briefcase,\n calendar: Calendar,\n 'check-square': CheckSquare,\n 'dollar-sign': DollarSign,\n // For brand-vs-lucide variants the kebab key is the BRAND (most-common\n // social-platform intent); `'<name>-lucide'` carries the lucide outline.\n facebook: FacebookIcon,\n 'facebook-lucide': Facebook,\n 'file-text': FileText,\n // `'github-lucide'` is the lucide outline glyph (used by\n // `social_platforms.icon_name='Github'`); `'github'` is the brand icon\n // (used by `chat_admin_slash_commands.icon_name='github'`). The PascalCase\n // alias `'Github' → 'github-lucide'` routes the lucide glyph for\n // social-platform DB rows; `'GitHubIcon' → 'github'` keeps brand routing\n // consistent for callers that store the PascalCase component name.\n github: GitHubIcon,\n 'github-lucide': Github,\n globe: Globe,\n 'graduation-cap': GraduationCap,\n headphones: Headphones,\n info: Info,\n instagram: InstagramIcon,\n 'instagram-lucide': Instagram,\n mail: Mail,\n megaphone: Megaphone,\n 'message-circle': MessageCircle,\n 'message-square': MessageSquare,\n newspaper: Newspaper,\n package: Package,\n 'pen-square': PenSquare,\n rocket: Rocket,\n search: Search,\n send: Send,\n shield: Shield,\n star: Star,\n table: TableProperties,\n 'trending-up': TrendingUp,\n twitter: Twitter,\n users: Users,\n video: Video,\n youtube: YouTubeIcon,\n 'youtube-lucide': Youtube,\n // brand-only icons (no lucide variant in current use)\n slack: SlackIcon,\n clickup: ClickUpIcon,\n hubspot: HubspotIcon,\n linkedin: LinkedInIcon,\n x: XLogo,\n openframe: OpenFrameLogoIcon,\n}\n\n/**\n * PascalCase → kebab-case alias table for DB columns that store icon\n * names as component identifiers (currently `social_platforms.icon_name`).\n * Keep these in sync with the actual rows in those tables.\n */\nconst PASCAL_TO_KEBAB_ALIASES: Record<string, string> = {\n // Lucide PascalCase names → kebab equivalents\n Activity: 'activity',\n Banknote: 'banknote',\n Bell: 'bell',\n BookOpen: 'book-open',\n Box: 'box',\n Briefcase: 'briefcase',\n Calendar: 'calendar',\n CheckSquare: 'check-square',\n DollarSign: 'dollar-sign',\n // Lucide PascalCase → the `-lucide` kebab variant (kebab default is the\n // brand icon for the social-platform names below).\n Facebook: 'facebook-lucide',\n FileText: 'file-text',\n Github: 'github-lucide',\n Globe: 'globe',\n GraduationCap: 'graduation-cap',\n Headphones: 'headphones',\n Info: 'info',\n Instagram: 'instagram-lucide',\n Mail: 'mail',\n Megaphone: 'megaphone',\n MessageCircle: 'message-circle',\n MessageSquare: 'message-square',\n Newspaper: 'newspaper',\n Package: 'package',\n PenSquare: 'pen-square',\n Rocket: 'rocket',\n Search: 'search',\n Send: 'send',\n Shield: 'shield',\n Star: 'star',\n TableProperties: 'table',\n TrendingUp: 'trending-up',\n Twitter: 'twitter',\n Users: 'users',\n Video: 'video',\n Youtube: 'youtube-lucide',\n // Brand-icon PascalCase exports → kebab (kebab default is the brand)\n SlackIcon: 'slack',\n GitHubIcon: 'github',\n ClickUpIcon: 'clickup',\n HubspotIcon: 'hubspot',\n LinkedInIcon: 'linkedin',\n FacebookIcon: 'facebook',\n InstagramIcon: 'instagram',\n YouTubeIcon: 'youtube',\n XLogo: 'x',\n OpenFrameLogo: 'openframe',\n}\n\n/**\n * Normalize an icon key to the registry's canonical kebab-case form.\n * Accepts PascalCase variants (from `social_platforms.icon_name`-style\n * columns) and passes kebab-case keys through unchanged.\n */\nexport function normalizeIconKey(key: string): string {\n return PASCAL_TO_KEBAB_ALIASES[key] ?? key\n}\n\n/**\n * Resolve an `icon_name` to its React component. Accepts both PascalCase\n * and kebab-case keys (normalizes PascalCase first). Unknown / null /\n * undefined names fall back to `FileText` — chips always render with\n * SOMETHING rather than a missing glyph. The fallback is intentional:\n * a typo in a new admin-authored row shouldn't crash render.\n */\nexport function getIconComponent(\n iconName: string | null | undefined,\n): ComponentType<{ className?: string; color?: string }> {\n if (!iconName) return FileText\n return ICON_REGISTRY[normalizeIconKey(iconName)] ?? FileText\n}\n\n// ---------------------------------------------------------------------------\n// Sized-icon rendering helper (migrated from `src/utils/dynamic-icons.tsx`)\n// ---------------------------------------------------------------------------\n\nexport type DynamicIconSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n\nconst SIZE_CLASSES: Record<DynamicIconSize, string> = {\n xs: 'w-3 h-3', // 12px\n sm: 'w-4 h-4', // 16px\n md: 'w-6 h-6', // 24px\n lg: 'w-8 h-8', // 32px\n xl: 'w-12 h-12', // 48px\n}\n\n/**\n * Optional per-icon color overrides. Keys are kebab-case (canonical\n * registry form) — pass any incoming key through `normalizeIconKey()`\n * before lookup.\n */\nconst ICON_COLORS: Record<string, { color?: string; fill?: string }> = {\n linkedin: { color: '#0A66C2' },\n facebook: { color: '#1877F2' },\n youtube: { color: '#FF0000' },\n}\n\n/**\n * Render an icon by name with a size preset + optional className.\n * Replaces the old `src/utils/dynamic-icons.tsx#getDynamicIcon`. Accepts\n * both PascalCase and kebab-case keys (via `normalizeIconKey()`).\n */\nexport function getDynamicIcon(\n iconName: string | undefined | null,\n size: DynamicIconSize = 'md',\n className?: string,\n): ReactNode {\n if (!iconName) {\n console.warn('[getDynamicIcon] No iconName provided, using Globe fallback')\n return createElement(Globe, { className: SIZE_CLASSES[size] })\n }\n\n const sizeClass = SIZE_CLASSES[size]\n const finalClassName = className ? `${sizeClass} ${className}` : sizeClass\n\n const canonicalKey = normalizeIconKey(iconName)\n const IconComponent = ICON_REGISTRY[canonicalKey]\n\n if (IconComponent) {\n const colorConfig = ICON_COLORS[canonicalKey] || {}\n return createElement(IconComponent, { className: finalClassName, ...colorConfig })\n }\n\n console.error(\n `[getDynamicIcon] Icon NOT found in registry: \"${iconName}\" (normalized to \"${canonicalKey}\"). Available icons:`,\n Object.keys(ICON_REGISTRY),\n )\n return createElement(Globe, { className: finalClassName })\n}\n","/**\n * Single source of truth for \"what does clicking THIS row do?\" across\n * the chat-adjacent surfaces (source chips, inline cards, search results).\n *\n * Lib-side refactor of the hub's `lib/utils/source-row-cta.ts`. Drops:\n * - `currentPlatform()` — host injects via `ctx.currentPlatform`\n * - `traceCompose()` logging — lib stays log-free; hosts can wrap\n * - `tableIdForDocumentType` — host injects via `ctx.tableIdForDocumentType`\n *\n * The contract:\n * - `icon` + `iconLabel` — for visual + a11y. Sourced from\n * `SOURCE_ICON_NAMES` keyed by the row's `sourceRepo` (config id),\n * resolved to a React component via `ICON_REGISTRY`.\n * - `href` — destination URL. `null` means \"no public viewer; Ask is\n * the only action\". Honors the inline card / chip / search bar's\n * existing fallback chain (externalUrl → in-app path → null) and\n * also runs `safeHref()` on the final value so a hostile RAG row\n * can't land `javascript:` / `data:` in the DOM.\n * - `askable` — whether the Ask drill-in flow can fire. Requires a\n * non-empty `id` AND a `documentType`.\n * - `chatRef` — pre-synthesized `ChatRef` for the Ask handler.\n */\n\nimport React from 'react'\nimport { FileText } from 'lucide-react'\nimport { getSourceIconName } from './source-icons'\nimport { getIconComponent } from './icon-registry'\nimport { getBaseUrl } from '../../../utils/cn'\nimport { safeHref } from './compact-card-classes'\nimport type { ChatRef } from '../chat-ref.types'\nimport type { ComposeContentUrl } from '../../../utils/content-href'\n\n/** Path sanitization — keep alphanumerics, slash, dash, dot, underscore;\n * strip everything else. Defends against a hostile mapper returning a\n * path with `..` traversal or query-string injection. */\nfunction sanitizePath(path: string): string {\n return (path || '').replace(/[^a-zA-Z0-9/_\\-.]/g, '')\n}\n\nexport interface SourceRowInput {\n /** RagTableConfig.id (e.g. 'blog-posts', 'financial-cap-table'). Drives\n * icon resolution via `SOURCE_ICON_NAMES` + `ICON_REGISTRY`. */\n sourceRepo?: string | null\n /** The config's `documentType` (e.g. 'blog_post', 'cap_table'). Drives\n * the Ask drill-in's `ChatRef.type`. */\n documentType?: string | null\n /** Primary-key value of the row (config.primaryKey). Required for Ask. */\n id?: string | null\n /** Display title. */\n title: string\n /** Resolved public URL when the row HAS a viewer. `null`/undefined when\n * the row has no public destination — surface decides Ask-only behavior. */\n externalUrl?: string | null\n /** Platform that owns `externalUrl`. */\n targetPlatform?: string | null\n /** In-app navigator path (e.g. `legal/fundraising/.../safe`). */\n path?: string | null\n}\n\nexport interface SourceRowContext {\n /** Chat shell's base route — `/data-room`, `/knowledge-base`, etc. */\n baseRoute?: string\n /** Cross-platform target for the chip / card click. */\n chipBasePlatform?: string\n /**\n * Host-supplied current-platform identifier. Used as the\n * `targetPlatform` for path-based fallback URLs that point to the\n * host's own surface. When omitted, path-based fallback rows get a\n * `null` targetPlatform — consumers fall back to their legacy\n * origin-check.\n */\n currentPlatform?: string | null\n /**\n * Host-supplied reverse map: documentType → tableId. Used to derive\n * `sourceRepo` from `documentType` when the caller didn't supply it\n * (inline cards know `ref.type` but not `sourceRepo`). When omitted,\n * the resolver falls back to the documentType reverse-map miss and\n * keeps the FileText icon.\n */\n tableIdForDocumentType?: (documentType: string) => string | null\n /**\n * Host-supplied unified content-href resolver (`runtime.composeContentUrl`).\n * When provided, entity rows WITH a public `externalUrl` resolve their href\n * through it — so the host's `hostedTypes` / `overrides` route a chat card to\n * the SAME destination as the equivalent page-view card (in-app for hosted\n * types, the hub URL otherwise). When omitted, the row's `externalUrl` is used\n * verbatim (legacy).\n */\n composeContentUrl?: ComposeContentUrl\n /**\n * Host-supplied per-`documentType` doc-viewer targets — the UNIFIED, DYNAMIC\n * replacement for the single `chipBasePlatform`. Maps a doc-table documentType\n * (`'markdown'`, `'data_room_doc'`, …) → the platform whose PUBLIC doc viewer\n * hosts it + that viewer's base path. A doc chip with no `externalUrl` resolves\n * to `getBaseUrl(platform)/<basePath>/<path>` PER ROW — so a chat mixing several\n * doc sources sends EACH to its own home (markdown→flamingo/knowledge-base,\n * data_room_doc→company-hub/data-room) instead of one static fallback for all.\n * Wins over `chipBasePlatform` when a row's documentType has an entry.\n */\n docPlatformTargets?: Record<string, { platform: string; basePath: string }>\n}\n\nexport interface SourceRowCTA {\n /** Source icon component. Always defined — falls back to `FileText`. */\n icon: React.ComponentType<{ className?: string }>\n /** Human-readable label for the icon. */\n iconLabel: string\n /** Resolved destination URL. `null` when the row has no openable\n * target. Already passed through `safeHref()`. */\n href: string | null\n /** Platform that owns the destination. `null` when the row has no\n * openable target OR the destination is external/unknown. */\n targetPlatform: string | null\n /** When true, the surface can fire the Ask drill-in flow for this row. */\n askable: boolean\n /** Pre-synthesized `ChatRef` for the Ask handler. `null` when not askable. */\n chatRef: ChatRef | null\n}\n\n/**\n * Build a `SourceRowContext` from a `ChatRuntime` + the surface's `baseRoute` /\n * `chipBasePlatform`. The runtime-derived fields (`currentPlatform`, `composeContentUrl`,\n * `docPlatformTargets`) are wired identically at every `resolveSourceRowCTA` call site\n * (source chips + inline cards), so they live here once — adding a ctx field is then a\n * one-line change instead of editing all three call sites.\n */\nexport function sourceRowCtxFromRuntime(\n runtime: {\n source?: string\n composeContentUrl?: ComposeContentUrl\n docPlatformTargets?: SourceRowContext['docPlatformTargets']\n },\n surface: { baseRoute?: string; chipBasePlatform?: string } = {},\n): SourceRowContext {\n return {\n currentPlatform: runtime.source ?? null,\n composeContentUrl: runtime.composeContentUrl,\n docPlatformTargets: runtime.docPlatformTargets,\n baseRoute: surface.baseRoute,\n chipBasePlatform: surface.chipBasePlatform,\n }\n}\n\n/**\n * Pure icon-resolution helper — `sourceRepo` (RagTableConfig.id) →\n * icon_name via `SOURCE_ICON_NAMES`, then icon_name → React component\n * via `ICON_REGISTRY`.\n */\nfunction pickSourceIcon(sourceRepo: string | null, documentType: string | null | undefined) {\n const iconName = sourceRepo ? getSourceIconName(sourceRepo) : undefined\n const icon = iconName ? getIconComponent(iconName) : FileText\n const iconLabel = documentType ?? 'Source'\n return { icon, iconLabel }\n}\n\n/**\n * Doc-table documentTypes — rows that carry an in-app `path` (not an entity\n * `externalUrl`) and resolve to a doc viewer. The SAME set an embedder keys its\n * `docPlatformTargets` map by (markdown = product docs, data_room_doc = data room),\n * declared once so the two can't silently diverge.\n */\nexport const DOC_TABLE_TYPES = ['markdown', 'data_room_doc'] as const\n\n/**\n * Only doc-table rows (DOC_TABLE_TYPES) fall back to doc-viewer navigation when no\n * `externalUrl` is set; the in-app/per-platform viewer can render them. Entity-table\n * rows MUST come with an explicit `externalUrl` from the mapper.\n */\nfunction shouldFallbackToPathNav(row: SourceRowInput): boolean {\n return !!row.documentType && (DOC_TABLE_TYPES as readonly string[]).includes(row.documentType)\n}\n\n/**\n * Resolve the CTA contract for a row. Pure function — no DOM, no fetch,\n * no logging. Call from any surface (chip, card, search result) with the\n * same row shape and get the same answer back.\n */\nexport function resolveSourceRowCTA(\n row: SourceRowInput,\n ctx: SourceRowContext = {},\n): SourceRowCTA {\n // Derive sourceRepo from documentType when the caller didn't supply\n // it. Inline cards know `ref.type` (documentType) but not `sourceRepo`;\n // chips and search results have sourceRepo directly. The host supplies\n // the reverse map via `ctx.tableIdForDocumentType`.\n const sourceRepo = row.sourceRepo\n ?? (row.documentType && ctx.tableIdForDocumentType ? ctx.tableIdForDocumentType(row.documentType) : null)\n ?? null\n\n const { icon, iconLabel } = pickSourceIcon(sourceRepo, row.documentType)\n\n // URL resolution. `idValue` (row primary key) is shared with the Ask\n // drill-in check below.\n let href: string | null = null\n let targetPlatform: string | null = null\n const idValue = (row.id ?? '').trim()\n if (row.path && shouldFallbackToPathNav(row) && !row.externalUrl) {\n // Doc-table (markdown / data-room PDF) with no public URL. Resolve where its\n // viewer lives, in priority order (NOT a content type the composeContentUrl\n // seam knows — docs carry a tree `path`, not a slug/id):\n // 1. docPlatformTargets[documentType] — per-doc-type cross-platform target.\n // The UNIFIED, DYNAMIC path: a chat mixing multiple doc sources routes\n // EACH row to its own home (markdown→flamingo, data_room_doc→company-hub).\n // 2. chipBasePlatform — legacy SINGLE cross-platform fallback (one platform\n // for every doc; only safe when a surface sees just one doc source).\n // 3. baseRoute — the host serves the doc viewer in-app → relative path nav.\n // else → null (no viewer configured → Ask-only).\n const safePath = sanitizePath(row.path)\n if (safePath) {\n const docTarget = row.documentType ? ctx.docPlatformTargets?.[row.documentType] : undefined\n if (docTarget) {\n // Trim leading/trailing (and collapse empty) segments WITHOUT a regex — the\n // slash-stripping regex `/^\\/+|\\/+$/g` tripped CodeQL's js/polynomial-redos (high)\n // since `\\/+$` backtracks on inputs with many '/'. split/filter/join is linear.\n const seg = docTarget.basePath.split('/').filter(Boolean).join('/')\n const base = `${getBaseUrl(docTarget.platform)}${seg ? `/${seg}` : ''}/`\n href = safeHref(new URL(safePath, base).toString()) ?? null\n targetPlatform = docTarget.platform\n } else if (ctx.chipBasePlatform) {\n const base = `${getBaseUrl(ctx.chipBasePlatform)}/knowledge-base/`\n href = safeHref(new URL(safePath, base).toString()) ?? null\n targetPlatform = ctx.chipBasePlatform\n } else if (ctx.baseRoute) {\n const synthetic = `https://_internal_.local${ctx.baseRoute.startsWith('/') ? ctx.baseRoute : '/' + ctx.baseRoute}/`\n const absolute = new URL(safePath, synthetic).toString()\n href = safeHref(absolute.replace('https://_internal_.local', '')) ?? null\n targetPlatform = ctx.currentPlatform ?? null\n }\n }\n } else if (ctx.composeContentUrl && row.documentType && row.externalUrl) {\n // Entity content WITH a public viewer → the unified content-href seam. The\n // host's hostedTypes/overrides route hosted types in-app (slug relativized\n // from the externalUrl) and everything else to the externalUrl verbatim —\n // the SAME resolver the page-view cards use, so chat + page links match.\n const composed = ctx.composeContentUrl({\n type: row.documentType,\n identifier: idValue,\n externalUrl: row.externalUrl,\n targetPlatform: row.targetPlatform ?? null,\n })\n href = composed.href ? (safeHref(composed.href) ?? null) : null\n targetPlatform = composed.targetPlatform\n } else if (row.externalUrl) {\n // No composer wired → legacy: the RAG externalUrl verbatim.\n href = safeHref(row.externalUrl) ?? null\n targetPlatform = row.targetPlatform ?? null\n }\n\n // Ask drill-in viability. (`idValue` computed above.)\n const askable = !!(idValue && row.documentType)\n const chatRef: ChatRef | null = askable\n ? ({\n type: row.documentType!,\n ...(sourceRepo ? { sourceRepo } : {}),\n id: idValue,\n title: row.title,\n url: href,\n targetPlatform,\n // Carry `path` so a downstream inline card (rendered from this\n // chatRef on a subsequent turn) gets the same doc-tree-swap\n // routing the chip already used. Without it, the Ask drill-in\n // would resolve to a same-tab full-page nav even when an in-app\n // path is available.\n ...(row.path ? { metadata: { path: row.path } } : {}),\n } as ChatRef)\n : null\n\n return { icon, iconLabel, href, targetPlatform, askable, chatRef }\n}\n\n/**\n * Convenience helper for callers that just need the source icon —\n * inline cards' tracking strip + the search-bar dropdown row, today.\n *\n * Single source of truth: every consumer (chips, search results,\n * inline cards) routes through `SOURCE_ICON_NAMES` + `ICON_REGISTRY`.\n *\n * Accepts a string for backward-compatibility with the single-arg call\n * shape — treated as `documentType`.\n */\nexport function resolveSourceIcon(\n input: { sourceRepo?: string | null; documentType?: string | null } | string | null | undefined,\n ctx: { tableIdForDocumentType?: (documentType: string) => string | null } = {},\n): {\n Icon: React.ComponentType<{ className?: string }>\n label: string\n} {\n if (!input) return { Icon: FileText, label: 'Source' }\n const { sourceRepo, documentType } = typeof input === 'string'\n ? { sourceRepo: null, documentType: input }\n : input\n if (!sourceRepo && !documentType) return { Icon: FileText, label: 'Source' }\n const resolvedRepo = sourceRepo\n ?? (documentType && ctx.tableIdForDocumentType ? ctx.tableIdForDocumentType(documentType) : null)\n ?? null\n const { icon, iconLabel } = pickSourceIcon(resolvedRepo, documentType)\n return { Icon: icon, label: iconLabel }\n}\n","/**\n * The SINGLE content-href authority for every embeddable surface — the\n * `ChatRuntime.composeContentUrl` seam. Page views (onboarding catalog/detail,\n * product releases) AND chat surfaces (entity cards, source chips, search\n * results) all resolve a content link through this one function, so a given\n * content type lands in the SAME place no matter where it's rendered.\n *\n * ## Why this exists\n *\n * Before unification there were two link builders: `composeContentUrl` (pages)\n * and `resolveSourceRowCTA` (chat), and only the former honored the embedder's\n * in-app routing config — so the same `product_release` soft-navigated in-app on\n * the releases page but opened OUT to the hub as a chat card. Now `resolveSource-\n * RowCTA` delegates its href to this seam, so one config (`hostedTypes` /\n * `overrides`) governs internal-vs-external for pages and chat alike.\n *\n * `makeComposeContentUrl` builds the embedder default from a small config: the\n * set of types THIS host serves in-app (→ relative href, soft-navigates) vs.\n * everything else (→ the canonical hub URL, opens out). The hub wires its own\n * composer (`composeContentUrlFromPlatforms` — cross-platform topology) to the\n * same seam. Pure + server-safe (no React, no browser APIs).\n *\n * composeContentUrl: makeComposeContentUrl({\n * hostedTypes: new Set(['onboarding_guide', 'product_release']),\n * contentOrigin: VITE_HUB_ORIGIN,\n * })\n */\n\n/**\n * Type → in-app route suffix. The public-hostable subset of the hub's\n * `PUBLIC_URL_PATHS` (`lib/utils/content-url-builder.ts`); the hub keeps\n * its own copy, this is the embedder default and must stay in sync. (The\n * cross-repo boundary makes a shared import impossible; the lib test pins this\n * constant against a literal copy of itself, so it does NOT detect hub-side\n * drift — values match today and are kept aligned by hand.)\n */\nexport const DEFAULT_CONTENT_SUFFIXES: Record<string, string> = {\n onboarding_guide: 'onboarding-guides',\n product_release: 'releases',\n blog_post: 'blog',\n case_study: 'case-studies',\n customer_interview: 'interviews',\n investor_update: 'investor-updates',\n webinar: 'webinars',\n podcast: 'podcasts',\n event: 'events',\n // Author profile pages (`/authors/<slug>`) — identified by the profile\n // slug. Mirrors the hub's `PUBLIC_URL_PATHS.author`, which derives from\n // `AUTHORS_PATH` in the hub's `lib/utils/breadcrumbs.ts` (the hub-side\n // SSOT for the segment).\n author: 'authors',\n}\n\n/** Input to the unified content-href seam. ONE object covers both callers:\n * page views pass `type` + `identifier` (the slug); chat rows pass `type` +\n * `identifier` (the primary-key id) + `externalUrl` (the canonical hub URL,\n * from which the slug is recovered for in-app routing). */\nexport interface ComposeContentUrlInput {\n /** The content's documentType (e.g. `'product_release'`, `'blog_post'`). */\n type: string\n /** Content identifier. Page views pass the slug; chat rows pass the\n * primary-key id (the slug is recovered from `externalUrl` when hosted). */\n identifier: string\n /** Hydrated platform junction (page views). Unused by the embedder default\n * — `hostedTypes` membership decides in-vs-out — but threaded for the hub\n * composer's cross-platform topology. */\n platforms?: Array<{ name?: string }>\n /** The canonical hub URL when the caller already has it (chat entity rows\n * carry it from the RAG mapper). Hosted types relativize it to an in-app\n * path; non-hosted types use it verbatim (authoritative). Absent for pages. */\n externalUrl?: string | null\n /** Platform that owns `externalUrl` (chat rows). Passed through on the\n * non-hosted branch. */\n targetPlatform?: string | null\n}\n\nexport interface ContentHrefOptions {\n /** Types THIS host serves in-app → relative href (soft-nav). Everything\n * else resolves to the row's `externalUrl` / `contentOrigin` (opens out). */\n hostedTypes: ReadonlySet<string>\n /** Hub origin for non-hosted types with no `externalUrl` (e.g.\n * `https://openframe.app`). */\n contentOrigin: string\n /** Per-type route suffix. Defaults to {@link DEFAULT_CONTENT_SUFFIXES}. */\n suffixes?: Record<string, string>\n /** Per-type full override — wins over the suffix logic. Receives the same\n * `identifier` the seam was called with. */\n overrides?: Record<string, (identifier: string) => { href: string; targetPlatform: string | null }>\n}\n\n/** The unified `composeContentUrl` seam shape on `ChatRuntime`. ALWAYS returns\n * a tuple (never null) — the seam type is non-nullable and callers read\n * `.href` unconditionally. */\nexport type ComposeContentUrl = (\n input: ComposeContentUrlInput,\n) => { href: string; targetPlatform: string | null }\n\n/** Last non-empty path segment of a URL (relative or absolute) — the content\n * slug of a canonical detail URL like `https://hub/releases/my-release`.\n * Returns `null` on a malformed URL or empty path. */\nfunction lastPathSegment(url: string): string | null {\n try {\n // Resolve against a dummy base so relative inputs parse too.\n const pathname = new URL(url, 'https://_.local').pathname\n const segs = pathname.split('/').filter(Boolean)\n return segs.length > 0 ? segs[segs.length - 1] : null\n } catch {\n return null\n }\n}\n\n/**\n * Build the embedder's `composeContentUrl` for the unified seam.\n *\n * Resolution order (the merged rule used by BOTH page views and chat cards):\n * 1. `overrides[type]` — explicit per-type href.\n * 2. `hostedTypes.has(type)` → relative `/<suffix>/<slug>` (in-app, soft-nav).\n * Chat rows carry the hub URL not the slug, so the slug is recovered from\n * `externalUrl`; page views pass the slug as `identifier`.\n * 3. `externalUrl` present → use it verbatim (RAG-authoritative hub URL; the\n * chat non-hosted case).\n * 4. else → `${contentOrigin}/<suffix>/<identifier>` (page-view non-hosted).\n *\n * In-vs-out is decided by `hostedTypes` membership — NOT platform equality,\n * since an embedder has a free-form `source`. The `platforms` arg is part of the\n * seam signature but unused here.\n */\nexport function makeComposeContentUrl(opts: ContentHrefOptions): ComposeContentUrl {\n const suffixes = opts.suffixes ?? DEFAULT_CONTENT_SUFFIXES\n return ({ type, identifier, externalUrl, targetPlatform }) => {\n const override =\n opts.overrides && Object.prototype.hasOwnProperty.call(opts.overrides, type)\n ? opts.overrides[type]\n : undefined\n if (override) return override(identifier)\n\n const seg =\n (Object.prototype.hasOwnProperty.call(suffixes, type) ? suffixes[type] : undefined) ?? type\n\n if (opts.hostedTypes.has(type)) {\n // In-app (soft-nav). Recover the slug from the hub URL for chat rows;\n // page views already pass the slug as `identifier`. Guard: if the recovered\n // segment is the type's OWN suffix (e.g. a malformed/list externalUrl like\n // `https://hub/releases/` → `'releases'`), fall back to `identifier` instead\n // of emitting a nonsensical `/releases/releases`.\n const recovered = externalUrl ? lastPathSegment(externalUrl) : null\n const slug = recovered && recovered !== seg ? recovered : identifier\n return { href: `/${seg}/${slug}`, targetPlatform: null }\n }\n // Not hosted → opens out. Prefer the RAG-authoritative `externalUrl` (chat);\n // else compose against the hub origin (page views with no externalUrl).\n return externalUrl\n ? { href: externalUrl, targetPlatform: targetPlatform ?? null }\n : { href: `${opts.contentOrigin}/${seg}/${identifier}`, targetPlatform: null }\n }\n}\n\n/**\n * Default href shape when `runtime.composeContentUrl` is NOT wired\n * (single-platform embedders without cross-platform topology). Shared by\n * every catalog/detail view that composes a content link (onboarding guides,\n * product releases, …) so the no-composer fallback has ONE source — both\n * views pass their `basePath`-derived shape through here.\n */\nexport function buildDefaultHref(\n basePath: string,\n slug: string,\n): { href: string; targetPlatform: string | null } {\n return { href: `${basePath}/${slug}`, targetPlatform: null }\n}\n\n/**\n * Resolve a content link via the host's `composeContentUrl` when wired, else the\n * same-origin `buildDefaultHref` fallback — the exact ternary every catalog/detail page\n * VIEW repeated. Centralizes the `composeContentUrl` input shape + the fallback in one\n * place so a future input-shape change lands once, not per view.\n */\nexport function resolveContentHref(\n composeContentUrl: ComposeContentUrl | undefined,\n args: { type: string; slug: string; basePath: string; platforms?: ComposeContentUrlInput['platforms'] },\n): { href: string; targetPlatform: string | null } {\n return composeContentUrl\n ? composeContentUrl({ type: args.type, identifier: args.slug, platforms: args.platforms })\n : buildDefaultHref(args.basePath, args.slug)\n}\n","\"use client\"\n\nimport * as PopoverPrimitive from \"@radix-ui/react-popover\"\nimport * as ScrollAreaPrimitive from \"@radix-ui/react-scroll-area\"\nimport * as React from \"react\"\nimport { cn } from \"../../utils/cn\"\nimport { useDebounce } from \"../../hooks/ui/use-debounce\"\nimport { useAutoLimitTags } from \"../../hooks/ui/use-auto-limit-tags\"\nimport { SearchIcon } from \"../icons-v2-generated\"\nimport { XmarkCircleIcon } from \"../icons-v2-generated/signs-and-symbols/xmark-circle-icon\"\nimport { Tag } from \"./tag\"\nimport { HiddenTagsPopup } from \"./hidden-tags-popup\"\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface SearchResult {\n id: string\n title: string\n description?: string\n path?: string\n type?: string\n icon?: React.ReactNode\n metadata?: Record<string, unknown>\n}\n\nexport interface FilterChipData {\n id: string\n label: string\n variant?: \"selected\" | \"category\" | \"subcategory\" | \"tag\"\n}\n\nexport interface SearchInputProps {\n /** Placeholder text shown in the input */\n placeholder?: string\n /** Controlled value */\n value?: string\n /** Default value for uncontrolled mode */\n defaultValue?: string\n /** Called when input value changes (raw, not debounced) */\n onChange?: (value: string) => void\n /** Called when user presses Enter */\n onSubmit?: (value: string) => void\n /** Search results to display in the dropdown */\n results?: SearchResult[]\n /** Whether results are loading */\n isLoading?: boolean\n /** Called when a result row is selected.\n *\n * `modifiers` carries the click event's modifier-key state when the\n * user picked the row via mouse — pass through so the consumer can\n * honor cmd/ctrl/shift/middle-click for background-tab navigation\n * (the row is a `<div role=\"option\">` rather than an `<a>`, so the\n * browser doesn't background-tab natively even with `target=\"_blank\"`).\n * Empty `{}` when the row was selected via keyboard Enter. */\n onResultSelect?: (\n result: SearchResult,\n modifiers?: { metaKey?: boolean; ctrlKey?: boolean; shiftKey?: boolean; altKey?: boolean; button?: number },\n ) => void\n /** Debounce delay in ms. 0 disables debounce. Default 300 */\n debounceMs?: number\n /** Custom renderer for a single result row */\n renderResult?: (result: SearchResult, isHighlighted: boolean) => React.ReactNode\n /** Group results by a key derived from each result */\n groupBy?: (result: SearchResult) => string\n /** Text shown when query meets minQueryLength but no results */\n emptyResultsText?: string\n /** Force-control dropdown visibility. Default: auto */\n showDropdown?: boolean\n /** Filter chips rendered inline before the input */\n filterChips?: FilterChipData[]\n /** Called when a filter chip is removed */\n onFilterRemove?: (id: string) => void\n /** Element rendered before the input. Default: SearchIcon */\n startAdornment?: React.ReactNode\n /** Element rendered after the input */\n endAdornment?: React.ReactNode\n /** Extra class names for the outer container */\n className?: string\n /** Extra class names for the dropdown */\n dropdownClassName?: string\n /** Minimum characters before showing results. Default 2 */\n minQueryLength?: number\n /** Maximum visible filter chips. \"auto\" measures available width. Default \"auto\" */\n limitTags?: number | \"auto\"\n /** Custom render for the \"+N\" overflow text */\n getLimitTagsText?: (more: number) => React.ReactNode\n}\n\n// ---------------------------------------------------------------------------\n// Shared styles (consistent with Autocomplete / Input)\n// ---------------------------------------------------------------------------\n\nconst containerStyles = cn(\n // Layout & spacing — matches lib Input component\n \"flex items-center gap-2 rounded-[6px] border px-3 h-11 md:h-12 cursor-text\",\n \"has-[:focus-visible]:outline-none\",\n \"group\",\n \"transition-colors duration-200\",\n // Theme palette — matches lib Input component\n \"bg-ods-card border-ods-border has-[:focus]:border-ods-accent\"\n)\n\nconst innerInputStyles = cn(\n \"flex-1 min-w-[60px] bg-transparent border-none outline-none\",\n \"text-h4\",\n \"text-ods-text-primary placeholder:text-ods-text-secondary\",\n \"disabled:cursor-not-allowed\",\n \"touch-manipulation\"\n)\n\n// ---------------------------------------------------------------------------\n// Helper: chip variant → Tag variant mapping\n// ---------------------------------------------------------------------------\n\nfunction chipVariantToTagVariant(variant?: FilterChipData[\"variant\"]): \"primary\" | \"outline\" | \"badge\" {\n switch (variant) {\n case \"selected\":\n return \"primary\"\n // Content tags render with the unified badge skin (ods-card + ods-border,\n // mono uppercase) — identical to the public EntityTagBadges display.\n case \"tag\":\n return \"badge\"\n case \"category\":\n case \"subcategory\":\n default:\n return \"outline\"\n }\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nexport function SearchInput({\n placeholder = \"Search...\",\n value,\n defaultValue = \"\",\n onChange,\n onSubmit,\n results = [],\n isLoading = false,\n onResultSelect,\n debounceMs = 300,\n renderResult,\n groupBy,\n emptyResultsText = \"No results found\",\n showDropdown: showDropdownProp,\n filterChips = [],\n onFilterRemove,\n startAdornment,\n endAdornment,\n className,\n dropdownClassName,\n minQueryLength = 2,\n limitTags = \"auto\",\n getLimitTagsText = (more: number) => `+${more}`,\n}: SearchInputProps) {\n // ---- Controlled / uncontrolled ----\n const [internalValue, setInternalValue] = React.useState(defaultValue)\n const currentValue = onChange ? (value ?? \"\") : internalValue\n\n // ---- Debounce ----\n const debouncedValue = useDebounce(currentValue, debounceMs)\n\n // ---- Popover state ----\n const [isOpen, setIsOpen] = React.useState(false)\n const [highlightedIndex, setHighlightedIndex] = React.useState(-1)\n\n const containerRef = React.useRef<HTMLDivElement>(null)\n\n // ---- Auto-limit tags ----\n const currentPlaceholder = filterChips.length > 0 ? \"Add filter...\" : placeholder\n\n const {\n visibleCount: rawVisibleCount, middleRef, measureRef, textMeasureRef, badgeRef, inputRef,\n } = useAutoLimitTags({\n count: filterChips.length,\n limitTags,\n // When chips exist, pass empty placeholder so the hook only reserves input minWidth,\n // not the full placeholder text width — gives more room for chips on narrow screens\n placeholder: filterChips.length > 0 ? \"\" : placeholder,\n })\n\n // Always show at least 1 chip when chips exist (industry standard: Gmail, MUI, Ant Design)\n const visibleCount = filterChips.length > 0 ? Math.max(1, rawVisibleCount) : rawVisibleCount\n\n // ---- Hidden tags popup ----\n const hiddenTagsRef = React.useRef<HTMLDivElement>(null)\n const hiddenTagsPopupRef = React.useRef<HTMLDivElement>(null)\n const [showHiddenTags, setShowHiddenTags] = React.useState(false)\n\n React.useEffect(() => {\n if (!showHiddenTags) return\n const handleClick = (e: MouseEvent) => {\n const target = e.target as Node\n if (!hiddenTagsRef.current?.contains(target) && !hiddenTagsPopupRef.current?.contains(target)) {\n setShowHiddenTags(false)\n }\n }\n document.addEventListener(\"mousedown\", handleClick)\n return () => document.removeEventListener(\"mousedown\", handleClick)\n }, [showHiddenTags])\n\n // ---- Derived chip slicing ----\n const hiddenCount = filterChips.length - visibleCount\n const visibleChips = filterChips.slice(0, visibleCount)\n const hiddenChips = filterChips.slice(visibleCount)\n\n // ---- Derive flat list (possibly grouped) ----\n const { flatResults, groups } = React.useMemo(() => {\n if (!groupBy) return { flatResults: results, groups: null }\n\n const grouped = new Map<string, SearchResult[]>()\n for (const r of results) {\n const key = groupBy(r)\n const arr = grouped.get(key)\n if (arr) {\n arr.push(r)\n } else {\n grouped.set(key, [r])\n }\n }\n return { flatResults: results, groups: grouped }\n }, [results, groupBy])\n\n // ---- Auto-show logic ----\n const meetsMinQuery = debouncedValue.length >= minQueryLength\n const autoShow = meetsMinQuery\n const dropdownVisible = showDropdownProp ?? (isOpen && autoShow)\n\n // ---- Reset highlight when results change ----\n React.useEffect(() => {\n setHighlightedIndex(-1)\n }, [flatResults.length])\n\n // ---- Handlers ----\n const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const newVal = e.target.value\n if (onChange) {\n onChange(newVal)\n } else {\n setInternalValue(newVal)\n }\n if (!isOpen) setIsOpen(true)\n setHighlightedIndex(-1)\n }\n\n const handleClear = (e: React.MouseEvent) => {\n e.stopPropagation()\n e.preventDefault()\n if (onChange) {\n onChange(\"\")\n } else {\n setInternalValue(\"\")\n }\n inputRef.current?.focus()\n }\n\n const handleResultClick = (\n result: SearchResult,\n e?: React.MouseEvent,\n ) => {\n onResultSelect?.(\n result,\n e\n ? {\n metaKey: e.metaKey,\n ctrlKey: e.ctrlKey,\n shiftKey: e.shiftKey,\n altKey: e.altKey,\n button: e.button,\n }\n : undefined,\n )\n setIsOpen(false)\n }\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n switch (e.key) {\n case \"ArrowDown\":\n e.preventDefault()\n if (!isOpen) setIsOpen(true)\n setHighlightedIndex((prev) =>\n prev < flatResults.length - 1 ? prev + 1 : 0\n )\n break\n case \"ArrowUp\":\n e.preventDefault()\n setHighlightedIndex((prev) =>\n prev > 0 ? prev - 1 : flatResults.length - 1\n )\n break\n case \"Enter\":\n e.preventDefault()\n if (highlightedIndex >= 0 && flatResults[highlightedIndex]) {\n handleResultClick(flatResults[highlightedIndex])\n } else {\n onSubmit?.(currentValue)\n }\n break\n case \"Escape\":\n setIsOpen(false)\n setHighlightedIndex(-1)\n break\n case \"Backspace\":\n if (!currentValue && filterChips.length > 0 && onFilterRemove) {\n onFilterRemove(filterChips[filterChips.length - 1].id)\n }\n break\n }\n }\n\n const handleOpenChange = (open: boolean) => {\n setIsOpen(open)\n }\n\n // ---- Default result renderer ----\n const defaultRenderResult = (result: SearchResult, isHighlighted: boolean) => (\n <div className=\"flex items-center gap-3 w-full min-w-0\">\n {result.icon && (\n <span className=\"flex-shrink-0 text-ods-text-secondary [&_svg]:size-4\">\n {result.icon}\n </span>\n )}\n <div className=\"min-w-0 flex-1\">\n <div className={cn(\n \"text-sm font-medium leading-5 truncate\",\n isHighlighted ? \"text-ods-accent\" : \"text-ods-text-primary\"\n )} title={result.title}>\n {result.title}\n </div>\n {result.description && (\n <div className=\"text-xs leading-4 text-ods-text-secondary truncate mt-0.5\" title={result.description}>\n {result.description}\n </div>\n )}\n </div>\n {result.type && (\n <span className=\"flex-shrink-0 text-[11px] font-medium text-ods-text-muted uppercase tracking-wider\">\n {result.type}\n </span>\n )}\n </div>\n )\n\n // ---- Render a result row ----\n const renderRow = (result: SearchResult, index: number) => {\n const isHighlighted = index === highlightedIndex\n return (\n <div\n key={result.id}\n role=\"option\"\n aria-selected={isHighlighted}\n className={cn(\n \"flex items-center min-h-10 px-3 cursor-pointer transition-colors border-b border-ods-border last:border-b-0\",\n isHighlighted && \"bg-ods-bg-hover\",\n !isHighlighted && \"hover:bg-ods-bg-hover\"\n )}\n onClick={(e) => handleResultClick(result, e)}\n onMouseEnter={() => setHighlightedIndex(index)}\n >\n {renderResult ? renderResult(result, isHighlighted) : defaultRenderResult(result, isHighlighted)}\n </div>\n )\n }\n\n // ---- Dropdown content ----\n const renderDropdownContent = () => {\n if (isLoading) {\n return (\n <div className=\"px-4 py-3 text-ods-text-secondary text-[14px]\">\n Loading...\n </div>\n )\n }\n\n if (flatResults.length === 0) {\n return (\n <div className=\"px-4 py-3 text-ods-text-secondary text-[14px]\">\n {emptyResultsText}\n </div>\n )\n }\n\n if (groups) {\n let globalIndex = 0\n return Array.from(groups.entries()).map(([groupLabel, groupResults]) => (\n <div key={groupLabel}>\n <div className=\"px-4 py-2 text-[12px] font-semibold text-ods-text-secondary uppercase tracking-wide bg-ods-bg\">\n {groupLabel}\n </div>\n {groupResults.map((result) => {\n const idx = globalIndex++\n return renderRow(result, idx)\n })}\n </div>\n ))\n }\n\n return flatResults.map((result, index) => renderRow(result, index))\n }\n\n // ---- Determine if we have a value worth clearing ----\n const hasValue = currentValue.length > 0\n\n return (\n <div className={cn(\"relative\", className)} ref={containerRef}>\n <PopoverPrimitive.Root open={dropdownVisible} onOpenChange={handleOpenChange} modal={false}>\n <PopoverPrimitive.Anchor asChild>\n <div\n className={cn(\n containerStyles,\n \"hover:bg-ods-bg-hover hover:border-ods-border-hover active:bg-ods-bg-active active:border-ods-border-active\",\n dropdownVisible && \"!border-ods-accent\"\n )}\n onClick={() => {\n inputRef.current?.focus()\n setIsOpen(true)\n }}\n >\n {/* Start Adornment — pinned left, shrink-0 */}\n <span className=\"flex-shrink-0 text-ods-text-secondary transition-colors duration-200 group-has-[:focus]:text-ods-accent [&_svg]:size-4 md:[&_svg]:size-6\">\n {startAdornment !== undefined ? startAdornment : <SearchIcon />}\n </span>\n\n {/* Middle zone: chips + input — overflow hidden, single line */}\n <div ref={middleRef} className=\"flex-1 flex items-center gap-2 min-w-0 overflow-hidden\">\n {/* Visible filter chips */}\n {visibleChips.map((chip) => (\n <Tag\n key={chip.id}\n variant={chipVariantToTagVariant(chip.variant)}\n label={chip.label}\n labelClassName=\"truncate max-w-[120px]\"\n onClose={onFilterRemove ? () => onFilterRemove(chip.id) : undefined}\n />\n ))}\n\n {/* \"+N\" overflow badge */}\n {hiddenCount > 0 && (\n <div ref={hiddenTagsRef} className=\"shrink-0\">\n <button\n ref={badgeRef}\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n setShowHiddenTags((prev) => !prev)\n }}\n className={cn(\n \"flex items-center h-8 px-2\",\n \"bg-ods-card border border-ods-border rounded-[6px]\",\n \"font-mono text-[14px] font-medium leading-5 text-ods-text-secondary uppercase tracking-[-0.28px]\",\n \"hover:bg-ods-bg-hover transition-colors cursor-pointer\",\n )}\n aria-label={`${hiddenCount} more selected filters`}\n >\n {getLimitTagsText(hiddenCount)}\n </button>\n </div>\n )}\n\n {/* Input */}\n <input\n ref={inputRef}\n type=\"text\"\n value={currentValue}\n onChange={handleChange}\n onKeyDown={handleKeyDown}\n onFocus={() => {\n setIsOpen(true)\n setShowHiddenTags(false)\n }}\n placeholder={currentPlaceholder}\n className={innerInputStyles}\n />\n </div>\n\n {/* End adornment / Clear — pinned right, shrink-0 */}\n <div className=\"flex items-center gap-1 shrink-0 ml-auto\">\n {hasValue && (\n <button\n type=\"button\"\n onClick={handleClear}\n className=\"flex items-center justify-center hover:opacity-70 transition-opacity\"\n aria-label=\"Clear search\"\n >\n <XmarkCircleIcon className=\"text-ods-text-secondary\" size={24} />\n </button>\n )}\n {endAdornment}\n </div>\n </div>\n </PopoverPrimitive.Anchor>\n\n <PopoverPrimitive.Content\n className={cn(\n \"z-50 w-[var(--radix-popover-trigger-width)] mt-1\",\n \"bg-ods-card border border-ods-border rounded-[6px] overflow-hidden shadow-lg\",\n \"data-[state=open]:animate-in data-[state=closed]:animate-out\",\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n \"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95\",\n \"data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2\",\n dropdownClassName\n )}\n sideOffset={4}\n align=\"start\"\n onOpenAutoFocus={(e) => {\n e.preventDefault()\n inputRef.current?.focus()\n }}\n onInteractOutside={(e) => {\n if (containerRef.current?.contains(e.target as Node)) {\n e.preventDefault()\n }\n }}\n >\n <ScrollAreaPrimitive.Root className=\"overflow-hidden\">\n <ScrollAreaPrimitive.Viewport className=\"max-h-[320px] w-full\">\n <div role=\"listbox\">\n {renderDropdownContent()}\n </div>\n </ScrollAreaPrimitive.Viewport>\n <ScrollAreaPrimitive.Scrollbar className=\"hidden\" orientation=\"vertical\">\n <ScrollAreaPrimitive.Thumb />\n </ScrollAreaPrimitive.Scrollbar>\n </ScrollAreaPrimitive.Root>\n </PopoverPrimitive.Content>\n </PopoverPrimitive.Root>\n\n {/* Hidden tags popup — outside overflow-hidden, positioned under badge */}\n {showHiddenTags && hiddenCount > 0 && (\n <HiddenTagsPopup\n ref={hiddenTagsPopupRef}\n items={hiddenChips.map(chip => ({ label: chip.label, value: chip.id }))}\n style={{\n left: badgeRef.current\n ? badgeRef.current.getBoundingClientRect().left -\n (containerRef.current?.getBoundingClientRect().left ?? 0)\n : 0,\n }}\n onRemove={(value) => {\n onFilterRemove?.(value as string)\n if (hiddenCount <= 1) setShowHiddenTags(false)\n }}\n />\n )}\n\n {/* Off-screen measurement: placeholder text width — fixed positioning avoids scroll contribution */}\n <span\n ref={textMeasureRef}\n aria-hidden=\"true\"\n className=\"fixed -left-[9999px] top-0 pointer-events-none whitespace-nowrap text-ods-text-primary\"\n >\n {currentPlaceholder}\n </span>\n\n {/* Off-screen measurement: all chip widths */}\n <div\n ref={measureRef}\n aria-hidden=\"true\"\n className=\"fixed -left-[9999px] top-0 flex gap-2 pointer-events-none\"\n >\n {filterChips.map((chip) => (\n <Tag\n key={`m-${chip.id}`}\n variant={chipVariantToTagVariant(chip.variant)}\n label={chip.label}\n labelClassName=\"truncate max-w-[120px]\"\n onClose={() => {}}\n />\n ))}\n </div>\n </div>\n )\n}\n\nexport default SearchInput\n"],"mappings":"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","names":["HiddenTagsPopup","jsx","jsxs","jsx","jsxs","Tag","FileText","FileText","React","jsx","jsxs","value"]}
|