@flamingo-stack/openframe-frontend-core 0.0.266 → 0.0.267

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 (85) hide show
  1. package/dist/{chunk-G5GQVAPI.cjs → chunk-5QJQAFTP.cjs} +126 -57
  2. package/dist/chunk-5QJQAFTP.cjs.map +1 -0
  3. package/dist/chunk-722TTRIJ.cjs +29 -0
  4. package/dist/chunk-722TTRIJ.cjs.map +1 -0
  5. package/dist/{chunk-QXRXIHY6.cjs → chunk-AA77CC2Y.cjs} +12 -12
  6. package/dist/{chunk-QXRXIHY6.cjs.map → chunk-AA77CC2Y.cjs.map} +1 -1
  7. package/dist/{chunk-WB2JXKEI.cjs → chunk-CI5XE4LB.cjs} +37 -37
  8. package/dist/{chunk-WB2JXKEI.cjs.map → chunk-CI5XE4LB.cjs.map} +1 -1
  9. package/dist/{chunk-UUARMJJE.cjs → chunk-CICK3FUU.cjs} +100 -5
  10. package/dist/chunk-CICK3FUU.cjs.map +1 -0
  11. package/dist/{chunk-K4K4X62E.js → chunk-CYMMGPPV.js} +125 -56
  12. package/dist/chunk-CYMMGPPV.js.map +1 -0
  13. package/dist/{chunk-FRU4LQJ4.js → chunk-GJVSLS2R.js} +5 -7
  14. package/dist/{chunk-FRU4LQJ4.js.map → chunk-GJVSLS2R.js.map} +1 -1
  15. package/dist/{chunk-HTR3ARN6.js → chunk-ICOMPUMX.js} +343 -248
  16. package/dist/chunk-ICOMPUMX.js.map +1 -0
  17. package/dist/{chunk-DTJ32U7J.js → chunk-J6O57C7S.js} +8 -72
  18. package/dist/chunk-J6O57C7S.js.map +1 -0
  19. package/dist/{chunk-QAQ7AROS.cjs → chunk-O6UA475M.cjs} +302 -366
  20. package/dist/chunk-O6UA475M.cjs.map +1 -0
  21. package/dist/chunk-OO4ZGDRO.js +29 -0
  22. package/dist/chunk-OO4ZGDRO.js.map +1 -0
  23. package/dist/{chunk-TX2VP43R.js → chunk-P7JHUX4X.js} +2 -2
  24. package/dist/{chunk-NR535XIY.js → chunk-T2URJAX2.js} +66 -1
  25. package/dist/chunk-T2URJAX2.js.map +1 -0
  26. package/dist/{chunk-VYGS24RO.cjs → chunk-TPZI7NBN.cjs} +16 -18
  27. package/dist/chunk-TPZI7NBN.cjs.map +1 -0
  28. package/dist/{chunk-SRM7HE2A.js → chunk-XSK72QRR.js} +3 -3
  29. package/dist/{chunk-7VMEOFEA.cjs → chunk-Y2B6WDU6.cjs} +67 -2
  30. package/dist/chunk-Y2B6WDU6.cjs.map +1 -0
  31. package/dist/{chunk-Q52JZP3P.cjs → chunk-YV55JZAD.cjs} +27 -27
  32. package/dist/{chunk-Q52JZP3P.cjs.map → chunk-YV55JZAD.cjs.map} +1 -1
  33. package/dist/{chunk-KUYYJ2XM.js → chunk-Z6MKG53Z.js} +4 -4
  34. package/dist/components/chat/index.cjs +5 -5
  35. package/dist/components/chat/index.js +4 -4
  36. package/dist/components/contact/index.cjs +6 -6
  37. package/dist/components/contact/index.js +5 -5
  38. package/dist/components/faq/faq-section.d.ts +20 -23
  39. package/dist/components/faq/faq-section.d.ts.map +1 -1
  40. package/dist/components/faq/index.cjs +4 -4
  41. package/dist/components/faq/index.js +3 -3
  42. package/dist/components/faq-accordion.d.ts +0 -5
  43. package/dist/components/faq-accordion.d.ts.map +1 -1
  44. package/dist/components/features/index.cjs +7 -8
  45. package/dist/components/features/index.cjs.map +1 -1
  46. package/dist/components/features/index.js +6 -7
  47. package/dist/components/index.cjs +84 -84
  48. package/dist/components/index.cjs.map +1 -1
  49. package/dist/components/index.js +13 -13
  50. package/dist/components/navigation/index.cjs +5 -5
  51. package/dist/components/navigation/index.js +4 -4
  52. package/dist/components/onboarding-guides/index.cjs +24 -25
  53. package/dist/components/onboarding-guides/index.cjs.map +1 -1
  54. package/dist/components/onboarding-guides/index.js +4 -5
  55. package/dist/components/onboarding-guides/index.js.map +1 -1
  56. package/dist/components/related-content/index.cjs +6 -6
  57. package/dist/components/related-content/index.js +5 -5
  58. package/dist/components/tickets/index.cjs +69 -69
  59. package/dist/components/tickets/index.cjs.map +1 -1
  60. package/dist/components/tickets/index.js +10 -10
  61. package/dist/components/ui/index.cjs +7 -7
  62. package/dist/components/ui/index.cjs.map +1 -1
  63. package/dist/components/ui/index.js +7 -7
  64. package/dist/index.cjs +10 -10
  65. package/dist/index.cjs.map +1 -1
  66. package/dist/index.js +10 -10
  67. package/package.json +1 -1
  68. package/src/components/faq/faq-section.tsx +212 -104
  69. package/src/components/faq-accordion.tsx +1 -15
  70. package/dist/chunk-7VMEOFEA.cjs.map +0 -1
  71. package/dist/chunk-DTJ32U7J.js.map +0 -1
  72. package/dist/chunk-FREG2KXV.js +0 -128
  73. package/dist/chunk-FREG2KXV.js.map +0 -1
  74. package/dist/chunk-G5GQVAPI.cjs.map +0 -1
  75. package/dist/chunk-HTR3ARN6.js.map +0 -1
  76. package/dist/chunk-JMU4RU5J.cjs +0 -128
  77. package/dist/chunk-JMU4RU5J.cjs.map +0 -1
  78. package/dist/chunk-K4K4X62E.js.map +0 -1
  79. package/dist/chunk-NR535XIY.js.map +0 -1
  80. package/dist/chunk-QAQ7AROS.cjs.map +0 -1
  81. package/dist/chunk-UUARMJJE.cjs.map +0 -1
  82. package/dist/chunk-VYGS24RO.cjs.map +0 -1
  83. /package/dist/{chunk-TX2VP43R.js.map → chunk-P7JHUX4X.js.map} +0 -0
  84. /package/dist/{chunk-SRM7HE2A.js.map → chunk-XSK72QRR.js.map} +0 -0
  85. /package/dist/{chunk-KUYYJ2XM.js.map → chunk-Z6MKG53Z.js.map} +0 -0
@@ -6,13 +6,11 @@ var _chunkT7AJBKZPcjs = require('./chunk-T7AJBKZP.cjs');
6
6
 
7
7
 
8
8
 
9
- var _chunk7VMEOFEAcjs = require('./chunk-7VMEOFEA.cjs');
10
9
 
10
+ var _chunkY2B6WDU6cjs = require('./chunk-Y2B6WDU6.cjs');
11
11
 
12
12
 
13
-
14
-
15
- var _chunkJMU4RU5Jcjs = require('./chunk-JMU4RU5J.cjs');
13
+ var _chunk722TTRIJcjs = require('./chunk-722TTRIJ.cjs');
16
14
 
17
15
 
18
16
 
@@ -76,19 +74,9 @@ function FaqAccordion({ items, defaultOpenIds = [] }) {
76
74
  "aria-expanded": isOpen,
77
75
  className: "flex w-full items-center justify-between px-6 md:px-8 py-6 text-left focus:outline-none transition-colors cursor-pointer",
78
76
  children: [
79
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex min-w-0 flex-col items-start gap-2 pr-4", children: [
80
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h3", { children: item.question }),
81
- item.badge && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
82
- _chunkJMU4RU5Jcjs.StatusBadge,
83
- {
84
- text: item.badge.toUpperCase(),
85
- variant: "card",
86
- className: _chunkJMU4RU5Jcjs.TAG_BADGE_CLASS
87
- }
88
- )
89
- ] }),
77
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "min-w-0 pr-4", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h3", { children: item.question }) }),
90
78
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex-shrink-0", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
91
- _chunk7VMEOFEAcjs.ChevronButton,
79
+ _chunkY2B6WDU6cjs.ChevronButton,
92
80
  {
93
81
  "aria-label": isOpen ? "Collapse question" : "Expand question",
94
82
  size: "md",
@@ -115,6 +103,9 @@ function FaqAccordion({ items, defaultOpenIds = [] }) {
115
103
  }) });
116
104
  }
117
105
 
106
+ // src/components/faq/faq-section.tsx
107
+ _chunkFIG2RKZFcjs.init_cn.call(void 0, );
108
+
118
109
  // src/components/faq/json-ld.ts
119
110
  var DEFAULT_NAME = "Frequently Asked Questions";
120
111
  var DEFAULT_DESCRIPTION = "Answers to common questions.";
@@ -145,15 +136,114 @@ function buildFaqJsonLdFromFaqs(faqs, opts = {}) {
145
136
 
146
137
  var DEFAULT_HEADING_TEXT = "Frequently Asked Questions";
147
138
  function buildFaqsUrl(entityType, entityId, minResults, apiBaseUrl = "") {
148
- return _chunk7VMEOFEAcjs.buildSuggestionUrl.call(void 0, "/api/faqs", { apiBaseUrl, entityType, entityId, count: minResults });
139
+ return _chunkY2B6WDU6cjs.buildSuggestionUrl.call(void 0, "/api/faqs", { apiBaseUrl, entityType, entityId, count: minResults });
140
+ }
141
+ function sectionSlug(section) {
142
+ return "faq-" + section.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
149
143
  }
150
- function toAccordionItems(faqs) {
151
- return faqs.map((faq) => ({
152
- id: faq.id,
153
- question: faq.question,
154
- answer: faq.answer,
155
- badge: faq.section || void 0
156
- }));
144
+ function groupFaqsBySection(faqs) {
145
+ const order = [];
146
+ const byName = /* @__PURE__ */ new Map();
147
+ let uncategorized = null;
148
+ for (const faq of faqs) {
149
+ const item = { id: faq.id, question: faq.question, answer: faq.answer };
150
+ const name = _optionalChain([faq, 'access', _2 => _2.section, 'optionalAccess', _3 => _3.trim, 'call', _4 => _4()]);
151
+ if (!name) {
152
+ if (!uncategorized) uncategorized = { section: null, slug: null, items: [] };
153
+ uncategorized.items.push(item);
154
+ continue;
155
+ }
156
+ let group = byName.get(name);
157
+ if (!group) {
158
+ group = { section: name, slug: sectionSlug(name), items: [] };
159
+ byName.set(name, group);
160
+ order.push(name);
161
+ }
162
+ group.items.push(item);
163
+ }
164
+ const groups = order.map((name) => byName.get(name));
165
+ if (uncategorized) groups.push(uncategorized);
166
+ return groups;
167
+ }
168
+ var FAQ_NAV_HEADER_OFFSET = 96;
169
+ function GroupedFaqList({
170
+ groups,
171
+ categoryHeadingAs
172
+ }) {
173
+ const CategoryHeading = categoryHeadingAs;
174
+ const navGroups = _react.useMemo.call(void 0, () => groups.filter((g) => g.slug), [groups]);
175
+ const [activeSlug, setActiveSlug] = _react.useState.call(void 0, _nullishCoalesce(_optionalChain([navGroups, 'access', _5 => _5[0], 'optionalAccess', _6 => _6.slug]), () => ( null)));
176
+ const slugKey = navGroups.map((g) => g.slug).join("|");
177
+ _react.useEffect.call(void 0, () => {
178
+ if (navGroups.length < 2) return;
179
+ const tops = /* @__PURE__ */ new Map();
180
+ const observer = new IntersectionObserver(
181
+ (entries) => {
182
+ for (const entry of entries) {
183
+ const id = entry.target.id;
184
+ if (entry.isIntersecting) tops.set(id, entry.boundingClientRect.top);
185
+ else tops.delete(id);
186
+ }
187
+ let bestId = null;
188
+ let bestTop = Number.POSITIVE_INFINITY;
189
+ for (const [id, top] of tops) {
190
+ if (top < bestTop) {
191
+ bestTop = top;
192
+ bestId = id;
193
+ }
194
+ }
195
+ if (bestId) setActiveSlug(bestId);
196
+ },
197
+ { rootMargin: `-${FAQ_NAV_HEADER_OFFSET}px 0px -55% 0px`, threshold: 0 }
198
+ );
199
+ for (const group of navGroups) {
200
+ const el = group.slug ? document.getElementById(group.slug) : null;
201
+ if (el) observer.observe(el);
202
+ }
203
+ return () => observer.disconnect();
204
+ }, [slugKey]);
205
+ const handleJump = _react.useCallback.call(void 0,
206
+ (e, slug) => {
207
+ e.preventDefault();
208
+ setActiveSlug(slug);
209
+ _chunkY2B6WDU6cjs.scrollElementIntoView.call(void 0, document.getElementById(slug), {
210
+ headerOffset: FAQ_NAV_HEADER_OFFSET
211
+ });
212
+ if (typeof history !== "undefined") history.replaceState(null, "", `#${slug}`);
213
+ },
214
+ []
215
+ );
216
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "space-y-8", children: [
217
+ navGroups.length > 1 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "nav", { "aria-label": "FAQ categories", className: "flex flex-wrap gap-2", children: navGroups.map((group) => {
218
+ const isActive = group.slug === activeSlug;
219
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
220
+ "a",
221
+ {
222
+ href: `#${group.slug}`,
223
+ "aria-current": isActive ? "true" : void 0,
224
+ onClick: (e) => handleJump(e, group.slug),
225
+ className: _chunkFIG2RKZFcjs.cn.call(void 0,
226
+ "rounded-full border px-4 py-2 text-sm font-medium font-['DM_Sans'] transition-colors",
227
+ isActive ? "border-ods-text-primary bg-ods-card text-ods-text-primary" : "border-ods-border bg-ods-card text-ods-text-secondary hover:border-ods-text-secondary hover:text-ods-text-primary"
228
+ ),
229
+ children: group.section
230
+ },
231
+ group.slug
232
+ );
233
+ }) }),
234
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "space-y-10", children: groups.map((group) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
235
+ "section",
236
+ {
237
+ id: _nullishCoalesce(group.slug, () => ( void 0)),
238
+ className: "scroll-mt-24 space-y-4",
239
+ children: [
240
+ group.section && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, CategoryHeading, { className: _chunk722TTRIJcjs.SECTION_HEADING_CLASS, children: group.section }),
241
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, FaqAccordion, { items: group.items })
242
+ ]
243
+ },
244
+ _nullishCoalesce(group.slug, () => ( "faq-uncategorized"))
245
+ )) })
246
+ ] });
157
247
  }
158
248
  function FaqSkeleton() {
159
249
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "space-y-8 animate-pulse", children: [
@@ -169,7 +259,6 @@ function FaqSection({
169
259
  entityType,
170
260
  entityId,
171
261
  heading,
172
- variant,
173
262
  emitJsonLd = false,
174
263
  jsonLd,
175
264
  className,
@@ -182,45 +271,25 @@ function FaqSection({
182
271
  [initialFaqs]
183
272
  );
184
273
  const { data, isLoading, error } = _chunkT7AJBKZPcjs.useSelfFetch.call(void 0, url, { initialData });
185
- const faqs = _nullishCoalesce(_optionalChain([data, 'optionalAccess', _2 => _2.faqs]), () => ( []));
186
- const isEmbedded = variant ? variant === "embedded" : heading === null;
187
- const headingNode = heading === void 0 ? isEmbedded ? (
188
- // Embedded default is an <h2> — a host page already owns the <h1>,
189
- // and the schema/heading pair must read "Frequently Asked Questions"
190
- // (one FAQ heading per page, never tag/section names).
191
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h2", { className: _chunkJMU4RU5Jcjs.SECTION_HEADING_CLASS, children: DEFAULT_HEADING_TEXT })
192
- ) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h1", { className: _chunkJMU4RU5Jcjs.PAGE_HEADING_CLASS, children: DEFAULT_HEADING_TEXT }) : heading;
193
- if (error && isEmbedded) {
194
- return null;
195
- }
196
- if (!isLoading && !error && faqs.length === 0 && isEmbedded) {
197
- return null;
198
- }
274
+ const faqs = _nullishCoalesce(_optionalChain([data, 'optionalAccess', _7 => _7.faqs]), () => ( []));
275
+ const groups = _react.useMemo.call(void 0, () => faqs.length > 0 ? groupFaqsBySection(faqs) : [], [faqs]);
276
+ const headingNode = heading === void 0 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h2", { className: _chunk722TTRIJcjs.SECTION_HEADING_CLASS, children: DEFAULT_HEADING_TEXT }) : heading;
277
+ if (error) return null;
278
+ if (!isLoading && faqs.length === 0) return null;
199
279
  if (isLoading && faqs.length === 0) {
200
- if (isEmbedded) {
201
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, FaqSkeleton, {}) });
202
- }
203
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "main", { className: _nullishCoalesce(className, () => ( "bg-ods-bg")), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "max-w-[1920px] px-6 md:px-20 py-6 md:py-10 mx-auto", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, FaqSkeleton, {}) }) });
204
- }
205
- if (error && faqs.length === 0) {
206
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "main", { className: _nullishCoalesce(className, () => ( "bg-ods-bg flex items-center justify-center py-20")), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "text-ods-text-primary font-medium", children: "Failed to load FAQs. Please try again later." }) });
280
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, FaqSkeleton, {}) });
207
281
  }
208
282
  const schema = emitJsonLd ? buildFaqJsonLdFromFaqs(faqs, jsonLd) : null;
209
- const accordion = /* @__PURE__ */ _jsxruntime.jsx.call(void 0, FaqAccordion, { items: toAccordionItems(faqs) });
210
- const body = isEmbedded ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "section", { className: _nullishCoalesce(className, () => ( "space-y-10")), children: [
211
- headingNode,
212
- accordion
213
- ] }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "main", { className: _nullishCoalesce(className, () => ( "bg-ods-bg")), children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "max-w-[1920px] px-6 md:px-20 py-6 md:py-10 mx-auto space-y-10", children: [
214
- headingNode,
215
- accordion
216
- ] }) });
217
283
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
218
- body,
284
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "section", { className: _nullishCoalesce(className, () => ( "space-y-10")), children: [
285
+ headingNode,
286
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, GroupedFaqList, { groups, categoryHeadingAs: heading === null ? "h2" : "h3" })
287
+ ] }),
219
288
  schema && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
220
289
  "script",
221
290
  {
222
291
  type: "application/ld+json",
223
- dangerouslySetInnerHTML: { __html: _chunk7VMEOFEAcjs.serializeJsonLd.call(void 0, schema) }
292
+ dangerouslySetInnerHTML: { __html: _chunkY2B6WDU6cjs.serializeJsonLd.call(void 0, schema) }
224
293
  }
225
294
  )
226
295
  ] });
@@ -230,4 +299,4 @@ function FaqSection({
230
299
 
231
300
 
232
301
  exports.FaqAccordion = FaqAccordion; exports.FaqSection = FaqSection;
233
- //# sourceMappingURL=chunk-G5GQVAPI.cjs.map
302
+ //# sourceMappingURL=chunk-5QJQAFTP.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-5QJQAFTP.cjs","../src/components/faq/faq-section.tsx","../src/components/faq-accordion.tsx","../src/components/faq/json-ld.ts"],"names":["jsx","jsxs"],"mappings":"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","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-5QJQAFTP.cjs","sourcesContent":[null,"\"use client\"\n\nimport React, { useCallback, useEffect, useMemo, useState } from 'react'\nimport type { Faq } from '../../types/faq'\nimport { FaqAccordion, type FaqItem } from '../faq-accordion'\nimport { useSelfFetch } from '../../hooks/use-self-fetch'\nimport { buildSuggestionUrl } from '../../utils/suggestion-url'\nimport { serializeJsonLd } from '../../utils/common'\nimport { scrollElementIntoView } from '../../utils/scroll-into-view'\nimport { cn } from '../../utils/cn'\nimport { buildFaqJsonLdFromFaqs, type FaqSchemaOptions } from './json-ld'\nimport { SECTION_HEADING_CLASS } from '../layout/page-heading'\n\nexport interface FaqSectionProps {\n /**\n * SSR hydrate. When provided, the hook skips the first client fetch (per\n * useSelfFetch contract). The consuming server page resolves FAQs then drills\n * them into this prop — the lib never re-fetches what the host already gated on.\n */\n initialFaqs?: Faq[]\n /** Both required together for entity-attached FAQs; partial → bare /api/faqs. */\n entityType?: string\n entityId?: number | string\n /**\n * Heading node above the grouped list. `undefined` → default\n * `<h2>`\"Frequently Asked Questions\". `null` → no heading (the host page\n * owns the `<h1>`, as the standalone /faqs surface does). A React node lets a\n * platform drill its own <PageHeading> without the lib referencing platform\n * state. Also drives category nesting so the document outline stays correct:\n * `null` → categories render `<h2>` (directly under the page `<h1>`);\n * otherwise categories render `<h3>` beneath this heading.\n */\n heading?: React.ReactNode | null\n /** Inject FAQPage schema.org JSON-LD as a <script>. Off by default so embeds\n * don't emit duplicate schema. */\n emitJsonLd?: boolean\n /** Overrides for the JSON-LD's name/description/url. */\n jsonLd?: FaqSchemaOptions\n className?: string\n /** Maps to /api/faqs `?count=` (the 5-tier fill target). Absent → param\n * not sent (server default applies). */\n minResults?: number\n /** Fetch-URL prefix for third-party embeds / reverse proxies\n * ('' = same-origin relative). */\n apiBaseUrl?: string\n}\n\nconst DEFAULT_HEADING_TEXT = 'Frequently Asked Questions'\n\n/** URL composition shared with RelatedContentSection (`buildSuggestionUrl`)\n * — byte-identical to the historical `buildFaqsUrl` output when\n * `minResults`/`apiBaseUrl` are absent. */\nfunction buildFaqsUrl(\n entityType?: string,\n entityId?: number | string,\n minResults?: number,\n apiBaseUrl = '',\n): string {\n return buildSuggestionUrl('/api/faqs', { apiBaseUrl, entityType, entityId, count: minResults })\n}\n\n/** Stable, URL-safe anchor id for a category. Prefixed so it can't collide\n * with other in-page ids, and so a bare numeric/blank section still yields a\n * valid id. */\nfunction sectionSlug(section: string): string {\n return (\n 'faq-' +\n section\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n )\n}\n\ninterface FaqGroup {\n /** null → the uncategorized bucket: no heading, no jump pill, rendered last. */\n section: string | null\n slug: string | null\n items: FaqItem[]\n}\n\n/** Group FAQs by `faq.section`, preserving the server's first-seen\n * (display_order) order for BOTH the section order and the rows within each\n * section. The uncategorized bucket (blank/missing section) sinks to the end\n * since it renders without a heading. Items carry NO badge here — the `<h2>`\n * IS the category, so a per-row chip would be redundant. */\nfunction groupFaqsBySection(faqs: Faq[]): FaqGroup[] {\n const order: string[] = []\n const byName = new Map<string, FaqGroup>()\n let uncategorized: FaqGroup | null = null\n for (const faq of faqs) {\n const item: FaqItem = { id: faq.id, question: faq.question, answer: faq.answer }\n const name = faq.section?.trim()\n if (!name) {\n if (!uncategorized) uncategorized = { section: null, slug: null, items: [] }\n uncategorized.items.push(item)\n continue\n }\n let group = byName.get(name)\n if (!group) {\n group = { section: name, slug: sectionSlug(name), items: [] }\n byName.set(name, group)\n order.push(name)\n }\n group.items.push(item)\n }\n const groups = order.map((name) => byName.get(name)!)\n if (uncategorized) groups.push(uncategorized)\n return groups\n}\n\n/** The standard hub sticky-header height — same offset `useNavLink`'s hash\n * scroll uses, so a category jump lands below the header, not under it. */\nconst FAQ_NAV_HEADER_OFFSET = 96\n\n/**\n * Grouped FAQ layout: a category jump-nav above stacked `<h2>` category\n * sections (each its own accordion). Isolated into its own component so the\n * scroll-spy hooks only mount in grouped mode — `FaqSection`'s own hooks stay\n * unconditional.\n *\n * The pills are real `<a href=\"#slug\">` anchors (crawlable in-page links,\n * deep-linkable, work without JS); the click handler upgrades the jump to the\n * cancellation-proof `scrollElementIntoView` tween and syncs the URL hash.\n */\nfunction GroupedFaqList({\n groups,\n categoryHeadingAs,\n}: {\n groups: FaqGroup[]\n /** Heading tag for each category, so the document outline nests correctly\n * under whatever owns the heading above this block: `h2` on the standalone\n * page (the page owns the `<h1>`), `h3` beneath an embed's `<h2>` title.\n * The VISUAL is `SECTION_HEADING_CLASS` either way, so categories look\n * identical on every surface. */\n categoryHeadingAs: 'h2' | 'h3'\n}) {\n const CategoryHeading = categoryHeadingAs\n const navGroups = useMemo(() => groups.filter((g) => g.slug), [groups])\n const [activeSlug, setActiveSlug] = useState<string | null>(navGroups[0]?.slug ?? null)\n // Identity-stable key for the section set so the observer re-binds only when\n // the categories actually change (not on every parent re-render).\n const slugKey = navGroups.map((g) => g.slug).join('|')\n\n // Scroll-spy: mark the pill for the category currently at the top of the\n // viewport. rootMargin drops the trigger line just below the sticky header\n // and ignores the bottom ~55% so \"active\" is the section being read, not the\n // next one peeking in.\n useEffect(() => {\n if (navGroups.length < 2) return\n const tops = new Map<string, number>()\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n const id = (entry.target as HTMLElement).id\n if (entry.isIntersecting) tops.set(id, entry.boundingClientRect.top)\n else tops.delete(id)\n }\n let bestId: string | null = null\n let bestTop = Number.POSITIVE_INFINITY\n for (const [id, top] of tops) {\n if (top < bestTop) {\n bestTop = top\n bestId = id\n }\n }\n if (bestId) setActiveSlug(bestId)\n },\n { rootMargin: `-${FAQ_NAV_HEADER_OFFSET}px 0px -55% 0px`, threshold: 0 },\n )\n for (const group of navGroups) {\n const el = group.slug ? document.getElementById(group.slug) : null\n if (el) observer.observe(el)\n }\n return () => observer.disconnect()\n // slugKey encodes the section set; re-observe only when it changes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [slugKey])\n\n const handleJump = useCallback(\n (e: React.MouseEvent<HTMLAnchorElement>, slug: string) => {\n e.preventDefault()\n setActiveSlug(slug)\n scrollElementIntoView(document.getElementById(slug), {\n headerOffset: FAQ_NAV_HEADER_OFFSET,\n })\n if (typeof history !== 'undefined') history.replaceState(null, '', `#${slug}`)\n },\n [],\n )\n\n return (\n <div className=\"space-y-8\">\n {navGroups.length > 1 && (\n <nav aria-label=\"FAQ categories\" className=\"flex flex-wrap gap-2\">\n {navGroups.map((group) => {\n const isActive = group.slug === activeSlug\n return (\n <a\n key={group.slug}\n href={`#${group.slug}`}\n aria-current={isActive ? 'true' : undefined}\n onClick={(e) => handleJump(e, group.slug as string)}\n className={cn(\n \"rounded-full border px-4 py-2 text-sm font-medium font-['DM_Sans'] transition-colors\",\n isActive\n ? 'border-ods-text-primary bg-ods-card text-ods-text-primary'\n : 'border-ods-border bg-ods-card text-ods-text-secondary hover:border-ods-text-secondary hover:text-ods-text-primary',\n )}\n >\n {group.section}\n </a>\n )\n })}\n </nav>\n )}\n <div className=\"space-y-10\">\n {groups.map((group) => (\n <section\n key={group.slug ?? 'faq-uncategorized'}\n id={group.slug ?? undefined}\n className=\"scroll-mt-24 space-y-4\"\n >\n {group.section && (\n <CategoryHeading className={SECTION_HEADING_CLASS}>{group.section}</CategoryHeading>\n )}\n <FaqAccordion items={group.items} />\n </section>\n ))}\n </div>\n </div>\n )\n}\n\nfunction FaqSkeleton() {\n return (\n <div className=\"space-y-8 animate-pulse\">\n <div className=\"h-12 md:h-14 w-2/3 rounded bg-ods-border\" />\n <div className=\"rounded-3xl border border-ods-border overflow-hidden bg-ods-card divide-y divide-ods-border w-full\">\n {Array.from({ length: 8 }).map((_, idx) => (\n <div key={idx} className=\"flex items-center justify-between px-6 md:px-8 py-6\">\n <div className=\"h-6 w-5/6 rounded bg-ods-border\" />\n <div className=\"h-10 w-10 rounded-md bg-ods-border\" />\n </div>\n ))}\n </div>\n </div>\n )\n}\n\ninterface FaqsResponse {\n faqs: Faq[]\n}\n\n/**\n * The FAQ display surface — ONE rendering on every host: the list grouped by\n * `faq.section` (each category a heading + its own accordion, with a category\n * jump-nav once there are 2+ categories). There is no flat/ungrouped mode and\n * no page-vs-embedded shell fork; the standalone /faqs page and every embed\n * render through this single path, so they cannot drift.\n *\n * - Standalone /faqs page: pass `initialFaqs` (SSR) + `heading={null}` (the\n * page owns the <h1>) + `emitJsonLd` with `jsonLd` overrides for SEO.\n * - Per-entity embed: pass `entityType` + `entityId` (no `initialFaqs`); the\n * hook self-fetches `GET /api/faqs`, and `heading` is this block's own <h2>.\n *\n * CONTRACT: the consuming app MUST implement `GET /api/faqs`. On a fetch error\n * (or zero FAQs) the component renders nothing so the host page isn't\n * disfigured. The host always supplies the page shell — this renders a bare\n * <section>.\n */\nexport function FaqSection({\n initialFaqs,\n entityType,\n entityId,\n heading,\n emitJsonLd = false,\n jsonLd,\n className,\n minResults,\n apiBaseUrl = '',\n}: FaqSectionProps) {\n const url = buildFaqsUrl(entityType, entityId, minResults, apiBaseUrl)\n // Memoized — useSelfFetch re-syncs on [initialData]; a fresh per-render\n // wrapper object would setState-loop under re-rendering parents.\n const initialData = useMemo<FaqsResponse | undefined>(\n () => (initialFaqs ? { faqs: initialFaqs } : undefined),\n [initialFaqs],\n )\n const { data, isLoading, error } = useSelfFetch<FaqsResponse>(url, { initialData })\n\n const faqs = data?.faqs ?? []\n // Grouped before the early returns so the hook order stays stable.\n const groups = useMemo(() => (faqs.length > 0 ? groupFaqsBySection(faqs) : []), [faqs])\n\n // `undefined` → default <h2> title; `null` → the host page owns the <h1>, so\n // no title renders here. `heading === null` also makes the category headings\n // <h2> (directly under the page <h1>); otherwise they nest as <h3>.\n const headingNode =\n heading === undefined ? <h2 className={SECTION_HEADING_CLASS}>{DEFAULT_HEADING_TEXT}</h2> : heading\n\n // Degrade silently — never show an error banner or an empty section shell\n // where FAQs would be (host pages and the standalone surface both rely on it).\n if (error) return null\n if (!isLoading && faqs.length === 0) return null\n if (isLoading && faqs.length === 0) {\n return (\n <div className={className}>\n <FaqSkeleton />\n </div>\n )\n }\n\n const schema = emitJsonLd ? buildFaqJsonLdFromFaqs(faqs, jsonLd) : null\n\n return (\n <>\n <section className={className ?? 'space-y-10'}>\n {headingNode}\n <GroupedFaqList groups={groups} categoryHeadingAs={heading === null ? 'h2' : 'h3'} />\n </section>\n {schema && (\n <script\n type=\"application/ld+json\"\n // eslint-disable-next-line react/no-danger\n // serializeJsonLd, NOT raw JSON.stringify — FAQ answers are\n // admin-entered; an embedded \"</script>\" must not break the tag.\n dangerouslySetInnerHTML={{ __html: serializeJsonLd(schema) }}\n />\n )}\n </>\n )\n}\n","\"use client\"\n\nimport React, { useRef, useState, useEffect, useCallback } from 'react'\nimport { ChevronButton } from './ui/chevron-button'\nimport { cn } from \"../utils/cn\"\n\nexport interface FaqItem {\n id: number | string\n question: string\n answer: string\n}\n\ninterface FaqAccordionProps {\n items: FaqItem[]\n defaultOpenIds?: (number | string)[]\n}\n\n// Utility to measure scrollHeight outside render cycle\nconst useMeasuredHeight = (isOpen: boolean) => {\n const ref = useRef<HTMLDivElement | null>(null)\n const [maxHeight, setMaxHeight] = useState<string>('0px')\n\n const measure = useCallback(() => {\n if (ref.current) {\n const height = ref.current.scrollHeight\n setMaxHeight(`${height}px`)\n }\n }, [])\n\n // Update height only when section is open\n useEffect(() => {\n if (isOpen) {\n measure()\n } else {\n setMaxHeight('0px')\n }\n }, [isOpen, measure])\n\n return { ref, maxHeight }\n}\n\nexport function FaqAccordion({ items, defaultOpenIds = [] }: FaqAccordionProps) {\n const [openSet, setOpenSet] = useState<Set<string | number>>(new Set(defaultOpenIds))\n\n const toggle = (id: string | number) => {\n setOpenSet(prev => {\n const next = new Set(prev)\n if (next.has(id)) next.delete(id)\n else next.add(id)\n return next\n })\n }\n\n return (\n <div className=\"rounded-3xl border border-ods-border divide-y divide-ods-border bg-ods-card overflow-hidden\">\n {items.map(item => {\n const isOpen = openSet.has(item.id)\n const { ref, maxHeight } = useMeasuredHeight(isOpen)\n\n return (\n <div\n key={item.id}\n className={cn('group transition-colors hover:bg-[#1E1E1E]', isOpen ? 'bg-ods-bg' : 'bg-transparent')}\n >\n {/* Header */}\n <div\n role=\"button\"\n tabIndex={0}\n onClick={() => toggle(item.id)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n toggle(item.id);\n }\n }}\n aria-expanded={isOpen}\n className=\"flex w-full items-center justify-between px-6 md:px-8 py-6 text-left focus:outline-none transition-colors cursor-pointer\"\n >\n <div className=\"min-w-0 pr-4\">\n <h3>\n {item.question}\n </h3>\n </div>\n <div className=\"flex-shrink-0\">\n <ChevronButton\n aria-label={isOpen ? 'Collapse question' : 'Expand question'}\n size=\"md\"\n isExpanded={isOpen}\n backgroundColor=\"transparent\"\n borderColor=\"#3A3A3A\"\n />\n </div>\n </div>\n {/* Content wrapper with max-height animation */}\n <div\n style={{ maxHeight, transition: 'max-height 0.35s ease-in-out, opacity 0.35s ease-in-out', opacity: isOpen ? 1 : 0 }}\n className=\"overflow-hidden group-hover:bg-[#1E1E1E]/30\"\n >\n <div ref={ref} className=\"px-6 md:px-8 pb-6 text-ods-text-primary text-h4\">\n {item.answer}\n </div>\n </div>\n </div>\n )\n })}\n </div>\n )\n} ","/**\n * Pure FAQ JSON-LD builder. No React, no client-only deps — safe to import from\n * Server Components via the `./components/faq` subpath export. (Do NOT import\n * through the root `./components` barrel — that barrel is `\"use client\"` and\n * dragging it into a Server Component would force the client graph into the\n * server.)\n *\n * The hub used to harcode `name`/`description` here for OpenMSP; in the lib we\n * accept overrides so every embedder can supply its own platform branding.\n */\nimport type { Faq } from '../../types/faq'\n\nexport interface FaqSchemaOptions {\n name?: string\n description?: string\n url?: string\n}\n\nconst DEFAULT_NAME = 'Frequently Asked Questions'\nconst DEFAULT_DESCRIPTION =\n 'Answers to common questions.'\n\nexport function baseFaqSchema(opts: FaqSchemaOptions = {}) {\n return {\n '@context': 'https://schema.org',\n '@type': 'FAQPage',\n name: opts.name ?? DEFAULT_NAME,\n description: opts.description ?? DEFAULT_DESCRIPTION,\n ...(opts.url ? { url: opts.url } : {}),\n } as const\n}\n\nexport function buildFaqJsonLdFromFaqs(faqs: Faq[], opts: FaqSchemaOptions = {}) {\n return {\n ...baseFaqSchema(opts),\n mainEntity: faqs.map((faq) => ({\n '@type': 'Question',\n name: faq.question,\n acceptedAnswer: {\n '@type': 'Answer',\n text: faq.answer,\n },\n })),\n } as const\n}\n"]}
@@ -0,0 +1,29 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});"use client";
2
+
3
+ // src/components/layout/page-heading.tsx
4
+ var _jsxruntime = require('react/jsx-runtime');
5
+ var PAGE_HEADING_CLASS = "text-h1 text-ods-text-primary";
6
+ var SECTION_HEADING_CLASS = "text-h2 text-ods-text-primary";
7
+ var DESCRIPTION_CLASS = "mt-6 max-w-[640px] font-['DM_Sans'] text-[16px] md:text-[18px] leading-[24px] md:leading-[28px] text-ods-text-secondary";
8
+ function PageHeading({
9
+ children,
10
+ as: Tag = "h1",
11
+ description,
12
+ className,
13
+ descriptionClassName
14
+ }) {
15
+ const headingClass = className ? `${PAGE_HEADING_CLASS} ${className}` : PAGE_HEADING_CLASS;
16
+ const descClass = descriptionClassName ? `${DESCRIPTION_CLASS} ${descriptionClassName}` : DESCRIPTION_CLASS;
17
+ const hasDescription = description != null && description !== "" && typeof description !== "boolean";
18
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
19
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Tag, { className: headingClass, children }),
20
+ hasDescription && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: descClass, children: description })
21
+ ] });
22
+ }
23
+
24
+
25
+
26
+
27
+
28
+ exports.PAGE_HEADING_CLASS = PAGE_HEADING_CLASS; exports.SECTION_HEADING_CLASS = SECTION_HEADING_CLASS; exports.PageHeading = PageHeading;
29
+ //# sourceMappingURL=chunk-722TTRIJ.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-722TTRIJ.cjs","../src/components/layout/page-heading.tsx"],"names":[],"mappings":"AAAA,qFAAY;AACZ;AACA;AC6DI,+CAAA;AAnDG,IAAM,mBAAA,EAAqB,+BAAA;AAS3B,IAAM,sBAAA,EAAwB,+BAAA;AAErC,IAAM,kBAAA,EACJ,yHAAA;AAwBK,SAAS,WAAA,CAAY;AAAA,EAC1B,QAAA;AAAA,EACA,EAAA,EAAI,IAAA,EAAM,IAAA;AAAA,EACV,WAAA;AAAA,EACA,SAAA;AAAA,EACA;AACF,CAAA,EAAqB;AACnB,EAAA,MAAM,aAAA,EAAe,UAAA,EAAY,CAAA,EAAA;AACf,EAAA;AAKhB,EAAA;AAEA,EAAA;AACO,oBAAA;AACc,IAAA;AACrB,EAAA;AAEJ;AD9CoC;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-722TTRIJ.cjs","sourcesContent":[null,"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"]}
@@ -9,7 +9,7 @@
9
9
 
10
10
 
11
11
 
12
- var _chunkUUARMJJEcjs = require('./chunk-UUARMJJE.cjs');
12
+ var _chunkCICK3FUUcjs = require('./chunk-CICK3FUU.cjs');
13
13
 
14
14
 
15
15
  var _chunkDAAFVOTLcjs = require('./chunk-DAAFVOTL.cjs');
@@ -47,7 +47,7 @@ function DocSearchResultRow({
47
47
  }) {
48
48
  const docType = _optionalChain([result, 'access', _2 => _2.metadata, 'optionalAccess', _3 => _3.documentType]) || void 0;
49
49
  const sourceRepo = _optionalChain([result, 'access', _4 => _4.metadata, 'optionalAccess', _5 => _5.sourceRepo]) || void 0;
50
- const { Icon: SourceIcon, label: iconLabel } = _chunkUUARMJJEcjs.resolveSourceIcon.call(void 0, {
50
+ const { Icon: SourceIcon, label: iconLabel } = _chunkCICK3FUUcjs.resolveSourceIcon.call(void 0, {
51
51
  sourceRepo,
52
52
  documentType: docType
53
53
  });
@@ -90,7 +90,7 @@ function DocSearchBar({
90
90
  renderResult
91
91
  }) {
92
92
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
93
- _chunkUUARMJJEcjs.SearchInput,
93
+ _chunkCICK3FUUcjs.SearchInput,
94
94
  {
95
95
  placeholder,
96
96
  value: query,
@@ -199,7 +199,7 @@ function resolveSearchResultAction(result, source, runtimeMode) {
199
199
  const externalUrl = meta.externalUrl;
200
200
  if (externalUrl) {
201
201
  const targetPlatform = meta.targetPlatform;
202
- const isNewTab = _chunkUUARMJJEcjs.decideNewTab.call(void 0, {
202
+ const isNewTab = _chunkCICK3FUUcjs.decideNewTab.call(void 0, {
203
203
  href: externalUrl,
204
204
  targetPlatform,
205
205
  surface: "useUnifiedNav",
@@ -300,7 +300,7 @@ function useDocSearch(config) {
300
300
  if (_optionalChain([runtime, 'optionalAccess', _13 => _13.navigation, 'access', _14 => _14.mode]) === "embed") {
301
301
  setKeepOpen(true);
302
302
  const targetPlatform = _nullishCoalesce(_optionalChain([result, 'access', _15 => _15.metadata, 'optionalAccess', _16 => _16.targetPlatform]), () => ( null));
303
- _chunkUUARMJJEcjs.resolveExternalNavigation.call(void 0, {
303
+ _chunkCICK3FUUcjs.resolveExternalNavigation.call(void 0, {
304
304
  href: action.href,
305
305
  targetPlatform,
306
306
  runtime
@@ -309,18 +309,18 @@ function useDocSearch(config) {
309
309
  }
310
310
  if (wantsNewTab) {
311
311
  setKeepOpen(true);
312
- window.open(action.href, "_blank", _chunkUUARMJJEcjs.NEW_TAB_FEATURES);
312
+ window.open(action.href, "_blank", _chunkCICK3FUUcjs.NEW_TAB_FEATURES);
313
313
  return;
314
314
  }
315
315
  setKeepOpen(false);
316
316
  const path = baseRoute && action.href.startsWith(`${baseRoute}/`) ? action.href.slice(baseRoute.length + 1) : null;
317
317
  if (path && _optionalChain([onInPageSwap, 'optionalCall', _17 => _17(path)])) return;
318
- router.push(_chunkUUARMJJEcjs.stripSameOriginToPath.call(void 0, action.href));
318
+ router.push(_chunkCICK3FUUcjs.stripSameOriginToPath.call(void 0, action.href));
319
319
  return;
320
320
  }
321
321
  case "navigate-new-tab":
322
322
  setKeepOpen(true);
323
- window.open(action.href, "_blank", _chunkUUARMJJEcjs.NEW_TAB_FEATURES);
323
+ window.open(action.href, "_blank", _chunkCICK3FUUcjs.NEW_TAB_FEATURES);
324
324
  return;
325
325
  case "ask-ai":
326
326
  setKeepOpen(false);
@@ -358,7 +358,7 @@ function DetailPageSkeleton({
358
358
  showImageGallery = true
359
359
  } = {}) {
360
360
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
361
- _chunkUUARMJJEcjs.PageContainer,
361
+ _chunkCICK3FUUcjs.PageContainer,
362
362
  {
363
363
  as: "article",
364
364
  backgroundClassName: "bg-ods-bg",
@@ -419,7 +419,7 @@ function ArticleAuthorByline({
419
419
  }) {
420
420
  const runtime = _chunkMEAR2WHScjs.useChatRuntime.call(void 0, );
421
421
  if (!author) return null;
422
- const proxiedAvatar = avatar ? proxyImageUrl ? proxyImageUrl(avatar) : _nullishCoalesce(_chunkUUARMJJEcjs.getProxiedImageUrl.call(void 0, avatar, {
422
+ const proxiedAvatar = avatar ? proxyImageUrl ? proxyImageUrl(avatar) : _nullishCoalesce(_chunkCICK3FUUcjs.getProxiedImageUrl.call(void 0, avatar, {
423
423
  proxyPrefix: _optionalChain([runtime, 'optionalAccess', _18 => _18.endpoints, 'access', _19 => _19.imageProxyUrlPrefix]),
424
424
  skipDomains: _optionalChain([runtime, 'optionalAccess', _20 => _20.endpoints, 'access', _21 => _21.imageProxySkipDomains]),
425
425
  directHttps: true
@@ -427,7 +427,7 @@ function ArticleAuthorByline({
427
427
  const avatarSizeClass = size === "lg" ? "w-16 h-16" : "w-14 h-14";
428
428
  const avatarIconClass = size === "lg" ? "w-8 h-8" : "w-7 h-7";
429
429
  const avatarDim = size === "lg" ? 64 : 56;
430
- const formattedBio = _chunkUUARMJJEcjs.formatBioText.call(void 0, _nullishCoalesce(bio, () => ( null)));
430
+ const formattedBio = _chunkCICK3FUUcjs.formatBioText.call(void 0, _nullishCoalesce(bio, () => ( null)));
431
431
  const dateLabel = publishedAt ? formatDate(publishedAt) : "";
432
432
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
433
433
  "div",
@@ -483,4 +483,4 @@ function ArticleAuthorByline({
483
483
 
484
484
 
485
485
  exports.formatRelativePath = formatRelativePath; exports.DocSearchResultRow = DocSearchResultRow; exports.DocSearchBar = DocSearchBar; exports.mapDocSearchResults = mapDocSearchResults; exports.resolveSearchResultAction = resolveSearchResultAction; exports.useDocSearch = useDocSearch; exports.DetailPageSkeleton = DetailPageSkeleton; exports.ArticleAuthorByline = ArticleAuthorByline;
486
- //# sourceMappingURL=chunk-QXRXIHY6.cjs.map
486
+ //# sourceMappingURL=chunk-AA77CC2Y.cjs.map