@flamingo-stack/openframe-frontend-core 0.0.691 → 0.0.692-2509.7676.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/dist/{chunk-LCENRN2N.cjs → chunk-5XPK2QGU.cjs} +3 -3
  2. package/dist/{chunk-LCENRN2N.cjs.map → chunk-5XPK2QGU.cjs.map} +1 -1
  3. package/dist/{chunk-SA4KQCNB.cjs → chunk-7BNWMOPM.cjs} +36 -36
  4. package/dist/{chunk-SA4KQCNB.cjs.map → chunk-7BNWMOPM.cjs.map} +1 -1
  5. package/dist/{chunk-6Z26ZMR4.js → chunk-A5NHAHV6.js} +2 -2
  6. package/dist/{chunk-Y3Q76RAA.cjs → chunk-ATREVNIN.cjs} +33 -33
  7. package/dist/{chunk-Y3Q76RAA.cjs.map → chunk-ATREVNIN.cjs.map} +1 -1
  8. package/dist/{chunk-FNMQ2J44.js → chunk-AVAZ6BD6.js} +2 -2
  9. package/dist/{chunk-DDWLF2JV.cjs → chunk-CG7OWW7X.cjs} +4 -4
  10. package/dist/{chunk-DDWLF2JV.cjs.map → chunk-CG7OWW7X.cjs.map} +1 -1
  11. package/dist/{chunk-KMW2PXBU.js → chunk-CKO6NKNE.js} +750 -212
  12. package/dist/chunk-CKO6NKNE.js.map +1 -0
  13. package/dist/{chunk-ZLYLHP64.cjs → chunk-CKUPANVU.cjs} +4 -4
  14. package/dist/{chunk-ZLYLHP64.cjs.map → chunk-CKUPANVU.cjs.map} +1 -1
  15. package/dist/{chunk-H54RDLVC.cjs → chunk-DKJDO7YM.cjs} +18 -18
  16. package/dist/{chunk-H54RDLVC.cjs.map → chunk-DKJDO7YM.cjs.map} +1 -1
  17. package/dist/{chunk-N4S7RBUM.cjs → chunk-DUURFYGS.cjs} +3 -3
  18. package/dist/{chunk-N4S7RBUM.cjs.map → chunk-DUURFYGS.cjs.map} +1 -1
  19. package/dist/{chunk-CWXRZVDI.cjs → chunk-EXWS2QSD.cjs} +588 -50
  20. package/dist/chunk-EXWS2QSD.cjs.map +1 -0
  21. package/dist/{chunk-WZYIAKIH.cjs → chunk-F6LXGP6S.cjs} +3 -3
  22. package/dist/{chunk-WZYIAKIH.cjs.map → chunk-F6LXGP6S.cjs.map} +1 -1
  23. package/dist/{chunk-4N2JOPAQ.cjs → chunk-FTK3S57R.cjs} +13 -13
  24. package/dist/{chunk-4N2JOPAQ.cjs.map → chunk-FTK3S57R.cjs.map} +1 -1
  25. package/dist/{chunk-MJI5GVT7.js → chunk-IQTQIBZ3.js} +4 -4
  26. package/dist/{chunk-BXE2RZEV.cjs → chunk-KDCIYRZV.cjs} +17 -17
  27. package/dist/{chunk-BXE2RZEV.cjs.map → chunk-KDCIYRZV.cjs.map} +1 -1
  28. package/dist/{chunk-ESFPY5FF.cjs → chunk-KQZMM3QJ.cjs} +51 -51
  29. package/dist/{chunk-ESFPY5FF.cjs.map → chunk-KQZMM3QJ.cjs.map} +1 -1
  30. package/dist/{chunk-Y7ZQZOE6.js → chunk-LYJJH4D6.js} +2 -2
  31. package/dist/{chunk-TSNGVWQO.js → chunk-LYQULSK4.js} +5 -5
  32. package/dist/{chunk-KVDLL4LU.js → chunk-MCNQKS6I.js} +2 -2
  33. package/dist/{chunk-DC3G4XGP.js → chunk-PKZGXRU2.js} +3 -3
  34. package/dist/{chunk-Y3BC7TOJ.cjs → chunk-Q25VYAEL.cjs} +17 -17
  35. package/dist/{chunk-Y3BC7TOJ.cjs.map → chunk-Q25VYAEL.cjs.map} +1 -1
  36. package/dist/{chunk-JSG7WZKV.js → chunk-RK47E3PP.js} +2 -2
  37. package/dist/{chunk-4HZXOQRM.js → chunk-RLNNV46K.js} +2 -2
  38. package/dist/{chunk-BXAGR3PO.cjs → chunk-RTKF5AYA.cjs} +3 -3
  39. package/dist/{chunk-BXAGR3PO.cjs.map → chunk-RTKF5AYA.cjs.map} +1 -1
  40. package/dist/{chunk-Z5FHXCXF.js → chunk-TEIHEFXP.js} +4 -4
  41. package/dist/{chunk-XBYF4SGD.js → chunk-TXFGG47A.js} +2 -2
  42. package/dist/{chunk-5N2ZSB42.cjs → chunk-WTYQCT4A.cjs} +10 -10
  43. package/dist/{chunk-5N2ZSB42.cjs.map → chunk-WTYQCT4A.cjs.map} +1 -1
  44. package/dist/{chunk-CMYDGVTZ.js → chunk-X5XFKI6N.js} +2 -2
  45. package/dist/{chunk-7WYWPELF.js → chunk-Z7QCNCBN.js} +5 -5
  46. package/dist/{chunk-DZMDG7PC.js → chunk-ZAV2F4VX.js} +2 -2
  47. package/dist/components/case-studies/index.cjs +11 -11
  48. package/dist/components/case-studies/index.js +2 -2
  49. package/dist/components/contact/index.cjs +3 -3
  50. package/dist/components/contact/index.js +2 -2
  51. package/dist/components/docs/index.cjs +6 -6
  52. package/dist/components/docs/index.js +5 -5
  53. package/dist/components/embeds/index.cjs +3 -3
  54. package/dist/components/embeds/index.js +2 -2
  55. package/dist/components/faq/index.cjs +3 -3
  56. package/dist/components/faq/index.js +2 -2
  57. package/dist/components/features/index.cjs +34 -2
  58. package/dist/components/features/index.cjs.map +1 -1
  59. package/dist/components/features/index.d.ts +1 -0
  60. package/dist/components/features/index.d.ts.map +1 -1
  61. package/dist/components/features/index.js +33 -1
  62. package/dist/components/features/remote-session/index.d.ts +13 -0
  63. package/dist/components/features/remote-session/index.d.ts.map +1 -0
  64. package/dist/components/features/remote-session/remote-access-request-card.d.ts +19 -0
  65. package/dist/components/features/remote-session/remote-access-request-card.d.ts.map +1 -0
  66. package/dist/components/features/remote-session/remote-session-block.d.ts +41 -0
  67. package/dist/components/features/remote-session/remote-session-block.d.ts.map +1 -0
  68. package/dist/components/features/remote-session/remote-session-chat-panel.d.ts +23 -0
  69. package/dist/components/features/remote-session/remote-session-chat-panel.d.ts.map +1 -0
  70. package/dist/components/features/remote-session/remote-session-dialog.d.ts +15 -0
  71. package/dist/components/features/remote-session/remote-session-dialog.d.ts.map +1 -0
  72. package/dist/components/features/remote-session/remote-session-overlay.d.ts +12 -0
  73. package/dist/components/features/remote-session/remote-session-overlay.d.ts.map +1 -0
  74. package/dist/components/features/remote-session/remote-session-parts.d.ts +34 -0
  75. package/dist/components/features/remote-session/remote-session-parts.d.ts.map +1 -0
  76. package/dist/components/features/remote-session/types.d.ts +33 -0
  77. package/dist/components/features/remote-session/types.d.ts.map +1 -0
  78. package/dist/components/help-center-pages/index.cjs +49 -49
  79. package/dist/components/help-center-pages/index.js +13 -13
  80. package/dist/components/index.cjs +64 -32
  81. package/dist/components/index.cjs.map +1 -1
  82. package/dist/components/index.js +44 -12
  83. package/dist/components/index.js.map +1 -1
  84. package/dist/components/meeting-scheduler/index.cjs +22 -22
  85. package/dist/components/meeting-scheduler/index.js +1 -1
  86. package/dist/components/navigation/index.cjs +5 -5
  87. package/dist/components/navigation/index.js +4 -4
  88. package/dist/components/onboarding-guides/index.cjs +5 -5
  89. package/dist/components/onboarding-guides/index.js +4 -4
  90. package/dist/components/tickets/index.cjs +6 -6
  91. package/dist/components/tickets/index.js +5 -5
  92. package/dist/components/ui/index.cjs +2 -2
  93. package/dist/components/ui/index.js +1 -1
  94. package/dist/index.cjs +38 -6
  95. package/dist/index.cjs.map +1 -1
  96. package/dist/index.js +37 -5
  97. package/dist/index.js.map +1 -1
  98. package/package.json +1 -1
  99. package/src/components/features/__tests__/remote-session.test.tsx +97 -0
  100. package/src/components/features/index.ts +1 -0
  101. package/src/components/features/remote-session/index.ts +36 -0
  102. package/src/components/features/remote-session/remote-access-request-card.tsx +102 -0
  103. package/src/components/features/remote-session/remote-session-block.tsx +182 -0
  104. package/src/components/features/remote-session/remote-session-chat-panel.tsx +178 -0
  105. package/src/components/features/remote-session/remote-session-dialog.tsx +77 -0
  106. package/src/components/features/remote-session/remote-session-overlay.tsx +33 -0
  107. package/src/components/features/remote-session/remote-session-parts.tsx +138 -0
  108. package/src/components/features/remote-session/types.ts +41 -0
  109. package/src/stories/RemoteSessionAccessRequest.stories.tsx +71 -0
  110. package/src/stories/RemoteSessionBlock.stories.tsx +164 -0
  111. package/src/stories/RemoteSessionDialog.stories.tsx +49 -0
  112. package/src/stories/RemoteSessionIndicator.stories.tsx +44 -0
  113. package/dist/chunk-CWXRZVDI.cjs.map +0 -1
  114. package/dist/chunk-KMW2PXBU.js.map +0 -1
  115. /package/dist/{chunk-6Z26ZMR4.js.map → chunk-A5NHAHV6.js.map} +0 -0
  116. /package/dist/{chunk-FNMQ2J44.js.map → chunk-AVAZ6BD6.js.map} +0 -0
  117. /package/dist/{chunk-MJI5GVT7.js.map → chunk-IQTQIBZ3.js.map} +0 -0
  118. /package/dist/{chunk-Y7ZQZOE6.js.map → chunk-LYJJH4D6.js.map} +0 -0
  119. /package/dist/{chunk-TSNGVWQO.js.map → chunk-LYQULSK4.js.map} +0 -0
  120. /package/dist/{chunk-KVDLL4LU.js.map → chunk-MCNQKS6I.js.map} +0 -0
  121. /package/dist/{chunk-DC3G4XGP.js.map → chunk-PKZGXRU2.js.map} +0 -0
  122. /package/dist/{chunk-JSG7WZKV.js.map → chunk-RK47E3PP.js.map} +0 -0
  123. /package/dist/{chunk-4HZXOQRM.js.map → chunk-RLNNV46K.js.map} +0 -0
  124. /package/dist/{chunk-Z5FHXCXF.js.map → chunk-TEIHEFXP.js.map} +0 -0
  125. /package/dist/{chunk-XBYF4SGD.js.map → chunk-TXFGG47A.js.map} +0 -0
  126. /package/dist/{chunk-CMYDGVTZ.js.map → chunk-X5XFKI6N.js.map} +0 -0
  127. /package/dist/{chunk-7WYWPELF.js.map → chunk-Z7QCNCBN.js.map} +0 -0
  128. /package/dist/{chunk-DZMDG7PC.js.map → chunk-ZAV2F4VX.js.map} +0 -0
@@ -1,7 +1,7 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }"use client";
2
2
 
3
3
 
4
- var _chunkCWXRZVDIcjs = require('./chunk-CWXRZVDI.cjs');
4
+ var _chunkEXWS2QSDcjs = require('./chunk-EXWS2QSD.cjs');
5
5
 
6
6
 
7
7
  var _chunkMIZU2OTOcjs = require('./chunk-MIZU2OTO.cjs');
@@ -69,7 +69,7 @@ function DocSearchBar({
69
69
  renderResult
70
70
  }) {
71
71
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
72
- _chunkCWXRZVDIcjs.SearchInput,
72
+ _chunkEXWS2QSDcjs.SearchInput,
73
73
  {
74
74
  placeholder,
75
75
  value: query,
@@ -370,4 +370,4 @@ function useDocSearch(config) {
370
370
 
371
371
 
372
372
  exports.formatRelativePath = formatRelativePath; exports.DocSearchResultRow = DocSearchResultRow; exports.DocSearchBar = DocSearchBar; exports.mapDocSearchResults = mapDocSearchResults; exports.resolveSearchResultAction = resolveSearchResultAction; exports.useDocSearch = useDocSearch;
373
- //# sourceMappingURL=chunk-LCENRN2N.cjs.map
373
+ //# sourceMappingURL=chunk-5XPK2QGU.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-LCENRN2N.cjs","../src/components/shared/doc-search/format-relative-path.ts","../src/components/shared/doc-search/doc-search-result-row.tsx","../src/components/shared/doc-search/doc-search-bar.tsx","../src/components/shared/doc-search/map-doc-search-results.ts","../src/components/shared/doc-search/resolve-search-result-action.ts","../src/components/shared/doc-search/use-doc-search.ts"],"names":["jsx"],"mappings":"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","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-LCENRN2N.cjs","sourcesContent":[null,"/**\n * Format a full document path as a breadcrumb trail.\n * Shows parent folders only (excludes the last segment / filename).\n *\n * @example\n * formatRelativePath('openframe-oss-tenant/architecture/api-controllers.md')\n * // → 'Openframe oss tenant / Architecture'\n */\nexport function formatRelativePath(fullPath: string): string {\n if (!fullPath) return '';\n const segments = fullPath.replace(/\\.md$/, '').split('/');\n // Show only parent path (exclude the filename itself since the title already shows it)\n const parentSegments = segments.length > 1 ? segments.slice(0, -1) : segments;\n return parentSegments.map(seg => seg.charAt(0).toUpperCase() + seg.slice(1).replace(/-/g, ' ')).join(' / ');\n}\n","'use client';\n\n/**\n * Single row in the `<SearchInput>` dropdown — the standard layout\n * used by every doc-search-backed surface (company-hub data-room\n * search bar, onboarding-guide catalog search, …). Single source of\n * truth for the row appearance so search dropdowns are visually\n * identical everywhere.\n *\n * Resolves the source icon via the same `resolveSourceIcon()`\n * registry the inline chat-card refs use, so a row pointing at e.g.\n * an onboarding-guide surfaces the SAME `<GraduationCap>` glyph the\n * chat card surfaces — no cross-surface drift.\n */\n\nimport { resolveSourceIcon } from '../../chat/utils/source-row-cta';\nimport { formatRelativePath } from './format-relative-path';\n\n/**\n * Minimal result shape this row renders. Compatible with any\n * doc-search hook whose result type exposes `{ title?, path?,\n * metadata? }`. The two hub consumers (onboarding-guide catalog,\n * data-room sidebar) both satisfy this shape via their\n * `useDocSearch` hook result.\n */\nexport interface DocSearchResultRowEntry {\n title?: string;\n path?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface DocSearchResultRowProps {\n result: DocSearchResultRowEntry;\n isHighlighted: boolean;\n}\n\nexport function DocSearchResultRow({ result, isHighlighted }: DocSearchResultRowProps) {\n const docType = (result.metadata?.documentType as string) || undefined;\n const sourceRepo = (result.metadata?.sourceRepo as string) || undefined;\n const { Icon: SourceIcon, label: iconLabel } = resolveSourceIcon({\n sourceRepo,\n documentType: docType,\n });\n const isGroup = result.metadata?.isGroup as boolean | undefined;\n\n return (\n <div className=\"flex w-full min-w-0 items-center gap-3\">\n <span className=\"flex-shrink-0 text-ods-text-secondary\" title={iconLabel}>\n <SourceIcon className=\"size-4\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className={`truncate text-h6 ${isHighlighted ? 'text-ods-accent' : 'text-ods-text-primary'}`}>\n {result.title || result.path}\n </div>\n {!isGroup && result.path?.includes('/') && (\n <div className=\"mt-0.5 truncate text-ods-text-secondary text-h6\">{formatRelativePath(result.path)}</div>\n )}\n </div>\n </div>\n );\n}\n","'use client';\n\n/**\n * `<DocSearchBar>` — the canonical RAG-search dropdown surface.\n *\n * Mounted by every doc-search consumer (data-room sidebar, onboarding-\n * guide catalog, and any future surface that needs typeahead against\n * `/api/docs/search`). Wraps `<SearchInput>` with the lib's standard\n * `<DocSearchResultRow>` so the dropdown looks identical everywhere.\n *\n * ## Why a presentation component, not a \"search bar that owns its\n * own hook\"\n *\n * The data-fetching hook (`useDocSearch`) lives hub-side because it\n * depends on hub-only context (`useDocNavigation`, the rag-table-\n * config registry, the hub's `decideNewTab` helper). Moving the hook\n * would cascade ~5 more file migrations into the lib.\n *\n * Instead, the hook stays hub-side and callers pass its result into\n * this component as plain props. Both consumers shrink to ~5 lines.\n */\n\nimport type { ReactNode } from 'react';\nimport { SearchInput, type SearchResult } from '../../ui/search-input';\nimport { DocSearchResultRow } from './doc-search-result-row';\n\nexport interface DocSearchBarProps {\n placeholder: string;\n query: string;\n onQueryChange: (value: string) => void;\n /** Hook-fetched results. Reuses the lib's `<SearchInput>` `SearchResult`\n * shape directly so callers don't translate. */\n results: SearchResult[];\n isLoading: boolean;\n /** Result selection handler. Mirrors `<SearchInput>` — the second\n * `modifiers` argument is preserved so cmd-click / shift-click on\n * a result row still forces new-tab behavior. Hub `useDocSearch`\n * reads these to short-circuit to `window.open()`. */\n onResultSelect: (\n result: SearchResult,\n modifiers?: {\n metaKey?: boolean;\n ctrlKey?: boolean;\n shiftKey?: boolean;\n altKey?: boolean;\n button?: number;\n },\n ) => void;\n /** Lets the caller's hook force the dropdown open after a recent\n * internal action (e.g. result navigation). `undefined` falls back\n * to `<SearchInput>`'s built-in focus/hover heuristics. */\n showDropdown?: boolean;\n /** Defaults to 2 — matches the existing data-room and onboarding-\n * guide consumers. Override only if a surface needs different\n * typeahead semantics. */\n minQueryLength?: number;\n /** Defaults to 0 — both existing consumers debounce inside the\n * hook, not the input. */\n debounceMs?: number;\n className?: string;\n /** Optional row-renderer override. Defaults to the lib's standard\n * `<DocSearchResultRow>` (source icon + title + path breadcrumb).\n * Override only when a surface needs custom row chrome. */\n renderResult?: (result: SearchResult, isHighlighted: boolean) => ReactNode;\n}\n\nexport function DocSearchBar({\n placeholder,\n query,\n onQueryChange,\n results,\n isLoading,\n onResultSelect,\n showDropdown,\n minQueryLength = 2,\n debounceMs = 0,\n className = 'w-full',\n renderResult,\n}: DocSearchBarProps) {\n return (\n <SearchInput\n placeholder={placeholder}\n value={query}\n onChange={onQueryChange}\n results={results}\n isLoading={isLoading}\n onResultSelect={onResultSelect}\n showDropdown={showDropdown || undefined}\n debounceMs={debounceMs}\n minQueryLength={minQueryLength}\n className={className}\n renderResult={\n renderResult ??\n ((result, isHighlighted) => <DocSearchResultRow result={result} isHighlighted={isHighlighted} />)\n }\n />\n );\n}\n","/**\n * Map RAG `/api/docs/search` wire results into the `<DocSearchBar>`\n * dropdown's row shape, collapsing entity-table rows into grouped\n * results so the dropdown lists ONE \"Cap Table (12 records)\" row\n * instead of 12 individual rows.\n *\n * Pure transform — no telemetry, no navigation, no React deps. Lifted\n * from the hub's `hooks/use-docs.ts:mapDocSearchResults` (the hub's\n * `traceCompose` call was hub-only telemetry and is intentionally\n * dropped — callers that want logging can wrap this helper).\n */\n\nimport { recordCountLabel } from '../../../utils/source-grouping';\nimport { getSourceLabel } from '../../../utils/source-icons';\nimport type { SearchResult } from '../../ui/search-input';\nimport type { DocSearchResult } from './types';\n\n/** Source repos that should be collapsed into grouped results in the search bar.\n * A SEARCH rule, deliberately narrower than the chat strip's `groupsByTable`:\n * a search result is a destination, so only rows that share one are folded.\n * Only financial tables (all rows link to the same admin page).\n * Content tables (blog, webinar, podcast, etc.) stay individual since each has a unique URL. */\nconst SEARCH_GROUP_REPOS = new Set([\n 'financial-cap-table',\n 'financial-kpis',\n 'financial-pnl',\n 'financial-balance-sheet',\n 'financial-cash-flow',\n]);\n\nexport function mapDocSearchResults(docs: DocSearchResult[]): SearchResult[] {\n const entityGroups = new Map<string, DocSearchResult[]>();\n // Track insertion order — groups appear where the FIRST row of that\n // repo appeared in the response.\n const order: Array<{ type: 'entity'; repo: string } | { type: 'doc'; doc: DocSearchResult }> = [];\n const seenRepos = new Set<string>();\n\n for (const doc of docs) {\n if (doc.sourceRepo && SEARCH_GROUP_REPOS.has(doc.sourceRepo)) {\n const group = entityGroups.get(doc.sourceRepo) || [];\n group.push(doc);\n entityGroups.set(doc.sourceRepo, group);\n if (!seenRepos.has(doc.sourceRepo)) {\n seenRepos.add(doc.sourceRepo);\n order.push({ type: 'entity', repo: doc.sourceRepo });\n }\n } else {\n order.push({ type: 'doc', doc });\n }\n }\n\n const results: SearchResult[] = [];\n for (const entry of order) {\n if (entry.type === 'entity') {\n // An 'entity' entry is only pushed AFTER its group lands in the map, so\n // a miss is unreachable — but dropping one group beats throwing away the\n // whole search dropdown if that ever stops holding.\n const rows = entityGroups.get(entry.repo);\n if (!rows || rows.length === 0) continue;\n results.push({\n id: `group-${entry.repo}`,\n // Same table label and count as the chat's grouped chip: one owner each.\n title: `${getSourceLabel(entry.repo)} ${recordCountLabel(rows.length)}`,\n path: rows[0].path,\n type: 'file',\n metadata: {\n documentType: rows[0].documentType,\n externalUrl: rows[0].externalUrl,\n sourceRepo: entry.repo,\n id: rows[0].entityId,\n isGroup: true,\n items: rows.map(r => ({\n name: r.name,\n externalUrl: r.externalUrl,\n id: r.entityId,\n sourceRepo: r.sourceRepo,\n documentType: r.documentType,\n })),\n },\n });\n } else {\n const doc = entry.doc;\n const isNonMarkdown = doc.documentType && doc.documentType !== 'markdown';\n results.push({\n id: doc.path,\n title: doc.name,\n description: isNonMarkdown ? doc.name : doc.snippet,\n path: doc.path,\n type: doc.type,\n metadata: {\n matchType: doc.matchType,\n ...(doc.documentType ? { documentType: doc.documentType } : {}),\n ...(doc.externalUrl ? { externalUrl: doc.externalUrl } : {}),\n ...(doc.targetPlatform != null ? { targetPlatform: doc.targetPlatform } : {}),\n ...(doc.sourceRepo ? { sourceRepo: doc.sourceRepo } : {}),\n ...(doc.entityId ? { id: doc.entityId } : {}),\n },\n });\n }\n }\n\n return results;\n}\n","/**\n * Resolve what should happen when the user picks a search result.\n * Returns one of five typed actions so the caller is a single switch.\n *\n * Resolution order:\n * 1. `externalUrl` present → resolve the href through the host's\n * `composeContentUrl` seam (when wired), then use `decideNewTab` to\n * choose same-tab vs new-tab against the resolved `targetPlatform`.\n * 2. Row has `id` + `sourceRepo` + `documentType` → synth an Ask-AI\n * action (entity drill-in via primary key, no URL).\n * 3. Row has only `path` → legacy navigation fallback.\n * 4. Nothing actionable → noop.\n *\n * Lifted from the hub's `hooks/use-docs.ts:resolveSearchResultAction`.\n * Pure — no React, no telemetry.\n */\n\nimport type { ComposeContentUrl } from '../../../utils/content-href';\nimport type { ChatRef } from '../../chat/chat-ref.types';\nimport { decideNewTab } from '../../chat/utils/decide-new-tab';\nimport type { SearchResult } from '../../ui/search-input';\n\n/** True for `https://…` and protocol-relative `//…`. A composed href that is\n * NOT absolute is an in-app route the host claimed via `hostedTypes` /\n * `overrides`. */\nfunction isAbsoluteHref(href: string): boolean {\n return /^(?:[a-z][a-z0-9+.-]*:)?\\/\\//i.test(href);\n}\n\nexport type SearchResultAction =\n | { kind: 'navigate-same-tab'; href: string }\n | { kind: 'navigate-new-tab'; href: string }\n | { kind: 'ask-ai'; detail: { source: string; ref: ChatRef } }\n | { kind: 'route'; path: string }\n | { kind: 'noop' };\n\nexport function resolveSearchResultAction(\n result: SearchResult,\n source: string,\n runtimeMode?: 'host' | 'embed',\n /** The host's unified content-href seam (`runtime.composeContentUrl`).\n * When wired, an entity row's RAG `externalUrl` is re-resolved through it,\n * so a type the host serves in-app (`hostedTypes`) or deep-links itself\n * (`overrides`) lands on the SAME href the catalog cards and chat cards\n * use. Omitted → the legacy verbatim-`externalUrl` behavior. */\n composeContentUrl?: ComposeContentUrl,\n): SearchResultAction {\n const meta = result.metadata ?? {};\n const externalUrl = meta.externalUrl as string | undefined;\n const rowId = meta.id as string | undefined;\n const sourceRepo = meta.sourceRepo as string | undefined;\n const documentType = meta.documentType as string | undefined;\n if (externalUrl) {\n const targetPlatform = meta.targetPlatform as string | null | undefined;\n // Unified content-href seam — the same call `resolveSourceRowCTA` makes\n // for chat rows. Hosted types relativize the slug out of `externalUrl`;\n // everything else comes back verbatim, so an unwired host (or a row with\n // no `documentType`) keeps today's behavior byte-for-byte.\n const composed =\n composeContentUrl && documentType\n ? composeContentUrl({\n type: documentType,\n identifier: rowId ?? '',\n externalUrl,\n targetPlatform: targetPlatform ?? null,\n })\n : null;\n const href = composed?.href || externalUrl;\n // A RELATIVE composed href means the host claimed this row as in-app.\n // Short-circuit BEFORE `decideNewTab`: in embed mode that helper forces\n // new-tab unconditionally, which would fire `window.open()` on a path\n // that only resolves inside this app.\n if (composed && !isAbsoluteHref(href)) {\n return { kind: 'navigate-same-tab', href };\n }\n // Same pure helper `useNavLink` and `useUnifiedNav` call — single\n // decision rule across cards, chips, and autocomplete rows. Thread\n // the caller's `source` as `currentSource` so the platform-vs-\n // platform comparison matches the hub's pre-migration behavior.\n const isNewTab = decideNewTab({\n href,\n targetPlatform: composed ? composed.targetPlatform : targetPlatform,\n surface: 'useUnifiedNav',\n runtimeMode,\n currentSource: source,\n });\n return isNewTab ? { kind: 'navigate-new-tab', href } : { kind: 'navigate-same-tab', href };\n }\n if (rowId && sourceRepo && documentType) {\n return {\n kind: 'ask-ai',\n detail: {\n source,\n ref: { type: documentType, id: rowId, title: result.title, url: null },\n },\n };\n }\n if (result.path) {\n return { kind: 'route', path: result.path };\n }\n return { kind: 'noop' };\n}\n","'use client';\n\n/**\n * `useDocSearch` — debounced RAG-search hook against `/api/docs/search`.\n *\n * Pure fetch + navigation glue. Embedders can mount this directly\n * (any host with a reverse-proxy that exposes `/api/docs/search` will\n * work). Hub callers wire it into the lib `<DocSearchBar>` for the\n * canonical typeahead dropdown.\n *\n * ## What moved from hub to lib\n *\n * Lifted from `multi-platform-hub/hooks/use-docs.ts:useDocSearch`. Two\n * hub-only concerns are now optional injection points instead of\n * direct imports:\n *\n * - `useDocNavigation()` (hub's in-page doc-tree swap) → optional\n * `onInPageSwap?: (path: string) => boolean` config callback. When\n * present and returns true, the hook treats a same-origin result\n * click as \"handled in-page\"; when absent or returns false, the\n * hook falls back to `onNavigate(path)` (`router.push` on hub,\n * `window.location.assign` on bare embedders).\n * - `traceCompose` (hub-only telemetry) → dropped. The lib has no\n * equivalent runtime-context yet; bring it back when there is one.\n *\n * Everything else (debounce, `useChatRuntime` for embed-mode short-\n * circuit, embed-shim router, the action-resolver + result-mapper) is\n * now lib-resident.\n *\n * ## Result hrefs go through the content-href seam\n *\n * A row's href is NOT the RAG `externalUrl` verbatim any more: when the\n * host wires `runtime.composeContentUrl` (or passes `composeContentUrl`\n * here), the row is re-resolved through it — the SAME seam the catalog\n * cards and the chat entity cards use. So a host that serves onboarding\n * guides at `/help-center/onboarding-guides/<slug>` gets that path from\n * the dropdown too, instead of bouncing the user to the canonical hub\n * URL. Unwired hosts keep the verbatim-`externalUrl` behavior.\n */\n\nimport { useState, useEffect, useCallback } from 'react';\nimport { useChatRuntime } from '../../../contexts/chat-runtime-context';\nimport { useRouter } from '../../../embed-shims';\nimport { useDebounce } from '../../../hooks/ui/use-debounce';\nimport type { ComposeContentUrl } from '../../../utils/content-href';\nimport { contentFetch } from '../../../utils/embed-content-fetch';\nimport {\n resolveExternalNavigation,\n stripSameOriginToPath,\n NEW_TAB_FEATURES,\n} from '../../chat/utils/chat-nav-resolution';\nimport type { SearchResult } from '../../ui/search-input';\nimport { mapDocSearchResults } from './map-doc-search-results';\nimport { resolveSearchResultAction } from './resolve-search-result-action';\nimport type { DocSearchResult } from './types';\n\nexport interface UseDocSearchConfig {\n /** Discriminator passed to `/api/docs/search?source=` (e.g.\n * `'openframe'`). Embedders set it to whatever discriminator their\n * reverse-proxy expects. */\n source: string;\n /** Base route prefix this search lives under (e.g. `'/onboarding-guides'`).\n * When a result's href starts with `${baseRoute}/`, the hook\n * attempts the optional in-page swap path before falling through\n * to a full nav. */\n baseRoute: string;\n /** Imperative navigation fallback. Called when no override\n * (in-page swap, new-tab) applies. Hub callers pass\n * `(path) => router.push(path)`; embedders pass an equivalent. */\n onNavigate: (path: string) => void;\n /** Optional `RagTableConfig.id` list to narrow the search to specific\n * tables (e.g. `['onboarding-guides']`). Forwarded to\n * `/api/docs/search?tableIds=…` which intersects with the source's\n * standing set. */\n tableIds?: string[];\n /** Optional in-page swap callback. When the result's href is under\n * `baseRoute` AND this callback returns true, the hook treats the\n * click as handled in-page (no router push). Hub's\n * `<DocumentationSection>` wires this to\n * `useDocNavigation().navigate(path)`. */\n onInPageSwap?: (path: string) => boolean;\n /** Optional endpoint override. Defaults to `'/api/docs/search'`\n * (the hub's reverse-proxy route). Embedders with a different\n * path can override. */\n searchEndpoint?: string;\n /** Optional content-href seam override. Same fall-back chain as\n * `searchEndpoint`: prop → `runtime.composeContentUrl` → none. Wired,\n * it makes a result row honor the host's `hostedTypes` / `overrides`\n * instead of navigating to the RAG's canonical hub URL — so a row\n * lands where the catalog card for the same entity lands. */\n composeContentUrl?: ComposeContentUrl;\n}\n\n/** One shared empty array, so \"no matches\" keeps a stable identity. */\nconst NO_RESULTS: SearchResult[] = [];\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/** `Array.isArray` narrows `unknown` to `any[]`; this keeps the elements `unknown`. */\nfunction isUnknownArray(value: unknown): value is readonly unknown[] {\n return Array.isArray(value);\n}\n\nfunction readString(source: Record<string, unknown>, key: string): string | undefined {\n const value = source[key];\n return typeof value === 'string' ? value : undefined;\n}\n\n/**\n * Decode `/api/docs/search`'s `{ success, data }` envelope.\n *\n * `Response.json()` is typed `any`, so the rows used to reach\n * `mapDocSearchResults` through an `as DocSearchResult[]` that asserted a\n * shape nothing had checked. `path` and `name` are the two that bite: the\n * mapper uses `path` as the row's React `id` and `name` as its title, so a row\n * missing either renders as a blank, key-colliding entry in the dropdown.\n * Such rows are dropped; the rest of the result set still lists.\n *\n * Returns null when the envelope itself is not a successful result set.\n */\nfunction toDocSearchResults(payload: unknown): DocSearchResult[] | null {\n if (!isRecord(payload) || !payload.success || !isUnknownArray(payload.data)) return null;\n\n const rows: DocSearchResult[] = [];\n for (const entry of payload.data) {\n if (!isRecord(entry)) continue;\n const path = readString(entry, 'path');\n const name = readString(entry, 'name');\n const type = entry.type;\n if (path === undefined || name === undefined) continue;\n\n const targetPlatform = entry.targetPlatform;\n rows.push({\n path,\n name,\n type: type === 'folder' ? 'folder' : 'file',\n snippet: readString(entry, 'snippet') ?? '',\n matchType: entry.matchType === 'title' ? 'title' : 'content',\n documentType: readString(entry, 'documentType'),\n externalUrl: readString(entry, 'externalUrl'),\n targetPlatform: typeof targetPlatform === 'string' ? targetPlatform : null,\n sourceRepo: readString(entry, 'sourceRepo'),\n entityId: readString(entry, 'entityId'),\n });\n }\n return rows;\n}\n\nexport function useDocSearch(config: UseDocSearchConfig) {\n const { source, baseRoute, onNavigate, tableIds, onInPageSwap, searchEndpoint, composeContentUrl } = config;\n const tableIdsKey = tableIds && tableIds.length > 0 ? tableIds.join(',') : '';\n\n const router = useRouter();\n // Optional chat-runtime read — when present and mode='embed' the\n // search-result row click short-circuits to a new-tab open against\n // the absolutized URL. Null/host preserves today's behavior.\n // Also used as the proxy-prefix fallback for `searchEndpoint`, matching\n // how tickets resolves `findTicketUrl`.\n const runtime = useChatRuntime();\n const resolvedSearchEndpoint = searchEndpoint ?? runtime?.endpoints.docsSearchUrl ?? '/api/docs/search';\n // Content-href seam, same prop → runtime → none chain. Threading it into\n // `resolveSearchResultAction` is what makes the dropdown obey the host's\n // route map; without it the row navigates to the RAG's canonical hub URL\n // even when the host serves that content in-app under a different path.\n const resolvedComposeContentUrl = composeContentUrl ?? runtime?.composeContentUrl;\n\n const [query, setQuery] = useState('');\n const [results, setResults] = useState<SearchResult[]>(NO_RESULTS);\n const [isFetching, setIsFetching] = useState(false);\n const debouncedQuery = useDebounce(query, 300);\n\n // Falling below the two-character floor empties the dropdown. Adjusted while\n // rendering (React's documented pattern for a reset the render already has\n // the answer to) rather than from an effect, which committed one more frame\n // of the previous matches under a query that no longer searches for them.\n const searchable = debouncedQuery.trim().length >= 2;\n const [wasSearchable, setWasSearchable] = useState(searchable);\n if (wasSearchable !== searchable) {\n setWasSearchable(searchable);\n if (!searchable) {\n setResults(NO_RESULTS);\n setIsFetching(false);\n }\n }\n\n useEffect(() => {\n if (!searchable) return undefined;\n\n let cancelled = false;\n\n async function fetchResults() {\n setIsFetching(true);\n try {\n const params = new URLSearchParams({\n q: debouncedQuery,\n source,\n limit: '10',\n });\n if (tableIdsKey) params.set('tableIds', tableIdsKey);\n\n const response = await contentFetch(`${resolvedSearchEndpoint}?${params.toString()}`);\n if (!response.ok) {\n throw new Error(`Search request failed: ${response.status}`);\n }\n\n const json: unknown = await response.json();\n\n if (!cancelled) {\n // An envelope that is not a successful result set now clears the\n // dropdown instead of leaving it untouched. Previously a 200 that\n // carried `success: false` (or a non-array `data`) fell through\n // every branch, so the PREVIOUS query's matches stayed on screen\n // under the new query — the throw path already reset them.\n const rows = toDocSearchResults(json);\n setResults(rows ? mapDocSearchResults(rows) : NO_RESULTS);\n }\n } catch (error) {\n console.error('Doc search error:', error);\n if (!cancelled) {\n setResults(NO_RESULTS);\n }\n } finally {\n if (!cancelled) {\n setIsFetching(false);\n }\n }\n }\n\n // Never rejects — try/catch/finally, every write gated on `cancelled`.\n void fetchResults();\n\n return () => {\n cancelled = true;\n };\n }, [debouncedQuery, searchable, source, tableIdsKey, resolvedSearchEndpoint]);\n\n // Derived loading state — single source of truth for \"should the\n // dropdown show 'Loading...' instead of 'No results found'\":\n const isLoading = query.trim().length >= 2 && (query !== debouncedQuery || isFetching);\n\n // Track whether dropdown should stay open (external link opened in new tab).\n const [keepOpen, setKeepOpen] = useState(false);\n\n const handleResultSelect = useCallback(\n (\n result: SearchResult,\n modifiers?: {\n metaKey?: boolean;\n ctrlKey?: boolean;\n shiftKey?: boolean;\n altKey?: boolean;\n button?: number;\n },\n ) => {\n const action = resolveSearchResultAction(result, source, runtime?.navigation.mode, resolvedComposeContentUrl);\n // Modifier / non-primary mouse click → force new tab regardless of\n // same-tab/new-tab decision. The dropdown row is a `<div>`, not an\n // `<a target=\"_blank\">`, so the browser doesn't background-tab\n // natively on cmd-click. Honor it explicitly here for parity with\n // the anchor-based surfaces (cards, chips, related-content). Plain\n // Enter from the keyboard passes `modifiers === undefined`.\n const wantsNewTab =\n modifiers &&\n (modifiers.metaKey ||\n modifiers.ctrlKey ||\n modifiers.shiftKey ||\n modifiers.altKey ||\n (typeof modifiers.button === 'number' && modifiers.button !== 0));\n switch (action.kind) {\n case 'navigate-same-tab': {\n if (wantsNewTab) {\n setKeepOpen(true);\n window.open(action.href, '_blank', NEW_TAB_FEATURES);\n return;\n }\n // Same-origin click:\n // 1. If the href is under the current doc-tree's baseRoute AND\n // an `onInPageSwap` callback is wired AND returns true →\n // consider in-page swap handled.\n // 2. Otherwise → embed-shim `router.push()` (soft RSC nav on\n // Next.js hosts, window.location.assign on bare hosts).\n setKeepOpen(false);\n const path =\n baseRoute && action.href.startsWith(`${baseRoute}/`) ? action.href.slice(baseRoute.length + 1) : null;\n if (path && onInPageSwap?.(path)) return;\n router.push(stripSameOriginToPath(action.href));\n return;\n }\n case 'navigate-new-tab':\n // Cross-origin (e.g. clicking a flamingo.run release from\n // product-hub) — open in a new tab. Keep dropdown open so the\n // user can pick another result without re-searching.\n setKeepOpen(true);\n // Embed-mode short-circuit — the row was clicked while the panel is\n // hosted inside an embedding app, so absolutize against the\n // embedder-supplied content origin and honor its `openExternal`\n // override. Not the only path an embed host takes: a composed\n // RELATIVE href resolves to `navigate-same-tab` above and never\n // reaches `decideNewTab`, which is what lets an embedder keep its\n // own in-app routes in-app.\n if (runtime?.navigation.mode === 'embed') {\n const targetPlatform = (result.metadata?.targetPlatform as string | null | undefined) ?? null;\n resolveExternalNavigation({\n href: action.href,\n targetPlatform,\n runtime,\n }).open();\n return;\n }\n window.open(action.href, '_blank', NEW_TAB_FEATURES);\n return;\n case 'ask-ai':\n // Row is searchable-but-not-openable (cap_table positions,\n // financial-kpi snapshots, anything backed by\n // `resolveUrl: () => null`). Dispatch a CustomEvent that\n // GlobalAskAI listens for — opens chat + drills via\n // `entityIdFilter` (primary-key only, same as inline-card Ask).\n setKeepOpen(false);\n window.dispatchEvent(new CustomEvent('ask-ai:open-with-ref', { detail: action.detail }));\n return;\n case 'route':\n // Final fallback: legacy navigation by path. Hits when a row\n // has neither URL nor pk metadata — a mapper/API regression.\n setKeepOpen(false);\n onNavigate(action.path);\n return;\n case 'noop':\n return;\n }\n },\n [onNavigate, source, baseRoute, router, onInPageSwap, runtime, resolvedComposeContentUrl],\n );\n\n // Reset keepOpen when query changes. Adjusted while rendering, not from an\n // effect: the dropdown's open state is read from this in the same render the\n // keystroke produced, so an effect held it pinned open for one extra frame.\n const [keepOpenForQuery, setKeepOpenForQuery] = useState(query);\n if (keepOpenForQuery !== query) {\n setKeepOpenForQuery(query);\n setKeepOpen(false);\n }\n\n return {\n query,\n setQuery,\n results,\n isLoading,\n handleResultSelect,\n keepDropdownOpen: keepOpen,\n };\n}\n"]}
1
+ {"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-5XPK2QGU.cjs","../src/components/shared/doc-search/format-relative-path.ts","../src/components/shared/doc-search/doc-search-result-row.tsx","../src/components/shared/doc-search/doc-search-bar.tsx","../src/components/shared/doc-search/map-doc-search-results.ts","../src/components/shared/doc-search/resolve-search-result-action.ts","../src/components/shared/doc-search/use-doc-search.ts"],"names":["jsx"],"mappings":"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","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-5XPK2QGU.cjs","sourcesContent":[null,"/**\n * Format a full document path as a breadcrumb trail.\n * Shows parent folders only (excludes the last segment / filename).\n *\n * @example\n * formatRelativePath('openframe-oss-tenant/architecture/api-controllers.md')\n * // → 'Openframe oss tenant / Architecture'\n */\nexport function formatRelativePath(fullPath: string): string {\n if (!fullPath) return '';\n const segments = fullPath.replace(/\\.md$/, '').split('/');\n // Show only parent path (exclude the filename itself since the title already shows it)\n const parentSegments = segments.length > 1 ? segments.slice(0, -1) : segments;\n return parentSegments.map(seg => seg.charAt(0).toUpperCase() + seg.slice(1).replace(/-/g, ' ')).join(' / ');\n}\n","'use client';\n\n/**\n * Single row in the `<SearchInput>` dropdown — the standard layout\n * used by every doc-search-backed surface (company-hub data-room\n * search bar, onboarding-guide catalog search, …). Single source of\n * truth for the row appearance so search dropdowns are visually\n * identical everywhere.\n *\n * Resolves the source icon via the same `resolveSourceIcon()`\n * registry the inline chat-card refs use, so a row pointing at e.g.\n * an onboarding-guide surfaces the SAME `<GraduationCap>` glyph the\n * chat card surfaces — no cross-surface drift.\n */\n\nimport { resolveSourceIcon } from '../../chat/utils/source-row-cta';\nimport { formatRelativePath } from './format-relative-path';\n\n/**\n * Minimal result shape this row renders. Compatible with any\n * doc-search hook whose result type exposes `{ title?, path?,\n * metadata? }`. The two hub consumers (onboarding-guide catalog,\n * data-room sidebar) both satisfy this shape via their\n * `useDocSearch` hook result.\n */\nexport interface DocSearchResultRowEntry {\n title?: string;\n path?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface DocSearchResultRowProps {\n result: DocSearchResultRowEntry;\n isHighlighted: boolean;\n}\n\nexport function DocSearchResultRow({ result, isHighlighted }: DocSearchResultRowProps) {\n const docType = (result.metadata?.documentType as string) || undefined;\n const sourceRepo = (result.metadata?.sourceRepo as string) || undefined;\n const { Icon: SourceIcon, label: iconLabel } = resolveSourceIcon({\n sourceRepo,\n documentType: docType,\n });\n const isGroup = result.metadata?.isGroup as boolean | undefined;\n\n return (\n <div className=\"flex w-full min-w-0 items-center gap-3\">\n <span className=\"flex-shrink-0 text-ods-text-secondary\" title={iconLabel}>\n <SourceIcon className=\"size-4\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className={`truncate text-h6 ${isHighlighted ? 'text-ods-accent' : 'text-ods-text-primary'}`}>\n {result.title || result.path}\n </div>\n {!isGroup && result.path?.includes('/') && (\n <div className=\"mt-0.5 truncate text-ods-text-secondary text-h6\">{formatRelativePath(result.path)}</div>\n )}\n </div>\n </div>\n );\n}\n","'use client';\n\n/**\n * `<DocSearchBar>` — the canonical RAG-search dropdown surface.\n *\n * Mounted by every doc-search consumer (data-room sidebar, onboarding-\n * guide catalog, and any future surface that needs typeahead against\n * `/api/docs/search`). Wraps `<SearchInput>` with the lib's standard\n * `<DocSearchResultRow>` so the dropdown looks identical everywhere.\n *\n * ## Why a presentation component, not a \"search bar that owns its\n * own hook\"\n *\n * The data-fetching hook (`useDocSearch`) lives hub-side because it\n * depends on hub-only context (`useDocNavigation`, the rag-table-\n * config registry, the hub's `decideNewTab` helper). Moving the hook\n * would cascade ~5 more file migrations into the lib.\n *\n * Instead, the hook stays hub-side and callers pass its result into\n * this component as plain props. Both consumers shrink to ~5 lines.\n */\n\nimport type { ReactNode } from 'react';\nimport { SearchInput, type SearchResult } from '../../ui/search-input';\nimport { DocSearchResultRow } from './doc-search-result-row';\n\nexport interface DocSearchBarProps {\n placeholder: string;\n query: string;\n onQueryChange: (value: string) => void;\n /** Hook-fetched results. Reuses the lib's `<SearchInput>` `SearchResult`\n * shape directly so callers don't translate. */\n results: SearchResult[];\n isLoading: boolean;\n /** Result selection handler. Mirrors `<SearchInput>` — the second\n * `modifiers` argument is preserved so cmd-click / shift-click on\n * a result row still forces new-tab behavior. Hub `useDocSearch`\n * reads these to short-circuit to `window.open()`. */\n onResultSelect: (\n result: SearchResult,\n modifiers?: {\n metaKey?: boolean;\n ctrlKey?: boolean;\n shiftKey?: boolean;\n altKey?: boolean;\n button?: number;\n },\n ) => void;\n /** Lets the caller's hook force the dropdown open after a recent\n * internal action (e.g. result navigation). `undefined` falls back\n * to `<SearchInput>`'s built-in focus/hover heuristics. */\n showDropdown?: boolean;\n /** Defaults to 2 — matches the existing data-room and onboarding-\n * guide consumers. Override only if a surface needs different\n * typeahead semantics. */\n minQueryLength?: number;\n /** Defaults to 0 — both existing consumers debounce inside the\n * hook, not the input. */\n debounceMs?: number;\n className?: string;\n /** Optional row-renderer override. Defaults to the lib's standard\n * `<DocSearchResultRow>` (source icon + title + path breadcrumb).\n * Override only when a surface needs custom row chrome. */\n renderResult?: (result: SearchResult, isHighlighted: boolean) => ReactNode;\n}\n\nexport function DocSearchBar({\n placeholder,\n query,\n onQueryChange,\n results,\n isLoading,\n onResultSelect,\n showDropdown,\n minQueryLength = 2,\n debounceMs = 0,\n className = 'w-full',\n renderResult,\n}: DocSearchBarProps) {\n return (\n <SearchInput\n placeholder={placeholder}\n value={query}\n onChange={onQueryChange}\n results={results}\n isLoading={isLoading}\n onResultSelect={onResultSelect}\n showDropdown={showDropdown || undefined}\n debounceMs={debounceMs}\n minQueryLength={minQueryLength}\n className={className}\n renderResult={\n renderResult ??\n ((result, isHighlighted) => <DocSearchResultRow result={result} isHighlighted={isHighlighted} />)\n }\n />\n );\n}\n","/**\n * Map RAG `/api/docs/search` wire results into the `<DocSearchBar>`\n * dropdown's row shape, collapsing entity-table rows into grouped\n * results so the dropdown lists ONE \"Cap Table (12 records)\" row\n * instead of 12 individual rows.\n *\n * Pure transform — no telemetry, no navigation, no React deps. Lifted\n * from the hub's `hooks/use-docs.ts:mapDocSearchResults` (the hub's\n * `traceCompose` call was hub-only telemetry and is intentionally\n * dropped — callers that want logging can wrap this helper).\n */\n\nimport { recordCountLabel } from '../../../utils/source-grouping';\nimport { getSourceLabel } from '../../../utils/source-icons';\nimport type { SearchResult } from '../../ui/search-input';\nimport type { DocSearchResult } from './types';\n\n/** Source repos that should be collapsed into grouped results in the search bar.\n * A SEARCH rule, deliberately narrower than the chat strip's `groupsByTable`:\n * a search result is a destination, so only rows that share one are folded.\n * Only financial tables (all rows link to the same admin page).\n * Content tables (blog, webinar, podcast, etc.) stay individual since each has a unique URL. */\nconst SEARCH_GROUP_REPOS = new Set([\n 'financial-cap-table',\n 'financial-kpis',\n 'financial-pnl',\n 'financial-balance-sheet',\n 'financial-cash-flow',\n]);\n\nexport function mapDocSearchResults(docs: DocSearchResult[]): SearchResult[] {\n const entityGroups = new Map<string, DocSearchResult[]>();\n // Track insertion order — groups appear where the FIRST row of that\n // repo appeared in the response.\n const order: Array<{ type: 'entity'; repo: string } | { type: 'doc'; doc: DocSearchResult }> = [];\n const seenRepos = new Set<string>();\n\n for (const doc of docs) {\n if (doc.sourceRepo && SEARCH_GROUP_REPOS.has(doc.sourceRepo)) {\n const group = entityGroups.get(doc.sourceRepo) || [];\n group.push(doc);\n entityGroups.set(doc.sourceRepo, group);\n if (!seenRepos.has(doc.sourceRepo)) {\n seenRepos.add(doc.sourceRepo);\n order.push({ type: 'entity', repo: doc.sourceRepo });\n }\n } else {\n order.push({ type: 'doc', doc });\n }\n }\n\n const results: SearchResult[] = [];\n for (const entry of order) {\n if (entry.type === 'entity') {\n // An 'entity' entry is only pushed AFTER its group lands in the map, so\n // a miss is unreachable — but dropping one group beats throwing away the\n // whole search dropdown if that ever stops holding.\n const rows = entityGroups.get(entry.repo);\n if (!rows || rows.length === 0) continue;\n results.push({\n id: `group-${entry.repo}`,\n // Same table label and count as the chat's grouped chip: one owner each.\n title: `${getSourceLabel(entry.repo)} ${recordCountLabel(rows.length)}`,\n path: rows[0].path,\n type: 'file',\n metadata: {\n documentType: rows[0].documentType,\n externalUrl: rows[0].externalUrl,\n sourceRepo: entry.repo,\n id: rows[0].entityId,\n isGroup: true,\n items: rows.map(r => ({\n name: r.name,\n externalUrl: r.externalUrl,\n id: r.entityId,\n sourceRepo: r.sourceRepo,\n documentType: r.documentType,\n })),\n },\n });\n } else {\n const doc = entry.doc;\n const isNonMarkdown = doc.documentType && doc.documentType !== 'markdown';\n results.push({\n id: doc.path,\n title: doc.name,\n description: isNonMarkdown ? doc.name : doc.snippet,\n path: doc.path,\n type: doc.type,\n metadata: {\n matchType: doc.matchType,\n ...(doc.documentType ? { documentType: doc.documentType } : {}),\n ...(doc.externalUrl ? { externalUrl: doc.externalUrl } : {}),\n ...(doc.targetPlatform != null ? { targetPlatform: doc.targetPlatform } : {}),\n ...(doc.sourceRepo ? { sourceRepo: doc.sourceRepo } : {}),\n ...(doc.entityId ? { id: doc.entityId } : {}),\n },\n });\n }\n }\n\n return results;\n}\n","/**\n * Resolve what should happen when the user picks a search result.\n * Returns one of five typed actions so the caller is a single switch.\n *\n * Resolution order:\n * 1. `externalUrl` present → resolve the href through the host's\n * `composeContentUrl` seam (when wired), then use `decideNewTab` to\n * choose same-tab vs new-tab against the resolved `targetPlatform`.\n * 2. Row has `id` + `sourceRepo` + `documentType` → synth an Ask-AI\n * action (entity drill-in via primary key, no URL).\n * 3. Row has only `path` → legacy navigation fallback.\n * 4. Nothing actionable → noop.\n *\n * Lifted from the hub's `hooks/use-docs.ts:resolveSearchResultAction`.\n * Pure — no React, no telemetry.\n */\n\nimport type { ComposeContentUrl } from '../../../utils/content-href';\nimport type { ChatRef } from '../../chat/chat-ref.types';\nimport { decideNewTab } from '../../chat/utils/decide-new-tab';\nimport type { SearchResult } from '../../ui/search-input';\n\n/** True for `https://…` and protocol-relative `//…`. A composed href that is\n * NOT absolute is an in-app route the host claimed via `hostedTypes` /\n * `overrides`. */\nfunction isAbsoluteHref(href: string): boolean {\n return /^(?:[a-z][a-z0-9+.-]*:)?\\/\\//i.test(href);\n}\n\nexport type SearchResultAction =\n | { kind: 'navigate-same-tab'; href: string }\n | { kind: 'navigate-new-tab'; href: string }\n | { kind: 'ask-ai'; detail: { source: string; ref: ChatRef } }\n | { kind: 'route'; path: string }\n | { kind: 'noop' };\n\nexport function resolveSearchResultAction(\n result: SearchResult,\n source: string,\n runtimeMode?: 'host' | 'embed',\n /** The host's unified content-href seam (`runtime.composeContentUrl`).\n * When wired, an entity row's RAG `externalUrl` is re-resolved through it,\n * so a type the host serves in-app (`hostedTypes`) or deep-links itself\n * (`overrides`) lands on the SAME href the catalog cards and chat cards\n * use. Omitted → the legacy verbatim-`externalUrl` behavior. */\n composeContentUrl?: ComposeContentUrl,\n): SearchResultAction {\n const meta = result.metadata ?? {};\n const externalUrl = meta.externalUrl as string | undefined;\n const rowId = meta.id as string | undefined;\n const sourceRepo = meta.sourceRepo as string | undefined;\n const documentType = meta.documentType as string | undefined;\n if (externalUrl) {\n const targetPlatform = meta.targetPlatform as string | null | undefined;\n // Unified content-href seam — the same call `resolveSourceRowCTA` makes\n // for chat rows. Hosted types relativize the slug out of `externalUrl`;\n // everything else comes back verbatim, so an unwired host (or a row with\n // no `documentType`) keeps today's behavior byte-for-byte.\n const composed =\n composeContentUrl && documentType\n ? composeContentUrl({\n type: documentType,\n identifier: rowId ?? '',\n externalUrl,\n targetPlatform: targetPlatform ?? null,\n })\n : null;\n const href = composed?.href || externalUrl;\n // A RELATIVE composed href means the host claimed this row as in-app.\n // Short-circuit BEFORE `decideNewTab`: in embed mode that helper forces\n // new-tab unconditionally, which would fire `window.open()` on a path\n // that only resolves inside this app.\n if (composed && !isAbsoluteHref(href)) {\n return { kind: 'navigate-same-tab', href };\n }\n // Same pure helper `useNavLink` and `useUnifiedNav` call — single\n // decision rule across cards, chips, and autocomplete rows. Thread\n // the caller's `source` as `currentSource` so the platform-vs-\n // platform comparison matches the hub's pre-migration behavior.\n const isNewTab = decideNewTab({\n href,\n targetPlatform: composed ? composed.targetPlatform : targetPlatform,\n surface: 'useUnifiedNav',\n runtimeMode,\n currentSource: source,\n });\n return isNewTab ? { kind: 'navigate-new-tab', href } : { kind: 'navigate-same-tab', href };\n }\n if (rowId && sourceRepo && documentType) {\n return {\n kind: 'ask-ai',\n detail: {\n source,\n ref: { type: documentType, id: rowId, title: result.title, url: null },\n },\n };\n }\n if (result.path) {\n return { kind: 'route', path: result.path };\n }\n return { kind: 'noop' };\n}\n","'use client';\n\n/**\n * `useDocSearch` — debounced RAG-search hook against `/api/docs/search`.\n *\n * Pure fetch + navigation glue. Embedders can mount this directly\n * (any host with a reverse-proxy that exposes `/api/docs/search` will\n * work). Hub callers wire it into the lib `<DocSearchBar>` for the\n * canonical typeahead dropdown.\n *\n * ## What moved from hub to lib\n *\n * Lifted from `multi-platform-hub/hooks/use-docs.ts:useDocSearch`. Two\n * hub-only concerns are now optional injection points instead of\n * direct imports:\n *\n * - `useDocNavigation()` (hub's in-page doc-tree swap) → optional\n * `onInPageSwap?: (path: string) => boolean` config callback. When\n * present and returns true, the hook treats a same-origin result\n * click as \"handled in-page\"; when absent or returns false, the\n * hook falls back to `onNavigate(path)` (`router.push` on hub,\n * `window.location.assign` on bare embedders).\n * - `traceCompose` (hub-only telemetry) → dropped. The lib has no\n * equivalent runtime-context yet; bring it back when there is one.\n *\n * Everything else (debounce, `useChatRuntime` for embed-mode short-\n * circuit, embed-shim router, the action-resolver + result-mapper) is\n * now lib-resident.\n *\n * ## Result hrefs go through the content-href seam\n *\n * A row's href is NOT the RAG `externalUrl` verbatim any more: when the\n * host wires `runtime.composeContentUrl` (or passes `composeContentUrl`\n * here), the row is re-resolved through it — the SAME seam the catalog\n * cards and the chat entity cards use. So a host that serves onboarding\n * guides at `/help-center/onboarding-guides/<slug>` gets that path from\n * the dropdown too, instead of bouncing the user to the canonical hub\n * URL. Unwired hosts keep the verbatim-`externalUrl` behavior.\n */\n\nimport { useState, useEffect, useCallback } from 'react';\nimport { useChatRuntime } from '../../../contexts/chat-runtime-context';\nimport { useRouter } from '../../../embed-shims';\nimport { useDebounce } from '../../../hooks/ui/use-debounce';\nimport type { ComposeContentUrl } from '../../../utils/content-href';\nimport { contentFetch } from '../../../utils/embed-content-fetch';\nimport {\n resolveExternalNavigation,\n stripSameOriginToPath,\n NEW_TAB_FEATURES,\n} from '../../chat/utils/chat-nav-resolution';\nimport type { SearchResult } from '../../ui/search-input';\nimport { mapDocSearchResults } from './map-doc-search-results';\nimport { resolveSearchResultAction } from './resolve-search-result-action';\nimport type { DocSearchResult } from './types';\n\nexport interface UseDocSearchConfig {\n /** Discriminator passed to `/api/docs/search?source=` (e.g.\n * `'openframe'`). Embedders set it to whatever discriminator their\n * reverse-proxy expects. */\n source: string;\n /** Base route prefix this search lives under (e.g. `'/onboarding-guides'`).\n * When a result's href starts with `${baseRoute}/`, the hook\n * attempts the optional in-page swap path before falling through\n * to a full nav. */\n baseRoute: string;\n /** Imperative navigation fallback. Called when no override\n * (in-page swap, new-tab) applies. Hub callers pass\n * `(path) => router.push(path)`; embedders pass an equivalent. */\n onNavigate: (path: string) => void;\n /** Optional `RagTableConfig.id` list to narrow the search to specific\n * tables (e.g. `['onboarding-guides']`). Forwarded to\n * `/api/docs/search?tableIds=…` which intersects with the source's\n * standing set. */\n tableIds?: string[];\n /** Optional in-page swap callback. When the result's href is under\n * `baseRoute` AND this callback returns true, the hook treats the\n * click as handled in-page (no router push). Hub's\n * `<DocumentationSection>` wires this to\n * `useDocNavigation().navigate(path)`. */\n onInPageSwap?: (path: string) => boolean;\n /** Optional endpoint override. Defaults to `'/api/docs/search'`\n * (the hub's reverse-proxy route). Embedders with a different\n * path can override. */\n searchEndpoint?: string;\n /** Optional content-href seam override. Same fall-back chain as\n * `searchEndpoint`: prop → `runtime.composeContentUrl` → none. Wired,\n * it makes a result row honor the host's `hostedTypes` / `overrides`\n * instead of navigating to the RAG's canonical hub URL — so a row\n * lands where the catalog card for the same entity lands. */\n composeContentUrl?: ComposeContentUrl;\n}\n\n/** One shared empty array, so \"no matches\" keeps a stable identity. */\nconst NO_RESULTS: SearchResult[] = [];\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/** `Array.isArray` narrows `unknown` to `any[]`; this keeps the elements `unknown`. */\nfunction isUnknownArray(value: unknown): value is readonly unknown[] {\n return Array.isArray(value);\n}\n\nfunction readString(source: Record<string, unknown>, key: string): string | undefined {\n const value = source[key];\n return typeof value === 'string' ? value : undefined;\n}\n\n/**\n * Decode `/api/docs/search`'s `{ success, data }` envelope.\n *\n * `Response.json()` is typed `any`, so the rows used to reach\n * `mapDocSearchResults` through an `as DocSearchResult[]` that asserted a\n * shape nothing had checked. `path` and `name` are the two that bite: the\n * mapper uses `path` as the row's React `id` and `name` as its title, so a row\n * missing either renders as a blank, key-colliding entry in the dropdown.\n * Such rows are dropped; the rest of the result set still lists.\n *\n * Returns null when the envelope itself is not a successful result set.\n */\nfunction toDocSearchResults(payload: unknown): DocSearchResult[] | null {\n if (!isRecord(payload) || !payload.success || !isUnknownArray(payload.data)) return null;\n\n const rows: DocSearchResult[] = [];\n for (const entry of payload.data) {\n if (!isRecord(entry)) continue;\n const path = readString(entry, 'path');\n const name = readString(entry, 'name');\n const type = entry.type;\n if (path === undefined || name === undefined) continue;\n\n const targetPlatform = entry.targetPlatform;\n rows.push({\n path,\n name,\n type: type === 'folder' ? 'folder' : 'file',\n snippet: readString(entry, 'snippet') ?? '',\n matchType: entry.matchType === 'title' ? 'title' : 'content',\n documentType: readString(entry, 'documentType'),\n externalUrl: readString(entry, 'externalUrl'),\n targetPlatform: typeof targetPlatform === 'string' ? targetPlatform : null,\n sourceRepo: readString(entry, 'sourceRepo'),\n entityId: readString(entry, 'entityId'),\n });\n }\n return rows;\n}\n\nexport function useDocSearch(config: UseDocSearchConfig) {\n const { source, baseRoute, onNavigate, tableIds, onInPageSwap, searchEndpoint, composeContentUrl } = config;\n const tableIdsKey = tableIds && tableIds.length > 0 ? tableIds.join(',') : '';\n\n const router = useRouter();\n // Optional chat-runtime read — when present and mode='embed' the\n // search-result row click short-circuits to a new-tab open against\n // the absolutized URL. Null/host preserves today's behavior.\n // Also used as the proxy-prefix fallback for `searchEndpoint`, matching\n // how tickets resolves `findTicketUrl`.\n const runtime = useChatRuntime();\n const resolvedSearchEndpoint = searchEndpoint ?? runtime?.endpoints.docsSearchUrl ?? '/api/docs/search';\n // Content-href seam, same prop → runtime → none chain. Threading it into\n // `resolveSearchResultAction` is what makes the dropdown obey the host's\n // route map; without it the row navigates to the RAG's canonical hub URL\n // even when the host serves that content in-app under a different path.\n const resolvedComposeContentUrl = composeContentUrl ?? runtime?.composeContentUrl;\n\n const [query, setQuery] = useState('');\n const [results, setResults] = useState<SearchResult[]>(NO_RESULTS);\n const [isFetching, setIsFetching] = useState(false);\n const debouncedQuery = useDebounce(query, 300);\n\n // Falling below the two-character floor empties the dropdown. Adjusted while\n // rendering (React's documented pattern for a reset the render already has\n // the answer to) rather than from an effect, which committed one more frame\n // of the previous matches under a query that no longer searches for them.\n const searchable = debouncedQuery.trim().length >= 2;\n const [wasSearchable, setWasSearchable] = useState(searchable);\n if (wasSearchable !== searchable) {\n setWasSearchable(searchable);\n if (!searchable) {\n setResults(NO_RESULTS);\n setIsFetching(false);\n }\n }\n\n useEffect(() => {\n if (!searchable) return undefined;\n\n let cancelled = false;\n\n async function fetchResults() {\n setIsFetching(true);\n try {\n const params = new URLSearchParams({\n q: debouncedQuery,\n source,\n limit: '10',\n });\n if (tableIdsKey) params.set('tableIds', tableIdsKey);\n\n const response = await contentFetch(`${resolvedSearchEndpoint}?${params.toString()}`);\n if (!response.ok) {\n throw new Error(`Search request failed: ${response.status}`);\n }\n\n const json: unknown = await response.json();\n\n if (!cancelled) {\n // An envelope that is not a successful result set now clears the\n // dropdown instead of leaving it untouched. Previously a 200 that\n // carried `success: false` (or a non-array `data`) fell through\n // every branch, so the PREVIOUS query's matches stayed on screen\n // under the new query — the throw path already reset them.\n const rows = toDocSearchResults(json);\n setResults(rows ? mapDocSearchResults(rows) : NO_RESULTS);\n }\n } catch (error) {\n console.error('Doc search error:', error);\n if (!cancelled) {\n setResults(NO_RESULTS);\n }\n } finally {\n if (!cancelled) {\n setIsFetching(false);\n }\n }\n }\n\n // Never rejects — try/catch/finally, every write gated on `cancelled`.\n void fetchResults();\n\n return () => {\n cancelled = true;\n };\n }, [debouncedQuery, searchable, source, tableIdsKey, resolvedSearchEndpoint]);\n\n // Derived loading state — single source of truth for \"should the\n // dropdown show 'Loading...' instead of 'No results found'\":\n const isLoading = query.trim().length >= 2 && (query !== debouncedQuery || isFetching);\n\n // Track whether dropdown should stay open (external link opened in new tab).\n const [keepOpen, setKeepOpen] = useState(false);\n\n const handleResultSelect = useCallback(\n (\n result: SearchResult,\n modifiers?: {\n metaKey?: boolean;\n ctrlKey?: boolean;\n shiftKey?: boolean;\n altKey?: boolean;\n button?: number;\n },\n ) => {\n const action = resolveSearchResultAction(result, source, runtime?.navigation.mode, resolvedComposeContentUrl);\n // Modifier / non-primary mouse click → force new tab regardless of\n // same-tab/new-tab decision. The dropdown row is a `<div>`, not an\n // `<a target=\"_blank\">`, so the browser doesn't background-tab\n // natively on cmd-click. Honor it explicitly here for parity with\n // the anchor-based surfaces (cards, chips, related-content). Plain\n // Enter from the keyboard passes `modifiers === undefined`.\n const wantsNewTab =\n modifiers &&\n (modifiers.metaKey ||\n modifiers.ctrlKey ||\n modifiers.shiftKey ||\n modifiers.altKey ||\n (typeof modifiers.button === 'number' && modifiers.button !== 0));\n switch (action.kind) {\n case 'navigate-same-tab': {\n if (wantsNewTab) {\n setKeepOpen(true);\n window.open(action.href, '_blank', NEW_TAB_FEATURES);\n return;\n }\n // Same-origin click:\n // 1. If the href is under the current doc-tree's baseRoute AND\n // an `onInPageSwap` callback is wired AND returns true →\n // consider in-page swap handled.\n // 2. Otherwise → embed-shim `router.push()` (soft RSC nav on\n // Next.js hosts, window.location.assign on bare hosts).\n setKeepOpen(false);\n const path =\n baseRoute && action.href.startsWith(`${baseRoute}/`) ? action.href.slice(baseRoute.length + 1) : null;\n if (path && onInPageSwap?.(path)) return;\n router.push(stripSameOriginToPath(action.href));\n return;\n }\n case 'navigate-new-tab':\n // Cross-origin (e.g. clicking a flamingo.run release from\n // product-hub) — open in a new tab. Keep dropdown open so the\n // user can pick another result without re-searching.\n setKeepOpen(true);\n // Embed-mode short-circuit — the row was clicked while the panel is\n // hosted inside an embedding app, so absolutize against the\n // embedder-supplied content origin and honor its `openExternal`\n // override. Not the only path an embed host takes: a composed\n // RELATIVE href resolves to `navigate-same-tab` above and never\n // reaches `decideNewTab`, which is what lets an embedder keep its\n // own in-app routes in-app.\n if (runtime?.navigation.mode === 'embed') {\n const targetPlatform = (result.metadata?.targetPlatform as string | null | undefined) ?? null;\n resolveExternalNavigation({\n href: action.href,\n targetPlatform,\n runtime,\n }).open();\n return;\n }\n window.open(action.href, '_blank', NEW_TAB_FEATURES);\n return;\n case 'ask-ai':\n // Row is searchable-but-not-openable (cap_table positions,\n // financial-kpi snapshots, anything backed by\n // `resolveUrl: () => null`). Dispatch a CustomEvent that\n // GlobalAskAI listens for — opens chat + drills via\n // `entityIdFilter` (primary-key only, same as inline-card Ask).\n setKeepOpen(false);\n window.dispatchEvent(new CustomEvent('ask-ai:open-with-ref', { detail: action.detail }));\n return;\n case 'route':\n // Final fallback: legacy navigation by path. Hits when a row\n // has neither URL nor pk metadata — a mapper/API regression.\n setKeepOpen(false);\n onNavigate(action.path);\n return;\n case 'noop':\n return;\n }\n },\n [onNavigate, source, baseRoute, router, onInPageSwap, runtime, resolvedComposeContentUrl],\n );\n\n // Reset keepOpen when query changes. Adjusted while rendering, not from an\n // effect: the dropdown's open state is read from this in the same render the\n // keystroke produced, so an effect held it pinned open for one extra frame.\n const [keepOpenForQuery, setKeepOpenForQuery] = useState(query);\n if (keepOpenForQuery !== query) {\n setKeepOpenForQuery(query);\n setKeepOpen(false);\n }\n\n return {\n query,\n setQuery,\n results,\n isLoading,\n handleResultSelect,\n keepDropdownOpen: keepOpen,\n };\n}\n"]}
@@ -5,13 +5,13 @@ var _chunkN3526CTHcjs = require('./chunk-N3526CTH.cjs');
5
5
 
6
6
 
7
7
 
8
- var _chunk5N2ZSB42cjs = require('./chunk-5N2ZSB42.cjs');
8
+ var _chunkWTYQCT4Acjs = require('./chunk-WTYQCT4A.cjs');
9
9
 
10
10
 
11
11
  var _chunkDVSPRY2Vcjs = require('./chunk-DVSPRY2V.cjs');
12
12
 
13
13
 
14
- var _chunkWZYIAKIHcjs = require('./chunk-WZYIAKIH.cjs');
14
+ var _chunkF6LXGP6Scjs = require('./chunk-F6LXGP6S.cjs');
15
15
 
16
16
 
17
17
  var _chunkUSKQURNFcjs = require('./chunk-USKQURNF.cjs');
@@ -30,7 +30,7 @@ var _chunkUSKQURNFcjs = require('./chunk-USKQURNF.cjs');
30
30
 
31
31
 
32
32
 
33
- var _chunkCWXRZVDIcjs = require('./chunk-CWXRZVDI.cjs');
33
+ var _chunkEXWS2QSDcjs = require('./chunk-EXWS2QSD.cjs');
34
34
 
35
35
 
36
36
 
@@ -89,8 +89,8 @@ var _chunkX7CEWD6Vcjs = require('./chunk-X7CEWD6V.cjs');
89
89
  // src/components/shared/product-release/product-releases-view.tsx
90
90
  var _jsxruntime = require('react/jsx-runtime');
91
91
  var DEFAULT_ENDPOINT = "/api/releases";
92
- var DEFAULT_SEARCH_PARAM_KEY = _chunkCWXRZVDIcjs.DEV_SECTION_PARAM_KEYS.search;
93
- var DEFAULT_STATUS_PARAM_KEY = _chunkCWXRZVDIcjs.DEV_SECTION_PARAM_KEYS.releaseStatus;
92
+ var DEFAULT_SEARCH_PARAM_KEY = _chunkEXWS2QSDcjs.DEV_SECTION_PARAM_KEYS.search;
93
+ var DEFAULT_STATUS_PARAM_KEY = _chunkEXWS2QSDcjs.DEV_SECTION_PARAM_KEYS.releaseStatus;
94
94
  var DEFAULT_PAGE_PARAM_KEY = "page";
95
95
  function ReleaseRow({
96
96
  release,
@@ -99,7 +99,7 @@ function ReleaseRow({
99
99
  }) {
100
100
  const runtime = _chunkU2YLMFJKcjs.useChatRuntime.call(void 0, );
101
101
  const router = _chunkSADRZ4YIcjs.useRouter.call(void 0, );
102
- const cta = _chunkCWXRZVDIcjs.resolveContentHref.call(void 0, _optionalChain([runtime, 'optionalAccess', _2 => _2.composeContentUrl]), {
102
+ const cta = _chunkEXWS2QSDcjs.resolveContentHref.call(void 0, _optionalChain([runtime, 'optionalAccess', _2 => _2.composeContentUrl]), {
103
103
  type: "product_release",
104
104
  slug: release.slug,
105
105
  basePath,
@@ -171,10 +171,10 @@ function ProductReleasesView({
171
171
  router.replace(`${pathname}?${params.toString()}`, { scroll: false });
172
172
  };
173
173
  if (error) {
174
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: _chunkX7CEWD6Vcjs.cn.call(void 0, "w-full", className), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkCWXRZVDIcjs.LoadError, { message: "Failed to load releases.", onRetry: reload }) });
174
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: _chunkX7CEWD6Vcjs.cn.call(void 0, "w-full", className), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkEXWS2QSDcjs.LoadError, { message: "Failed to load releases.", onRetry: reload }) });
175
175
  }
176
176
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: _chunkX7CEWD6Vcjs.cn.call(void 0, "flex w-full flex-col gap-[40px]", className), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "min-h-[600px]", children: showEmpty ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex h-[600px] items-center justify-center", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
177
- _chunk5N2ZSB42cjs.ListEmptyState,
177
+ _chunkWTYQCT4Acjs.ListEmptyState,
178
178
  {
179
179
  isFiltered: hasActiveFilters,
180
180
  filtered: {
@@ -269,7 +269,7 @@ function MediaGalleryStrip({ items, className }) {
269
269
  var _lucidereact = require('lucide-react');
270
270
 
271
271
 
272
- var DefaultMarkdownRenderer = _chunkCWXRZVDIcjs.RichMarkdownRenderer;
272
+ var DefaultMarkdownRenderer = _chunkEXWS2QSDcjs.RichMarkdownRenderer;
273
273
  var DEFAULT_ENDPOINT2 = "/api/releases";
274
274
  function ReleaseDetailPage({
275
275
  authorHref,
@@ -288,7 +288,7 @@ function ReleaseDetailPage({
288
288
  }) {
289
289
  const router = _chunkSADRZ4YIcjs.useRouter.call(void 0, );
290
290
  const captions = _chunkGZRU2A4Lcjs.useCaptions.call(void 0, );
291
- const renderShell = (node) => shell ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkCWXRZVDIcjs.PageShell, { children: node }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "page-shell-content", children: node });
291
+ const renderShell = (node) => shell ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkEXWS2QSDcjs.PageShell, { children: node }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "page-shell-content", children: node });
292
292
  const seed = initialData == null ? void 0 : initialData;
293
293
  const {
294
294
  data: release,
@@ -347,7 +347,7 @@ function ReleaseDetailPage({
347
347
  return renderShell(
348
348
  // Match the loaded page's top offset (TitleBlock's
349
349
  // `pt-[var(--spacing-system-l)]`) so content doesn't jump on load.
350
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "pt-[var(--spacing-system-l)]", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkWZYIAKIHcjs.DetailPageSkeleton, { bare: true, metadataColumns: 4, showImageGallery: true }) })
350
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "pt-[var(--spacing-system-l)]", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkF6LXGP6Scjs.DetailPageSkeleton, { bare: true, metadataColumns: 4, showImageGallery: true }) })
351
351
  );
352
352
  }
353
353
  if (error || !release) {
@@ -386,7 +386,7 @@ function ReleaseDetailPage({
386
386
  const improvements = release.improvements;
387
387
  return renderShell(
388
388
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
389
- _chunkCWXRZVDIcjs.PageLayout,
389
+ _chunkEXWS2QSDcjs.PageLayout,
390
390
  {
391
391
  title: releaseTitle,
392
392
  subtitle: `Version: ${releaseVersion}`,
@@ -395,7 +395,7 @@ function ReleaseDetailPage({
395
395
  backButton: showBackButton ? { label: backLabel, onClick: () => router.push(backHref) } : void 0,
396
396
  children: [
397
397
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "space-y-6 md:space-y-8", children: [
398
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkCWXRZVDIcjs.EntityTagBadges, { tags: release.product_release_tags }),
398
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkEXWS2QSDcjs.EntityTagBadges, { tags: release.product_release_tags }),
399
399
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "grid w-full grid-cols-1 overflow-hidden rounded-md border border-ods-border md:grid-cols-4", children: [
400
400
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex flex-col gap-3 border-b border-ods-border bg-ods-card p-4 md:border-b-0 md:border-r", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-col gap-0", children: [
401
401
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "text-ods-text-primary text-h4", children: releaseType.toLocaleUpperCase() }),
@@ -466,7 +466,7 @@ function ReleaseDetailPage({
466
466
  ] })
467
467
  ] }) }) }),
468
468
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
469
- _chunkCWXRZVDIcjs.ReleaseChangelogSection,
469
+ _chunkEXWS2QSDcjs.ReleaseChangelogSection,
470
470
  {
471
471
  title: "Breaking Changes",
472
472
  entries: breakingChanges || [],
@@ -477,7 +477,7 @@ function ReleaseDetailPage({
477
477
  }
478
478
  ),
479
479
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
480
- _chunkCWXRZVDIcjs.ReleaseChangelogSection,
480
+ _chunkEXWS2QSDcjs.ReleaseChangelogSection,
481
481
  {
482
482
  title: "Features Added",
483
483
  entries: featuresAdded || [],
@@ -487,7 +487,7 @@ function ReleaseDetailPage({
487
487
  }
488
488
  ),
489
489
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
490
- _chunkCWXRZVDIcjs.ReleaseChangelogSection,
490
+ _chunkEXWS2QSDcjs.ReleaseChangelogSection,
491
491
  {
492
492
  title: "Bugs Fixed",
493
493
  entries: bugFixed || [],
@@ -497,7 +497,7 @@ function ReleaseDetailPage({
497
497
  }
498
498
  ),
499
499
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
500
- _chunkCWXRZVDIcjs.ReleaseChangelogSection,
500
+ _chunkEXWS2QSDcjs.ReleaseChangelogSection,
501
501
  {
502
502
  title: "Improvements",
503
503
  entries: improvements || [],
@@ -523,7 +523,7 @@ function ReleaseDetailPage({
523
523
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "tracking-[-0.28px] text-ods-text-secondary text-h5", children: "Related Enhancements and Bug-fixes" }),
524
524
  (() => {
525
525
  const deliveryCount = deliveryData ? deliveryData.completed.length + deliveryData.inProgress.length : 0;
526
- return deliveryLoading ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DeliverySection, { data: deliveryData, isLoading: deliveryLoading }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkCWXRZVDIcjs.FadePreview, { hiddenCount: deliveryCount - 1, collapsedHeight: 300, resetKey: deliveryCount, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "space-y-4", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DeliverySection, { data: deliveryData, isLoading: false }) }) });
526
+ return deliveryLoading ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DeliverySection, { data: deliveryData, isLoading: deliveryLoading }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkEXWS2QSDcjs.FadePreview, { hiddenCount: deliveryCount - 1, collapsedHeight: 300, resetKey: deliveryCount, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "space-y-4", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DeliverySection, { data: deliveryData, isLoading: false }) }) });
527
527
  })()
528
528
  ] }),
529
529
  (_optionalChain([githubReleases, 'optionalAccess', _10 => _10.length]) || _optionalChain([knowledgeBaseLinks, 'optionalAccess', _11 => _11.length]) || migrationGuideUrl || documentationUrl) && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "w-full space-y-1", children: [
@@ -602,7 +602,7 @@ function ReleaseDetailPage({
602
602
  // src/components/shared/product-release/release-detail-skeleton.tsx
603
603
 
604
604
  function ReleaseDetailSkeleton() {
605
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkWZYIAKIHcjs.DetailPageSkeleton, { metadataColumns: 4, showImageGallery: true });
605
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkF6LXGP6Scjs.DetailPageSkeleton, { metadataColumns: 4, showImageGallery: true });
606
606
  }
607
607
 
608
608
  // src/components/shared/roadmap/use-roadmap-voting.ts
@@ -777,7 +777,7 @@ function RoadmapGridSingle({
777
777
  _chunk53O6PQ3Fcjs.RoadmapCard,
778
778
  {
779
779
  item,
780
- id: _chunkCWXRZVDIcjs.devSectionAnchorId.call(void 0, "roadmap", item.id),
780
+ id: _chunkEXWS2QSDcjs.devSectionAnchorId.call(void 0, "roadmap", item.id),
781
781
  userVote: getVote(item.id),
782
782
  onVote: (voteType) => onVote(item.id, voteType),
783
783
  isVoting: votingTasks.has(item.id)
@@ -893,7 +893,7 @@ function RoadmapGrid({
893
893
  );
894
894
  }
895
895
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
896
- _chunkCWXRZVDIcjs.Accordion,
896
+ _chunkEXWS2QSDcjs.Accordion,
897
897
  {
898
898
  type: "multiple",
899
899
  value: expandedQuarters,
@@ -903,13 +903,13 @@ function RoadmapGrid({
903
903
  const itemCount = _optionalChain([itemsByQuarter, 'access', _20 => _20[quarter], 'optionalAccess', _21 => _21.length]) || 0;
904
904
  const isExpanded = expandedQuarters.includes(quarter);
905
905
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
906
- _chunkCWXRZVDIcjs.AccordionItem,
906
+ _chunkEXWS2QSDcjs.AccordionItem,
907
907
  {
908
908
  value: quarter,
909
909
  id: `quarter-${quarter.replace(/\s+/g, "-").toLowerCase()}`,
910
910
  className: "border-0",
911
911
  children: [
912
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkCWXRZVDIcjs.AccordionTrigger, { className: "w-full p-0 hover:no-underline [&>svg]:ml-auto [&>svg]:h-5 [&>svg]:w-5 [&>svg]:shrink-0 [&>svg]:text-ods-text-secondary", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-3", children: [
912
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkEXWS2QSDcjs.AccordionTrigger, { className: "w-full p-0 hover:no-underline [&>svg]:ml-auto [&>svg]:h-5 [&>svg]:w-5 [&>svg]:shrink-0 [&>svg]:text-ods-text-secondary", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-3", children: [
913
913
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
914
914
  "h3",
915
915
  {
@@ -939,7 +939,7 @@ function RoadmapGrid({
939
939
  }
940
940
  )
941
941
  ] }) }),
942
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkCWXRZVDIcjs.AccordionContent, { className: "overflow-hidden pb-0 pt-4 data-[state=closed]:animate-none data-[state=open]:animate-none", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
942
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkEXWS2QSDcjs.AccordionContent, { className: "overflow-hidden pb-0 pt-4 data-[state=closed]:animate-none data-[state=open]:animate-none", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
943
943
  RoadmapGridSingle,
944
944
  {
945
945
  items: itemsByQuarter[quarter],
@@ -991,8 +991,8 @@ function RoadmapGridSkeleton({ count = 4, showLeftMargin = true }) {
991
991
 
992
992
 
993
993
  var DEFAULT_ENDPOINT3 = "/api/roadmap";
994
- var DEFAULT_SEARCH_PARAM_KEY2 = _chunkCWXRZVDIcjs.DEV_SECTION_PARAM_KEYS.search;
995
- var DEFAULT_STATUS_PARAM_KEY2 = _chunkCWXRZVDIcjs.DEV_SECTION_PARAM_KEYS.status;
994
+ var DEFAULT_SEARCH_PARAM_KEY2 = _chunkEXWS2QSDcjs.DEV_SECTION_PARAM_KEYS.search;
995
+ var DEFAULT_STATUS_PARAM_KEY2 = _chunkEXWS2QSDcjs.DEV_SECTION_PARAM_KEYS.status;
996
996
  function RoadmapView({
997
997
  endpoint = DEFAULT_ENDPOINT3,
998
998
  initialItems,
@@ -1015,7 +1015,7 @@ function RoadmapView({
1015
1015
  const items = _nullishCoalesce(_optionalChain([data, 'optionalAccess', _22 => _22.items]), () => ( []));
1016
1016
  _chunkLNKLB7GMcjs.useScrollToHash.call(void 0, data, { headerOffset: _chunkQRA2JXE4cjs.STICKY_HEADER_OFFSET_PX });
1017
1017
  if (error) {
1018
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkCWXRZVDIcjs.LoadError, { message: "Failed to load roadmap.", onRetry: reload });
1018
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkEXWS2QSDcjs.LoadError, { message: "Failed to load roadmap.", onRetry: reload });
1019
1019
  }
1020
1020
  if (isLoading && !data) {
1021
1021
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, RoadmapGridSkeleton, { showLeftMargin });
@@ -1068,7 +1068,7 @@ function DeliveryTable({ items, isLoading = false }) {
1068
1068
  // → `#delivery-<external_id>`; `useScrollToHash` in
1069
1069
  // `delivery-lists.tsx` finds the row by id and scrolls. The
1070
1070
  // outer wrapper here ONLY exists for the row separators.
1071
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "border-b border-ods-border last:border-b-0", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk5N2ZSB42cjs.DeliveryRow, { item, id: _chunkCWXRZVDIcjs.devSectionAnchorId.call(void 0, "delivery", item.id) }) }, item.id)
1071
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "border-b border-ods-border last:border-b-0", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkWTYQCT4Acjs.DeliveryRow, { item, id: _chunkEXWS2QSDcjs.devSectionAnchorId.call(void 0, "delivery", item.id) }) }, item.id)
1072
1072
  )) }) });
1073
1073
  }
1074
1074
 
@@ -1077,8 +1077,8 @@ function DeliveryTable({ items, isLoading = false }) {
1077
1077
 
1078
1078
  var DEFAULT_COMPLETED_ENDPOINT = "/api/delivery/completed";
1079
1079
  var DEFAULT_IN_PROGRESS_ENDPOINT = "/api/delivery/in-progress";
1080
- var DEFAULT_SEARCH_PARAM_KEY3 = _chunkCWXRZVDIcjs.DEV_SECTION_PARAM_KEYS.search;
1081
- var DEFAULT_TASK_TYPE_PARAM_KEY = _chunkCWXRZVDIcjs.DEV_SECTION_PARAM_KEYS.deliveryTaskType;
1080
+ var DEFAULT_SEARCH_PARAM_KEY3 = _chunkEXWS2QSDcjs.DEV_SECTION_PARAM_KEYS.search;
1081
+ var DEFAULT_TASK_TYPE_PARAM_KEY = _chunkEXWS2QSDcjs.DEV_SECTION_PARAM_KEYS.deliveryTaskType;
1082
1082
  function DeliveryLists({
1083
1083
  completedApiEndpoint = DEFAULT_COMPLETED_ENDPOINT,
1084
1084
  inProgressApiEndpoint = DEFAULT_IN_PROGRESS_ENDPOINT,
@@ -1145,11 +1145,11 @@ function DeliveryLists({
1145
1145
  const hasActiveFilters = searchQuery !== "" || taskTypeFilter !== "all";
1146
1146
  const hasResults = showCompleted && filteredCompleted.length > 0 || showInProgress && filteredInProgress.length > 0;
1147
1147
  if (error) {
1148
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "w-full", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkCWXRZVDIcjs.LoadError, { message: error, onRetry: () => window.location.reload() }) });
1148
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "w-full", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkEXWS2QSDcjs.LoadError, { message: error, onRetry: () => window.location.reload() }) });
1149
1149
  }
1150
1150
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex w-full flex-col gap-[40px]", children: [
1151
1151
  !isLoading && !hasResults && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1152
- _chunk5N2ZSB42cjs.ListEmptyState,
1152
+ _chunkWTYQCT4Acjs.ListEmptyState,
1153
1153
  {
1154
1154
  isFiltered: hasActiveFilters,
1155
1155
  filtered: {
@@ -1303,7 +1303,7 @@ function LegalDocumentPage({
1303
1303
  initialData = null,
1304
1304
  initialLastUpdatedLabel = null,
1305
1305
  apiEndpoint,
1306
- MarkdownRenderer = _chunkCWXRZVDIcjs.RichMarkdownRenderer,
1306
+ MarkdownRenderer = _chunkEXWS2QSDcjs.RichMarkdownRenderer,
1307
1307
  backButton,
1308
1308
  shell = true
1309
1309
  }) {
@@ -1316,7 +1316,7 @@ function LegalDocumentPage({
1316
1316
  const fallbackLastUpdatedLabel = _optionalChain([data, 'optionalAccess', _27 => _27.lastSynced]) != null ? _chunk53O6PQ3Fcjs.formatLegalDate.call(void 0, data.lastSynced) : null;
1317
1317
  const effectiveLastUpdatedLabel = _nullishCoalesce(initialLastUpdatedLabel, () => ( fallbackLastUpdatedLabel));
1318
1318
  const subtitle = effectiveLastUpdatedLabel ? `Last Updated: ${effectiveLastUpdatedLabel}` : fallbackDescription;
1319
- const inner = /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkCWXRZVDIcjs.PageLayout, { title, subtitle, backButton: backCfg, titleSize: "h1", titleWrap: true, children: [
1319
+ const inner = /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkEXWS2QSDcjs.PageLayout, { title, subtitle, backButton: backCfg, titleSize: "h1", titleWrap: true, children: [
1320
1320
  _optionalChain([data, 'optionalAccess', _28 => _28.sourceFile]) && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "p", { className: "text-ods-text-secondary opacity-75 text-h6", children: [
1321
1321
  "Source: ",
1322
1322
  data.sourceFile
@@ -1356,7 +1356,7 @@ function LegalDocumentPage({
1356
1356
  ] })
1357
1357
  ] }) }) }) }) })
1358
1358
  ] });
1359
- return shell ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkCWXRZVDIcjs.PageShell, { children: inner }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "page-shell-content", children: inner });
1359
+ return shell ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkEXWS2QSDcjs.PageShell, { children: inner }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "page-shell-content", children: inner });
1360
1360
  }
1361
1361
 
1362
1362
 
@@ -1373,4 +1373,4 @@ function LegalDocumentPage({
1373
1373
 
1374
1374
 
1375
1375
  exports.ProductReleasesView = ProductReleasesView; exports.MediaGalleryStrip = MediaGalleryStrip; exports.ReleaseDetailPage = ReleaseDetailPage; exports.ReleaseDetailSkeleton = ReleaseDetailSkeleton; exports.useRoadmapVoting = useRoadmapVoting; exports.RoadmapGrid = RoadmapGrid; exports.RoadmapGridSkeleton = RoadmapGridSkeleton; exports.RoadmapView = RoadmapView; exports.DeliveryTable = DeliveryTable; exports.DeliveryLists = DeliveryLists; exports.useLegalDocs = useLegalDocs; exports.LegalDocumentPage = LegalDocumentPage;
1376
- //# sourceMappingURL=chunk-SA4KQCNB.cjs.map
1376
+ //# sourceMappingURL=chunk-7BNWMOPM.cjs.map