@flamingo-stack/openframe-frontend-core 0.0.248 → 0.0.249

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 (100) hide show
  1. package/dist/{chunk-AGU44B7B.js → chunk-2FCXJTOV.js} +55 -357
  2. package/dist/chunk-2FCXJTOV.js.map +1 -0
  3. package/dist/{chunk-QHYCWQBH.cjs → chunk-3DZ7QYNA.cjs} +166 -15
  4. package/dist/chunk-3DZ7QYNA.cjs.map +1 -0
  5. package/dist/{chunk-4VSPLMB6.cjs → chunk-3VTHSACO.cjs} +34 -32
  6. package/dist/chunk-3VTHSACO.cjs.map +1 -0
  7. package/dist/{chunk-PMYD4WKR.cjs → chunk-4EED3YEI.cjs} +1550 -5971
  8. package/dist/chunk-4EED3YEI.cjs.map +1 -0
  9. package/dist/{chunk-RV2PYHCJ.js → chunk-4HHYUEMD.js} +3 -3
  10. package/dist/{chunk-UECRKCKH.js → chunk-4RDJZIHW.js} +115 -279
  11. package/dist/chunk-4RDJZIHW.js.map +1 -0
  12. package/dist/{chunk-672QV33W.js → chunk-5ZVPKTXM.js} +167 -16
  13. package/dist/chunk-5ZVPKTXM.js.map +1 -0
  14. package/dist/chunk-6JINAOI7.cjs +311 -0
  15. package/dist/chunk-6JINAOI7.cjs.map +1 -0
  16. package/dist/chunk-6LK4VEXE.cjs +4514 -0
  17. package/dist/chunk-6LK4VEXE.cjs.map +1 -0
  18. package/dist/chunk-DAAFVOTL.cjs +172 -0
  19. package/dist/chunk-DAAFVOTL.cjs.map +1 -0
  20. package/dist/{chunk-27VVOBDB.cjs → chunk-ELF4OOHC.cjs} +16 -180
  21. package/dist/chunk-ELF4OOHC.cjs.map +1 -0
  22. package/dist/{chunk-OZKPUBKX.cjs → chunk-LHZYI23D.cjs} +32 -30
  23. package/dist/chunk-LHZYI23D.cjs.map +1 -0
  24. package/dist/chunk-LKI3YCAX.js +4514 -0
  25. package/dist/chunk-LKI3YCAX.js.map +1 -0
  26. package/dist/{chunk-2Q5GVNXP.cjs → chunk-LZL6P7SN.cjs} +12 -12
  27. package/dist/{chunk-2Q5GVNXP.cjs.map → chunk-LZL6P7SN.cjs.map} +1 -1
  28. package/dist/chunk-M2OCXTNT.js +311 -0
  29. package/dist/chunk-M2OCXTNT.js.map +1 -0
  30. package/dist/{chunk-OTFJKBDW.js → chunk-PCXI7ZB2.js} +4928 -9349
  31. package/dist/chunk-PCXI7ZB2.js.map +1 -0
  32. package/dist/{chunk-PJT6RRSP.js → chunk-PVAYXHYF.js} +7 -5
  33. package/dist/{chunk-PJT6RRSP.js.map → chunk-PVAYXHYF.js.map} +1 -1
  34. package/dist/{chunk-GRTZ23GL.cjs → chunk-TU26IZPO.cjs} +16 -318
  35. package/dist/chunk-TU26IZPO.cjs.map +1 -0
  36. package/dist/{chunk-NFUXCXYN.js → chunk-VAKBNCV6.js} +7 -5
  37. package/dist/{chunk-NFUXCXYN.js.map → chunk-VAKBNCV6.js.map} +1 -1
  38. package/dist/chunk-ZXZJSTNV.js +172 -0
  39. package/dist/chunk-ZXZJSTNV.js.map +1 -0
  40. package/dist/components/chat/index.cjs +11 -7
  41. package/dist/components/chat/index.cjs.map +1 -1
  42. package/dist/components/chat/index.js +53 -49
  43. package/dist/components/contact/index.cjs +11 -8
  44. package/dist/components/contact/index.cjs.map +1 -1
  45. package/dist/components/contact/index.js +10 -7
  46. package/dist/components/features/index.cjs +11 -7
  47. package/dist/components/features/index.cjs.map +1 -1
  48. package/dist/components/features/index.js +26 -22
  49. package/dist/components/index.cjs +112 -109
  50. package/dist/components/index.cjs.map +1 -1
  51. package/dist/components/index.js +134 -131
  52. package/dist/components/index.js.map +1 -1
  53. package/dist/components/navigation/index.cjs +10 -7
  54. package/dist/components/navigation/index.cjs.map +1 -1
  55. package/dist/components/navigation/index.js +9 -6
  56. package/dist/components/onboarding-guides/index.cjs +43 -53
  57. package/dist/components/onboarding-guides/index.cjs.map +1 -1
  58. package/dist/components/onboarding-guides/index.js +25 -35
  59. package/dist/components/onboarding-guides/index.js.map +1 -1
  60. package/dist/components/onboarding-guides/onboarding-guides-catalog-skeleton.d.ts +11 -11
  61. package/dist/components/onboarding-guides/onboarding-guides-catalog-skeleton.d.ts.map +1 -1
  62. package/dist/components/related-content/index.cjs +11 -8
  63. package/dist/components/related-content/index.cjs.map +1 -1
  64. package/dist/components/related-content/index.js +10 -7
  65. package/dist/components/tickets/index.cjs +96 -93
  66. package/dist/components/tickets/index.cjs.map +1 -1
  67. package/dist/components/tickets/index.js +21 -18
  68. package/dist/components/tickets/index.js.map +1 -1
  69. package/dist/components/ui/file-manager/index.cjs +52 -50
  70. package/dist/components/ui/file-manager/index.cjs.map +1 -1
  71. package/dist/components/ui/file-manager/index.js +6 -4
  72. package/dist/components/ui/file-manager/index.js.map +1 -1
  73. package/dist/components/ui/index.cjs +16 -11
  74. package/dist/components/ui/index.cjs.map +1 -1
  75. package/dist/components/ui/index.js +102 -97
  76. package/dist/contexts/index.cjs +2 -2
  77. package/dist/contexts/index.js +5 -5
  78. package/dist/hooks/index.cjs +6 -4
  79. package/dist/hooks/index.cjs.map +1 -1
  80. package/dist/hooks/index.js +8 -6
  81. package/dist/index.cjs +17 -11
  82. package/dist/index.cjs.map +1 -1
  83. package/dist/index.js +157 -151
  84. package/package.json +1 -1
  85. package/src/components/onboarding-guides/onboarding-guides-catalog-skeleton.tsx +28 -33
  86. package/dist/chunk-27VVOBDB.cjs.map +0 -1
  87. package/dist/chunk-4VSPLMB6.cjs.map +0 -1
  88. package/dist/chunk-672QV33W.js.map +0 -1
  89. package/dist/chunk-AGU44B7B.js.map +0 -1
  90. package/dist/chunk-GRTZ23GL.cjs.map +0 -1
  91. package/dist/chunk-OTFJKBDW.js.map +0 -1
  92. package/dist/chunk-OZKPUBKX.cjs.map +0 -1
  93. package/dist/chunk-PMYD4WKR.cjs.map +0 -1
  94. package/dist/chunk-QHYCWQBH.cjs.map +0 -1
  95. package/dist/chunk-QWYJETDL.cjs +0 -162
  96. package/dist/chunk-QWYJETDL.cjs.map +0 -1
  97. package/dist/chunk-UECRKCKH.js.map +0 -1
  98. package/dist/chunk-WTTLEGEO.js +0 -162
  99. package/dist/chunk-WTTLEGEO.js.map +0 -1
  100. /package/dist/{chunk-RV2PYHCJ.js.map → chunk-4HHYUEMD.js.map} +0 -0
@@ -0,0 +1,4514 @@
1
+ "use client";
2
+ import {
3
+ useAutoLimitTags,
4
+ useDebounce,
5
+ useNearViewport,
6
+ useOgPlaceholder
7
+ } from "./chunk-ZXZJSTNV.js";
8
+ import {
9
+ useChatRuntime
10
+ } from "./chunk-4YKGXZ7W.js";
11
+ import {
12
+ next_image_default
13
+ } from "./chunk-LXC6P2EO.js";
14
+ import {
15
+ Button,
16
+ DropdownMenu,
17
+ DropdownMenuContent,
18
+ DropdownMenuTrigger,
19
+ SplitButton,
20
+ init_button2 as init_button
21
+ } from "./chunk-2FCXJTOV.js";
22
+ import {
23
+ init_next_link,
24
+ next_link_default
25
+ } from "./chunk-OHPI2HRK.js";
26
+ import {
27
+ ClickUpIcon,
28
+ FacebookIcon,
29
+ GitHubIcon,
30
+ HubspotIcon,
31
+ InstagramIcon,
32
+ LinkedInIcon,
33
+ OpenFrameLogo,
34
+ SlackIcon,
35
+ XLogo,
36
+ YouTubeIcon
37
+ } from "./chunk-265KKBV2.js";
38
+ import {
39
+ cn,
40
+ getBaseUrl,
41
+ init_cn
42
+ } from "./chunk-XTCBRQN2.js";
43
+ import {
44
+ AlertCircleIcon,
45
+ Chevron02DownIcon,
46
+ Chevron02LeftIcon,
47
+ Chevron02RightIcon,
48
+ Ellipsis01Icon,
49
+ PlayIcon,
50
+ XmarkCircleIcon
51
+ } from "./chunk-4WZOFD46.js";
52
+ import {
53
+ SearchIcon
54
+ } from "./chunk-6U3IUD57.js";
55
+
56
+ // src/components/ui/hidden-tags-popup.tsx
57
+ import { forwardRef } from "react";
58
+ init_cn();
59
+ import { jsx, jsxs } from "react/jsx-runtime";
60
+ var HiddenTagsPopup = forwardRef(function HiddenTagsPopup2({ items, onRemove, disabled, className, style }, ref) {
61
+ return /* @__PURE__ */ jsx(
62
+ "div",
63
+ {
64
+ ref,
65
+ style,
66
+ className: cn(
67
+ "absolute top-full left-0 mt-1 z-50 min-w-[200px]",
68
+ "bg-ods-card border border-ods-border rounded-[6px] shadow-lg",
69
+ "animate-in fade-in-0 zoom-in-95 duration-150",
70
+ className
71
+ ),
72
+ children: items.map((item) => /* @__PURE__ */ jsxs(
73
+ "div",
74
+ {
75
+ className: cn(
76
+ "flex items-center justify-between gap-3 px-3 h-11 md:h-12",
77
+ "border-b border-ods-border last:border-b-0"
78
+ ),
79
+ children: [
80
+ /* @__PURE__ */ jsx("span", { className: "text-h5 truncate uppercase text-ods-text-primary", title: typeof item.label === "string" ? item.label : void 0, children: item.label }),
81
+ !disabled && onRemove && /* @__PURE__ */ jsx(
82
+ "button",
83
+ {
84
+ type: "button",
85
+ onClick: (e) => {
86
+ e.stopPropagation();
87
+ onRemove(item.value);
88
+ },
89
+ className: "shrink-0 text-ods-text-secondary hover:text-ods-text-primary transition-colors",
90
+ "aria-label": `Remove ${String(item.label)}`,
91
+ children: /* @__PURE__ */ jsx(XmarkCircleIcon, { size: 20 })
92
+ }
93
+ )
94
+ ]
95
+ },
96
+ String(item.value)
97
+ ))
98
+ }
99
+ );
100
+ });
101
+
102
+ // src/components/ui/tag.tsx
103
+ import { cva } from "class-variance-authority";
104
+ init_cn();
105
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
106
+ var tagVariants = cva(
107
+ [
108
+ "text-h5 h-8 inline-flex items-center justify-center gap-[var(--spacing-system-xxs)] p-[var(--spacing-system-xsf)] rounded-md",
109
+ "transition-colors duration-150"
110
+ ],
111
+ {
112
+ variants: {
113
+ variant: {
114
+ primary: [
115
+ "bg-[var(--ods-open-yellow-base)] text-[var(--ods-system-greys-black)]",
116
+ "hover:bg-[var(--ods-open-yellow-hover)] active:bg-[var(--ods-open-yellow-action)]"
117
+ ],
118
+ outline: [
119
+ "bg-[var(--ods-system-greys-black)] text-[var(--ods-system-greys-white)] border border-[var(--ods-system-greys-soft-grey)]",
120
+ "hover:bg-[var(--ods-system-greys-black-hover)] hover:border-[var(--ods-system-greys-soft-grey-hover)]",
121
+ "active:bg-[var(--ods-system-greys-black-action)] active:border-[var(--ods-system-greys-soft-grey-action)]"
122
+ ],
123
+ success: [
124
+ "bg-[var(--ods-attention-green-success-secondary)] text-[var(--ods-attention-green-success)]",
125
+ "hover:bg-[#385029] active:bg-[#425a33]"
126
+ ],
127
+ warning: [
128
+ "bg-[var(--ods-attention-yellow-warning-secondary)] text-[var(--ods-attention-yellow-warning)]",
129
+ "hover:bg-[#544729] active:bg-[#5e5133]"
130
+ ],
131
+ error: [
132
+ "bg-[var(--ods-attention-red-error-secondary)] text-[var(--ods-attention-red-error)]",
133
+ "hover:bg-[#542b2b] active:bg-[#5e3535]"
134
+ ],
135
+ critical: [
136
+ "bg-[var(--ods-attention-red-error)] text-[var(--ods-attention-red-error-secondary)]",
137
+ "hover:bg-[var(--ods-attention-red-error-hover)] active:bg-[var(--ods-attention-red-error-action)]"
138
+ ],
139
+ grey: [
140
+ "bg-[var(--ods-system-greys-soft-grey)] text-[var(--ods-system-greys-grey)]",
141
+ "hover:bg-[var(--ods-system-greys-soft-grey-hover)] active:bg-[var(--ods-system-greys-soft-grey-action)]"
142
+ ],
143
+ // Matches the EntityTagBadges / StatusBadge tag skin (ods-card + ods-border,
144
+ // mono uppercase) so the tag-editor chips render identically to the public
145
+ // tag badges. Used for FilterChipData variant 'tag' (see search-input).
146
+ badge: [
147
+ "bg-ods-card text-ods-text-primary border border-ods-border font-mono uppercase tracking-wide",
148
+ "hover:border-ods-accent transition-colors"
149
+ ]
150
+ }
151
+ },
152
+ defaultVariants: {
153
+ variant: "primary"
154
+ }
155
+ }
156
+ );
157
+ var disabledTagClasses = [
158
+ "bg-[var(--ods-system-greys-soft-grey)] text-[var(--ods-system-greys-grey)]",
159
+ "border-transparent",
160
+ "cursor-not-allowed",
161
+ "pointer-events-none"
162
+ ];
163
+ function Tag({
164
+ label,
165
+ variant,
166
+ icon,
167
+ onClose,
168
+ className,
169
+ labelClassName,
170
+ disabled,
171
+ ...props
172
+ }) {
173
+ return /* @__PURE__ */ jsxs2(
174
+ "div",
175
+ {
176
+ className: cn(
177
+ tagVariants({ variant }),
178
+ disabled && disabledTagClasses,
179
+ className
180
+ ),
181
+ "aria-disabled": disabled || void 0,
182
+ ...props,
183
+ children: [
184
+ icon && /* @__PURE__ */ jsx2("span", { className: "flex items-center justify-center size-5 shrink-0", children: icon }),
185
+ /* @__PURE__ */ jsx2("span", { className: cn("truncate", labelClassName), title: typeof label === "string" ? label : void 0, children: label }),
186
+ onClose && /* @__PURE__ */ jsx2(
187
+ "button",
188
+ {
189
+ type: "button",
190
+ disabled,
191
+ onClick: (e) => {
192
+ e.stopPropagation();
193
+ onClose();
194
+ },
195
+ className: cn(
196
+ "flex items-center justify-center size-5 shrink-0 rounded-full opacity-70 transition-opacity",
197
+ disabled ? "cursor-not-allowed" : "hover:opacity-100"
198
+ ),
199
+ "aria-label": "Remove",
200
+ children: /* @__PURE__ */ jsx2(XmarkCircleIcon, { className: "size-4" })
201
+ }
202
+ )
203
+ ]
204
+ }
205
+ );
206
+ }
207
+
208
+ // src/components/ui/card.tsx
209
+ init_cn();
210
+ import * as React from "react";
211
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
212
+ var Card = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx3(
213
+ "div",
214
+ {
215
+ ref,
216
+ className: cn(
217
+ "rounded-lg border bg-card text-card-foreground shadow-sm",
218
+ className
219
+ ),
220
+ ...props
221
+ }
222
+ ));
223
+ Card.displayName = "Card";
224
+ var CardHeader = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx3(
225
+ "div",
226
+ {
227
+ ref,
228
+ className: cn("flex flex-col space-y-1.5 p-6", className),
229
+ ...props
230
+ }
231
+ ));
232
+ CardHeader.displayName = "CardHeader";
233
+ var CardTitle = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx3(
234
+ "div",
235
+ {
236
+ ref,
237
+ className: cn(
238
+ "text-2xl font-semibold leading-none tracking-tight",
239
+ className
240
+ ),
241
+ ...props
242
+ }
243
+ ));
244
+ CardTitle.displayName = "CardTitle";
245
+ var CardDescription = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx3(
246
+ "div",
247
+ {
248
+ ref,
249
+ className: cn("text-sm text-muted-foreground", className),
250
+ ...props
251
+ }
252
+ ));
253
+ CardDescription.displayName = "CardDescription";
254
+ var CardContent = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx3("div", { ref, className: cn("p-6 pt-0", className), ...props }));
255
+ CardContent.displayName = "CardContent";
256
+ var CardFooter = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx3(
257
+ "div",
258
+ {
259
+ ref,
260
+ className: cn("flex items-center p-6 pt-0", className),
261
+ ...props
262
+ }
263
+ ));
264
+ CardFooter.displayName = "CardFooter";
265
+ function CardHorizontal({ icon, title, description, className = "", borderLeft = true }) {
266
+ return /* @__PURE__ */ jsxs3(
267
+ "div",
268
+ {
269
+ className: cn(
270
+ "w-full flex flex-row items-center gap-3 md:gap-4 bg-ods-card p-4 md:p-6 min-h-[80px]",
271
+ borderLeft ? "border-l border-ods-border" : "",
272
+ className
273
+ ),
274
+ children: [
275
+ /* @__PURE__ */ jsx3("div", { className: "w-5 h-5 flex-shrink-0", children: icon }),
276
+ /* @__PURE__ */ jsxs3("div", { className: "flex flex-col min-w-0", children: [
277
+ /* @__PURE__ */ jsx3("span", { className: "font-medium text-sm md:text-sm text-ods-text-primary leading-tight mb-0.5 text-left", children: title }),
278
+ /* @__PURE__ */ jsx3("span", { className: "text-xs md:text-sm text-ods-text-secondary leading-tight text-left", children: description })
279
+ ] })
280
+ ]
281
+ }
282
+ );
283
+ }
284
+
285
+ // src/components/ui/tabs.tsx
286
+ init_cn();
287
+ import * as React2 from "react";
288
+ import * as TabsPrimitive from "@radix-ui/react-tabs";
289
+ import { jsx as jsx4 } from "react/jsx-runtime";
290
+ var Tabs = TabsPrimitive.Root;
291
+ var TabsList = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx4(
292
+ TabsPrimitive.List,
293
+ {
294
+ ref,
295
+ className: cn(
296
+ "inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
297
+ className
298
+ ),
299
+ ...props
300
+ }
301
+ ));
302
+ TabsList.displayName = TabsPrimitive.List.displayName;
303
+ var TabsTrigger = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx4(
304
+ TabsPrimitive.Trigger,
305
+ {
306
+ ref,
307
+ className: cn(
308
+ "inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
309
+ className
310
+ ),
311
+ ...props
312
+ }
313
+ ));
314
+ TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
315
+ var TabsContent = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx4(
316
+ TabsPrimitive.Content,
317
+ {
318
+ ref,
319
+ className: cn(
320
+ "mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
321
+ className
322
+ ),
323
+ ...props
324
+ }
325
+ ));
326
+ TabsContent.displayName = TabsPrimitive.Content.displayName;
327
+
328
+ // src/components/ui/status-badge.tsx
329
+ init_cn();
330
+ import { cva as cva2 } from "class-variance-authority";
331
+ import { jsx as jsx5 } from "react/jsx-runtime";
332
+ var statusBadgeVariants = cva2(
333
+ "inline-flex items-center justify-center rounded font-mono font-medium uppercase tracking-wide",
334
+ {
335
+ variants: {
336
+ variant: {
337
+ card: "px-3 py-1.5 text-sm",
338
+ button: "px-2 py-0.5 text-[10px] leading-none"
339
+ },
340
+ colorScheme: {
341
+ cyan: "bg-[var(--ods-flamingo-cyan-base)] text-ods-text-on-accent",
342
+ pink: "bg-[var(--ods-flamingo-pink-base)] text-ods-text-on-accent",
343
+ yellow: "bg-[var(--ods-flamingo-yellow-base)] text-ods-text-on-accent border border-[var(--ods-system-greys-black)]",
344
+ green: "bg-[var(--ods-flamingo-green-base)] text-ods-text-on-accent",
345
+ purple: "bg-[var(--ods-flamingo-purple-base)] text-ods-text-on-accent",
346
+ success: "bg-[var(--ods-attention-green-success-secondary)] text-[var(--ods-attention-green-success)]",
347
+ error: "bg-[var(--ods-attention-red-error-secondary)] text-[var(--ods-attention-red-error)]",
348
+ warning: "bg-[var(--ods-attention-yellow-warning-secondary)] text-[var(--ods-attention-yellow-warning)]",
349
+ default: "bg-ods-bg-secondary text-ods-text-primary",
350
+ // Border-only variants (no background) - for task type badges
351
+ accentBorder: "bg-transparent border-2 text-ods-accent border-ods-accent",
352
+ errorBorder: "bg-transparent border-2 text-[var(--ods-attention-red-error)] border-[var(--ods-attention-red-error)]",
353
+ whiteBorder: "bg-transparent border-2 text-ods-text-primary border-ods-text-primary"
354
+ }
355
+ },
356
+ defaultVariants: {
357
+ variant: "card",
358
+ colorScheme: "default"
359
+ }
360
+ }
361
+ );
362
+ function StatusBadge({
363
+ text,
364
+ variant,
365
+ colorScheme,
366
+ className,
367
+ singleLine,
368
+ ...props
369
+ }) {
370
+ const renderText = () => {
371
+ if (singleLine) return text;
372
+ if (variant === "button" && text.includes(" ")) {
373
+ const words = text.split(" ");
374
+ return /* @__PURE__ */ jsx5("span", { className: "flex flex-col items-center justify-center text-center gap-0", children: words.map((word, index) => /* @__PURE__ */ jsx5("span", { className: "block", children: word }, index)) });
375
+ }
376
+ return text;
377
+ };
378
+ return /* @__PURE__ */ jsx5(
379
+ "span",
380
+ {
381
+ className: cn(statusBadgeVariants({ variant, colorScheme }), className),
382
+ ...props,
383
+ children: renderText()
384
+ }
385
+ );
386
+ }
387
+
388
+ // src/components/ui/simple-markdown-renderer.tsx
389
+ import { useEffect, useState, useRef, useMemo, useCallback, memo } from "react";
390
+ import ReactMarkdown, { defaultUrlTransform } from "react-markdown";
391
+ import remarkGfm from "remark-gfm";
392
+ import remarkBreaks from "remark-breaks";
393
+ import rehypeHighlight from "rehype-highlight";
394
+ import rehypeRaw from "rehype-raw";
395
+ import { visit } from "unist-util-visit";
396
+ init_cn();
397
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
398
+ var EVENT_HANDLER_ATTR_RE = /^on[a-z]+$/i;
399
+ var JAVASCRIPT_URL_RE = /^[\s\x00-\x1f]*javascript:/i;
400
+ var DATA_URL_RE = /^[\s\x00-\x1f]*data:/i;
401
+ var URL_ATTRS = /* @__PURE__ */ new Set([
402
+ "href",
403
+ "src",
404
+ // `srcset` accepts `javascript:` on legacy browsers — same guard
405
+ // as the canonical hast-util-sanitize default schema's attr set.
406
+ // Multi-candidate scanning lives in `srcsetHasUnsafeCandidate` below;
407
+ // a single-URL check would miss a malicious second candidate like
408
+ // `"https://safe.png 1x, javascript:alert(1) 2x"`.
409
+ "srcset",
410
+ "formaction",
411
+ "xlink:href",
412
+ "poster",
413
+ "data",
414
+ "action",
415
+ "background"
416
+ ]);
417
+ function srcsetHasUnsafeCandidate(srcset) {
418
+ for (const candidate of srcset.split(",")) {
419
+ const url = candidate.trim().split(/\s+/)[0] ?? "";
420
+ if (JAVASCRIPT_URL_RE.test(url) || DATA_URL_RE.test(url)) return true;
421
+ }
422
+ return false;
423
+ }
424
+ var STRIP_ELEMENTS = /* @__PURE__ */ new Set([
425
+ "script",
426
+ "style",
427
+ "noscript",
428
+ "noembed",
429
+ "object",
430
+ "embed",
431
+ "applet",
432
+ "base",
433
+ "meta"
434
+ ]);
435
+ function rehypeStripUnsafe() {
436
+ return (tree) => {
437
+ visit(tree, "element", (node, index, parent) => {
438
+ const tag = String(node.tagName ?? "").toLowerCase();
439
+ if (STRIP_ELEMENTS.has(tag)) {
440
+ if (parent && typeof index === "number") {
441
+ parent.children.splice(index, 1);
442
+ return index;
443
+ }
444
+ node.children = [];
445
+ node.tagName = "span";
446
+ node.properties = {};
447
+ return;
448
+ }
449
+ if (!node.properties || typeof node.properties !== "object") return;
450
+ for (const key of Object.keys(node.properties)) {
451
+ if (EVENT_HANDLER_ATTR_RE.test(key)) {
452
+ delete node.properties[key];
453
+ continue;
454
+ }
455
+ if (URL_ATTRS.has(key.toLowerCase())) {
456
+ const raw = node.properties[key];
457
+ const v = Array.isArray(raw) ? raw[0] : raw;
458
+ if (typeof v === "string") {
459
+ const unsafe = key.toLowerCase() === "srcset" ? srcsetHasUnsafeCandidate(v) : JAVASCRIPT_URL_RE.test(v) || DATA_URL_RE.test(v);
460
+ if (unsafe) {
461
+ delete node.properties[key];
462
+ continue;
463
+ }
464
+ }
465
+ }
466
+ if (tag === "iframe" && key.toLowerCase() === "srcdoc") {
467
+ delete node.properties[key];
468
+ }
469
+ }
470
+ });
471
+ };
472
+ }
473
+ function cardAwareUrlTransform(url, key) {
474
+ if (key === "href" && typeof url === "string" && url.startsWith("card://")) return url;
475
+ return defaultUrlTransform(url);
476
+ }
477
+ var SAFE_HTML_TAGS = /* @__PURE__ */ new Set([
478
+ // Block + inline text
479
+ "a",
480
+ "abbr",
481
+ "address",
482
+ "article",
483
+ "aside",
484
+ "b",
485
+ "bdi",
486
+ "bdo",
487
+ "blockquote",
488
+ "br",
489
+ "caption",
490
+ "cite",
491
+ "code",
492
+ "col",
493
+ "colgroup",
494
+ "data",
495
+ "dd",
496
+ "del",
497
+ "details",
498
+ "dfn",
499
+ "div",
500
+ "dl",
501
+ "dt",
502
+ "em",
503
+ "figcaption",
504
+ "figure",
505
+ "footer",
506
+ "h1",
507
+ "h2",
508
+ "h3",
509
+ "h4",
510
+ "h5",
511
+ "h6",
512
+ "header",
513
+ "hgroup",
514
+ "hr",
515
+ "i",
516
+ "ins",
517
+ "kbd",
518
+ "li",
519
+ "main",
520
+ "mark",
521
+ "nav",
522
+ "ol",
523
+ "p",
524
+ "pre",
525
+ "q",
526
+ "rp",
527
+ "rt",
528
+ "ruby",
529
+ "s",
530
+ "samp",
531
+ "section",
532
+ "small",
533
+ "span",
534
+ "strong",
535
+ "sub",
536
+ "summary",
537
+ "sup",
538
+ "table",
539
+ "tbody",
540
+ "td",
541
+ "tfoot",
542
+ "th",
543
+ "thead",
544
+ "time",
545
+ "tr",
546
+ "u",
547
+ "ul",
548
+ "var",
549
+ "wbr",
550
+ // Media
551
+ "img",
552
+ "picture",
553
+ "source",
554
+ "audio",
555
+ "video",
556
+ "iframe",
557
+ "track",
558
+ // Forms (rehype-raw allows them; mostly harmless for chat output)
559
+ "button",
560
+ "input",
561
+ "label",
562
+ "select",
563
+ "option",
564
+ "optgroup",
565
+ "textarea",
566
+ "form",
567
+ "fieldset",
568
+ "legend"
569
+ ]);
570
+ var TAG_LIKE_REGEX = /<(\/?)([a-zA-Z][a-zA-Z0-9-]{0,63})((?:\s[^>]{0,4096}?)?)(\/?)>/g;
571
+ function escapeUnknownHtmlTags(text) {
572
+ if (!text || text.indexOf("<") === -1) return text;
573
+ const parts = [];
574
+ let cursor = 0;
575
+ const PROTECTED_SPAN_RE = /```[\s\S]*?```|`[^`\n]+`/g;
576
+ let span;
577
+ while ((span = PROTECTED_SPAN_RE.exec(text)) !== null) {
578
+ if (span.index > cursor) {
579
+ parts.push(escapeOutsideFences(text.slice(cursor, span.index)));
580
+ }
581
+ parts.push(span[0]);
582
+ cursor = span.index + span[0].length;
583
+ }
584
+ if (cursor < text.length) {
585
+ parts.push(escapeOutsideFences(text.slice(cursor)));
586
+ }
587
+ return parts.join("");
588
+ }
589
+ function escapeOutsideFences(segment) {
590
+ return segment.replace(TAG_LIKE_REGEX, (match, slash, tag, rest, selfClose) => {
591
+ const lower = tag.toLowerCase();
592
+ if (SAFE_HTML_TAGS.has(lower)) return match;
593
+ return `&lt;${slash}${tag}${rest}${selfClose}&gt;`;
594
+ });
595
+ }
596
+ var mermaidStyles = `
597
+ .mermaid-svg-container svg {
598
+ max-width: 100% !important;
599
+ height: auto !important;
600
+ min-height: 200px;
601
+ font-family: 'DM Sans', sans-serif !important;
602
+ font-size: 14px !important;
603
+ }
604
+ @media (min-width: 1520px) {
605
+ .mermaid-svg-container svg {
606
+ max-width: 900px !important;
607
+ max-height: 700px !important;
608
+ min-height: 300px;
609
+ font-size: 16px !important;
610
+ }
611
+ }
612
+ @media (min-width: 768px) and (max-width: 1519px) {
613
+ .mermaid-svg-container svg {
614
+ max-width: 700px !important;
615
+ max-height: 600px !important;
616
+ min-height: 250px;
617
+ font-size: 15px !important;
618
+ }
619
+ }
620
+ @media (max-width: 767px) {
621
+ .mermaid-svg-container svg {
622
+ max-width: 90vw !important;
623
+ max-height: 400px !important;
624
+ min-height: 200px;
625
+ font-size: 13px !important;
626
+ }
627
+ }
628
+ .mermaid-svg-container svg[width] { width: 100% !important; }
629
+ .mermaid-svg-container .node rect,
630
+ .mermaid-svg-container .node circle,
631
+ .mermaid-svg-container .node ellipse,
632
+ .mermaid-svg-container .node polygon { stroke-width: 2px !important; }
633
+ .mermaid-svg-container .edgePath path { stroke-width: 2px !important; }
634
+ @media (min-width: 768px) {
635
+ .mermaid-svg-container .node text,
636
+ .mermaid-svg-container .edgeLabel text { font-size: 14px !important; }
637
+ }
638
+ @media (min-width: 1520px) {
639
+ .mermaid-svg-container .node text,
640
+ .mermaid-svg-container .edgeLabel text { font-size: 16px !important; }
641
+ }
642
+ `;
643
+ var MermaidDiagram = ({ chart }) => {
644
+ const [svg, setSvg] = useState("");
645
+ const [error, setError] = useState("");
646
+ const [isLoading, setIsLoading] = useState(true);
647
+ const [mounted, setMounted] = useState(false);
648
+ useEffect(() => {
649
+ setMounted(true);
650
+ }, []);
651
+ useEffect(() => {
652
+ const renderMermaid = async () => {
653
+ try {
654
+ setIsLoading(true);
655
+ const { default: mermaid } = await import("mermaid");
656
+ mermaid.initialize({
657
+ startOnLoad: false,
658
+ theme: "dark",
659
+ themeVariables: {
660
+ primaryColor: "#FFC008",
661
+ primaryTextColor: "#FAFAFA",
662
+ primaryBorderColor: "#3A3A3A",
663
+ lineColor: "#888888",
664
+ secondaryColor: "#212121",
665
+ tertiaryColor: "#2A2A2A",
666
+ background: "transparent",
667
+ mainBkg: "transparent",
668
+ secondBkg: "transparent",
669
+ tertiaryBkg: "transparent",
670
+ cScale0: "#FFC008",
671
+ cScale1: "#4ECDC4",
672
+ cScale2: "#45B7D1",
673
+ cScale3: "#96CEB4",
674
+ cScale4: "#FFEAA7",
675
+ cScale5: "#DDA0DD",
676
+ cScale6: "#98D8C8",
677
+ cScale7: "#F7DC6F",
678
+ cScale8: "#BB8FCE",
679
+ cScale9: "#85C1E9",
680
+ taskTextColor: "#FAFAFA",
681
+ taskTextOutsideColor: "#FAFAFA",
682
+ activeTaskTextColor: "#1A1A1A",
683
+ nodeTextColor: "#FAFAFA"
684
+ },
685
+ flowchart: { useMaxWidth: true, htmlLabels: true, rankSpacing: 50, nodeSpacing: 30, curve: "basis" },
686
+ sequence: { useMaxWidth: true, width: 150 },
687
+ pie: { useMaxWidth: true, useWidth: void 0 },
688
+ fontFamily: "DM Sans, sans-serif",
689
+ fontSize: 14,
690
+ securityLevel: "loose"
691
+ });
692
+ const { svg: renderedSvg } = await mermaid.render(`mermaid-${Date.now()}`, chart);
693
+ setSvg(renderedSvg);
694
+ setIsLoading(false);
695
+ } catch (err) {
696
+ console.error("Mermaid rendering error:", err);
697
+ setError(`Failed to render diagram: ${err instanceof Error ? err.message : "Unknown error"}`);
698
+ setIsLoading(false);
699
+ }
700
+ };
701
+ if (mounted) {
702
+ renderMermaid();
703
+ }
704
+ }, [chart, mounted]);
705
+ if (error) {
706
+ return /* @__PURE__ */ jsxs4("div", { className: "error-state bg-ods-card border border-ods-border rounded-lg p-6 my-6", children: [
707
+ /* @__PURE__ */ jsx6("div", { className: "error-icon flex justify-center mb-4", children: /* @__PURE__ */ jsx6(AlertCircleIcon, { className: "w-12 h-12 text-ods-error" }) }),
708
+ /* @__PURE__ */ jsx6("div", { className: "error-title text-center font-sans font-semibold text-lg text-ods-error mb-2", children: "Diagram Error" }),
709
+ /* @__PURE__ */ jsx6("div", { className: "error-description text-center font-sans text-sm text-ods-text-secondary mb-4 break-words overflow-hidden max-w-full", children: /* @__PURE__ */ jsx6("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsx6("pre", { className: "whitespace-pre-wrap break-words text-xs", children: error }) }) })
710
+ ] });
711
+ }
712
+ if (isLoading || !svg) {
713
+ return /* @__PURE__ */ jsx6("div", { className: "skeleton-code bg-ods-card border border-ods-border rounded-lg p-6 min-h-[120px] flex items-center justify-center", children: /* @__PURE__ */ jsx6("div", { className: "animate-pulse text-ods-text-tertiary font-sans", children: isLoading ? "Loading diagram renderer..." : "Rendering diagram..." }) });
714
+ }
715
+ return /* @__PURE__ */ jsx6("div", { className: "mermaid-container rounded-lg p-4 md:p-6 lg:p-8 my-6 overflow-x-auto bg-ods-card border border-ods-border", children: /* @__PURE__ */ jsx6("div", { className: "flex justify-center items-center w-full min-h-[200px] md:min-h-[250px] lg:min-h-[300px]", children: /* @__PURE__ */ jsx6(
716
+ "div",
717
+ {
718
+ className: "mermaid-svg-container w-full flex justify-center max-w-full",
719
+ style: { fontSize: "14px" },
720
+ dangerouslySetInnerHTML: {
721
+ __html: svg.replace(
722
+ /<svg[^>]*>/,
723
+ (match) => match.replace(/width="[^"]*"/, 'width="100%"').replace(/height="[^"]*"/, 'height="auto"')
724
+ )
725
+ }
726
+ }
727
+ ) }) });
728
+ };
729
+ function extractText(node) {
730
+ if (typeof node === "string") return node;
731
+ if (Array.isArray(node)) return node.map(extractText).join("");
732
+ if (node?.props?.children) return extractText(node.props.children);
733
+ return "";
734
+ }
735
+ var TEXT_SIZE_PRESETS = {
736
+ default: {
737
+ h1: "text-heading-1",
738
+ h2: "text-heading-2",
739
+ h3: "text-2xl md:text-3xl",
740
+ h4: "text-xl",
741
+ h5: "text-lg md:text-xl",
742
+ h6: "text-base md:text-lg",
743
+ p: "md:text-h4 lg:text-h4",
744
+ li: "text-base md:text-lg",
745
+ blockquote: "text-lg",
746
+ code: "text-[14px]",
747
+ th: "text-xs md:text-sm",
748
+ td: "text-xs md:text-sm"
749
+ },
750
+ compact: {
751
+ h1: "text-heading-2",
752
+ h2: "text-heading-3",
753
+ h3: "text-xl md:text-2xl",
754
+ h4: "text-lg md:text-xl",
755
+ h5: "text-base md:text-lg",
756
+ h6: "text-sm md:text-base",
757
+ p: "text-base md:text-lg",
758
+ li: "text-base md:text-lg",
759
+ blockquote: "text-base md:text-lg",
760
+ code: "text-[13px]",
761
+ th: "text-xs md:text-sm",
762
+ td: "text-xs md:text-sm"
763
+ },
764
+ large: {
765
+ h1: "text-heading-1",
766
+ h2: "text-heading-1",
767
+ h3: "text-heading-2",
768
+ h4: "text-2xl md:text-3xl",
769
+ h5: "text-xl md:text-2xl",
770
+ h6: "text-lg md:text-xl",
771
+ p: "text-h3",
772
+ li: "text-lg md:text-xl",
773
+ blockquote: "text-xl md:text-2xl",
774
+ code: "text-[16px]",
775
+ th: "text-sm md:text-base",
776
+ td: "text-sm md:text-base"
777
+ }
778
+ };
779
+ function resolveTextSizeConfig(config) {
780
+ const defaultSizes = TEXT_SIZE_PRESETS.default;
781
+ if (!config) return defaultSizes;
782
+ if (typeof config === "string") return TEXT_SIZE_PRESETS[config];
783
+ if ("preset" in config) return { ...TEXT_SIZE_PRESETS[config.preset], ...config.overrides };
784
+ return { ...defaultSizes, ...config };
785
+ }
786
+ var SimpleMarkdownRendererImpl = ({
787
+ content,
788
+ className = "",
789
+ sectionIds,
790
+ demoteMarkdownH1ToH2 = false,
791
+ brokenLinks = [],
792
+ onInternalLinkClick,
793
+ currentPath: propCurrentPath,
794
+ onResolveLink,
795
+ preprocessContent,
796
+ componentOverrides,
797
+ textSize,
798
+ additionalRemarkPlugins
799
+ }) => {
800
+ const idCountsRef = useRef({});
801
+ const textSizes = useMemo(() => resolveTextSizeConfig(textSize), [textSize]);
802
+ const sectionIdMap = useMemo(() => {
803
+ const map = /* @__PURE__ */ new Map();
804
+ if (sectionIds) {
805
+ sectionIds.forEach((section) => {
806
+ const cleanTitle = section.title.replace(/[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]/gu, "").trim().toLowerCase();
807
+ map.set(section.title.toLowerCase(), section.id);
808
+ map.set(cleanTitle, section.id);
809
+ map.set(section.title, section.id);
810
+ });
811
+ }
812
+ return map;
813
+ }, [sectionIds]);
814
+ const generateHeadingId = useCallback((text, level) => {
815
+ if (sectionIds && (level === 1 || level === 2)) {
816
+ const variations = [
817
+ text,
818
+ text.toLowerCase(),
819
+ text.replace(/[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]/gu, "").trim(),
820
+ text.replace(/[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]/gu, "").trim().toLowerCase()
821
+ ];
822
+ for (const v of variations) {
823
+ const id = sectionIdMap.get(v);
824
+ if (id) return id;
825
+ }
826
+ }
827
+ const baseId = text.replace(/[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]/gu, "").trim().toLowerCase().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/^-+|-+$/g, "");
828
+ const cleanId = baseId || `section-${Object.keys(idCountsRef.current).length + 1}`;
829
+ if (idCountsRef.current[cleanId]) {
830
+ idCountsRef.current[cleanId]++;
831
+ return `${cleanId}-${idCountsRef.current[cleanId]}`;
832
+ }
833
+ idCountsRef.current[cleanId] = 1;
834
+ return cleanId;
835
+ }, [sectionIds, sectionIdMap]);
836
+ const processedContent = useMemo(
837
+ () => escapeUnknownHtmlTags(preprocessContent ? preprocessContent(content) : content),
838
+ [preprocessContent, content]
839
+ );
840
+ const makeHeading = useCallback(
841
+ (Tag2, level, headingClassName) => ({ children }) => {
842
+ const text = extractText(children);
843
+ const effectiveLevel = Tag2 === "h1" && demoteMarkdownH1ToH2 ? 2 : level;
844
+ const id = generateHeadingId(text, effectiveLevel);
845
+ const EffectiveTag = Tag2 === "h1" && demoteMarkdownH1ToH2 ? "h2" : Tag2;
846
+ return /* @__PURE__ */ jsx6(EffectiveTag, { id, className: headingClassName, children });
847
+ },
848
+ [generateHeadingId, demoteMarkdownH1ToH2]
849
+ );
850
+ const components = useMemo(() => ({
851
+ // --- code ---
852
+ code: ({ node, inline, className: codeClassName, children, ...props }) => {
853
+ const match = /language-(\w+)/.exec(codeClassName || "");
854
+ const language = match ? match[1] : "";
855
+ if (!inline && language === "mermaid") {
856
+ return /* @__PURE__ */ jsx6(MermaidDiagram, { chart: String(children).replace(/\n$/, "") });
857
+ }
858
+ if (!inline && match) {
859
+ return /* @__PURE__ */ jsxs4("div", { className: "code-block-container border rounded-lg my-6 overflow-hidden bg-ods-card border-ods-border", children: [
860
+ /* @__PURE__ */ jsx6("div", { className: "code-header border-b px-4 py-2 bg-ods-card border-ods-border", children: /* @__PURE__ */ jsx6("span", { className: "font-sans text-xs uppercase tracking-wide text-ods-text-tertiary", children: language || "code" }) }),
861
+ /* @__PURE__ */ jsx6("div", { className: "p-4", children: /* @__PURE__ */ jsx6("pre", { className: "overflow-x-auto", children: /* @__PURE__ */ jsx6(
862
+ "code",
863
+ {
864
+ className: cn(`language-${language} hljs`, textSizes.code),
865
+ style: {
866
+ fontFamily: "JetBrains Mono', 'SF Mono', Consolas, monospace",
867
+ background: "transparent",
868
+ color: "var(--ods-text-primary)"
869
+ },
870
+ ...props,
871
+ children
872
+ }
873
+ ) }) })
874
+ ] });
875
+ }
876
+ return /* @__PURE__ */ jsx6("code", { className: "font-mono text-[0.9em] px-1.5 py-0.5 rounded border bg-ods-card text-ods-text-primary border-ods-border", ...props, children });
877
+ },
878
+ // --- div (pass-through, overridable for embeds) ---
879
+ div: ({ node, className: divClassName, children, ...props }) => /* @__PURE__ */ jsx6("div", { className: divClassName, ...props, children }),
880
+ // --- blockquote ---
881
+ blockquote: ({ children }) => /* @__PURE__ */ jsx6("blockquote", { className: "border-l-4 border-ods-accent ml-0 pl-6 my-8 py-4 rounded-r-lg bg-ods-bg-secondary", children: /* @__PURE__ */ jsx6("div", { className: cn("font-sans leading-relaxed text-ods-text-secondary", textSizes.blockquote), children }) }),
882
+ // --- headings ---
883
+ h1: makeHeading("h1", 1, cn("font-sans font-bold mt-8 mb-4 first:mt-0 text-ods-text-primary", textSizes.h1)),
884
+ h2: makeHeading("h2", 2, cn("font-sans font-semibold mt-8 mb-4 pb-2 border-b text-ods-text-primary border-ods-border", textSizes.h2)),
885
+ h3: makeHeading("h3", 3, cn("font-sans font-semibold mt-6 mb-3 text-ods-text-primary", textSizes.h3)),
886
+ h4: makeHeading("h4", 4, cn("font-sans font-semibold mt-4 mb-2 text-ods-text-primary", textSizes.h4)),
887
+ h5: makeHeading("h5", 5, cn("font-sans font-semibold mt-3 mb-2 text-ods-text-primary", textSizes.h5)),
888
+ h6: makeHeading("h6", 6, cn("font-sans font-semibold mt-3 mb-1 text-ods-text-primary", textSizes.h6)),
889
+ // --- paragraph ---
890
+ p: ({ children }) => /* @__PURE__ */ jsx6("p", { className: cn("leading-relaxed mb-4 first:mt-0 last:mb-0 text-ods-text-primary", textSizes.p), children }),
891
+ // --- links ---
892
+ a: ({ href, children, className: linkClassName }) => {
893
+ const isBroken = brokenLinks.includes(href);
894
+ const isInternalDocLink = propCurrentPath !== void 0 && propCurrentPath !== null && href && !href.startsWith("http") && !href.startsWith("#");
895
+ if (isBroken) {
896
+ return /* @__PURE__ */ jsxs4("span", { className: "text-ods-accent cursor-not-allowed", children: [
897
+ children,
898
+ /* @__PURE__ */ jsx6("sup", { className: "ml-1 text-xs font-bold text-ods-attention-red-error", children: "[BROKEN]" })
899
+ ] });
900
+ }
901
+ if (isInternalDocLink && onInternalLinkClick) {
902
+ const currentPath = propCurrentPath ?? "";
903
+ return /* @__PURE__ */ jsx6(
904
+ "span",
905
+ {
906
+ className: "text-ods-accent no-underline relative transition-colors duration-200 hover:after:w-full after:content-[''] after:absolute after:w-0 after:h-0.5 after:-bottom-0.5 after:left-0 after:bg-ods-accent after:transition-all after:duration-300 cursor-pointer",
907
+ onClick: async (e) => {
908
+ e.preventDefault();
909
+ e.stopPropagation();
910
+ if (onResolveLink) {
911
+ try {
912
+ const result = await onResolveLink(href, currentPath);
913
+ if (result.type === "folder-no-readme" && result.action === "expand_folder") {
914
+ onInternalLinkClick(result.resolvedPath, { expandFolder: true, fromInternalLink: true });
915
+ } else if (result.type === "not-found") {
916
+ return;
917
+ } else if (result.success && result.resolvedPath) {
918
+ onInternalLinkClick(result.resolvedPath, { fromInternalLink: true });
919
+ }
920
+ } catch (error) {
921
+ console.error("Error resolving link:", error);
922
+ }
923
+ } else {
924
+ onInternalLinkClick(href, { fromInternalLink: true });
925
+ }
926
+ },
927
+ role: "link",
928
+ tabIndex: 0,
929
+ onKeyDown: (e) => {
930
+ if (e.key === "Enter" || e.key === " ") {
931
+ e.currentTarget.click();
932
+ }
933
+ },
934
+ children
935
+ }
936
+ );
937
+ }
938
+ return /* @__PURE__ */ jsx6(
939
+ "a",
940
+ {
941
+ href,
942
+ className: `text-ods-accent no-underline relative transition-colors duration-200 hover:after:w-full after:content-[''] after:absolute after:w-0 after:h-0.5 after:-bottom-0.5 after:left-0 after:bg-ods-accent after:transition-all after:duration-300 ${linkClassName || ""}`,
943
+ target: href?.startsWith("http") ? "_blank" : void 0,
944
+ rel: href?.startsWith("http") ? "noopener noreferrer" : void 0,
945
+ children
946
+ }
947
+ );
948
+ },
949
+ // --- images ---
950
+ // Inline content image renderer. Used by blog posts, docs, AND chat
951
+ // messages (where users may attach screenshots / photos via the +
952
+ // attachment button).
953
+ //
954
+ // Sizing rules (2025-2026 best practice — Claude.ai, ChatGPT,
955
+ // iMessage, Slack, Discord inline image patterns):
956
+ //
957
+ // - CAP max-width at 400px so inline images don't blow out the
958
+ // message column on wide panels. Click-to-expand opens a
959
+ // full-resolution modal for users who need detail.
960
+ // - CAP max-height at 400px so portrait-orientation images
961
+ // don't dominate vertical space (a 1000x3000 phone screenshot
962
+ // would otherwise push the next message off-screen).
963
+ // - Small images render at NATURAL pixel size — a 64x64
964
+ // thumbnail stays 64x64, not stretched to fill the column.
965
+ // - `object-contain` preserves aspect ratio when both dimensions
966
+ // are constrained (long landscape, tall portrait).
967
+ //
968
+ // Implementation: Next.js `<Image>` — the project's canonical
969
+ // image primitive. Gives us:
970
+ // - WebP/AVIF format conversion for modern browsers (smaller
971
+ // bytes for the same visual quality).
972
+ // - Responsive `srcset` via the `sizes` prop (browser picks the
973
+ // right variant for the viewport).
974
+ // - Automatic lazy-loading (`loading="lazy"` by default, mid-
975
+ // page images skipped until they near the viewport).
976
+ // - Automatic `decoding="async"` so image decode doesn't block
977
+ // paint.
978
+ //
979
+ // `width={400} height={400}` props are REQUIRED by Next.js
980
+ // `<Image>` (non-`fill` mode throws without them) but they're
981
+ // effectively a CEILING here, not the display size — the CSS
982
+ // overrides (`w-auto h-auto max-w-full max-h-[400px]`) drive
983
+ // the actual rendered size. The inline `style={{ width: 'auto',
984
+ // height: 'auto' }}` is belt-and-suspenders: Next.js Image sets
985
+ // matching HTML `width`/`height` attributes on the rendered
986
+ // `<img>` and inline style wins over both HTML attributes AND
987
+ // utility classes regardless of CSS-specificity surprises.
988
+ //
989
+ // Layout reservation trade-off: until image bytes arrive, the
990
+ // browser may reserve a placeholder box up to 400x400 (the props'
991
+ // intrinsic-ratio hint). Once loaded, the box collapses to the
992
+ // natural size if smaller. This is the standard Next.js Image
993
+ // behavior across the codebase — accepted for the optimizer +
994
+ // responsive-srcset benefits. Chat attachments hosted in side
995
+ // panels see this only on first render of a fresh attachment
996
+ // (cached re-renders pop in without a perceptible shift).
997
+ img: ({ src, alt }) => {
998
+ if (!src || typeof src !== "string" || src.trim() === "") return null;
999
+ return /* @__PURE__ */ jsx6(
1000
+ next_image_default,
1001
+ {
1002
+ src,
1003
+ alt: alt ?? "No image available",
1004
+ width: 400,
1005
+ height: 400,
1006
+ sizes: "(max-width: 400px) 100vw, 400px",
1007
+ className: "max-w-full max-h-[400px] w-auto h-auto rounded-lg object-contain",
1008
+ style: { width: "auto", height: "auto" }
1009
+ }
1010
+ );
1011
+ },
1012
+ // --- lists ---
1013
+ ul: ({ children }) => /* @__PURE__ */ jsx6("ul", { className: "list-disc list-outside my-4 ml-8 space-y-2 text-ods-text-primary", children }),
1014
+ ol: ({ children }) => /* @__PURE__ */ jsx6("ol", { className: "list-decimal list-outside my-4 ml-8 space-y-2 text-ods-text-primary", children }),
1015
+ li: ({ children }) => /* @__PURE__ */ jsx6("li", { className: cn("leading-relaxed pl-2", textSizes.li), children }),
1016
+ // --- tables ---
1017
+ table: ({ children }) => /* @__PURE__ */ jsx6("div", { className: "table-container my-6 overflow-x-auto", children: /* @__PURE__ */ jsx6("div", { className: "min-w-full border rounded-lg border-ods-border bg-ods-card", children: /* @__PURE__ */ jsx6("table", { className: "w-full table-fixed md:table-auto", children }) }) }),
1018
+ thead: ({ children }) => /* @__PURE__ */ jsx6("thead", { className: "bg-ods-bg-secondary", children }),
1019
+ th: ({ children }) => /* @__PURE__ */ jsx6("th", { className: cn("px-2 md:px-4 py-3 text-left font-semibold text-ods-accent border-r last:border-r-0 break-words border-ods-border", textSizes.th), children }),
1020
+ td: ({ children }) => /* @__PURE__ */ jsx6("td", { className: cn("px-2 md:px-4 py-3 border-r last:border-r-0 border-b break-words whitespace-normal text-ods-text-primary border-ods-border", textSizes.td), children }),
1021
+ // --- horizontal rule ---
1022
+ hr: () => /* @__PURE__ */ jsx6("hr", { className: "border-0 border-t my-8 border-ods-border" }),
1023
+ // --- merge overrides ---
1024
+ ...componentOverrides
1025
+ }), [makeHeading, brokenLinks, propCurrentPath, onInternalLinkClick, onResolveLink, componentOverrides, textSizes]);
1026
+ return /* @__PURE__ */ jsxs4("div", { className: `simple-markdown-renderer ${className}`, children: [
1027
+ /* @__PURE__ */ jsx6("style", { dangerouslySetInnerHTML: { __html: mermaidStyles } }),
1028
+ /* @__PURE__ */ jsx6("div", { className: "content-wrapper max-w-none break-words", children: /* @__PURE__ */ jsx6("article", { className: "prose prose-lg max-w-none", children: /* @__PURE__ */ jsx6(
1029
+ ReactMarkdown,
1030
+ {
1031
+ remarkPlugins: [remarkGfm, remarkBreaks, ...additionalRemarkPlugins ?? []],
1032
+ rehypePlugins: [
1033
+ // ORDER MATTERS: rehype-raw parses the raw HTML embedded
1034
+ // in the source markdown into HAST nodes; rehypeStripUnsafe
1035
+ // then walks the HAST tree and drops XSS vectors (on*
1036
+ // event handlers, javascript: URLs, script/style/iframe-srcdoc,
1037
+ // data: URIs). Reversing the order would have nothing to
1038
+ // sanitize (raw HTML would still be strings).
1039
+ rehypeRaw,
1040
+ rehypeStripUnsafe,
1041
+ [rehypeHighlight, { detect: true, ignoreMissing: true }]
1042
+ ],
1043
+ urlTransform: cardAwareUrlTransform,
1044
+ components,
1045
+ children: processedContent
1046
+ }
1047
+ ) }) })
1048
+ ] });
1049
+ };
1050
+ var SimpleMarkdownRenderer = memo(SimpleMarkdownRendererImpl);
1051
+
1052
+ // src/utils/format.ts
1053
+ function formatDate(date, options = {
1054
+ year: "numeric",
1055
+ month: "long",
1056
+ day: "numeric"
1057
+ }) {
1058
+ const dateObj = typeof date === "string" ? new Date(date) : date;
1059
+ if (isNaN(dateObj.getTime())) {
1060
+ console.warn("Invalid date provided to formatDate:", date);
1061
+ return "Invalid Date";
1062
+ }
1063
+ return dateObj.toLocaleDateString("en-US", options);
1064
+ }
1065
+ function formatNumber(num) {
1066
+ return num.toLocaleString();
1067
+ }
1068
+ function formatPrice(price, currency = "USD") {
1069
+ return new Intl.NumberFormat("en-US", {
1070
+ style: "currency",
1071
+ currency
1072
+ }).format(price);
1073
+ }
1074
+ function formatBytes(bytes, decimals = 2) {
1075
+ if (bytes === 0) return "0 Bytes";
1076
+ const k = 1024;
1077
+ const dm = decimals < 0 ? 0 : decimals;
1078
+ const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
1079
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
1080
+ return Number.parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
1081
+ }
1082
+ function formatBytesShort(bytes) {
1083
+ if (bytes === 0) return "0 B";
1084
+ const k = 1024;
1085
+ const sizes = ["B", "KB", "MB", "GB", "TB"];
1086
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
1087
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
1088
+ }
1089
+ function formatLargeNumber(num) {
1090
+ if (num === 0) return "0";
1091
+ const isNegative = num < 0;
1092
+ const absNum = Math.abs(num);
1093
+ let result;
1094
+ if (absNum >= 1e9) {
1095
+ result = `${Math.floor(absNum / 1e9)}B`;
1096
+ } else if (absNum >= 1e6) {
1097
+ result = `${Math.floor(absNum / 1e6)}M`;
1098
+ } else if (absNum >= 1e3) {
1099
+ result = `${Math.floor(absNum / 1e3)}K`;
1100
+ } else {
1101
+ result = Math.floor(absNum).toString();
1102
+ }
1103
+ return isNegative ? `-${result}` : result;
1104
+ }
1105
+ function formatAbbreviatedNumber(n) {
1106
+ if (n >= 1e9) {
1107
+ const value = n / 1e9;
1108
+ return `${Number.isInteger(value) ? value.toFixed(0) : value.toFixed(1)}B`;
1109
+ }
1110
+ if (n >= 1e6) {
1111
+ const value = n / 1e6;
1112
+ return `${Number.isInteger(value) ? value.toFixed(0) : value.toFixed(1)}M`;
1113
+ }
1114
+ if (n >= 1e3) {
1115
+ const value = n / 1e3;
1116
+ return `${Number.isInteger(value) ? value.toFixed(0) : value.toFixed(1)}K`;
1117
+ }
1118
+ return n.toLocaleString();
1119
+ }
1120
+ function getFirstLastInitials(name) {
1121
+ if (!name) return "";
1122
+ const words = name.trim().split(/\s+/);
1123
+ if (words.length === 0 || !words[0]) return "";
1124
+ if (words.length === 1) return words[0].charAt(0).toUpperCase();
1125
+ return (words[0].charAt(0) + words[words.length - 1].charAt(0)).toUpperCase();
1126
+ }
1127
+ function nameInitials(name, fallback = "E") {
1128
+ const source = typeof name === "string" ? name.trim() : "";
1129
+ const words = source.length > 0 ? source.split(/\s+/) : [];
1130
+ const letters = words.map((w) => w[0]).filter(Boolean).slice(0, 2).join("");
1131
+ return (letters || fallback).toUpperCase();
1132
+ }
1133
+ function formatDurationMMSS(seconds) {
1134
+ if (!seconds) return "";
1135
+ const hours = Math.floor(seconds / 3600);
1136
+ const minutes = Math.floor(seconds % 3600 / 60);
1137
+ const secs = Math.floor(seconds % 60);
1138
+ if (hours > 0) {
1139
+ return `${hours}:${minutes.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`;
1140
+ }
1141
+ return `${minutes}:${secs.toString().padStart(2, "0")}`;
1142
+ }
1143
+ function formatDurationCompact(seconds) {
1144
+ if (!seconds) return "";
1145
+ const hours = Math.floor(seconds / 3600);
1146
+ const minutes = Math.floor(seconds % 3600 / 60);
1147
+ if (hours > 0) {
1148
+ return `${hours}h ${minutes}m`;
1149
+ }
1150
+ return `${minutes} min`;
1151
+ }
1152
+ function formatTimeWithTimezone(date, timezone) {
1153
+ if (!date) return "";
1154
+ const dateObj = typeof date === "string" ? new Date(date) : date;
1155
+ const timeStr = dateObj.toLocaleTimeString("en-US", {
1156
+ hour: "numeric",
1157
+ minute: "2-digit",
1158
+ hour12: true
1159
+ });
1160
+ return timezone ? `${timeStr} ${timezone}` : timeStr;
1161
+ }
1162
+ function formatDurationFromRange(startAt, endAt) {
1163
+ if (!startAt || !endAt) return "";
1164
+ const start = typeof startAt === "string" ? new Date(startAt) : startAt;
1165
+ const end = typeof endAt === "string" ? new Date(endAt) : endAt;
1166
+ const durationMs = end.getTime() - start.getTime();
1167
+ const minutes = Math.round(durationMs / 6e4);
1168
+ if (minutes >= 60) {
1169
+ const hours = Math.floor(minutes / 60);
1170
+ const mins = minutes % 60;
1171
+ return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`;
1172
+ }
1173
+ return `${minutes}m`;
1174
+ }
1175
+ function formatDateUTC(value, options = {}) {
1176
+ const { fallback = "N/A", timezone = "UTC" } = options;
1177
+ if (value === null || value === void 0 || value === "") return fallback;
1178
+ let ms;
1179
+ if (typeof value === "number") {
1180
+ ms = value;
1181
+ } else {
1182
+ const n = Number(value);
1183
+ if (Number.isFinite(n) && n > 0 && /^-?\d+(\.\d+)?$/.test(value.trim())) {
1184
+ ms = n;
1185
+ } else {
1186
+ ms = Date.parse(value.includes("T") ? value : value + "T00:00:00Z");
1187
+ }
1188
+ }
1189
+ if (!Number.isFinite(ms)) return fallback;
1190
+ return new Date(ms).toLocaleDateString("en-US", {
1191
+ year: "numeric",
1192
+ month: "short",
1193
+ day: "numeric",
1194
+ timeZone: timezone === "local" ? void 0 : "UTC"
1195
+ });
1196
+ }
1197
+ function formatLegalDate(dateInput) {
1198
+ return new Intl.DateTimeFormat("en-US", {
1199
+ year: "numeric",
1200
+ month: "2-digit",
1201
+ day: "2-digit"
1202
+ }).format(new Date(dateInput));
1203
+ }
1204
+ function formatCurrency(value) {
1205
+ if (value == null) return "N/A";
1206
+ return new Intl.NumberFormat("en-US", {
1207
+ style: "currency",
1208
+ currency: "USD",
1209
+ maximumFractionDigits: 0
1210
+ }).format(value);
1211
+ }
1212
+ function formatPercent(value) {
1213
+ if (value == null) return "N/A";
1214
+ return `${value.toFixed(2)}%`;
1215
+ }
1216
+ function formatWholeDollars(price, currency = "USD") {
1217
+ return new Intl.NumberFormat("en-US", {
1218
+ style: "currency",
1219
+ currency,
1220
+ maximumFractionDigits: 0
1221
+ }).format(price);
1222
+ }
1223
+ function formatCompactMetric(value, format = "number", options) {
1224
+ if (value === 0 || value === null || value === void 0) {
1225
+ if (format === "currency") return `${options?.prefix || "$"}0`;
1226
+ if (format === "percentage") return "0%";
1227
+ if (format === "months") return `0 ${options?.suffix || "months"}`;
1228
+ return "0";
1229
+ }
1230
+ const absValue = Math.abs(value);
1231
+ const sign = value < 0 ? "-" : "";
1232
+ if (format === "currency") {
1233
+ const prefix = options?.prefix || "$";
1234
+ const compact = (val, divisor, suffix) => {
1235
+ const divided = val / divisor;
1236
+ const formatted = divided % 1 === 0 ? divided.toFixed(0) : divided.toFixed(1);
1237
+ return `${sign}${prefix}${formatted}${suffix}`;
1238
+ };
1239
+ if (absValue >= 1e9) return compact(absValue, 1e9, "B");
1240
+ if (absValue >= 1e6) return compact(absValue, 1e6, "M");
1241
+ if (absValue >= 1e3) return `${sign}${prefix}${(absValue / 1e3).toFixed(0)}K`;
1242
+ return `${sign}${prefix}${absValue.toLocaleString()}`;
1243
+ }
1244
+ if (format === "percentage") {
1245
+ return `${sign}${absValue}%`;
1246
+ }
1247
+ if (format === "months") {
1248
+ const rounded = Math.round(value * 10) / 10;
1249
+ return `${rounded} ${options?.suffix || "months"}`;
1250
+ }
1251
+ if (absValue >= 1e6) return `${sign}${(absValue / 1e6).toFixed(1)}M`;
1252
+ if (absValue >= 1e3) return `${sign}${(absValue / 1e3).toFixed(1)}K`;
1253
+ return value.toLocaleString();
1254
+ }
1255
+ function getTrendColors(direction, polarity = "positive") {
1256
+ if (direction === "neutral" || polarity === "neutral") {
1257
+ return {
1258
+ textClass: "text-ods-text-secondary",
1259
+ badgeClass: "bg-ods-border text-ods-text-secondary"
1260
+ };
1261
+ }
1262
+ const isPositiveOutcome = direction === "up" && polarity === "positive" || direction === "down" && polarity === "negative";
1263
+ if (isPositiveOutcome) {
1264
+ return {
1265
+ textClass: "text-ods-success",
1266
+ badgeClass: "bg-ods-success-secondary text-ods-success"
1267
+ };
1268
+ }
1269
+ return {
1270
+ textClass: "text-ods-error",
1271
+ badgeClass: "bg-ods-error-secondary text-ods-error"
1272
+ };
1273
+ }
1274
+ function formatDateRange(start, end) {
1275
+ if (!start || !end) return "";
1276
+ const fmt = (s) => {
1277
+ const bareMatch = /^(\d{4})-(\d{2})-(\d{2})$/.exec(s);
1278
+ const d = bareMatch ? new Date(Number(bareMatch[1]), Number(bareMatch[2]) - 1, Number(bareMatch[3])) : new Date(s);
1279
+ if (Number.isNaN(d.getTime())) return s;
1280
+ return d.toLocaleDateString(void 0, {
1281
+ month: "short",
1282
+ day: "numeric",
1283
+ year: "numeric"
1284
+ });
1285
+ };
1286
+ return `${fmt(start)} \u2014 ${fmt(end)}`;
1287
+ }
1288
+ function formatDateTimeAt(dateString) {
1289
+ const date = new Date(dateString);
1290
+ const dateStr = date.toLocaleDateString("en-US", {
1291
+ month: "short",
1292
+ day: "numeric",
1293
+ year: "numeric"
1294
+ });
1295
+ const timeStr = date.toLocaleTimeString("en-US", {
1296
+ hour: "numeric",
1297
+ minute: "2-digit",
1298
+ hour12: true
1299
+ });
1300
+ return `${dateStr} at ${timeStr}`;
1301
+ }
1302
+ function formatDurationFromMs(ms) {
1303
+ if (!ms || isNaN(ms) || ms < 0) return "0ms";
1304
+ if (ms < 1e3) return `${ms}ms`;
1305
+ if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
1306
+ return `${(ms / 6e4).toFixed(1)}m`;
1307
+ }
1308
+ function formatDuration(seconds) {
1309
+ if (seconds < 60) return `${seconds} seconds`;
1310
+ if (seconds < 3600) return `${Math.floor(seconds / 60)} minutes`;
1311
+ const hours = Math.floor(seconds / 3600);
1312
+ const minutes = Math.floor(seconds % 3600 / 60);
1313
+ if (minutes === 0) return `${hours} hour${hours > 1 ? "s" : ""}`;
1314
+ return `${hours} hour${hours > 1 ? "s" : ""} ${minutes} minute${minutes > 1 ? "s" : ""}`;
1315
+ }
1316
+ function formatUnderscoreText(text) {
1317
+ return text.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
1318
+ }
1319
+ function stripHtml(html) {
1320
+ let noTags = html;
1321
+ let prev;
1322
+ do {
1323
+ prev = noTags;
1324
+ noTags = noTags.replace(/<[^<>]*>/g, "");
1325
+ } while (noTags !== prev);
1326
+ return noTags.replace(/&nbsp;/g, " ").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&#x27;/g, "'").replace(/&apos;/g, "'").replace(/&amp;/g, "&").replace(/\s+/g, " ").trim();
1327
+ }
1328
+ function formatClassification(classification) {
1329
+ const customMappings = {
1330
+ openframe_selected: "OpenFrame Selected"
1331
+ };
1332
+ return customMappings[classification] || formatUnderscoreText(classification);
1333
+ }
1334
+ function formatPricingModel(pricingModel) {
1335
+ const customMappings = {
1336
+ one_time: "One-time Purchase",
1337
+ self_hosted: "Self-hosted"
1338
+ };
1339
+ return customMappings[pricingModel] || formatUnderscoreText(pricingModel);
1340
+ }
1341
+ function formatBioText(aboutHtml, fallback = "") {
1342
+ if (!aboutHtml || !aboutHtml.trim()) return fallback;
1343
+ if (aboutHtml.includes("<p")) {
1344
+ const paragraphs = aboutHtml.split(/<p[^<>]*>/).slice(1).map((part) => part.split("</p>")[0]).map((text) => stripHtml(text).trim()).filter((text) => text.length > 0);
1345
+ if (paragraphs.length > 0) return paragraphs.join(" ");
1346
+ }
1347
+ return stripHtml(aboutHtml).trim() || fallback;
1348
+ }
1349
+
1350
+ // src/components/ui/square-avatar.tsx
1351
+ import * as React4 from "react";
1352
+ init_cn();
1353
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1354
+ var SquareAvatar = React4.memo(React4.forwardRef(
1355
+ ({ className, src, alt, size = "md", fallback, variant = "square", initialsClassName, ...props }, ref) => {
1356
+ const sizeClasses = {
1357
+ sm: "h-8 w-8",
1358
+ md: "h-10 w-10",
1359
+ lg: "h-12 w-12",
1360
+ xl: "h-16 w-16"
1361
+ };
1362
+ const sizePx = {
1363
+ sm: 32,
1364
+ md: 40,
1365
+ lg: 48,
1366
+ xl: 64
1367
+ };
1368
+ const variantClasses = {
1369
+ square: "rounded-md",
1370
+ round: "rounded-full"
1371
+ };
1372
+ return /* @__PURE__ */ jsxs5(
1373
+ "div",
1374
+ {
1375
+ className: cn(
1376
+ "relative flex items-center justify-center shrink-0 overflow-hidden border border-ods-border bg-ods-bg",
1377
+ sizeClasses[size],
1378
+ variantClasses[variant],
1379
+ className
1380
+ ),
1381
+ ref,
1382
+ ...props,
1383
+ children: [
1384
+ /* @__PURE__ */ jsx7("div", { className: cn(
1385
+ // Initials use `text-ods-text-primary` so they stay readable
1386
+ // both on the default `bg-ods-bg` AND on accent-fill backgrounds
1387
+ // (`bg-ods-flamingo-pink` for the current user, `bg-ods-flamingo-cyan`
1388
+ // for Mingo). With `text-ods-text-secondary` (#888) on a pink fill
1389
+ // (#F357BB) the initials clocked ~2.0:1 contrast — failing WCAG AA
1390
+ // and visually rendering as a blank colored dot. Primary text
1391
+ // (#fafafa) on the same pink yields ~3.5:1 (AA Large) and on the
1392
+ // dark default bg yields ~15:1.
1393
+ "flex items-center justify-center text-xs font-medium text-ods-text-primary",
1394
+ initialsClassName,
1395
+ src && "hidden"
1396
+ ), children: getFirstLastInitials(fallback || alt) || "?" }),
1397
+ src && /* @__PURE__ */ jsx7(
1398
+ next_image_default,
1399
+ {
1400
+ className: "absolute inset-0 h-full w-full object-cover",
1401
+ src,
1402
+ alt: alt || "",
1403
+ width: sizePx[size],
1404
+ height: sizePx[size],
1405
+ onError: (e) => {
1406
+ e.currentTarget.style.display = "none";
1407
+ const el = e.currentTarget.previousElementSibling;
1408
+ if (el) el.classList.remove("hidden");
1409
+ }
1410
+ }
1411
+ )
1412
+ ]
1413
+ }
1414
+ );
1415
+ }
1416
+ ));
1417
+ SquareAvatar.displayName = "SquareAvatar";
1418
+
1419
+ // src/components/ui/actions-menu.tsx
1420
+ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
1421
+
1422
+ // node_modules/@radix-ui/react-use-controllable-state/dist/index.mjs
1423
+ import * as React6 from "react";
1424
+
1425
+ // node_modules/@radix-ui/react-use-layout-effect/dist/index.mjs
1426
+ import * as React5 from "react";
1427
+ var useLayoutEffect2 = globalThis?.document ? React5.useLayoutEffect : () => {
1428
+ };
1429
+
1430
+ // node_modules/@radix-ui/react-use-controllable-state/dist/index.mjs
1431
+ import * as React22 from "react";
1432
+ var useInsertionEffect = React6[" useInsertionEffect ".trim().toString()] || useLayoutEffect2;
1433
+ function useControllableState({
1434
+ prop,
1435
+ defaultProp,
1436
+ onChange = () => {
1437
+ },
1438
+ caller
1439
+ }) {
1440
+ const [uncontrolledProp, setUncontrolledProp, onChangeRef] = useUncontrolledState({
1441
+ defaultProp,
1442
+ onChange
1443
+ });
1444
+ const isControlled = prop !== void 0;
1445
+ const value = isControlled ? prop : uncontrolledProp;
1446
+ if (true) {
1447
+ const isControlledRef = React6.useRef(prop !== void 0);
1448
+ React6.useEffect(() => {
1449
+ const wasControlled = isControlledRef.current;
1450
+ if (wasControlled !== isControlled) {
1451
+ const from = wasControlled ? "controlled" : "uncontrolled";
1452
+ const to = isControlled ? "controlled" : "uncontrolled";
1453
+ console.warn(
1454
+ `${caller} is changing from ${from} to ${to}. Components should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled value for the lifetime of the component.`
1455
+ );
1456
+ }
1457
+ isControlledRef.current = isControlled;
1458
+ }, [isControlled, caller]);
1459
+ }
1460
+ const setValue = React6.useCallback(
1461
+ (nextValue) => {
1462
+ if (isControlled) {
1463
+ const value2 = isFunction(nextValue) ? nextValue(prop) : nextValue;
1464
+ if (value2 !== prop) {
1465
+ onChangeRef.current?.(value2);
1466
+ }
1467
+ } else {
1468
+ setUncontrolledProp(nextValue);
1469
+ }
1470
+ },
1471
+ [isControlled, prop, setUncontrolledProp, onChangeRef]
1472
+ );
1473
+ return [value, setValue];
1474
+ }
1475
+ function useUncontrolledState({
1476
+ defaultProp,
1477
+ onChange
1478
+ }) {
1479
+ const [value, setValue] = React6.useState(defaultProp);
1480
+ const prevValueRef = React6.useRef(value);
1481
+ const onChangeRef = React6.useRef(onChange);
1482
+ useInsertionEffect(() => {
1483
+ onChangeRef.current = onChange;
1484
+ }, [onChange]);
1485
+ React6.useEffect(() => {
1486
+ if (prevValueRef.current !== value) {
1487
+ onChangeRef.current?.(value);
1488
+ prevValueRef.current = value;
1489
+ }
1490
+ }, [value, prevValueRef]);
1491
+ return [value, setValue, onChangeRef];
1492
+ }
1493
+ function isFunction(value) {
1494
+ return typeof value === "function";
1495
+ }
1496
+
1497
+ // src/components/ui/actions-menu.tsx
1498
+ init_next_link();
1499
+ import { Check } from "lucide-react";
1500
+ import React7, { useCallback as useCallback3 } from "react";
1501
+ init_cn();
1502
+ init_button();
1503
+ import { Fragment, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
1504
+ var ROW_CLASSES = "flex flex-1 min-w-0 items-center gap-[var(--spacing-system-xsf)] p-[var(--spacing-system-s)] cursor-pointer transition-colors bg-ods-bg outline-none";
1505
+ var WRAPPER_CLASSES = "relative flex items-stretch border-b border-ods-border last:border-b-0";
1506
+ var SECONDARY_ACTION_CLASSES = "flex p-[var(--spacing-system-s)] shrink-0 items-center justify-center self-stretch border-l border-ods-border transition-colors hover:bg-ods-bg-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ods-focus [&_svg]:w-4 [&_svg]:h-4 md:[&_svg]:w-6 md:[&_svg]:h-6";
1507
+ var SecondaryAction = ({ action }) => {
1508
+ const handleClick = useCallback3(
1509
+ (e) => {
1510
+ e.stopPropagation();
1511
+ if (action.disabled) {
1512
+ e.preventDefault();
1513
+ return;
1514
+ }
1515
+ action.onClick?.();
1516
+ },
1517
+ [action]
1518
+ );
1519
+ const classes = cn(
1520
+ SECONDARY_ACTION_CLASSES,
1521
+ action.disabled && "cursor-not-allowed opacity-60 pointer-events-none"
1522
+ );
1523
+ if (action.href) {
1524
+ return /* @__PURE__ */ jsx8(
1525
+ next_link_default,
1526
+ {
1527
+ href: action.href,
1528
+ prefetch: false,
1529
+ target: action.openInNewTab ? "_blank" : void 0,
1530
+ rel: action.openInNewTab ? "noopener noreferrer" : void 0,
1531
+ "aria-label": action["aria-label"],
1532
+ "aria-disabled": action.disabled || void 0,
1533
+ tabIndex: action.disabled ? -1 : void 0,
1534
+ className: classes,
1535
+ onClick: handleClick,
1536
+ children: action.icon
1537
+ }
1538
+ );
1539
+ }
1540
+ return /* @__PURE__ */ jsx8(
1541
+ "button",
1542
+ {
1543
+ type: "button",
1544
+ "aria-label": action["aria-label"],
1545
+ disabled: action.disabled,
1546
+ className: classes,
1547
+ onClick: handleClick,
1548
+ children: action.icon
1549
+ }
1550
+ );
1551
+ };
1552
+ var MenuItem = ({ item, onItemClick }) => {
1553
+ const activate = useCallback3(() => {
1554
+ if (item.disabled) return;
1555
+ if (item.type === "checkbox") {
1556
+ item.onClick?.();
1557
+ onItemClick?.(item);
1558
+ return;
1559
+ }
1560
+ if (item.type === "submenu") return;
1561
+ item.onClick?.();
1562
+ onItemClick?.(item);
1563
+ }, [item, onItemClick]);
1564
+ const handleClick = useCallback3(
1565
+ (e) => {
1566
+ e.stopPropagation();
1567
+ e.preventDefault();
1568
+ activate();
1569
+ },
1570
+ [activate]
1571
+ );
1572
+ const handleKeyDown = useCallback3(
1573
+ (e) => {
1574
+ if (e.key !== "Enter" && e.key !== " ") return;
1575
+ e.preventDefault();
1576
+ e.stopPropagation();
1577
+ activate();
1578
+ },
1579
+ [activate]
1580
+ );
1581
+ const handleLinkClick = useCallback3(
1582
+ (e) => {
1583
+ if (item.disabled) {
1584
+ e.preventDefault();
1585
+ e.stopPropagation();
1586
+ return;
1587
+ }
1588
+ item.onClick?.();
1589
+ onItemClick?.(item);
1590
+ },
1591
+ [item, onItemClick]
1592
+ );
1593
+ if (item.type === "separator") {
1594
+ return /* @__PURE__ */ jsx8("div", { className: "bg-ods-system-greys-soft-grey h-1 w-full" });
1595
+ }
1596
+ const itemClasses = cn(
1597
+ ROW_CLASSES,
1598
+ item.disabled ? "text-ods-text-secondary cursor-not-allowed pointer-events-none opacity-60" : "text-ods-text-primary hover:bg-ods-bg-hover"
1599
+ );
1600
+ const subTriggerClasses = cn(
1601
+ itemClasses,
1602
+ "data-[state=open]:bg-ods-bg-active focus:bg-ods-bg-hover"
1603
+ );
1604
+ const renderAsLink = !!item.href && item.type !== "submenu" && item.type !== "checkbox";
1605
+ const rowContent = /* @__PURE__ */ jsxs6(Fragment, { children: [
1606
+ item.icon && /* @__PURE__ */ jsx8(
1607
+ "div",
1608
+ {
1609
+ className: cn(
1610
+ "w-4 h-4 md:w-6 md:h-6 flex-shrink-0 flex items-center justify-center",
1611
+ item.danger && "text-ods-error",
1612
+ item.disabled && "opacity-50"
1613
+ ),
1614
+ children: item.icon
1615
+ }
1616
+ ),
1617
+ /* @__PURE__ */ jsx8(
1618
+ "span",
1619
+ {
1620
+ className: cn(
1621
+ "flex-1 text-h4 font-medium leading-6",
1622
+ item.disabled ? "text-ods-text-secondary" : item.danger ? "text-ods-error" : "text-ods-text-primary"
1623
+ ),
1624
+ children: item.label
1625
+ }
1626
+ ),
1627
+ item.type === "checkbox" && /* @__PURE__ */ jsx8(
1628
+ "div",
1629
+ {
1630
+ className: cn(
1631
+ "w-4 h-4 md:w-6 md:h-6 flex items-center justify-center rounded-md transition-colors",
1632
+ item.checked ? "bg-ods-accent" : "border-2 border-ods-border bg-transparent"
1633
+ ),
1634
+ children: item.checked && /* @__PURE__ */ jsx8(Check, { className: "w-3 h-3 md:w-4 md:h-4 text-ods-text-on-accent", strokeWidth: 3 })
1635
+ }
1636
+ ),
1637
+ item.type === "submenu" && /* @__PURE__ */ jsx8(Chevron02RightIcon, { className: "w-4 h-4 md:w-6 md:h-6 text-ods-text-secondary" })
1638
+ ] });
1639
+ if (renderAsLink && item.href) {
1640
+ return /* @__PURE__ */ jsxs6("div", { className: WRAPPER_CLASSES, children: [
1641
+ /* @__PURE__ */ jsx8(
1642
+ next_link_default,
1643
+ {
1644
+ href: item.href,
1645
+ prefetch: false,
1646
+ className: itemClasses,
1647
+ onClick: handleLinkClick,
1648
+ "aria-disabled": item.disabled,
1649
+ tabIndex: item.disabled ? -1 : void 0,
1650
+ children: rowContent
1651
+ }
1652
+ ),
1653
+ item.iconAction && /* @__PURE__ */ jsx8(SecondaryAction, { action: item.iconAction })
1654
+ ] });
1655
+ }
1656
+ if (item.type === "submenu" && item.submenu) {
1657
+ return /* @__PURE__ */ jsxs6("div", { className: WRAPPER_CLASSES, children: [
1658
+ /* @__PURE__ */ jsxs6(DropdownMenuPrimitive.Sub, { children: [
1659
+ /* @__PURE__ */ jsx8(
1660
+ DropdownMenuPrimitive.SubTrigger,
1661
+ {
1662
+ disabled: item.disabled,
1663
+ className: subTriggerClasses,
1664
+ children: rowContent
1665
+ }
1666
+ ),
1667
+ /* @__PURE__ */ jsx8(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx8(
1668
+ DropdownMenuPrimitive.SubContent,
1669
+ {
1670
+ sideOffset: 4,
1671
+ className: "z-[1500] min-w-[256px] max-h-[var(--radix-popper-available-height)] bg-ods-bg border border-ods-border rounded-md shadow-xl overflow-y-auto p-0",
1672
+ children: item.submenu.map((subItem, index) => /* @__PURE__ */ jsx8(
1673
+ MenuItem,
1674
+ {
1675
+ item: subItem,
1676
+ onItemClick
1677
+ },
1678
+ subItem.id || index
1679
+ ))
1680
+ }
1681
+ ) })
1682
+ ] }),
1683
+ item.iconAction && /* @__PURE__ */ jsx8(SecondaryAction, { action: item.iconAction })
1684
+ ] });
1685
+ }
1686
+ return /* @__PURE__ */ jsxs6("div", { className: WRAPPER_CLASSES, children: [
1687
+ /* @__PURE__ */ jsx8(
1688
+ "div",
1689
+ {
1690
+ role: "menuitem",
1691
+ tabIndex: item.disabled ? -1 : 0,
1692
+ "aria-disabled": item.disabled,
1693
+ className: itemClasses,
1694
+ onClick: handleClick,
1695
+ onKeyDown: handleKeyDown,
1696
+ children: rowContent
1697
+ }
1698
+ ),
1699
+ item.iconAction && /* @__PURE__ */ jsx8(SecondaryAction, { action: item.iconAction })
1700
+ ] });
1701
+ };
1702
+ var GroupSeparator = () => /* @__PURE__ */ jsx8("div", { className: "bg-ods-bg-surface h-[3px] w-full" });
1703
+ var ActionsMenu = ({
1704
+ groups,
1705
+ className = "",
1706
+ onItemClick
1707
+ }) => {
1708
+ return /* @__PURE__ */ jsx8(
1709
+ "div",
1710
+ {
1711
+ className: `relative min-w-[256px] max-h-[var(--radix-popper-available-height)] bg-ods-bg border border-ods-border rounded-md shadow-lg overflow-y-auto ${className}`,
1712
+ children: groups.map((group, groupIndex) => {
1713
+ const groupKey = group.id || group.items.map((i) => i.id).join("|");
1714
+ return /* @__PURE__ */ jsxs6(React7.Fragment, { children: [
1715
+ group.items.map((item, itemIndex) => /* @__PURE__ */ jsx8(
1716
+ MenuItem,
1717
+ {
1718
+ item,
1719
+ onItemClick
1720
+ },
1721
+ item.id || `${groupKey}-${itemIndex}`
1722
+ )),
1723
+ group.separator && groupIndex < groups.length - 1 && /* @__PURE__ */ jsx8(GroupSeparator, {})
1724
+ ] }, groupKey);
1725
+ })
1726
+ }
1727
+ );
1728
+ };
1729
+ var ActionsMenuDropdown = ({
1730
+ groups,
1731
+ onItemClick,
1732
+ className,
1733
+ trigger,
1734
+ customTrigger,
1735
+ triggerAriaLabel = "More actions",
1736
+ triggerClassName,
1737
+ contentClassName,
1738
+ align = "end",
1739
+ side = "bottom",
1740
+ sideOffset = 6,
1741
+ open: openProp,
1742
+ onOpenChange,
1743
+ onCloseAutoFocus
1744
+ }) => {
1745
+ const [open = false, setOpen] = useControllableState({
1746
+ prop: openProp,
1747
+ defaultProp: false,
1748
+ onChange: onOpenChange
1749
+ });
1750
+ const handleItemClick = useCallback3(
1751
+ (item) => {
1752
+ onItemClick?.(item);
1753
+ if (item.type !== "checkbox" && item.type !== "submenu") {
1754
+ setOpen(false);
1755
+ }
1756
+ },
1757
+ [onItemClick, setOpen]
1758
+ );
1759
+ return /* @__PURE__ */ jsxs6(DropdownMenu, { open, onOpenChange: setOpen, modal: false, children: [
1760
+ /* @__PURE__ */ jsx8(DropdownMenuTrigger, { asChild: true, children: customTrigger ?? /* @__PURE__ */ jsx8(
1761
+ Button,
1762
+ {
1763
+ variant: "outline",
1764
+ size: "icon",
1765
+ "aria-label": triggerAriaLabel,
1766
+ className: triggerClassName || "bg-ods-card border-ods-border hover:bg-ods-bg-hover flex items-center justify-center focus-visible:ring-0",
1767
+ leftIcon: trigger ?? /* @__PURE__ */ jsx8(Ellipsis01Icon, { size: 24, className: "text-ods-text-primary" })
1768
+ }
1769
+ ) }),
1770
+ /* @__PURE__ */ jsx8(
1771
+ DropdownMenuContent,
1772
+ {
1773
+ align,
1774
+ side,
1775
+ sideOffset,
1776
+ onCloseAutoFocus,
1777
+ className: cn(
1778
+ "p-0 border-0 bg-transparent shadow-none overflow-visible",
1779
+ contentClassName
1780
+ ),
1781
+ children: /* @__PURE__ */ jsx8(
1782
+ ActionsMenu,
1783
+ {
1784
+ groups,
1785
+ onItemClick: handleItemClick,
1786
+ className
1787
+ }
1788
+ )
1789
+ }
1790
+ )
1791
+ ] });
1792
+ };
1793
+
1794
+ // src/components/chat/utils/is-cross-origin-url.ts
1795
+ function isCrossOriginUrl(url) {
1796
+ if (!url) return false;
1797
+ if (url.startsWith("/") && !url.startsWith("//")) return false;
1798
+ return true;
1799
+ }
1800
+
1801
+ // src/components/chat/utils/decide-new-tab.ts
1802
+ function decideNewTab(input) {
1803
+ const { href, targetPlatform, openIn, alwaysNewTab, runtimeMode, currentSource } = input;
1804
+ if (runtimeMode === "embed") return true;
1805
+ if (openIn === "new-tab") return true;
1806
+ if (alwaysNewTab === true) return true;
1807
+ if (openIn === "same-tab") return false;
1808
+ if (targetPlatform !== void 0 && targetPlatform !== null && currentSource) {
1809
+ return targetPlatform !== currentSource;
1810
+ }
1811
+ if (!href) return false;
1812
+ return isCrossOriginUrl(href);
1813
+ }
1814
+
1815
+ // src/components/chat/utils/nav-anchor-props.ts
1816
+ function computeIsNewTab(runtime, href, targetPlatform) {
1817
+ if (!href) return false;
1818
+ if (runtime.navigation.mode === "embed") return true;
1819
+ return runtime.navigation.decideNewTab?.({ href, targetPlatform }) ?? decideNewTab({
1820
+ href,
1821
+ targetPlatform,
1822
+ currentSource: runtime.source ?? ""
1823
+ });
1824
+ }
1825
+ function newTabAnchorAttrs(isNewTab) {
1826
+ return isNewTab ? { target: "_blank", rel: "noopener noreferrer" } : {};
1827
+ }
1828
+ function buildAnchorProps(href, isNewTab) {
1829
+ return href ? { href, ...newTabAnchorAttrs(isNewTab) } : void 0;
1830
+ }
1831
+
1832
+ // src/components/chat/utils/chat-nav-resolution.ts
1833
+ var NEW_TAB_FEATURES = "noopener,noreferrer";
1834
+ function isModifierClick(e) {
1835
+ return e.button !== 0 || !!e.metaKey || !!e.ctrlKey || !!e.shiftKey || !!e.altKey;
1836
+ }
1837
+ function stripSameOriginToPath(href) {
1838
+ if (!href.startsWith("http")) return href;
1839
+ try {
1840
+ const u = new URL(href);
1841
+ return u.pathname + u.search + u.hash;
1842
+ } catch {
1843
+ return href;
1844
+ }
1845
+ }
1846
+ function resolveHrefForRuntime(href, runtime) {
1847
+ if (runtime.navigation.mode !== "embed") return href;
1848
+ if (href.startsWith("http")) return href;
1849
+ if (href.startsWith("//")) return href;
1850
+ const origin = runtime.navigation.defaultContentOrigin;
1851
+ if (!origin) return href;
1852
+ return origin.replace(/\/+$/, "") + (href.startsWith("/") ? href : "/" + href);
1853
+ }
1854
+ function resolveExternalNavigation(args) {
1855
+ let abs = args.href;
1856
+ if (abs.startsWith("//")) abs = window.location.protocol + abs;
1857
+ if (!abs.startsWith("http")) {
1858
+ if (args.runtime.navigation.defaultContentOrigin) {
1859
+ let origin = args.runtime.navigation.defaultContentOrigin;
1860
+ while (origin.endsWith("/")) origin = origin.slice(0, -1);
1861
+ abs = origin + (abs.startsWith("/") ? abs : "/" + abs);
1862
+ } else if (args.runtime.navigation.mode === "embed" && process.env.NODE_ENV !== "production") {
1863
+ console.warn(
1864
+ "[chat-nav-resolution] relative href in embed mode with no defaultContentOrigin set on runtime.navigation \u2014 link will resolve against the embedder origin.",
1865
+ { href: abs, targetPlatform: args.targetPlatform }
1866
+ );
1867
+ }
1868
+ }
1869
+ const open = () => {
1870
+ if (args.runtime.navigation.openExternal) {
1871
+ args.runtime.navigation.openExternal(abs);
1872
+ } else {
1873
+ window.open(abs, "_blank", NEW_TAB_FEATURES);
1874
+ }
1875
+ };
1876
+ return { href: abs, open };
1877
+ }
1878
+
1879
+ // src/components/chat/entity-cards/entity-author-card.tsx
1880
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
1881
+ var EMPTY_AUTHOR_PLACEHOLDER = {
1882
+ full_name: "\u2014",
1883
+ avatar_url: null,
1884
+ job_title: "Unknown"
1885
+ };
1886
+ function EntityMetadataValueCell({
1887
+ value,
1888
+ label,
1889
+ className,
1890
+ uppercase = true
1891
+ }) {
1892
+ return /* @__PURE__ */ jsx9("div", { className: `bg-ods-card p-4 flex flex-col gap-3 ${className ?? ""}`, children: /* @__PURE__ */ jsxs7("div", { className: "flex flex-col gap-0", children: [
1893
+ /* @__PURE__ */ jsx9("p", { className: "text-h4 text-ods-text-primary", children: uppercase ? value.toLocaleUpperCase() : value }),
1894
+ /* @__PURE__ */ jsx9("p", { className: "font-['DM_Sans'] font-medium text-[14px] leading-[20px] text-ods-text-secondary", children: label })
1895
+ ] }) });
1896
+ }
1897
+ function EntityMetadataAuthorCell({
1898
+ author,
1899
+ roleLabel = "Author",
1900
+ className
1901
+ }) {
1902
+ const fullName = author.full_name || "Unknown Author";
1903
+ return /* @__PURE__ */ jsxs7("div", { className: `bg-ods-card p-4 flex items-center gap-3 ${className ?? ""}`, children: [
1904
+ /* @__PURE__ */ jsx9(
1905
+ SquareAvatar,
1906
+ {
1907
+ src: author.avatar_url || "",
1908
+ alt: fullName,
1909
+ fallback: nameInitials(fullName, ""),
1910
+ size: "md",
1911
+ variant: "round"
1912
+ }
1913
+ ),
1914
+ /* @__PURE__ */ jsxs7("div", { className: "flex flex-col gap-0 flex-1 min-w-0", children: [
1915
+ /* @__PURE__ */ jsx9("p", { className: "text-h3 tracking-[-0.36px] text-ods-text-primary truncate", children: fullName }),
1916
+ /* @__PURE__ */ jsx9("p", { className: "font-['DM_Sans'] font-medium text-[14px] leading-[20px] text-ods-text-secondary", children: author.job_title || roleLabel })
1917
+ ] })
1918
+ ] });
1919
+ }
1920
+ function EntityAuthorCard({
1921
+ author,
1922
+ roleLabel = "Author",
1923
+ publishedAt,
1924
+ publishedLabel = "Published",
1925
+ extraCells,
1926
+ renderEmptyAuthor = false,
1927
+ className
1928
+ }) {
1929
+ const hasAuthor = !!author?.full_name;
1930
+ if (!hasAuthor && !renderEmptyAuthor) return null;
1931
+ const effectiveAuthor = hasAuthor ? author : EMPTY_AUTHOR_PLACEHOLDER;
1932
+ const formatted = publishedAt ? formatDate(publishedAt) : "";
1933
+ const dateLabel = formatted === "Invalid Date" ? "" : formatted;
1934
+ const showDateCell = !!dateLabel;
1935
+ const extras = extraCells ?? [];
1936
+ const totalCells = extras.length + (showDateCell ? 1 : 0) + 1;
1937
+ const gridColsClass = totalCells >= 4 ? "md:grid-cols-4" : totalCells === 3 ? "md:grid-cols-3" : totalCells === 2 ? "md:grid-cols-2" : "md:grid-cols-1";
1938
+ const dividerClass = "border-b md:border-b-0 md:border-r border-ods-border";
1939
+ return /* @__PURE__ */ jsxs7(
1940
+ "div",
1941
+ {
1942
+ className: `grid grid-cols-1 ${gridColsClass} border border-ods-border rounded-md overflow-hidden w-full ${className ?? ""}`,
1943
+ children: [
1944
+ extras.map((cell, i) => /* @__PURE__ */ jsx9(
1945
+ EntityMetadataValueCell,
1946
+ {
1947
+ value: cell.value,
1948
+ label: cell.label,
1949
+ uppercase: cell.uppercase ?? true,
1950
+ className: dividerClass
1951
+ },
1952
+ `${cell.label}-${i}`
1953
+ )),
1954
+ showDateCell && /* @__PURE__ */ jsx9(
1955
+ EntityMetadataValueCell,
1956
+ {
1957
+ value: dateLabel,
1958
+ label: publishedLabel,
1959
+ uppercase: false,
1960
+ className: dividerClass
1961
+ }
1962
+ ),
1963
+ /* @__PURE__ */ jsx9(EntityMetadataAuthorCell, { author: effectiveAuthor, roleLabel })
1964
+ ]
1965
+ }
1966
+ );
1967
+ }
1968
+
1969
+ // src/components/chat/entity-cards/blog-image-placeholder.tsx
1970
+ import { jsx as jsx10 } from "react/jsx-runtime";
1971
+ function BlogImagePlaceholder({
1972
+ imageUrl,
1973
+ title,
1974
+ className = ""
1975
+ }) {
1976
+ if (!imageUrl) return null;
1977
+ return /* @__PURE__ */ jsx10("span", { className: `relative block w-full h-full overflow-hidden bg-ods-bg ${className}`, children: /* @__PURE__ */ jsx10(
1978
+ "img",
1979
+ {
1980
+ src: imageUrl,
1981
+ alt: `Cover image for ${title}`,
1982
+ className: "block w-full h-full object-contain",
1983
+ loading: "lazy",
1984
+ onError: (e) => {
1985
+ e.currentTarget.style.display = "none";
1986
+ }
1987
+ }
1988
+ ) });
1989
+ }
1990
+
1991
+ // src/utils/fetch-priority.ts
1992
+ import React8 from "react";
1993
+ var REACT_MAJOR = parseInt((React8.version || "0").split(".")[0], 10);
1994
+ var USE_CAMEL_CASE_FETCH_PRIORITY = REACT_MAJOR >= 19;
1995
+ function fetchPriorityProp(priority) {
1996
+ const value = priority === void 0 ? "auto" : typeof priority === "boolean" ? priority ? "high" : "low" : priority;
1997
+ return USE_CAMEL_CASE_FETCH_PRIORITY ? { fetchPriority: value } : { fetchpriority: value };
1998
+ }
1999
+
2000
+ // src/components/features/video.tsx
2001
+ import { useEffect as useEffect4, useMemo as useMemo3, useRef as useRef4, useState as useState3 } from "react";
2002
+ import MuxPlayer from "@mux/mux-player-react";
2003
+ import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
2004
+ if (typeof window !== "undefined") {
2005
+ const w = window;
2006
+ if (!w.chrome?.cast) {
2007
+ w.chrome = { ...w.chrome ?? {}, cast: { isAvailable: false } };
2008
+ }
2009
+ }
2010
+ if (typeof window !== "undefined" && typeof console !== "undefined") {
2011
+ const w = window;
2012
+ if (!w.__MEDIA_CHROME_WARN_PATCHED__) {
2013
+ w.__MEDIA_CHROME_WARN_PATCHED__ = true;
2014
+ const MEDIA_CHROME_NO_STYLESHEET_PREFIX = "Media Chrome: No style sheet found on style tag of";
2015
+ const originalWarn = console.warn.bind(console);
2016
+ console.warn = (...args) => {
2017
+ if (typeof args[0] === "string" && args[0].startsWith(MEDIA_CHROME_NO_STYLESHEET_PREFIX)) {
2018
+ return;
2019
+ }
2020
+ originalWarn(...args);
2021
+ };
2022
+ }
2023
+ }
2024
+ var YT_HOSTS = /* @__PURE__ */ new Set([
2025
+ "youtube.com",
2026
+ "www.youtube.com",
2027
+ "m.youtube.com",
2028
+ "youtu.be",
2029
+ "youtube-nocookie.com",
2030
+ "www.youtube-nocookie.com"
2031
+ ]);
2032
+ function isYouTubeUrl(url) {
2033
+ try {
2034
+ return YT_HOSTS.has(new URL(url, "http://placeholder.local").hostname.toLowerCase());
2035
+ } catch {
2036
+ return false;
2037
+ }
2038
+ }
2039
+ var YT_PATH_RE = /^\/(?:embed|v|shorts)\/([^/]+)\/?$/;
2040
+ var BARE_YT_ID_RE = /^[A-Za-z0-9_-]{11}$/;
2041
+ function extractYouTubeId(url) {
2042
+ if (!url) return null;
2043
+ if (BARE_YT_ID_RE.test(url)) return url;
2044
+ let u;
2045
+ try {
2046
+ u = new URL(url, "http://placeholder.local");
2047
+ } catch {
2048
+ return null;
2049
+ }
2050
+ if (!YT_HOSTS.has(u.hostname.toLowerCase())) return null;
2051
+ if (u.hostname.toLowerCase().endsWith("youtu.be")) {
2052
+ return u.pathname.split("/").filter(Boolean)[0] ?? null;
2053
+ }
2054
+ const v = u.searchParams.get("v");
2055
+ if (v) return v;
2056
+ const m = u.pathname.match(YT_PATH_RE);
2057
+ return m ? m[1] : null;
2058
+ }
2059
+ function Video(props) {
2060
+ const url = props.url;
2061
+ if (!url) return null;
2062
+ const effectiveKind = resolveKind(props, url);
2063
+ const layout = props.layout ?? "native";
2064
+ const inner = effectiveKind === "youtube" ? /* @__PURE__ */ jsx11(
2065
+ YouTubeFacade,
2066
+ {
2067
+ url,
2068
+ title: props.title,
2069
+ priority: props.priority,
2070
+ className: props.className,
2071
+ minimalControls: props.minimalControls
2072
+ }
2073
+ ) : /* @__PURE__ */ jsx11(
2074
+ FilePlayer,
2075
+ {
2076
+ url,
2077
+ poster: props.poster,
2078
+ muted: props.muted,
2079
+ srtContent: "srtContent" in props ? props.srtContent : null,
2080
+ captionsUrl: "captionsUrl" in props ? props.captionsUrl : null,
2081
+ className: props.className
2082
+ }
2083
+ );
2084
+ return wrapWithLayout(inner, layout);
2085
+ }
2086
+ function resolveKind(props, url) {
2087
+ if ("kind" in props) {
2088
+ if (props.kind === "youtube") return "youtube";
2089
+ if (props.kind === "file") return "file";
2090
+ }
2091
+ if (BARE_YT_ID_RE.test(url)) return "youtube";
2092
+ return isYouTubeUrl(url) ? "youtube" : "file";
2093
+ }
2094
+ function wrapWithLayout(inner, layout) {
2095
+ switch (layout) {
2096
+ case "centered":
2097
+ return /* @__PURE__ */ jsx11("div", { className: "flex justify-center w-full", children: /* @__PURE__ */ jsx11("div", { className: "w-full max-w-3xl aspect-video rounded-lg overflow-hidden border border-ods-border", children: inner }) });
2098
+ case "fill":
2099
+ return /* @__PURE__ */ jsx11("div", { className: "absolute inset-0 w-full h-full", children: inner });
2100
+ case "native":
2101
+ default:
2102
+ return inner;
2103
+ }
2104
+ }
2105
+ function FilePlayer({
2106
+ url,
2107
+ poster,
2108
+ muted,
2109
+ srtContent,
2110
+ captionsUrl,
2111
+ className
2112
+ }) {
2113
+ if (process.env.NODE_ENV !== "production" && srtContent && !captionsUrl) {
2114
+ console.warn(
2115
+ "[Video] srtContent supplied without captionsUrl \u2014 captions will not render. Pass captionsUrl (the VTT URL) instead; raw SRT text overlays are no longer supported."
2116
+ );
2117
+ }
2118
+ return /* @__PURE__ */ jsx11(
2119
+ MuxPlayer,
2120
+ {
2121
+ src: url,
2122
+ poster: poster || void 0,
2123
+ streamType: "on-demand",
2124
+ playsInline: true,
2125
+ muted,
2126
+ preferCmcd: "header",
2127
+ accentColor: "var(--ods-accent, var(--color-accent-primary))",
2128
+ className,
2129
+ style: { width: "100%", height: "100%" },
2130
+ children: captionsUrl ? /* @__PURE__ */ jsx11(
2131
+ "track",
2132
+ {
2133
+ kind: "captions",
2134
+ src: captionsUrl,
2135
+ srcLang: "en",
2136
+ label: "English",
2137
+ default: true
2138
+ }
2139
+ ) : null
2140
+ }
2141
+ );
2142
+ }
2143
+ function YouTubeFacade({
2144
+ url,
2145
+ title = "YouTube Video",
2146
+ priority,
2147
+ className,
2148
+ minimalControls
2149
+ }) {
2150
+ const videoId = extractYouTubeId(url);
2151
+ if (!videoId) return null;
2152
+ return /* @__PURE__ */ jsx11(YouTubeFacadeInner, { videoId, title, priority, className, minimalControls });
2153
+ }
2154
+ var YT_NOCOOKIE_ORIGIN = "https://www.youtube-nocookie.com";
2155
+ var YT_STATE_ENDED = 0;
2156
+ var YT_STATE_PLAYING = 1;
2157
+ var YT_PLAYING_BLUR_DELAY_MS = 1e3;
2158
+ function YouTubeFacadeInner({
2159
+ videoId,
2160
+ title,
2161
+ priority,
2162
+ className,
2163
+ minimalControls
2164
+ }) {
2165
+ const [activated, setActivated] = useState3(false);
2166
+ const iframeRef = useRef4(null);
2167
+ const { embedUrl, posterJpg, posterWebp } = useMemo3(() => {
2168
+ const params = new URLSearchParams({
2169
+ autoplay: "1",
2170
+ rel: "0",
2171
+ modestbranding: "1",
2172
+ playsinline: "1",
2173
+ enablejsapi: "1"
2174
+ });
2175
+ if (typeof window !== "undefined") {
2176
+ params.set("origin", window.location.origin);
2177
+ }
2178
+ if (minimalControls) {
2179
+ params.set("controls", "0");
2180
+ params.set("fs", "0");
2181
+ params.set("iv_load_policy", "3");
2182
+ params.set("cc_load_policy", "0");
2183
+ params.set("disablekb", "1");
2184
+ }
2185
+ return {
2186
+ embedUrl: `${YT_NOCOOKIE_ORIGIN}/embed/${videoId}?${params.toString()}`,
2187
+ posterJpg: `https://i.ytimg.com/vi/${videoId}/mqdefault.jpg`,
2188
+ posterWebp: `https://i.ytimg.com/vi_webp/${videoId}/mqdefault.webp`
2189
+ };
2190
+ }, [videoId, minimalControls]);
2191
+ useEffect4(() => {
2192
+ if (!activated) return;
2193
+ const iframe = iframeRef.current;
2194
+ if (!iframe) return;
2195
+ function subscribe() {
2196
+ iframe?.contentWindow?.postMessage(
2197
+ '{"event":"listening"}',
2198
+ YT_NOCOOKIE_ORIGIN
2199
+ );
2200
+ }
2201
+ iframe.addEventListener("load", subscribe);
2202
+ subscribe();
2203
+ let blurTimer = null;
2204
+ function handleMessage(event) {
2205
+ if (event.origin !== YT_NOCOOKIE_ORIGIN) return;
2206
+ if (typeof event.data !== "string") return;
2207
+ let payload = null;
2208
+ try {
2209
+ payload = JSON.parse(event.data);
2210
+ } catch {
2211
+ return;
2212
+ }
2213
+ if (!payload || payload.event !== "infoDelivery") return;
2214
+ const state = payload.info?.playerState;
2215
+ if (typeof state !== "number") return;
2216
+ if (state === YT_STATE_PLAYING) {
2217
+ if (blurTimer !== null) return;
2218
+ blurTimer = setTimeout(() => {
2219
+ blurTimer = null;
2220
+ iframeRef.current?.blur();
2221
+ }, YT_PLAYING_BLUR_DELAY_MS);
2222
+ return;
2223
+ }
2224
+ if (state === YT_STATE_ENDED) {
2225
+ setActivated(false);
2226
+ }
2227
+ }
2228
+ window.addEventListener("message", handleMessage);
2229
+ return () => {
2230
+ iframe.removeEventListener("load", subscribe);
2231
+ window.removeEventListener("message", handleMessage);
2232
+ if (blurTimer !== null) clearTimeout(blurTimer);
2233
+ };
2234
+ }, [activated]);
2235
+ const wrapperClass = `relative w-full ${className ?? ""}`;
2236
+ const wrapperStyle = { paddingBottom: "56.25%" };
2237
+ if (activated) {
2238
+ return /* @__PURE__ */ jsx11("div", { className: wrapperClass, style: wrapperStyle, children: /* @__PURE__ */ jsx11(
2239
+ "iframe",
2240
+ {
2241
+ ref: iframeRef,
2242
+ src: embedUrl,
2243
+ allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share",
2244
+ allowFullScreen: true,
2245
+ title,
2246
+ className: "absolute inset-0 w-full h-full border-0 rounded-lg"
2247
+ }
2248
+ ) });
2249
+ }
2250
+ return /* @__PURE__ */ jsx11("div", { className: wrapperClass, style: wrapperStyle, children: /* @__PURE__ */ jsxs8(
2251
+ "button",
2252
+ {
2253
+ type: "button",
2254
+ "aria-label": `Play: ${title}`,
2255
+ onClick: () => setActivated(true),
2256
+ className: "group absolute inset-0 p-0 m-0 border border-ods-border rounded-lg overflow-hidden bg-ods-card cursor-pointer",
2257
+ children: [
2258
+ /* @__PURE__ */ jsxs8("picture", { children: [
2259
+ /* @__PURE__ */ jsx11("source", { type: "image/webp", srcSet: posterWebp }),
2260
+ /* @__PURE__ */ jsx11(
2261
+ "img",
2262
+ {
2263
+ src: posterJpg,
2264
+ alt: title,
2265
+ loading: "lazy",
2266
+ ...fetchPriorityProp(priority),
2267
+ decoding: priority ? "sync" : "async",
2268
+ className: "absolute inset-0 w-full h-full object-cover"
2269
+ }
2270
+ )
2271
+ ] }),
2272
+ /* @__PURE__ */ jsx11("div", { className: "absolute inset-0 flex items-center justify-center bg-ods-bg-inverse bg-opacity-20 transition-opacity duration-200 group-hover:bg-opacity-30", children: /* @__PURE__ */ jsx11("span", { className: "flex items-center justify-center w-16 h-16 rounded-full bg-ods-accent text-ods-text-on-accent shadow-lg transition-transform duration-200 group-hover:scale-110", children: /* @__PURE__ */ jsx11(PlayIcon, { size: 24, color: "currentColor", className: "ml-1" }) }) })
2273
+ ]
2274
+ }
2275
+ ) });
2276
+ }
2277
+
2278
+ // src/components/features/video-ratio-tabs.tsx
2279
+ import { Fragment as Fragment2, jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
2280
+ var TAB_TRIGGER_CLASS = "rounded-none border-b-2 border-transparent data-[state=active]:border-ods-accent data-[state=active]:bg-transparent data-[state=active]:shadow-none px-4 py-2 text-sm text-ods-text-secondary data-[state=active]:text-ods-text-primary";
2281
+ var RATIO_GRID_CLASS = {
2282
+ portrait: "grid grid-cols-2 md:grid-cols-3 gap-4",
2283
+ square: "grid grid-cols-2 md:grid-cols-3 gap-4",
2284
+ landscape: "grid grid-cols-1 md:grid-cols-2 gap-4"
2285
+ };
2286
+ var RATIO_DISPLAY_GRID_CLASS = {
2287
+ portrait: "grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4",
2288
+ square: "grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4",
2289
+ landscape: "grid grid-cols-1 md:grid-cols-2 gap-6"
2290
+ };
2291
+ var RATIO_TAB_CONFIG = [
2292
+ { key: "portrait", label: "Portrait 9:16" },
2293
+ { key: "square", label: "Square 1:1" },
2294
+ { key: "landscape", label: "Landscape 16:9" }
2295
+ ];
2296
+ function RatioTabs({
2297
+ groups,
2298
+ defaultTab,
2299
+ className = ""
2300
+ }) {
2301
+ const activeTabs = RATIO_TAB_CONFIG.filter((t) => groups[t.key].count > 0);
2302
+ if (activeTabs.length <= 1) {
2303
+ const active = activeTabs[0];
2304
+ return active ? /* @__PURE__ */ jsx12(Fragment2, { children: groups[active.key].render() }) : null;
2305
+ }
2306
+ const firstTab = defaultTab && groups[defaultTab].count > 0 ? defaultTab : activeTabs[0].key;
2307
+ return /* @__PURE__ */ jsxs9(Tabs, { defaultValue: firstTab, className: `w-full ${className}`, children: [
2308
+ /* @__PURE__ */ jsx12(TabsList, { className: "inline-flex justify-start rounded-none bg-transparent h-auto p-0 gap-0 mb-2", children: activeTabs.map((t) => /* @__PURE__ */ jsxs9(TabsTrigger, { value: t.key, className: TAB_TRIGGER_CLASS, children: [
2309
+ t.label,
2310
+ " (",
2311
+ groups[t.key].count,
2312
+ ")"
2313
+ ] }, t.key)) }),
2314
+ activeTabs.map((t) => /* @__PURE__ */ jsx12(
2315
+ TabsContent,
2316
+ {
2317
+ value: t.key,
2318
+ forceMount: true,
2319
+ className: "data-[state=inactive]:hidden",
2320
+ children: groups[t.key].render()
2321
+ },
2322
+ t.key
2323
+ ))
2324
+ ] });
2325
+ }
2326
+ function detectAspectRatio(ratioString, width, height) {
2327
+ if (ratioString === "16:9") return "16:9";
2328
+ if (ratioString === "1:1") return "1:1";
2329
+ if (ratioString === "9:16") return "9:16";
2330
+ if (width && height) {
2331
+ if (Math.abs(width - height) < Math.min(width, height) * 0.1) return "1:1";
2332
+ if (width > height) return "16:9";
2333
+ }
2334
+ return "9:16";
2335
+ }
2336
+ function ratioToCategory(ratio) {
2337
+ if (ratio === "16:9") return "landscape";
2338
+ if (ratio === "1:1") return "square";
2339
+ return "portrait";
2340
+ }
2341
+ function groupByAspectRatio(items, getAspectRatio) {
2342
+ const portrait = [];
2343
+ const square = [];
2344
+ const landscape = [];
2345
+ for (const item of items) {
2346
+ const cat = ratioToCategory(getAspectRatio(item));
2347
+ if (cat === "landscape") landscape.push(item);
2348
+ else if (cat === "square") square.push(item);
2349
+ else portrait.push(item);
2350
+ }
2351
+ const filled = [portrait, square, landscape].filter((a) => a.length > 0).length;
2352
+ return { portrait, square, landscape, hasMultiple: filled > 1 };
2353
+ }
2354
+
2355
+ // src/components/features/video-bites-display.tsx
2356
+ import { useMemo as useMemo4 } from "react";
2357
+ import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
2358
+ var RATIO_TO_CSS_ASPECT = {
2359
+ portrait: "9 / 16",
2360
+ square: "1 / 1",
2361
+ landscape: "16 / 9"
2362
+ };
2363
+ function LazyBite({ ratio, children }) {
2364
+ const { ref, isNear } = useNearViewport("500px");
2365
+ return /* @__PURE__ */ jsx13("div", { ref, style: { aspectRatio: RATIO_TO_CSS_ASPECT[ratio] }, children: isNear ? children : /* @__PURE__ */ jsx13("div", { className: "w-full h-full bg-ods-card rounded-md" }) });
2366
+ }
2367
+ function VideoBitesDisplay({
2368
+ bites,
2369
+ title = "Video Highlights",
2370
+ filterPublished = true,
2371
+ showTitle = true
2372
+ }) {
2373
+ const grouped = useMemo4(() => {
2374
+ const filtered = filterPublished ? bites.filter((b) => b.published) : bites;
2375
+ const sorted = [...filtered].sort((a, b) => {
2376
+ if (!a.created_at && !b.created_at) return 0;
2377
+ if (!a.created_at) return 1;
2378
+ if (!b.created_at) return -1;
2379
+ return new Date(b.created_at).getTime() - new Date(a.created_at).getTime();
2380
+ });
2381
+ return groupByAspectRatio(
2382
+ sorted,
2383
+ (b) => detectAspectRatio(b.aspect_ratio)
2384
+ );
2385
+ }, [bites, filterPublished]);
2386
+ const totalCount = grouped.portrait.length + grouped.square.length + grouped.landscape.length;
2387
+ if (totalCount === 0) return null;
2388
+ return /* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-6 w-full min-w-0", children: [
2389
+ showTitle && /* @__PURE__ */ jsx13("h2", { className: "text-h1 tracking-[-1.12px] text-ods-text-primary break-words", children: title }),
2390
+ grouped.hasMultiple ? /* @__PURE__ */ jsx13(
2391
+ RatioTabs,
2392
+ {
2393
+ groups: {
2394
+ portrait: {
2395
+ count: grouped.portrait.length,
2396
+ render: () => /* @__PURE__ */ jsx13(BiteGrid, { bites: grouped.portrait, ratio: "portrait" })
2397
+ },
2398
+ square: {
2399
+ count: grouped.square.length,
2400
+ render: () => /* @__PURE__ */ jsx13(BiteGrid, { bites: grouped.square, ratio: "square" })
2401
+ },
2402
+ landscape: {
2403
+ count: grouped.landscape.length,
2404
+ render: () => /* @__PURE__ */ jsx13(BiteGrid, { bites: grouped.landscape, ratio: "landscape" })
2405
+ }
2406
+ }
2407
+ }
2408
+ ) : /* @__PURE__ */ jsx13(
2409
+ BiteGrid,
2410
+ {
2411
+ bites: grouped.portrait.length > 0 ? grouped.portrait : grouped.square.length > 0 ? grouped.square : grouped.landscape,
2412
+ ratio: grouped.portrait.length > 0 ? "portrait" : grouped.square.length > 0 ? "square" : "landscape"
2413
+ }
2414
+ )
2415
+ ] });
2416
+ }
2417
+ function BiteGrid({ bites, ratio }) {
2418
+ return /* @__PURE__ */ jsx13("div", { className: RATIO_DISPLAY_GRID_CLASS[ratio], children: bites.map((bite, index) => /* @__PURE__ */ jsx13(LazyBite, { ratio, children: /* @__PURE__ */ jsx13(VideoBiteCard, { url: bite.url, title: bite.title, thumbnailUrl: bite.thumbnail_url }) }, bite.url || index)) });
2419
+ }
2420
+ function VideoBiteCard({ url, title, thumbnailUrl }) {
2421
+ return /* @__PURE__ */ jsxs10(Card, { className: "overflow-hidden border border-ods-border bg-ods-card hover:border-ods-accent transition-colors flex flex-col h-full", children: [
2422
+ /* @__PURE__ */ jsx13("div", { className: "relative flex-1 min-h-0", children: /* @__PURE__ */ jsx13(Video, { url, poster: thumbnailUrl || void 0, layout: "fill" }) }),
2423
+ title && /* @__PURE__ */ jsx13("div", { className: "p-4", children: /* @__PURE__ */ jsx13("p", { className: "text-h4 text-ods-text-primary line-clamp-2", title, children: title }) })
2424
+ ] });
2425
+ }
2426
+
2427
+ // src/components/features/entity-video-section.tsx
2428
+ import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
2429
+ function EntityVideoSection({
2430
+ mainVideoUrl,
2431
+ youtubeUrl,
2432
+ highlightVideoUrl,
2433
+ highlightVideoThumbnail,
2434
+ mainVideoPoster,
2435
+ title = "Video",
2436
+ videoSummary,
2437
+ videoBites,
2438
+ bitesTitle = "Video Highlights",
2439
+ filterPublishedBites = true,
2440
+ MarkdownRenderer,
2441
+ srtContent,
2442
+ captionsUrl,
2443
+ priority = false
2444
+ }) {
2445
+ const hasFullVideo = !!(youtubeUrl || mainVideoUrl);
2446
+ const hasHighlight = !!highlightVideoUrl;
2447
+ const hasVideo = hasFullVideo || hasHighlight;
2448
+ if (!hasVideo && !videoSummary && (!videoBites || videoBites.length === 0)) {
2449
+ return null;
2450
+ }
2451
+ const fullVideoUrl = youtubeUrl || mainVideoUrl || null;
2452
+ const fullVideoKind = youtubeUrl ? "youtube" : "auto";
2453
+ return /* @__PURE__ */ jsxs11(Fragment3, { children: [
2454
+ hasVideo && (hasFullVideo && hasHighlight ? /* @__PURE__ */ jsxs11(Tabs, { defaultValue: "full-video", className: "w-full", children: [
2455
+ /* @__PURE__ */ jsxs11(TabsList, { className: "inline-flex justify-start rounded-none bg-transparent h-auto p-0 gap-0", children: [
2456
+ /* @__PURE__ */ jsx14(
2457
+ TabsTrigger,
2458
+ {
2459
+ value: "full-video",
2460
+ className: "rounded-none border-b-2 border-transparent data-[state=active]:border-ods-accent data-[state=active]:bg-transparent data-[state=active]:shadow-none px-4 md:px-6 py-3 text-ods-text-secondary data-[state=active]:text-ods-text-primary",
2461
+ children: "Full Video"
2462
+ }
2463
+ ),
2464
+ /* @__PURE__ */ jsx14(
2465
+ TabsTrigger,
2466
+ {
2467
+ value: "highlights",
2468
+ className: "rounded-none border-b-2 border-transparent data-[state=active]:border-ods-accent data-[state=active]:bg-transparent data-[state=active]:shadow-none px-4 md:px-6 py-3 text-ods-text-secondary data-[state=active]:text-ods-text-primary",
2469
+ children: "Highlights"
2470
+ }
2471
+ )
2472
+ ] }),
2473
+ /* @__PURE__ */ jsx14(TabsContent, { value: "full-video", className: "mt-4", children: /* @__PURE__ */ jsx14(
2474
+ Video,
2475
+ {
2476
+ kind: fullVideoKind,
2477
+ url: fullVideoUrl,
2478
+ poster: mainVideoPoster,
2479
+ title,
2480
+ srtContent,
2481
+ captionsUrl,
2482
+ layout: "centered",
2483
+ priority
2484
+ }
2485
+ ) }),
2486
+ /* @__PURE__ */ jsx14(TabsContent, { value: "highlights", className: "mt-4", children: /* @__PURE__ */ jsx14(
2487
+ Video,
2488
+ {
2489
+ url: highlightVideoUrl,
2490
+ poster: highlightVideoThumbnail,
2491
+ layout: "centered"
2492
+ }
2493
+ ) })
2494
+ ] }) : hasFullVideo ? /* @__PURE__ */ jsx14(
2495
+ Video,
2496
+ {
2497
+ kind: fullVideoKind,
2498
+ url: fullVideoUrl,
2499
+ poster: mainVideoPoster,
2500
+ title,
2501
+ srtContent,
2502
+ captionsUrl,
2503
+ layout: "centered",
2504
+ priority
2505
+ }
2506
+ ) : /* @__PURE__ */ jsx14(
2507
+ Video,
2508
+ {
2509
+ url: highlightVideoUrl,
2510
+ poster: highlightVideoThumbnail,
2511
+ layout: "centered",
2512
+ priority
2513
+ }
2514
+ )),
2515
+ videoSummary && MarkdownRenderer && /* @__PURE__ */ jsxs11("div", { className: "flex flex-col gap-6 w-full min-w-0", children: [
2516
+ /* @__PURE__ */ jsx14("h2", { className: "text-h1 tracking-[-1.12px] text-ods-text-primary break-words", children: "Summary" }),
2517
+ /* @__PURE__ */ jsx14("div", { className: "text-h4 text-ods-text-primary break-words overflow-hidden", children: /* @__PURE__ */ jsx14(MarkdownRenderer, { content: videoSummary }) })
2518
+ ] }),
2519
+ videoBites && videoBites.length > 0 && /* @__PURE__ */ jsx14(
2520
+ VideoBitesDisplay,
2521
+ {
2522
+ bites: videoBites,
2523
+ title: bitesTitle,
2524
+ filterPublished: filterPublishedBites
2525
+ }
2526
+ )
2527
+ ] });
2528
+ }
2529
+
2530
+ // src/components/chat/utils/compact-card-classes.ts
2531
+ var COMPACT_CARD_OUTER = "my-1.5 flex items-start gap-3 w-full p-2 rounded-lg border border-ods-border bg-ods-card no-underline transition-colors hover:border-ods-text-secondary/40";
2532
+ var COMPACT_CARD_OUTER_STATIC = "my-1.5 flex items-start gap-3 w-full p-2 rounded-lg border border-ods-border bg-ods-card no-underline cursor-default";
2533
+ var COMPACT_CARD_SKELETON_OUTER = "my-1.5 flex w-full animate-pulse items-start gap-3 rounded-lg border border-ods-border bg-ods-card p-2";
2534
+ var COMPACT_CARD_IMAGE_SLOT = "relative block shrink-0 self-start w-14 h-14 aspect-square overflow-hidden rounded-md bg-ods-bg";
2535
+ var COMPACT_CARD_SKELETON_IMAGE_SLOT = "block h-14 w-14 aspect-square shrink-0 self-start rounded-md bg-ods-bg";
2536
+ var COMPACT_CARD_ICON_SLOT = "flex h-14 w-14 aspect-square shrink-0 self-start items-center justify-center rounded-md bg-ods-bg text-ods-accent";
2537
+ var COMPACT_CARD_TEXT_COL = "flex min-w-0 flex-1 flex-col gap-0.5 min-h-14";
2538
+ var COMPACT_CARD_TITLE_ROW = "flex items-center min-w-0 h-5";
2539
+ var COMPACT_CARD_META_ROW_BOX = "flex items-center min-w-0 h-4";
2540
+ var COMPACT_CARD_TITLE = "truncate text-sm font-semibold leading-5 text-ods-text-primary";
2541
+ var COMPACT_CARD_SUBTITLE = "truncate text-[11px] leading-4 text-ods-text-secondary";
2542
+ var COMPACT_CARD_SUMMARY = "truncate text-[11px] leading-4 text-ods-text-secondary/80";
2543
+ var COMPACT_CARD_META_ROW = "flex items-center gap-1.5 min-w-0 text-[11px] leading-4 text-ods-text-secondary";
2544
+ var COMPACT_CARD_ROW_FILLER = "\xA0";
2545
+ var SAFE_URL_SCHEMES = ["http:", "https:", "mailto:"];
2546
+ function safeHref(url) {
2547
+ if (!url) return null;
2548
+ if (/[\u0000-\u001f\u007f\u200b-\u200d\u2028\u2029\ufeff]/.test(url)) return null;
2549
+ const trimmed = url.trim();
2550
+ if (!trimmed) return null;
2551
+ if (trimmed.startsWith("/") && !trimmed.startsWith("//")) return trimmed;
2552
+ if (trimmed.startsWith("#")) return trimmed;
2553
+ if (/^[a-z][a-z0-9+.-]*:$/i.test(trimmed)) return null;
2554
+ try {
2555
+ const parsed = new URL(trimmed, "https://_safehref_base.invalid");
2556
+ if (!SAFE_URL_SCHEMES.includes(parsed.protocol)) return null;
2557
+ if ((parsed.protocol === "https:" || parsed.protocol === "http:") && !parsed.hostname) return null;
2558
+ return trimmed;
2559
+ } catch {
2560
+ return null;
2561
+ }
2562
+ }
2563
+
2564
+ // src/components/chat/entity-cards/onboarding-guide-card.tsx
2565
+ init_next_link();
2566
+ init_cn();
2567
+ import { Clock, ExternalLink, GraduationCap, Play } from "lucide-react";
2568
+
2569
+ // src/components/chat/entity-cards/use-entity-card-link.ts
2570
+ import { useMemo as useMemo5 } from "react";
2571
+ function useEntityCardLink({
2572
+ href,
2573
+ targetPlatform,
2574
+ target,
2575
+ rel
2576
+ }) {
2577
+ const runtime = useChatRuntime();
2578
+ return useMemo5(() => {
2579
+ if (target !== void 0 || rel !== void 0) {
2580
+ const safeRel = rel ?? (target === "_blank" ? "noopener noreferrer" : void 0);
2581
+ return { target, rel: safeRel };
2582
+ }
2583
+ const newTab = runtime ? computeIsNewTab(runtime, href, targetPlatform ?? null) : false;
2584
+ return newTab ? { target: "_blank", rel: "noopener noreferrer" } : { target: void 0, rel: void 0 };
2585
+ }, [target, rel, href, targetPlatform, runtime]);
2586
+ }
2587
+
2588
+ // src/components/chat/entity-cards/use-entity-card-placeholder.ts
2589
+ var NO_OP_BUILDER = () => "";
2590
+ function useEntityCardPlaceholder({
2591
+ title,
2592
+ placeholderUrl,
2593
+ siteName = "",
2594
+ aspect = "wide"
2595
+ }) {
2596
+ const runtime = useChatRuntime();
2597
+ const builder = runtime?.resolvePlaceholderUrl ?? NO_OP_BUILDER;
2598
+ const enabled = placeholderUrl === void 0 && !!runtime?.resolvePlaceholderUrl;
2599
+ const derived = useOgPlaceholder(builder, title, siteName, enabled, aspect);
2600
+ return placeholderUrl !== void 0 ? placeholderUrl : derived;
2601
+ }
2602
+
2603
+ // src/components/chat/entity-cards/onboarding-guide-card.tsx
2604
+ import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
2605
+ function stripMarkdownPreview(text) {
2606
+ return text.replace(/```[\s\S]*?```/g, " ").replace(/^#{1,6}\s+/gm, "").replace(/!?\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/[*_~`>]/g, "").replace(/^\s*[-+]\s+/gm, "").replace(/-{3,}/g, " ").replace(/\s+/g, " ").trim().replace(/^([\s\S]{240})[\s\S]+$/, "$1\u2026");
2607
+ }
2608
+ var HORIZONTAL_SIZE_TOKENS = {
2609
+ default: {
2610
+ padding: "p-4",
2611
+ step: "w-8 h-8 text-sm",
2612
+ title: "text-h5",
2613
+ summaryClamp: "line-clamp-2"
2614
+ }
2615
+ };
2616
+ function OnboardingGuideCardSkeleton({ size = "default" }) {
2617
+ if (size === "catalog") {
2618
+ return /* @__PURE__ */ jsxs12("div", { className: "bg-ods-system-greys-black border border-ods-border rounded-lg overflow-hidden flex flex-col p-6 gap-4 animate-pulse", children: [
2619
+ /* @__PURE__ */ jsxs12("div", { className: "flex flex-col md:flex-row gap-4 md:gap-6", children: [
2620
+ /* @__PURE__ */ jsx15("div", { className: "w-full md:w-[256px] aspect-[1200/630] bg-ods-border rounded-lg flex-shrink-0" }),
2621
+ /* @__PURE__ */ jsxs12("div", { className: "flex-1 min-w-0 flex flex-col", children: [
2622
+ /* @__PURE__ */ jsxs12("div", { className: "min-h-[60px] md:min-h-[72px] flex flex-col gap-1.5 justify-start mb-3", children: [
2623
+ /* @__PURE__ */ jsx15("div", { className: "h-[25px] md:h-[30px] w-3/4 bg-ods-border rounded" }),
2624
+ /* @__PURE__ */ jsx15("div", { className: "h-[25px] md:h-[30px] w-1/2 bg-ods-border rounded" })
2625
+ ] }),
2626
+ /* @__PURE__ */ jsxs12("div", { className: "min-h-[46px] md:min-h-[52px] flex flex-col gap-2 justify-start", children: [
2627
+ /* @__PURE__ */ jsx15("div", { className: "h-3 w-full bg-ods-border/70 rounded" }),
2628
+ /* @__PURE__ */ jsx15("div", { className: "h-3 w-5/6 bg-ods-border/70 rounded" })
2629
+ ] })
2630
+ ] })
2631
+ ] }),
2632
+ /* @__PURE__ */ jsxs12("div", { className: "grid grid-cols-1 md:grid-cols-3 border border-ods-border rounded-md overflow-hidden w-full", children: [
2633
+ [0, 1].map((i) => /* @__PURE__ */ jsx15(
2634
+ "div",
2635
+ {
2636
+ className: "bg-ods-card p-4 flex flex-col gap-3 border-b md:border-b-0 md:border-r border-ods-border",
2637
+ children: /* @__PURE__ */ jsxs12("div", { className: "flex flex-col gap-2", children: [
2638
+ /* @__PURE__ */ jsx15("div", { className: "h-6 w-32 bg-ods-bg rounded" }),
2639
+ /* @__PURE__ */ jsx15("div", { className: "h-3 w-20 bg-ods-bg/60 rounded" })
2640
+ ] })
2641
+ },
2642
+ `cell-${i}`
2643
+ )),
2644
+ /* @__PURE__ */ jsxs12("div", { className: "bg-ods-card p-4 flex items-center gap-3", children: [
2645
+ /* @__PURE__ */ jsx15("div", { className: "h-10 w-10 rounded-full bg-ods-bg shrink-0" }),
2646
+ /* @__PURE__ */ jsxs12("div", { className: "flex flex-col gap-2 flex-1 min-w-0", children: [
2647
+ /* @__PURE__ */ jsx15("div", { className: "h-4 w-3/4 bg-ods-bg rounded" }),
2648
+ /* @__PURE__ */ jsx15("div", { className: "h-3 w-1/2 bg-ods-bg/60 rounded" })
2649
+ ] })
2650
+ ] })
2651
+ ] })
2652
+ ] });
2653
+ }
2654
+ if (size === "sm") {
2655
+ return /* @__PURE__ */ jsxs12("span", { className: COMPACT_CARD_SKELETON_OUTER, children: [
2656
+ /* @__PURE__ */ jsx15("span", { className: COMPACT_CARD_SKELETON_IMAGE_SLOT }),
2657
+ /* @__PURE__ */ jsxs12("span", { className: COMPACT_CARD_TEXT_COL, children: [
2658
+ /* @__PURE__ */ jsx15("span", { className: COMPACT_CARD_TITLE_ROW, children: /* @__PURE__ */ jsx15("span", { className: "h-3.5 w-3/5 rounded bg-ods-bg" }) }),
2659
+ /* @__PURE__ */ jsx15("span", { className: COMPACT_CARD_META_ROW_BOX, children: /* @__PURE__ */ jsx15("span", { className: "h-3 w-2/5 rounded bg-ods-bg/70" }) }),
2660
+ /* @__PURE__ */ jsx15("span", { className: COMPACT_CARD_META_ROW_BOX, children: /* @__PURE__ */ jsx15("span", { className: "h-3 w-11/12 rounded bg-ods-bg/40" }) })
2661
+ ] }),
2662
+ /* @__PURE__ */ jsx15("span", { className: "flex shrink-0 items-center self-start h-5", children: /* @__PURE__ */ jsx15("span", { className: "h-3.5 w-3.5 rounded bg-ods-bg" }) })
2663
+ ] });
2664
+ }
2665
+ const t = HORIZONTAL_SIZE_TOKENS.default;
2666
+ return /* @__PURE__ */ jsxs12(
2667
+ "span",
2668
+ {
2669
+ className: `flex items-start gap-3 rounded-md border border-ods-border bg-ods-card ${t.padding} animate-pulse`,
2670
+ children: [
2671
+ /* @__PURE__ */ jsx15("span", { className: `shrink-0 inline-flex items-center justify-center rounded-full bg-ods-bg ${t.step}` }),
2672
+ /* @__PURE__ */ jsxs12("span", { className: "flex flex-col gap-2 flex-1 min-w-0", children: [
2673
+ /* @__PURE__ */ jsx15("span", { className: "block h-4 w-2/3 rounded bg-ods-bg" }),
2674
+ /* @__PURE__ */ jsx15("span", { className: "block h-3 w-1/3 rounded bg-ods-bg/70" }),
2675
+ /* @__PURE__ */ jsx15("span", { className: "block h-3 w-full rounded bg-ods-bg/40" })
2676
+ ] })
2677
+ ]
2678
+ }
2679
+ );
2680
+ }
2681
+ function OnboardingGuideCard({
2682
+ guide,
2683
+ href,
2684
+ target: targetProp,
2685
+ rel: relProp,
2686
+ targetPlatform,
2687
+ placeholderUrl: placeholderUrlProp,
2688
+ size = "default",
2689
+ className
2690
+ }) {
2691
+ const { target, rel } = useEntityCardLink({
2692
+ href,
2693
+ targetPlatform,
2694
+ target: targetProp,
2695
+ rel: relProp
2696
+ });
2697
+ const placeholderUrl = useEntityCardPlaceholder({
2698
+ title: guide.title,
2699
+ placeholderUrl: placeholderUrlProp,
2700
+ aspect: size === "sm" ? "square" : "wide"
2701
+ });
2702
+ if (size === "catalog") {
2703
+ const coverImage = guide.featured_image || guide.main_video_thumbnail || guide.og_image_url || null;
2704
+ const hasVideoCover = !!(guide.main_video_thumbnail || guide.highlight_video_thumbnail);
2705
+ const stepLabel = typeof guide.step_order === "number" ? String(guide.step_order).padStart(2, "0") : "\u2014";
2706
+ const durationLabel = typeof guide.highlight_video_duration_ms === "number" && guide.highlight_video_duration_ms > 0 ? formatDurationMMSS(Math.floor(guide.highlight_video_duration_ms / 1e3)) : "";
2707
+ return /* @__PURE__ */ jsx15(
2708
+ next_link_default,
2709
+ {
2710
+ href,
2711
+ target,
2712
+ rel,
2713
+ prefetch: false,
2714
+ className: cn(
2715
+ "group block no-underline bg-ods-system-greys-black",
2716
+ "border border-ods-border rounded-lg overflow-hidden",
2717
+ "transition-all duration-300 ease-out",
2718
+ "transform hover:translate-y-[-2px]",
2719
+ "hover:border-ods-accent hover:shadow-lg hover:shadow-ods-accent/[0.08]",
2720
+ className
2721
+ ),
2722
+ "aria-label": `Open ${guide.title}`,
2723
+ children: /* @__PURE__ */ jsxs12("div", { className: "flex flex-col p-6 gap-4", children: [
2724
+ /* @__PURE__ */ jsxs12("div", { className: "flex flex-col md:flex-row gap-4 md:gap-6", children: [
2725
+ /* @__PURE__ */ jsx15("div", { className: "w-full md:w-[256px] flex-shrink-0", children: /* @__PURE__ */ jsxs12("div", { className: "relative rounded-lg overflow-hidden w-full aspect-[1200/630] bg-ods-bg", children: [
2726
+ coverImage ? /* @__PURE__ */ jsx15(
2727
+ next_image_default,
2728
+ {
2729
+ src: coverImage,
2730
+ alt: guide.title,
2731
+ fill: true,
2732
+ sizes: "(max-width: 768px) 100vw, 256px",
2733
+ className: "object-cover",
2734
+ unoptimized: true
2735
+ }
2736
+ ) : /* @__PURE__ */ jsx15(
2737
+ BlogImagePlaceholder,
2738
+ {
2739
+ title: guide.title,
2740
+ imageUrl: placeholderUrl ?? null,
2741
+ className: "absolute inset-0"
2742
+ }
2743
+ ),
2744
+ hasVideoCover && coverImage && /* @__PURE__ */ jsx15("span", { className: "absolute inset-0 flex items-center justify-center bg-black/30", children: /* @__PURE__ */ jsx15(Play, { className: "w-10 h-10 text-white", fill: "white" }) }),
2745
+ durationLabel && /* @__PURE__ */ jsxs12("span", { className: "absolute bottom-2 right-2 inline-flex items-center gap-1 px-2 py-1 rounded bg-black/60 text-white text-xs font-medium font-mono", children: [
2746
+ /* @__PURE__ */ jsx15(Clock, { className: "w-3 h-3" }),
2747
+ durationLabel
2748
+ ] })
2749
+ ] }) }),
2750
+ /* @__PURE__ */ jsxs12("div", { className: "flex-1 min-w-0 flex flex-col", children: [
2751
+ /* @__PURE__ */ jsx15("div", { className: "min-h-[60px] md:min-h-[72px] flex items-start mb-3", children: /* @__PURE__ */ jsx15("h3", { className: "font-['Azeret_Mono'] font-semibold text-xl md:text-2xl text-ods-text-primary leading-tight line-clamp-2", children: guide.title }) }),
2752
+ /* @__PURE__ */ jsx15("div", { className: "min-h-[46px] md:min-h-[52px]", children: /* @__PURE__ */ jsx15("p", { className: "font-['DM_Sans'] text-sm md:text-base text-ods-text-secondary leading-relaxed line-clamp-2", children: stripMarkdownPreview(guide.video_summary || guide.content || "") }) })
2753
+ ] })
2754
+ ] }),
2755
+ /* @__PURE__ */ jsx15(
2756
+ EntityAuthorCard,
2757
+ {
2758
+ author: guide.author,
2759
+ publishedAt: guide.published_at,
2760
+ renderEmptyAuthor: true,
2761
+ extraCells: [
2762
+ {
2763
+ value: `${guide.section} \xB7 Step ${stepLabel}`,
2764
+ label: "Section",
2765
+ uppercase: false
2766
+ }
2767
+ ]
2768
+ }
2769
+ )
2770
+ ] })
2771
+ }
2772
+ );
2773
+ }
2774
+ if (size === "sm") {
2775
+ const coverImage = guide.featured_image || guide.main_video_thumbnail || guide.og_image_url || null;
2776
+ const compactCover = coverImage || placeholderUrl || null;
2777
+ const hasVideoCover = !guide.featured_image && !!guide.main_video_thumbnail;
2778
+ const summary2 = stripMarkdownPreview(guide.video_summary || guide.content || "");
2779
+ const author = guide.author?.full_name?.trim() || "";
2780
+ const subtitleParts = [
2781
+ `Step ${guide.step_order}`,
2782
+ guide.section,
2783
+ author
2784
+ ].filter((s) => typeof s === "string" && s.length > 0);
2785
+ return /* @__PURE__ */ jsxs12(next_link_default, { href, target, rel, prefetch: false, className: cn(COMPACT_CARD_OUTER, className), children: [
2786
+ /* @__PURE__ */ jsxs12("span", { className: COMPACT_CARD_IMAGE_SLOT, children: [
2787
+ compactCover ? /* @__PURE__ */ jsx15(
2788
+ next_image_default,
2789
+ {
2790
+ src: compactCover,
2791
+ alt: guide.title,
2792
+ fill: true,
2793
+ sizes: "56px",
2794
+ className: "object-contain",
2795
+ unoptimized: true
2796
+ }
2797
+ ) : /* @__PURE__ */ jsx15("span", { className: "flex h-full w-full items-center justify-center text-ods-accent", children: /* @__PURE__ */ jsx15(GraduationCap, { className: "w-4 h-4" }) }),
2798
+ hasVideoCover && compactCover && /* @__PURE__ */ jsx15("span", { className: "absolute inset-0 flex items-center justify-center bg-black/30", children: /* @__PURE__ */ jsx15(Play, { className: "h-4 w-4 text-white", fill: "white" }) })
2799
+ ] }),
2800
+ /* @__PURE__ */ jsxs12("span", { className: COMPACT_CARD_TEXT_COL, children: [
2801
+ /* @__PURE__ */ jsx15("span", { className: COMPACT_CARD_TITLE_ROW, children: /* @__PURE__ */ jsx15("span", { className: cn(COMPACT_CARD_TITLE, "font-['Azeret_Mono']"), children: guide.title }) }),
2802
+ /* @__PURE__ */ jsx15("span", { className: COMPACT_CARD_META_ROW_BOX, children: /* @__PURE__ */ jsx15("span", { className: "truncate text-[11px] leading-4 text-[var(--color-accent-primary)]", children: subtitleParts.join(" \xB7 ") }) }),
2803
+ /* @__PURE__ */ jsx15("span", { className: COMPACT_CARD_META_ROW_BOX, children: /* @__PURE__ */ jsx15("span", { className: COMPACT_CARD_SUMMARY, children: summary2 || COMPACT_CARD_ROW_FILLER }) })
2804
+ ] }),
2805
+ /* @__PURE__ */ jsx15("span", { className: "flex shrink-0 items-center self-start h-5 text-ods-text-secondary", children: /* @__PURE__ */ jsx15(ExternalLink, { className: "w-3.5 h-3.5" }) })
2806
+ ] });
2807
+ }
2808
+ const t = HORIZONTAL_SIZE_TOKENS.default;
2809
+ const summary = stripMarkdownPreview(guide.video_summary || guide.content || "");
2810
+ return /* @__PURE__ */ jsxs12(
2811
+ next_link_default,
2812
+ {
2813
+ href,
2814
+ target,
2815
+ rel,
2816
+ prefetch: false,
2817
+ className: cn(
2818
+ `flex items-start gap-3 rounded-md border border-ods-border bg-ods-card hover:border-ods-accent transition-colors ${t.padding}`,
2819
+ className
2820
+ ),
2821
+ children: [
2822
+ /* @__PURE__ */ jsx15(
2823
+ "span",
2824
+ {
2825
+ className: `shrink-0 inline-flex items-center justify-center rounded-full bg-ods-accent/10 text-ods-accent font-semibold ${t.step}`,
2826
+ "aria-hidden": "true",
2827
+ children: guide.step_order
2828
+ }
2829
+ ),
2830
+ /* @__PURE__ */ jsxs12("span", { className: "flex flex-col gap-0.5 flex-1 min-w-0", children: [
2831
+ /* @__PURE__ */ jsx15("span", { className: `block ${t.title} text-ods-text-primary truncate`, children: guide.title }),
2832
+ /* @__PURE__ */ jsxs12("span", { className: "inline-flex items-center gap-1 font-['DM_Sans'] text-[12px] leading-[16px] text-ods-text-secondary", children: [
2833
+ /* @__PURE__ */ jsx15(GraduationCap, { className: "h-3 w-3 shrink-0" }),
2834
+ /* @__PURE__ */ jsx15("span", { className: "truncate", children: guide.section })
2835
+ ] }),
2836
+ summary && /* @__PURE__ */ jsx15("span", { className: `font-['DM_Sans'] text-[14px] leading-[20px] text-ods-text-secondary ${t.summaryClamp}`, children: summary })
2837
+ ] })
2838
+ ]
2839
+ }
2840
+ );
2841
+ }
2842
+
2843
+ // src/utils/source-icons.ts
2844
+ var SOURCE_ICON_NAMES = {
2845
+ // Doc tables
2846
+ "openframe-docs": "openframe",
2847
+ "data-room-docs": "shield",
2848
+ // CMS / programs
2849
+ "blog-posts": "newspaper",
2850
+ "product-releases": "rocket",
2851
+ "case-studies": "briefcase",
2852
+ "onboarding-guides": "graduation-cap",
2853
+ webinars: "video",
2854
+ events: "calendar",
2855
+ podcasts: "headphones",
2856
+ "customer-interviews": "users",
2857
+ // Financials
2858
+ "investor-updates": "mail",
2859
+ "financial-kpis": "activity",
2860
+ "financial-cap-table": "table",
2861
+ "financial-pnl": "trending-up",
2862
+ "financial-balance-sheet": "dollar-sign",
2863
+ "financial-cash-flow": "banknote",
2864
+ // ClickUp
2865
+ "clickup-roadmap": "clickup",
2866
+ "clickup-delivery": "clickup",
2867
+ "clickup-tasks-internal": "clickup",
2868
+ // GitHub
2869
+ "github-commits": "github",
2870
+ "github-pull-requests": "github",
2871
+ "github-pr-reviews": "github",
2872
+ "github-commits-public": "github",
2873
+ "github-pull-requests-public": "github",
2874
+ "github-pr-reviews-public": "github",
2875
+ // HubSpot
2876
+ "hubspot-tickets": "hubspot",
2877
+ "hubspot-tickets-anon": "hubspot",
2878
+ "hubspot-tickets-self": "hubspot",
2879
+ // Communications
2880
+ "slack-messages": "slack"
2881
+ };
2882
+ function getSourceIconName(tableId) {
2883
+ if (!tableId) return void 0;
2884
+ return SOURCE_ICON_NAMES[tableId];
2885
+ }
2886
+ var SOURCE_LABELS_BY_TABLE = {
2887
+ // Doc tables
2888
+ "openframe-docs": "OpenFrame Docs",
2889
+ "data-room-docs": "Data Room",
2890
+ // CMS / programs
2891
+ "blog-posts": "Blog Posts",
2892
+ "product-releases": "Product Releases",
2893
+ "case-studies": "Case Studies",
2894
+ "onboarding-guides": "Onboarding Guides",
2895
+ webinars: "Webinars",
2896
+ events: "Events",
2897
+ podcasts: "Podcasts",
2898
+ "customer-interviews": "Customer Interviews",
2899
+ // Financials
2900
+ "investor-updates": "Investor Updates",
2901
+ "financial-kpis": "Financial KPIs",
2902
+ "financial-cap-table": "Cap Table",
2903
+ "financial-pnl": "Profit & Loss",
2904
+ "financial-balance-sheet": "Balance Sheet",
2905
+ "financial-cash-flow": "Cash Flow",
2906
+ // ClickUp
2907
+ "clickup-roadmap": "ClickUp Roadmap",
2908
+ "clickup-delivery": "ClickUp Delivery",
2909
+ "clickup-tasks-internal": "ClickUp Tasks",
2910
+ // GitHub
2911
+ "github-commits": "GitHub Commits",
2912
+ "github-pull-requests": "GitHub Pull Requests",
2913
+ "github-pr-reviews": "GitHub PR Reviews",
2914
+ "github-commits-public": "OpenFrame Commits",
2915
+ "github-pull-requests-public": "OpenFrame Pull Requests",
2916
+ "github-pr-reviews-public": "OpenFrame PR Reviews",
2917
+ // HubSpot
2918
+ "hubspot-tickets": "HubSpot Tickets",
2919
+ "hubspot-tickets-anon": "Known Issues",
2920
+ "hubspot-tickets-self": "My Tickets",
2921
+ // Communications
2922
+ "slack-messages": "OpenMSP Community"
2923
+ };
2924
+ function getSourceLabel(tableId) {
2925
+ return SOURCE_LABELS_BY_TABLE[tableId] ?? tableId;
2926
+ }
2927
+ var DEFAULT_DOCUMENT_TYPE_TO_TABLE_ID = {
2928
+ // Doc tables
2929
+ markdown: "openframe-docs",
2930
+ data_room_doc: "data-room-docs",
2931
+ // CMS / programs
2932
+ blog_post: "blog-posts",
2933
+ product_release: "product-releases",
2934
+ case_study: "case-studies",
2935
+ onboarding_guide: "onboarding-guides",
2936
+ webinar: "webinars",
2937
+ event: "events",
2938
+ podcast: "podcasts",
2939
+ customer_interview: "customer-interviews",
2940
+ // Financials
2941
+ investor_update: "investor-updates",
2942
+ financial_kpi: "financial-kpis",
2943
+ cap_table: "financial-cap-table",
2944
+ profit_loss: "financial-pnl",
2945
+ balance_sheet: "financial-balance-sheet",
2946
+ cash_flow: "financial-cash-flow",
2947
+ // ClickUp
2948
+ roadmap_item: "clickup-roadmap",
2949
+ delivery_item: "clickup-delivery",
2950
+ internal_task: "clickup-tasks-internal",
2951
+ // GitHub
2952
+ github_commit: "github-commits",
2953
+ github_pull_request: "github-pull-requests",
2954
+ github_pr_review: "github-pr-reviews",
2955
+ github_commit_public: "github-commits-public",
2956
+ github_pull_request_public: "github-pull-requests-public",
2957
+ github_pr_review_public: "github-pr-reviews-public",
2958
+ // HubSpot
2959
+ hubspot_ticket: "hubspot-tickets",
2960
+ hubspot_ticket_anon: "hubspot-tickets-anon",
2961
+ hubspot_ticket_self: "hubspot-tickets-self",
2962
+ // Communications
2963
+ slack_message: "slack-messages"
2964
+ };
2965
+ function defaultTableIdForDocumentType(documentType) {
2966
+ return DEFAULT_DOCUMENT_TYPE_TO_TABLE_ID[documentType] ?? null;
2967
+ }
2968
+
2969
+ // src/components/chat/utils/icon-registry.ts
2970
+ import { createElement } from "react";
2971
+ import {
2972
+ Activity,
2973
+ Banknote,
2974
+ Bell,
2975
+ BookOpen,
2976
+ Box,
2977
+ Briefcase,
2978
+ Calendar,
2979
+ CheckSquare,
2980
+ DollarSign,
2981
+ Facebook,
2982
+ FileText,
2983
+ Github,
2984
+ Globe,
2985
+ GraduationCap as GraduationCap2,
2986
+ Headphones,
2987
+ Info,
2988
+ Instagram,
2989
+ Mail,
2990
+ Megaphone,
2991
+ MessageCircle,
2992
+ MessageSquare,
2993
+ Newspaper,
2994
+ Package,
2995
+ PenSquare,
2996
+ Rocket,
2997
+ Search,
2998
+ Send,
2999
+ Shield,
3000
+ Star,
3001
+ TableProperties,
3002
+ TrendingUp,
3003
+ Twitter,
3004
+ Users,
3005
+ Video as Video2,
3006
+ Youtube
3007
+ } from "lucide-react";
3008
+ var OpenFrameLogoIcon = ({ className }) => createElement(OpenFrameLogo, { className, upperPathColor: "currentColor" });
3009
+ var ICON_REGISTRY = {
3010
+ // lucide-react (kebab-case)
3011
+ activity: Activity,
3012
+ banknote: Banknote,
3013
+ bell: Bell,
3014
+ "book-open": BookOpen,
3015
+ box: Box,
3016
+ briefcase: Briefcase,
3017
+ calendar: Calendar,
3018
+ "check-square": CheckSquare,
3019
+ "dollar-sign": DollarSign,
3020
+ // For brand-vs-lucide variants the kebab key is the BRAND (most-common
3021
+ // social-platform intent); `'<name>-lucide'` carries the lucide outline.
3022
+ facebook: FacebookIcon,
3023
+ "facebook-lucide": Facebook,
3024
+ "file-text": FileText,
3025
+ // `'github-lucide'` is the lucide outline glyph (used by
3026
+ // `social_platforms.icon_name='Github'`); `'github'` is the brand icon
3027
+ // (used by `chat_admin_slash_commands.icon_name='github'`). The PascalCase
3028
+ // alias `'Github' → 'github-lucide'` routes the lucide glyph for
3029
+ // social-platform DB rows; `'GitHubIcon' → 'github'` keeps brand routing
3030
+ // consistent for callers that store the PascalCase component name.
3031
+ github: GitHubIcon,
3032
+ "github-lucide": Github,
3033
+ globe: Globe,
3034
+ "graduation-cap": GraduationCap2,
3035
+ headphones: Headphones,
3036
+ info: Info,
3037
+ instagram: InstagramIcon,
3038
+ "instagram-lucide": Instagram,
3039
+ mail: Mail,
3040
+ megaphone: Megaphone,
3041
+ "message-circle": MessageCircle,
3042
+ "message-square": MessageSquare,
3043
+ newspaper: Newspaper,
3044
+ package: Package,
3045
+ "pen-square": PenSquare,
3046
+ rocket: Rocket,
3047
+ search: Search,
3048
+ send: Send,
3049
+ shield: Shield,
3050
+ star: Star,
3051
+ table: TableProperties,
3052
+ "trending-up": TrendingUp,
3053
+ twitter: Twitter,
3054
+ users: Users,
3055
+ video: Video2,
3056
+ youtube: YouTubeIcon,
3057
+ "youtube-lucide": Youtube,
3058
+ // brand-only icons (no lucide variant in current use)
3059
+ slack: SlackIcon,
3060
+ clickup: ClickUpIcon,
3061
+ hubspot: HubspotIcon,
3062
+ linkedin: LinkedInIcon,
3063
+ x: XLogo,
3064
+ openframe: OpenFrameLogoIcon
3065
+ };
3066
+ var PASCAL_TO_KEBAB_ALIASES = {
3067
+ // Lucide PascalCase names → kebab equivalents
3068
+ Activity: "activity",
3069
+ Banknote: "banknote",
3070
+ Bell: "bell",
3071
+ BookOpen: "book-open",
3072
+ Box: "box",
3073
+ Briefcase: "briefcase",
3074
+ Calendar: "calendar",
3075
+ CheckSquare: "check-square",
3076
+ DollarSign: "dollar-sign",
3077
+ // Lucide PascalCase → the `-lucide` kebab variant (kebab default is the
3078
+ // brand icon for the social-platform names below).
3079
+ Facebook: "facebook-lucide",
3080
+ FileText: "file-text",
3081
+ Github: "github-lucide",
3082
+ Globe: "globe",
3083
+ GraduationCap: "graduation-cap",
3084
+ Headphones: "headphones",
3085
+ Info: "info",
3086
+ Instagram: "instagram-lucide",
3087
+ Mail: "mail",
3088
+ Megaphone: "megaphone",
3089
+ MessageCircle: "message-circle",
3090
+ MessageSquare: "message-square",
3091
+ Newspaper: "newspaper",
3092
+ Package: "package",
3093
+ PenSquare: "pen-square",
3094
+ Rocket: "rocket",
3095
+ Search: "search",
3096
+ Send: "send",
3097
+ Shield: "shield",
3098
+ Star: "star",
3099
+ TableProperties: "table",
3100
+ TrendingUp: "trending-up",
3101
+ Twitter: "twitter",
3102
+ Users: "users",
3103
+ Video: "video",
3104
+ Youtube: "youtube-lucide",
3105
+ // Brand-icon PascalCase exports → kebab (kebab default is the brand)
3106
+ SlackIcon: "slack",
3107
+ GitHubIcon: "github",
3108
+ ClickUpIcon: "clickup",
3109
+ HubspotIcon: "hubspot",
3110
+ LinkedInIcon: "linkedin",
3111
+ FacebookIcon: "facebook",
3112
+ InstagramIcon: "instagram",
3113
+ YouTubeIcon: "youtube",
3114
+ XLogo: "x",
3115
+ OpenFrameLogo: "openframe"
3116
+ };
3117
+ function normalizeIconKey(key) {
3118
+ return PASCAL_TO_KEBAB_ALIASES[key] ?? key;
3119
+ }
3120
+ function getIconComponent(iconName) {
3121
+ if (!iconName) return FileText;
3122
+ return ICON_REGISTRY[normalizeIconKey(iconName)] ?? FileText;
3123
+ }
3124
+ var SIZE_CLASSES = {
3125
+ xs: "w-3 h-3",
3126
+ // 12px
3127
+ sm: "w-4 h-4",
3128
+ // 16px
3129
+ md: "w-6 h-6",
3130
+ // 24px
3131
+ lg: "w-8 h-8",
3132
+ // 32px
3133
+ xl: "w-12 h-12"
3134
+ // 48px
3135
+ };
3136
+ var ICON_COLORS = {
3137
+ linkedin: { color: "#0A66C2" },
3138
+ facebook: { color: "#1877F2" },
3139
+ youtube: { color: "#FF0000" }
3140
+ };
3141
+ function getDynamicIcon(iconName, size = "md", className) {
3142
+ if (!iconName) {
3143
+ console.warn("[getDynamicIcon] No iconName provided, using Globe fallback");
3144
+ return createElement(Globe, { className: SIZE_CLASSES[size] });
3145
+ }
3146
+ const sizeClass = SIZE_CLASSES[size];
3147
+ const finalClassName = className ? `${sizeClass} ${className}` : sizeClass;
3148
+ const canonicalKey = normalizeIconKey(iconName);
3149
+ const IconComponent = ICON_REGISTRY[canonicalKey];
3150
+ if (IconComponent) {
3151
+ const colorConfig = ICON_COLORS[canonicalKey] || {};
3152
+ return createElement(IconComponent, { className: finalClassName, ...colorConfig });
3153
+ }
3154
+ console.error(
3155
+ `[getDynamicIcon] Icon NOT found in registry: "${iconName}" (normalized to "${canonicalKey}"). Available icons:`,
3156
+ Object.keys(ICON_REGISTRY)
3157
+ );
3158
+ return createElement(Globe, { className: finalClassName });
3159
+ }
3160
+
3161
+ // src/components/chat/utils/source-row-cta.ts
3162
+ import { FileText as FileText2 } from "lucide-react";
3163
+ init_cn();
3164
+ function sanitizePath(path) {
3165
+ return (path || "").replace(/[^a-zA-Z0-9/_\-.]/g, "");
3166
+ }
3167
+ function sourceRowCtxFromRuntime(runtime, surface = {}) {
3168
+ return {
3169
+ currentPlatform: runtime.source ?? null,
3170
+ composeContentUrl: runtime.composeContentUrl,
3171
+ docPlatformTargets: runtime.docPlatformTargets,
3172
+ baseRoute: surface.baseRoute,
3173
+ chipBasePlatform: surface.chipBasePlatform
3174
+ };
3175
+ }
3176
+ function pickSourceIcon(sourceRepo, documentType) {
3177
+ const iconName = sourceRepo ? getSourceIconName(sourceRepo) : void 0;
3178
+ const icon = iconName ? getIconComponent(iconName) : FileText2;
3179
+ const iconLabel = documentType ?? "Source";
3180
+ return { icon, iconLabel };
3181
+ }
3182
+ var DOC_TABLE_TYPES = ["markdown", "data_room_doc"];
3183
+ function shouldFallbackToPathNav(row) {
3184
+ return !!row.documentType && DOC_TABLE_TYPES.includes(row.documentType);
3185
+ }
3186
+ function resolveSourceRowCTA(row, ctx = {}) {
3187
+ const sourceRepo = row.sourceRepo ?? (row.documentType && ctx.tableIdForDocumentType ? ctx.tableIdForDocumentType(row.documentType) : null) ?? null;
3188
+ const { icon, iconLabel } = pickSourceIcon(sourceRepo, row.documentType);
3189
+ let href = null;
3190
+ let targetPlatform = null;
3191
+ const idValue = (row.id ?? "").trim();
3192
+ if (row.path && shouldFallbackToPathNav(row) && !row.externalUrl) {
3193
+ const safePath = sanitizePath(row.path);
3194
+ if (safePath) {
3195
+ const docTarget = row.documentType ? ctx.docPlatformTargets?.[row.documentType] : void 0;
3196
+ if (docTarget) {
3197
+ const seg = docTarget.basePath.split("/").filter(Boolean).join("/");
3198
+ const base = `${getBaseUrl(docTarget.platform)}${seg ? `/${seg}` : ""}/`;
3199
+ href = safeHref(new URL(safePath, base).toString()) ?? null;
3200
+ targetPlatform = docTarget.platform;
3201
+ } else if (ctx.chipBasePlatform) {
3202
+ const base = `${getBaseUrl(ctx.chipBasePlatform)}/knowledge-base/`;
3203
+ href = safeHref(new URL(safePath, base).toString()) ?? null;
3204
+ targetPlatform = ctx.chipBasePlatform;
3205
+ } else if (ctx.baseRoute) {
3206
+ const synthetic = `https://_internal_.local${ctx.baseRoute.startsWith("/") ? ctx.baseRoute : "/" + ctx.baseRoute}/`;
3207
+ const absolute = new URL(safePath, synthetic).toString();
3208
+ href = safeHref(absolute.replace("https://_internal_.local", "")) ?? null;
3209
+ targetPlatform = ctx.currentPlatform ?? null;
3210
+ }
3211
+ }
3212
+ } else if (ctx.composeContentUrl && row.documentType && row.externalUrl) {
3213
+ const composed = ctx.composeContentUrl({
3214
+ type: row.documentType,
3215
+ identifier: idValue,
3216
+ externalUrl: row.externalUrl,
3217
+ targetPlatform: row.targetPlatform ?? null
3218
+ });
3219
+ href = composed.href ? safeHref(composed.href) ?? null : null;
3220
+ targetPlatform = composed.targetPlatform;
3221
+ } else if (row.externalUrl) {
3222
+ href = safeHref(row.externalUrl) ?? null;
3223
+ targetPlatform = row.targetPlatform ?? null;
3224
+ }
3225
+ const askable = !!(idValue && row.documentType);
3226
+ const chatRef = askable ? {
3227
+ type: row.documentType,
3228
+ ...sourceRepo ? { sourceRepo } : {},
3229
+ id: idValue,
3230
+ title: row.title,
3231
+ url: href,
3232
+ targetPlatform,
3233
+ // Carry `path` so a downstream inline card (rendered from this
3234
+ // chatRef on a subsequent turn) gets the same doc-tree-swap
3235
+ // routing the chip already used. Without it, the Ask drill-in
3236
+ // would resolve to a same-tab full-page nav even when an in-app
3237
+ // path is available.
3238
+ ...row.path ? { metadata: { path: row.path } } : {}
3239
+ } : null;
3240
+ return { icon, iconLabel, href, targetPlatform, askable, chatRef };
3241
+ }
3242
+ function resolveSourceIcon(input, ctx = {}) {
3243
+ if (!input) return { Icon: FileText2, label: "Source" };
3244
+ const { sourceRepo, documentType } = typeof input === "string" ? { sourceRepo: null, documentType: input } : input;
3245
+ if (!sourceRepo && !documentType) return { Icon: FileText2, label: "Source" };
3246
+ const resolvedRepo = sourceRepo ?? (documentType && ctx.tableIdForDocumentType ? ctx.tableIdForDocumentType(documentType) : null) ?? null;
3247
+ const { icon, iconLabel } = pickSourceIcon(resolvedRepo, documentType);
3248
+ return { Icon: icon, label: iconLabel };
3249
+ }
3250
+
3251
+ // src/components/ui/page-actions.tsx
3252
+ init_cn();
3253
+ import React11 from "react";
3254
+ init_button();
3255
+ import { Fragment as Fragment4, jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
3256
+ function actionKey(action, idx) {
3257
+ return `${action.label ?? action.ariaLabel ?? "action"}-${idx}`;
3258
+ }
3259
+ function actionToMenuItems(action, idx) {
3260
+ if (action.submenu && action.submenu.length > 0) {
3261
+ if (!action.label) return action.submenu;
3262
+ return action.submenu.map((item) => ({
3263
+ ...item,
3264
+ label: `${action.label} (${item.label})`
3265
+ }));
3266
+ }
3267
+ if (!action.label) return [];
3268
+ return [{
3269
+ id: `action-${idx}`,
3270
+ label: action.label,
3271
+ icon: action.icon,
3272
+ onClick: action.onClick,
3273
+ disabled: action.disabled,
3274
+ href: action.href,
3275
+ iconAction: action.iconAction ? {
3276
+ icon: action.iconAction.icon,
3277
+ "aria-label": action.iconAction["aria-label"],
3278
+ onClick: action.iconAction.onClick,
3279
+ href: action.iconAction.href,
3280
+ openInNewTab: action.iconAction.openInNewTab,
3281
+ disabled: action.iconAction.disabled
3282
+ } : void 0
3283
+ }];
3284
+ }
3285
+ function renderActionButton(action, opts = {}) {
3286
+ if (action.iconAction) {
3287
+ return /* @__PURE__ */ jsx16(
3288
+ SplitButton,
3289
+ {
3290
+ variant: action.variant ?? void 0,
3291
+ href: action.href,
3292
+ prefetch: action.prefetch,
3293
+ openInNewTab: action.openInNewTab,
3294
+ onClick: action.onClick,
3295
+ disabled: action.disabled,
3296
+ mainDisabled: action.mainDisabled,
3297
+ leftIcon: action.icon,
3298
+ fullWidth: opts.fullWidth,
3299
+ iconAction: action.iconAction,
3300
+ children: action.label
3301
+ }
3302
+ );
3303
+ }
3304
+ if (action.submenu && action.submenu.length > 0) {
3305
+ return /* @__PURE__ */ jsx16(
3306
+ ActionsMenuDropdown,
3307
+ {
3308
+ groups: [{ items: action.submenu }],
3309
+ customTrigger: /* @__PURE__ */ jsx16(
3310
+ Button,
3311
+ {
3312
+ variant: "outline",
3313
+ disabled: action.disabled,
3314
+ loading: action.loading,
3315
+ leftIcon: action.icon,
3316
+ splitIcon: /* @__PURE__ */ jsx16(Chevron02DownIcon, { className: "h-4 w-4" }),
3317
+ className: opts.fullWidth ? "flex-1" : void 0,
3318
+ children: action.label
3319
+ }
3320
+ )
3321
+ }
3322
+ );
3323
+ }
3324
+ const isIconOnly = opts.iconOnly || !action.label || action.iconOnlyOnDesktop;
3325
+ if (isIconOnly) {
3326
+ const iconNode = action.iconOnlyOnDesktop ? /* @__PURE__ */ jsx16("span", { className: "inline-flex [&_svg]:!text-ods-text-primary", children: action.icon }) : action.icon;
3327
+ return /* @__PURE__ */ jsx16(
3328
+ Button,
3329
+ {
3330
+ variant: action.variant,
3331
+ size: "icon",
3332
+ href: action.href,
3333
+ prefetch: action.prefetch,
3334
+ openInNewTab: action.openInNewTab,
3335
+ onClick: action.onClick,
3336
+ disabled: action.disabled,
3337
+ loading: action.loading,
3338
+ leftIcon: iconNode,
3339
+ "aria-label": action.label ?? action.ariaLabel
3340
+ }
3341
+ );
3342
+ }
3343
+ return /* @__PURE__ */ jsx16(
3344
+ Button,
3345
+ {
3346
+ variant: action.variant,
3347
+ href: action.href,
3348
+ prefetch: action.prefetch,
3349
+ openInNewTab: action.openInNewTab,
3350
+ onClick: action.onClick,
3351
+ disabled: action.disabled,
3352
+ loading: action.loading,
3353
+ leftIcon: action.icon,
3354
+ className: opts.fullWidth ? "flex-1" : void 0,
3355
+ children: action.label
3356
+ }
3357
+ );
3358
+ }
3359
+ var ACTIONS_GAP = "gap-[var(--spacing-system-xs)]";
3360
+ function PageActions({
3361
+ variant = "icon-buttons",
3362
+ actions,
3363
+ menuActions,
3364
+ selector,
3365
+ className
3366
+ }) {
3367
+ if (variant === "icon-buttons") {
3368
+ return /* @__PURE__ */ jsx16(IconButtonsVariant, { actions, menuActions, selector, className });
3369
+ }
3370
+ if (variant === "menu-primary") {
3371
+ return /* @__PURE__ */ jsx16(MenuPrimaryVariant, { actions, menuActions: menuActions || [], selector, className });
3372
+ }
3373
+ return /* @__PURE__ */ jsx16(PrimaryButtonsVariant, { actions, className });
3374
+ }
3375
+ function IconButtonsVariant({
3376
+ actions,
3377
+ menuActions,
3378
+ selector,
3379
+ className
3380
+ }) {
3381
+ const desktopActions = actions.filter((a) => !a.showOnlyMobile);
3382
+ const hasMenuActions = !!menuActions && menuActions.some((g) => g.items.length > 0);
3383
+ const isSingleAction = actions.length === 1 && !actions[0].submenu?.length;
3384
+ const singleAction = isSingleAction ? actions[0] : null;
3385
+ const useSingleActionMobile = isSingleAction && !hasMenuActions;
3386
+ return /* @__PURE__ */ jsxs13(Fragment4, { children: [
3387
+ /* @__PURE__ */ jsxs13("div", { className: cn("hidden md:flex items-center", ACTIONS_GAP, className), children: [
3388
+ selector,
3389
+ desktopActions.map((action, idx) => /* @__PURE__ */ jsx16(React11.Fragment, { children: renderActionButton(action) }, actionKey(action, idx))),
3390
+ hasMenuActions && /* @__PURE__ */ jsx16(ActionsMenuDropdown, { groups: menuActions })
3391
+ ] }),
3392
+ /* @__PURE__ */ jsx16("div", { className: cn("flex md:hidden", className), children: useSingleActionMobile && singleAction ? renderActionButton(singleAction, { iconOnly: true }) : /* @__PURE__ */ jsx16(
3393
+ ActionsMenuDropdown,
3394
+ {
3395
+ groups: [
3396
+ { items: actions.flatMap(actionToMenuItems) },
3397
+ ...menuActions ?? []
3398
+ ]
3399
+ }
3400
+ ) })
3401
+ ] });
3402
+ }
3403
+ function PrimaryButtonsVariant({
3404
+ actions,
3405
+ className
3406
+ }) {
3407
+ const sortedActions = [...actions].sort((a, b) => {
3408
+ if (a.variant === "accent" && b.variant !== "accent") return 1;
3409
+ if (a.variant !== "accent" && b.variant === "accent") return -1;
3410
+ return 0;
3411
+ });
3412
+ const desktopActions = sortedActions.filter((a) => !a.showOnlyMobile);
3413
+ return /* @__PURE__ */ jsxs13(Fragment4, { children: [
3414
+ /* @__PURE__ */ jsx16("div", { className: cn("hidden md:flex items-center", ACTIONS_GAP, className), children: desktopActions.map((action, idx) => /* @__PURE__ */ jsx16(React11.Fragment, { children: renderActionButton(action) }, `desktop-${actionKey(action, idx)}`)) }),
3415
+ /* @__PURE__ */ jsx16(MobileBottomActions, { actions: sortedActions })
3416
+ ] });
3417
+ }
3418
+ function MenuPrimaryVariant({
3419
+ actions,
3420
+ menuActions,
3421
+ selector,
3422
+ className
3423
+ }) {
3424
+ const desktopActions = actions.filter((a) => !a.showOnlyMobile);
3425
+ const hasMenuActions = menuActions.some((g) => g.items.length > 0);
3426
+ return /* @__PURE__ */ jsxs13(Fragment4, { children: [
3427
+ /* @__PURE__ */ jsxs13("div", { className: cn("hidden md:flex items-center", ACTIONS_GAP, className), children: [
3428
+ selector,
3429
+ hasMenuActions && /* @__PURE__ */ jsx16(ActionsMenuDropdown, { groups: menuActions }),
3430
+ desktopActions.map((action, idx) => /* @__PURE__ */ jsx16(React11.Fragment, { children: renderActionButton({ ...action, variant: action.variant || "accent" }) }, `desktop-${actionKey(action, idx)}`))
3431
+ ] }),
3432
+ /* @__PURE__ */ jsx16("div", { className: cn("flex md:hidden", className), children: /* @__PURE__ */ jsx16(
3433
+ ActionsMenuDropdown,
3434
+ {
3435
+ groups: [
3436
+ { items: actions.flatMap(actionToMenuItems) },
3437
+ ...menuActions
3438
+ ]
3439
+ }
3440
+ ) })
3441
+ ] });
3442
+ }
3443
+ function MobileBottomActions({ actions }) {
3444
+ return /* @__PURE__ */ jsx16("div", { className: cn(
3445
+ "fixed md:hidden bottom-0 left-0 right-0 z-50",
3446
+ "bg-ods-card border-t border-ods-border",
3447
+ "flex items-start pt-6 pb-6 px-6",
3448
+ ACTIONS_GAP
3449
+ ), children: actions.map((action, idx) => /* @__PURE__ */ jsx16(React11.Fragment, { children: renderActionButton(action, { fullWidth: !!action.label }) }, `mobile-${actionKey(action, idx)}`)) });
3450
+ }
3451
+ function usePageActionsBottomPadding(variant) {
3452
+ return variant === "primary-buttons" || variant === "menu-primary" ? "pb-40 md:pb-0" : "";
3453
+ }
3454
+
3455
+ // src/components/layout/page-container.tsx
3456
+ init_cn();
3457
+
3458
+ // src/components/layout/back-button.tsx
3459
+ init_cn();
3460
+ import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
3461
+ function BackButton({ label = "Back", className, type = "button", ...props }) {
3462
+ return /* @__PURE__ */ jsxs14(
3463
+ "button",
3464
+ {
3465
+ type,
3466
+ className: cn(
3467
+ "group inline-flex items-center justify-center self-start rounded-md",
3468
+ "gap-[var(--spacing-system-xsf)] py-[var(--spacing-system-sf)]",
3469
+ "text-ods-text-secondary hover:text-ods-text-primary",
3470
+ "transition-colors duration-200",
3471
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ods-focus",
3472
+ className
3473
+ ),
3474
+ ...props,
3475
+ children: [
3476
+ /* @__PURE__ */ jsx17(Chevron02LeftIcon, { className: "size-6 shrink-0" }),
3477
+ /* @__PURE__ */ jsx17("span", { className: "text-h4", children: label })
3478
+ ]
3479
+ }
3480
+ );
3481
+ }
3482
+
3483
+ // src/components/layout/page-container.tsx
3484
+ import { jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
3485
+ function isAdvancedProps(props) {
3486
+ return "variant" in props || "title" in props || "subtitle" in props || "backButton" in props || "headerActions" in props || "headerContent" in props || "showHeader" in props || "contentClassName" in props || "actions" in props || "actionsVariant" in props;
3487
+ }
3488
+ function PageContainer(props) {
3489
+ if (isAdvancedProps(props)) {
3490
+ return renderAdvancedPageContainer(props);
3491
+ } else {
3492
+ return renderLegacyPageContainer(props);
3493
+ }
3494
+ }
3495
+ function renderLegacyPageContainer({
3496
+ children,
3497
+ className = "",
3498
+ fullWidthBackground = true,
3499
+ backgroundClassName = "bg-ods-bg",
3500
+ backgroundStyle,
3501
+ contentPadding,
3502
+ maxWidth = "max-w-[1920px]",
3503
+ as: Component = "section",
3504
+ id
3505
+ }) {
3506
+ const content = /* @__PURE__ */ jsx18("div", { className: cn(maxWidth, contentPadding, "mx-auto", className), children });
3507
+ if (fullWidthBackground) {
3508
+ return /* @__PURE__ */ jsx18(
3509
+ Component,
3510
+ {
3511
+ className: cn("w-full", backgroundClassName),
3512
+ style: backgroundStyle,
3513
+ id,
3514
+ children: content
3515
+ }
3516
+ );
3517
+ }
3518
+ return /* @__PURE__ */ jsx18(Component, { className: "w-full", id, children: /* @__PURE__ */ jsx18("div", { className: cn(maxWidth, contentPadding, "mx-auto", backgroundClassName, className), style: backgroundStyle, children }) });
3519
+ }
3520
+ function renderAdvancedPageContainer({
3521
+ children,
3522
+ variant = "content",
3523
+ title,
3524
+ subtitle,
3525
+ backButton,
3526
+ headerActions,
3527
+ headerContent,
3528
+ actions,
3529
+ actionsVariant,
3530
+ menuActions,
3531
+ padding = "none",
3532
+ background = "transparent",
3533
+ className,
3534
+ contentClassName,
3535
+ showHeader = true
3536
+ }) {
3537
+ const getActionsVariant = () => {
3538
+ if (actionsVariant) return actionsVariant;
3539
+ if (variant === "list") return "icon-buttons";
3540
+ return "primary-buttons";
3541
+ };
3542
+ const renderActions = () => {
3543
+ if (!actions || actions.length === 0) return null;
3544
+ return /* @__PURE__ */ jsx18(PageActions, { variant: getActionsVariant(), actions, menuActions });
3545
+ };
3546
+ const needsBottomPadding = actions && actions.length > 0 && getActionsVariant() === "primary-buttons";
3547
+ const paddingClasses = {
3548
+ none: "",
3549
+ sm: "p-4",
3550
+ md: "p-6",
3551
+ lg: "p-8"
3552
+ };
3553
+ const backgroundClasses = {
3554
+ default: "bg-ods-bg",
3555
+ card: "bg-ods-card",
3556
+ transparent: ""
3557
+ };
3558
+ const renderHeader = () => {
3559
+ if (!showHeader) return null;
3560
+ if (headerContent) {
3561
+ return /* @__PURE__ */ jsx18("div", { className: cn(
3562
+ "flex items-end justify-between gap-4"
3563
+ ), children: headerContent });
3564
+ }
3565
+ if (variant === "detail") {
3566
+ return /* @__PURE__ */ jsxs15("div", { className: "flex items-end justify-between md:flex-col md:items-start md:justify-start lg:flex-row lg:items-end lg:justify-between gap-4", children: [
3567
+ /* @__PURE__ */ jsxs15("div", { className: "flex flex-col gap-2 flex-1 min-w-0", children: [
3568
+ backButton && /* @__PURE__ */ jsx18(
3569
+ BackButton,
3570
+ {
3571
+ onClick: backButton.onClick,
3572
+ label: backButton.label,
3573
+ className: "hidden md:inline-flex"
3574
+ }
3575
+ ),
3576
+ title && /* @__PURE__ */ jsx18("h1", { className: "text-h2 tracking-[-0.64px] text-ods-text-primary", children: title }),
3577
+ subtitle && /* @__PURE__ */ jsx18("div", { className: "text-ods-text-secondary font-['DM_Sans'] font-medium text-[16px]", children: subtitle })
3578
+ ] }),
3579
+ (headerActions || actions) && /* @__PURE__ */ jsxs15("div", { className: "flex gap-2 items-center shrink-0", children: [
3580
+ headerActions,
3581
+ renderActions()
3582
+ ] })
3583
+ ] });
3584
+ }
3585
+ if (variant === "list") {
3586
+ return /* @__PURE__ */ jsxs15("div", { className: "flex items-center justify-between md:flex-col md:items-start md:justify-start lg:flex-row lg:items-center lg:justify-between gap-4", children: [
3587
+ /* @__PURE__ */ jsxs15("div", { className: "flex flex-col gap-1 flex-1 min-w-0", children: [
3588
+ backButton && /* @__PURE__ */ jsx18(
3589
+ BackButton,
3590
+ {
3591
+ onClick: backButton.onClick,
3592
+ label: backButton.label,
3593
+ className: "hidden md:inline-flex"
3594
+ }
3595
+ ),
3596
+ title && /* @__PURE__ */ jsx18("h1", { className: "text-h2 tracking-[-0.48px] text-ods-text-primary", children: title }),
3597
+ subtitle && /* @__PURE__ */ jsx18("div", { className: "text-ods-text-secondary font-['DM_Sans'] font-medium text-[16px]", children: subtitle })
3598
+ ] }),
3599
+ (headerActions || actions) && /* @__PURE__ */ jsxs15("div", { className: "flex gap-3 items-center shrink-0", children: [
3600
+ headerActions,
3601
+ renderActions()
3602
+ ] })
3603
+ ] });
3604
+ }
3605
+ if (variant === "form") {
3606
+ return /* @__PURE__ */ jsxs15("div", { className: "flex items-end justify-between md:flex-col md:items-start md:justify-start lg:flex-row lg:items-end lg:justify-between gap-4", children: [
3607
+ /* @__PURE__ */ jsxs15("div", { className: "flex flex-col gap-2 flex-1 min-w-0", children: [
3608
+ backButton && /* @__PURE__ */ jsx18(
3609
+ BackButton,
3610
+ {
3611
+ onClick: backButton.onClick,
3612
+ label: backButton.label,
3613
+ className: "hidden md:inline-flex"
3614
+ }
3615
+ ),
3616
+ title && /* @__PURE__ */ jsx18("h1", { className: "text-[32px] font-['Azeret_Mono:SemiBold',_sans-serif] font-semibold text-ods-text-primary tracking-[-0.64px]", children: title })
3617
+ ] }),
3618
+ (headerActions || actions) && /* @__PURE__ */ jsxs15("div", { className: "flex gap-4 items-center shrink-0", children: [
3619
+ headerActions,
3620
+ renderActions()
3621
+ ] })
3622
+ ] });
3623
+ }
3624
+ return /* @__PURE__ */ jsxs15("div", { className: "flex items-center justify-between md:flex-col md:items-start md:justify-start lg:flex-row lg:items-center lg:justify-between gap-4", children: [
3625
+ (title || subtitle) && /* @__PURE__ */ jsxs15("div", { className: "flex flex-col gap-1 flex-1 min-w-0", children: [
3626
+ title && /* @__PURE__ */ jsx18("h1", { className: "text-h2 text-ods-text-primary", children: title }),
3627
+ subtitle && /* @__PURE__ */ jsx18("div", { className: "text-ods-text-secondary font-['DM_Sans'] font-medium text-[16px]", children: subtitle })
3628
+ ] }),
3629
+ (headerActions || actions) && /* @__PURE__ */ jsxs15("div", { className: "flex gap-3 items-center shrink-0", children: [
3630
+ headerActions,
3631
+ renderActions()
3632
+ ] })
3633
+ ] });
3634
+ };
3635
+ const getContainerClasses = () => {
3636
+ const baseClasses = [
3637
+ "flex flex-col w-full",
3638
+ backgroundClasses[background],
3639
+ paddingClasses[padding]
3640
+ ];
3641
+ switch (variant) {
3642
+ case "list":
3643
+ return cn(baseClasses, "gap-4 md:gap-6", className);
3644
+ case "detail":
3645
+ return cn(baseClasses, "gap-4 md:gap-6", className);
3646
+ case "form":
3647
+ return cn(baseClasses, "gap-6 md:gap-10", className);
3648
+ case "content":
3649
+ default:
3650
+ return cn(baseClasses, "gap-4 md:gap-6", className);
3651
+ }
3652
+ };
3653
+ const getContentClasses = () => {
3654
+ const mobilePadding = needsBottomPadding ? "pb-28 md:pb-0" : "";
3655
+ switch (variant) {
3656
+ case "detail":
3657
+ return cn("flex-1 overflow-auto", mobilePadding, contentClassName);
3658
+ case "list":
3659
+ return cn("flex flex-col gap-4 md:gap-6", mobilePadding, contentClassName);
3660
+ case "form":
3661
+ return cn("flex flex-col gap-4 md:gap-10", mobilePadding, contentClassName);
3662
+ case "content":
3663
+ default:
3664
+ return cn("flex-1", mobilePadding, contentClassName);
3665
+ }
3666
+ };
3667
+ return /* @__PURE__ */ jsxs15("div", { className: getContainerClasses(), children: [
3668
+ renderHeader(),
3669
+ /* @__PURE__ */ jsx18("div", { className: getContentClasses(), children })
3670
+ ] });
3671
+ }
3672
+ var ListPageContainer = (props) => /* @__PURE__ */ jsx18(PageContainer, { ...props, variant: "list" });
3673
+ var DetailPageContainer = (props) => /* @__PURE__ */ jsx18(PageContainer, { ...props, variant: "detail" });
3674
+ var FormPageContainer = (props) => /* @__PURE__ */ jsx18(PageContainer, { ...props, variant: "form" });
3675
+ var ContentPageContainer = (props) => /* @__PURE__ */ jsx18(PageContainer, { ...props, variant: "content" });
3676
+
3677
+ // src/components/layout/article-detail-layout.tsx
3678
+ import { jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
3679
+ function PageShell({ children, schemas }) {
3680
+ return /* @__PURE__ */ jsxs16("main", { className: "bg-ods-bg min-h-screen", children: [
3681
+ schemas,
3682
+ /* @__PURE__ */ jsx19("div", { className: "max-w-[1920px] mx-auto px-6 md:px-20 py-6 md:py-10", children })
3683
+ ] });
3684
+ }
3685
+ function ArticleDetailLayout({ children, schemas }) {
3686
+ return /* @__PURE__ */ jsxs16("main", { className: "bg-ods-bg min-h-screen", children: [
3687
+ schemas,
3688
+ /* @__PURE__ */ jsx19("div", { className: "max-w-[1280px] mx-auto px-6 md:px-20 py-6 md:py-10", children })
3689
+ ] });
3690
+ }
3691
+
3692
+ // src/utils/content-href.ts
3693
+ var DEFAULT_CONTENT_SUFFIXES = {
3694
+ onboarding_guide: "onboarding-guides",
3695
+ product_release: "releases",
3696
+ blog_post: "blog",
3697
+ case_study: "case-studies",
3698
+ customer_interview: "interviews",
3699
+ investor_update: "investor-updates",
3700
+ webinar: "webinars",
3701
+ podcast: "podcasts",
3702
+ event: "events"
3703
+ };
3704
+ function lastPathSegment(url) {
3705
+ try {
3706
+ const pathname = new URL(url, "https://_.local").pathname;
3707
+ const segs = pathname.split("/").filter(Boolean);
3708
+ return segs.length > 0 ? segs[segs.length - 1] : null;
3709
+ } catch {
3710
+ return null;
3711
+ }
3712
+ }
3713
+ function makeComposeContentUrl(opts) {
3714
+ const suffixes = opts.suffixes ?? DEFAULT_CONTENT_SUFFIXES;
3715
+ return ({ type, identifier, externalUrl, targetPlatform }) => {
3716
+ const override = opts.overrides && Object.prototype.hasOwnProperty.call(opts.overrides, type) ? opts.overrides[type] : void 0;
3717
+ if (override) return override(identifier);
3718
+ const seg = (Object.prototype.hasOwnProperty.call(suffixes, type) ? suffixes[type] : void 0) ?? type;
3719
+ if (opts.hostedTypes.has(type)) {
3720
+ const recovered = externalUrl ? lastPathSegment(externalUrl) : null;
3721
+ const slug = recovered && recovered !== seg ? recovered : identifier;
3722
+ return { href: `/${seg}/${slug}`, targetPlatform: null };
3723
+ }
3724
+ return externalUrl ? { href: externalUrl, targetPlatform: targetPlatform ?? null } : { href: `${opts.contentOrigin}/${seg}/${identifier}`, targetPlatform: null };
3725
+ };
3726
+ }
3727
+ function buildDefaultHref(basePath, slug) {
3728
+ return { href: `${basePath}/${slug}`, targetPlatform: null };
3729
+ }
3730
+ function resolveContentHref(composeContentUrl, args) {
3731
+ return composeContentUrl ? composeContentUrl({ type: args.type, identifier: args.slug, platforms: args.platforms }) : buildDefaultHref(args.basePath, args.slug);
3732
+ }
3733
+
3734
+ // src/components/ui/error-state.tsx
3735
+ init_button();
3736
+ init_cn();
3737
+ import { AlertTriangle, RefreshCw, Home } from "lucide-react";
3738
+ import { jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
3739
+ function ErrorState({
3740
+ title = "Error",
3741
+ message,
3742
+ variant = "error",
3743
+ showIcon = true,
3744
+ showRetry = false,
3745
+ showHome = false,
3746
+ onRetry,
3747
+ onHome,
3748
+ className,
3749
+ containerClassName
3750
+ }) {
3751
+ const getVariantStyles = () => {
3752
+ switch (variant) {
3753
+ case "error":
3754
+ return {
3755
+ bg: "bg-ods-attention-red-error/20",
3756
+ border: "border-ods-attention-red-error",
3757
+ text: "text-ods-attention-red-error",
3758
+ icon: "text-ods-attention-red-error"
3759
+ };
3760
+ case "warning":
3761
+ return {
3762
+ bg: "bg-ods-attention-yellow-warning/20",
3763
+ border: "border-ods-attention-yellow-warning",
3764
+ text: "text-ods-attention-yellow-warning",
3765
+ icon: "text-ods-attention-yellow-warning"
3766
+ };
3767
+ case "info":
3768
+ return {
3769
+ bg: "bg-ods-bg-surface",
3770
+ border: "border-ods-border",
3771
+ text: "text-ods-text-secondary",
3772
+ icon: "text-ods-text-secondary"
3773
+ };
3774
+ }
3775
+ };
3776
+ const styles = getVariantStyles();
3777
+ return /* @__PURE__ */ jsx20("div", { className: cn("p-6", containerClassName), children: /* @__PURE__ */ jsx20("div", { className: cn(
3778
+ "rounded-lg p-4 border",
3779
+ styles.bg,
3780
+ styles.border,
3781
+ className
3782
+ ), children: /* @__PURE__ */ jsxs17("div", { className: "flex items-start gap-3", children: [
3783
+ showIcon && /* @__PURE__ */ jsx20(AlertTriangle, { className: cn("h-5 w-5 mt-0.5 flex-shrink-0", styles.icon) }),
3784
+ /* @__PURE__ */ jsxs17("div", { className: "flex-1", children: [
3785
+ /* @__PURE__ */ jsx20("h3", { className: cn("font-semibold mb-1", styles.text), children: title }),
3786
+ /* @__PURE__ */ jsx20("p", { className: cn("text-sm", styles.text), children: message }),
3787
+ (showRetry || showHome) && /* @__PURE__ */ jsxs17("div", { className: "flex gap-2 mt-3", children: [
3788
+ showRetry && onRetry && /* @__PURE__ */ jsx20(
3789
+ Button,
3790
+ {
3791
+ onClick: onRetry,
3792
+ variant: "outline",
3793
+ size: "small-legacy",
3794
+ className: "h-8",
3795
+ leftIcon: /* @__PURE__ */ jsx20(RefreshCw, { className: "h-4 w-4" }),
3796
+ children: "Try Again"
3797
+ }
3798
+ ),
3799
+ showHome && onHome && /* @__PURE__ */ jsx20(
3800
+ Button,
3801
+ {
3802
+ onClick: onHome,
3803
+ variant: "outline",
3804
+ size: "small-legacy",
3805
+ className: "h-8",
3806
+ leftIcon: /* @__PURE__ */ jsx20(Home, { className: "h-4 w-4" }),
3807
+ children: "Go Home"
3808
+ }
3809
+ )
3810
+ ] })
3811
+ ] })
3812
+ ] }) }) });
3813
+ }
3814
+ function PageError({ message, onRetry, onHome }) {
3815
+ return /* @__PURE__ */ jsx20(
3816
+ ErrorState,
3817
+ {
3818
+ title: "Page Error",
3819
+ message,
3820
+ variant: "error",
3821
+ showRetry: !!onRetry,
3822
+ showHome: !!onHome,
3823
+ onRetry,
3824
+ onHome
3825
+ }
3826
+ );
3827
+ }
3828
+ function LoadError({ message, onRetry }) {
3829
+ return /* @__PURE__ */ jsx20(
3830
+ ErrorState,
3831
+ {
3832
+ title: "Loading Error",
3833
+ message,
3834
+ variant: "error",
3835
+ showRetry: !!onRetry,
3836
+ onRetry
3837
+ }
3838
+ );
3839
+ }
3840
+ function NotFoundError({ message = "The requested item was not found", onHome }) {
3841
+ return /* @__PURE__ */ jsx20(
3842
+ ErrorState,
3843
+ {
3844
+ title: "Not Found",
3845
+ message,
3846
+ variant: "warning",
3847
+ showHome: !!onHome,
3848
+ onHome
3849
+ }
3850
+ );
3851
+ }
3852
+
3853
+ // src/components/features/entity-tag-badges.tsx
3854
+ import Link from "next/link";
3855
+ import { jsx as jsx21, jsxs as jsxs18 } from "react/jsx-runtime";
3856
+ var BADGE_CLASS = "bg-ods-card border border-ods-border";
3857
+ function EntityTagBadges({ tags, basePath, max, className }) {
3858
+ const items = (tags || []).filter((t) => !!t && !!t.name);
3859
+ if (items.length === 0) return null;
3860
+ const visibleLimit = max == null ? items.length : Math.max(0, max);
3861
+ const shown = items.slice(0, visibleLimit);
3862
+ const overflow = items.length - shown.length;
3863
+ return /* @__PURE__ */ jsxs18("div", { className: `flex flex-wrap items-center gap-2 w-full ${className || ""}`, children: [
3864
+ shown.map((tag) => {
3865
+ const key = tag.tag_id ?? tag.id ?? tag.slug ?? tag.name;
3866
+ const label = (tag.name || "").toUpperCase();
3867
+ if (basePath && tag.slug) {
3868
+ return /* @__PURE__ */ jsx21(Link, { href: `${basePath}?tags=${tag.slug}`, className: "inline-flex", children: /* @__PURE__ */ jsx21(
3869
+ StatusBadge,
3870
+ {
3871
+ text: label,
3872
+ variant: "card",
3873
+ className: `${BADGE_CLASS} cursor-pointer transition-colors hover:border-ods-accent`
3874
+ }
3875
+ ) }, key);
3876
+ }
3877
+ return /* @__PURE__ */ jsx21(StatusBadge, { text: label, variant: "card", className: BADGE_CLASS }, key);
3878
+ }),
3879
+ overflow > 0 && /* @__PURE__ */ jsx21(StatusBadge, { text: `+${overflow}`, variant: "card", className: `${BADGE_CLASS} text-ods-text-secondary` })
3880
+ ] });
3881
+ }
3882
+
3883
+ // src/components/features/mux-origins.ts
3884
+ var MUX_STREAM_ORIGIN = "https://stream.mux.com";
3885
+ var MUX_IMAGE_ORIGIN = "https://image.mux.com";
3886
+
3887
+ // src/components/features/use-video-warmup.ts
3888
+ import { useEffect as useEffect5 } from "react";
3889
+ import ReactDOM from "react-dom";
3890
+ function useVideoOriginPreconnect({
3891
+ supabaseStorageOrigin
3892
+ } = {}) {
3893
+ const runtime = useChatRuntime();
3894
+ const resolvedOrigin = supabaseStorageOrigin ?? runtime?.endpoints.supabaseStorageOrigin;
3895
+ try {
3896
+ ReactDOM.preconnect(MUX_STREAM_ORIGIN, { crossOrigin: "anonymous" });
3897
+ ReactDOM.preconnect(MUX_IMAGE_ORIGIN, { crossOrigin: "anonymous" });
3898
+ if (resolvedOrigin) {
3899
+ ReactDOM.preconnect(resolvedOrigin, { crossOrigin: "anonymous" });
3900
+ }
3901
+ } catch (err) {
3902
+ if (process.env.NODE_ENV !== "production") {
3903
+ console.warn("[useVideoOriginPreconnect] preconnect failed:", err);
3904
+ }
3905
+ }
3906
+ }
3907
+ function useVideoWarmup({
3908
+ videoUrl,
3909
+ supabaseStorageOrigin,
3910
+ nearMargin = "1000px"
3911
+ } = {}) {
3912
+ const runtime = useChatRuntime();
3913
+ const resolvedOrigin = supabaseStorageOrigin ?? runtime?.endpoints.supabaseStorageOrigin;
3914
+ useVideoOriginPreconnect({ supabaseStorageOrigin: resolvedOrigin });
3915
+ const { ref, isNear } = useNearViewport(nearMargin);
3916
+ useEffect5(() => {
3917
+ if (!isNear || !videoUrl || !resolvedOrigin) return;
3918
+ const conn = navigator.connection;
3919
+ if (conn?.saveData === true) return;
3920
+ let videoOrigin;
3921
+ try {
3922
+ videoOrigin = new URL(videoUrl, "http://placeholder.local").origin;
3923
+ } catch {
3924
+ return;
3925
+ }
3926
+ if (videoOrigin !== resolvedOrigin) return;
3927
+ const link = document.createElement("link");
3928
+ link.rel = "preload";
3929
+ link.as = "video";
3930
+ link.href = videoUrl;
3931
+ link.crossOrigin = "anonymous";
3932
+ if ("fetchPriority" in link) {
3933
+ ;
3934
+ link.fetchPriority = "low";
3935
+ }
3936
+ document.head.appendChild(link);
3937
+ return () => {
3938
+ link.remove();
3939
+ };
3940
+ }, [isNear, videoUrl, resolvedOrigin]);
3941
+ return { ref, isNear };
3942
+ }
3943
+
3944
+ // src/components/features/captions-url.ts
3945
+ function getCaptionsUrl(entityType, entityId, srtContent) {
3946
+ if (!srtContent) return void 0;
3947
+ const hash = `${srtContent.length}-${srtContent.slice(0, 8).replace(/\s/g, "")}`;
3948
+ return `/api/captions/${entityType}/${entityId}?v=${hash}`;
3949
+ }
3950
+
3951
+ // src/components/ui/search-input.tsx
3952
+ init_cn();
3953
+ import * as PopoverPrimitive from "@radix-ui/react-popover";
3954
+ import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
3955
+ import * as React12 from "react";
3956
+ import { jsx as jsx22, jsxs as jsxs19 } from "react/jsx-runtime";
3957
+ var containerStyles = cn(
3958
+ // Layout & spacing — matches lib Input component
3959
+ "flex items-center gap-2 rounded-[6px] border px-3 h-11 md:h-12 cursor-text",
3960
+ "has-[:focus-visible]:outline-none",
3961
+ "group",
3962
+ "transition-colors duration-200",
3963
+ // Theme palette — matches lib Input component
3964
+ "bg-ods-card border-ods-border has-[:focus]:border-ods-accent"
3965
+ );
3966
+ var innerInputStyles = cn(
3967
+ "flex-1 min-w-[60px] bg-transparent border-none outline-none",
3968
+ "text-h4",
3969
+ "text-ods-text-primary placeholder:text-ods-text-secondary",
3970
+ "disabled:cursor-not-allowed",
3971
+ "touch-manipulation"
3972
+ );
3973
+ function chipVariantToTagVariant(variant) {
3974
+ switch (variant) {
3975
+ case "selected":
3976
+ return "primary";
3977
+ // Content tags render with the unified badge skin (ods-card + ods-border,
3978
+ // mono uppercase) — identical to the public EntityTagBadges display.
3979
+ case "tag":
3980
+ return "badge";
3981
+ case "category":
3982
+ case "subcategory":
3983
+ default:
3984
+ return "outline";
3985
+ }
3986
+ }
3987
+ function SearchInput({
3988
+ placeholder = "Search...",
3989
+ value,
3990
+ defaultValue = "",
3991
+ onChange,
3992
+ onSubmit,
3993
+ results = [],
3994
+ isLoading = false,
3995
+ onResultSelect,
3996
+ debounceMs = 300,
3997
+ renderResult,
3998
+ groupBy,
3999
+ emptyResultsText = "No results found",
4000
+ showDropdown: showDropdownProp,
4001
+ filterChips = [],
4002
+ onFilterRemove,
4003
+ startAdornment,
4004
+ endAdornment,
4005
+ className,
4006
+ dropdownClassName,
4007
+ minQueryLength = 2,
4008
+ limitTags = "auto",
4009
+ getLimitTagsText = (more) => `+${more}`
4010
+ }) {
4011
+ const [internalValue, setInternalValue] = React12.useState(defaultValue);
4012
+ const currentValue = onChange ? value ?? "" : internalValue;
4013
+ const debouncedValue = useDebounce(currentValue, debounceMs);
4014
+ const [isOpen, setIsOpen] = React12.useState(false);
4015
+ const [highlightedIndex, setHighlightedIndex] = React12.useState(-1);
4016
+ const containerRef = React12.useRef(null);
4017
+ const currentPlaceholder = filterChips.length > 0 ? "Add filter..." : placeholder;
4018
+ const {
4019
+ visibleCount: rawVisibleCount,
4020
+ middleRef,
4021
+ measureRef,
4022
+ textMeasureRef,
4023
+ badgeRef,
4024
+ inputRef
4025
+ } = useAutoLimitTags({
4026
+ count: filterChips.length,
4027
+ limitTags,
4028
+ // When chips exist, pass empty placeholder so the hook only reserves input minWidth,
4029
+ // not the full placeholder text width — gives more room for chips on narrow screens
4030
+ placeholder: filterChips.length > 0 ? "" : placeholder
4031
+ });
4032
+ const visibleCount = filterChips.length > 0 ? Math.max(1, rawVisibleCount) : rawVisibleCount;
4033
+ const hiddenTagsRef = React12.useRef(null);
4034
+ const hiddenTagsPopupRef = React12.useRef(null);
4035
+ const [showHiddenTags, setShowHiddenTags] = React12.useState(false);
4036
+ React12.useEffect(() => {
4037
+ if (!showHiddenTags) return;
4038
+ const handleClick = (e) => {
4039
+ const target = e.target;
4040
+ if (!hiddenTagsRef.current?.contains(target) && !hiddenTagsPopupRef.current?.contains(target)) {
4041
+ setShowHiddenTags(false);
4042
+ }
4043
+ };
4044
+ document.addEventListener("mousedown", handleClick);
4045
+ return () => document.removeEventListener("mousedown", handleClick);
4046
+ }, [showHiddenTags]);
4047
+ const hiddenCount = filterChips.length - visibleCount;
4048
+ const visibleChips = filterChips.slice(0, visibleCount);
4049
+ const hiddenChips = filterChips.slice(visibleCount);
4050
+ const { flatResults, groups } = React12.useMemo(() => {
4051
+ if (!groupBy) return { flatResults: results, groups: null };
4052
+ const grouped = /* @__PURE__ */ new Map();
4053
+ for (const r of results) {
4054
+ const key = groupBy(r);
4055
+ const arr = grouped.get(key);
4056
+ if (arr) {
4057
+ arr.push(r);
4058
+ } else {
4059
+ grouped.set(key, [r]);
4060
+ }
4061
+ }
4062
+ return { flatResults: results, groups: grouped };
4063
+ }, [results, groupBy]);
4064
+ const meetsMinQuery = debouncedValue.length >= minQueryLength;
4065
+ const autoShow = meetsMinQuery;
4066
+ const dropdownVisible = showDropdownProp ?? (isOpen && autoShow);
4067
+ React12.useEffect(() => {
4068
+ setHighlightedIndex(-1);
4069
+ }, [flatResults.length]);
4070
+ const handleChange = (e) => {
4071
+ const newVal = e.target.value;
4072
+ if (onChange) {
4073
+ onChange(newVal);
4074
+ } else {
4075
+ setInternalValue(newVal);
4076
+ }
4077
+ if (!isOpen) setIsOpen(true);
4078
+ setHighlightedIndex(-1);
4079
+ };
4080
+ const handleClear = (e) => {
4081
+ e.stopPropagation();
4082
+ e.preventDefault();
4083
+ if (onChange) {
4084
+ onChange("");
4085
+ } else {
4086
+ setInternalValue("");
4087
+ }
4088
+ inputRef.current?.focus();
4089
+ };
4090
+ const handleResultClick = (result, e) => {
4091
+ onResultSelect?.(
4092
+ result,
4093
+ e ? {
4094
+ metaKey: e.metaKey,
4095
+ ctrlKey: e.ctrlKey,
4096
+ shiftKey: e.shiftKey,
4097
+ altKey: e.altKey,
4098
+ button: e.button
4099
+ } : void 0
4100
+ );
4101
+ setIsOpen(false);
4102
+ };
4103
+ const handleKeyDown = (e) => {
4104
+ switch (e.key) {
4105
+ case "ArrowDown":
4106
+ e.preventDefault();
4107
+ if (!isOpen) setIsOpen(true);
4108
+ setHighlightedIndex(
4109
+ (prev) => prev < flatResults.length - 1 ? prev + 1 : 0
4110
+ );
4111
+ break;
4112
+ case "ArrowUp":
4113
+ e.preventDefault();
4114
+ setHighlightedIndex(
4115
+ (prev) => prev > 0 ? prev - 1 : flatResults.length - 1
4116
+ );
4117
+ break;
4118
+ case "Enter":
4119
+ e.preventDefault();
4120
+ if (highlightedIndex >= 0 && flatResults[highlightedIndex]) {
4121
+ handleResultClick(flatResults[highlightedIndex]);
4122
+ } else {
4123
+ onSubmit?.(currentValue);
4124
+ }
4125
+ break;
4126
+ case "Escape":
4127
+ setIsOpen(false);
4128
+ setHighlightedIndex(-1);
4129
+ break;
4130
+ case "Backspace":
4131
+ if (!currentValue && filterChips.length > 0 && onFilterRemove) {
4132
+ onFilterRemove(filterChips[filterChips.length - 1].id);
4133
+ }
4134
+ break;
4135
+ }
4136
+ };
4137
+ const handleOpenChange = (open) => {
4138
+ setIsOpen(open);
4139
+ };
4140
+ const defaultRenderResult = (result, isHighlighted) => /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-3 w-full min-w-0", children: [
4141
+ result.icon && /* @__PURE__ */ jsx22("span", { className: "flex-shrink-0 text-ods-text-secondary [&_svg]:size-4", children: result.icon }),
4142
+ /* @__PURE__ */ jsxs19("div", { className: "min-w-0 flex-1", children: [
4143
+ /* @__PURE__ */ jsx22("div", { className: cn(
4144
+ "text-sm font-medium leading-5 truncate",
4145
+ isHighlighted ? "text-ods-accent" : "text-ods-text-primary"
4146
+ ), title: result.title, children: result.title }),
4147
+ result.description && /* @__PURE__ */ jsx22("div", { className: "text-xs leading-4 text-ods-text-secondary truncate mt-0.5", title: result.description, children: result.description })
4148
+ ] }),
4149
+ result.type && /* @__PURE__ */ jsx22("span", { className: "flex-shrink-0 text-[11px] font-medium text-ods-text-muted uppercase tracking-wider", children: result.type })
4150
+ ] });
4151
+ const renderRow = (result, index) => {
4152
+ const isHighlighted = index === highlightedIndex;
4153
+ return /* @__PURE__ */ jsx22(
4154
+ "div",
4155
+ {
4156
+ role: "option",
4157
+ "aria-selected": isHighlighted,
4158
+ className: cn(
4159
+ "flex items-center min-h-10 px-3 cursor-pointer transition-colors border-b border-ods-border last:border-b-0",
4160
+ isHighlighted && "bg-ods-bg-hover",
4161
+ !isHighlighted && "hover:bg-ods-bg-hover"
4162
+ ),
4163
+ onClick: (e) => handleResultClick(result, e),
4164
+ onMouseEnter: () => setHighlightedIndex(index),
4165
+ children: renderResult ? renderResult(result, isHighlighted) : defaultRenderResult(result, isHighlighted)
4166
+ },
4167
+ result.id
4168
+ );
4169
+ };
4170
+ const renderDropdownContent = () => {
4171
+ if (isLoading) {
4172
+ return /* @__PURE__ */ jsx22("div", { className: "px-4 py-3 text-ods-text-secondary text-[14px]", children: "Loading..." });
4173
+ }
4174
+ if (flatResults.length === 0) {
4175
+ return /* @__PURE__ */ jsx22("div", { className: "px-4 py-3 text-ods-text-secondary text-[14px]", children: emptyResultsText });
4176
+ }
4177
+ if (groups) {
4178
+ let globalIndex = 0;
4179
+ return Array.from(groups.entries()).map(([groupLabel, groupResults]) => /* @__PURE__ */ jsxs19("div", { children: [
4180
+ /* @__PURE__ */ jsx22("div", { className: "px-4 py-2 text-[12px] font-semibold text-ods-text-secondary uppercase tracking-wide bg-ods-bg", children: groupLabel }),
4181
+ groupResults.map((result) => {
4182
+ const idx = globalIndex++;
4183
+ return renderRow(result, idx);
4184
+ })
4185
+ ] }, groupLabel));
4186
+ }
4187
+ return flatResults.map((result, index) => renderRow(result, index));
4188
+ };
4189
+ const hasValue = currentValue.length > 0;
4190
+ return /* @__PURE__ */ jsxs19("div", { className: cn("relative", className), ref: containerRef, children: [
4191
+ /* @__PURE__ */ jsxs19(PopoverPrimitive.Root, { open: dropdownVisible, onOpenChange: handleOpenChange, modal: false, children: [
4192
+ /* @__PURE__ */ jsx22(PopoverPrimitive.Anchor, { asChild: true, children: /* @__PURE__ */ jsxs19(
4193
+ "div",
4194
+ {
4195
+ className: cn(
4196
+ containerStyles,
4197
+ "hover:bg-ods-bg-hover hover:border-ods-border-hover active:bg-ods-bg-active active:border-ods-border-active",
4198
+ dropdownVisible && "!border-ods-accent"
4199
+ ),
4200
+ onClick: () => {
4201
+ inputRef.current?.focus();
4202
+ setIsOpen(true);
4203
+ },
4204
+ children: [
4205
+ /* @__PURE__ */ jsx22("span", { className: "flex-shrink-0 text-ods-text-secondary transition-colors duration-200 group-has-[:focus]:text-ods-accent [&_svg]:size-4 md:[&_svg]:size-6", children: startAdornment !== void 0 ? startAdornment : /* @__PURE__ */ jsx22(SearchIcon, {}) }),
4206
+ /* @__PURE__ */ jsxs19("div", { ref: middleRef, className: "flex-1 flex items-center gap-2 min-w-0 overflow-hidden", children: [
4207
+ visibleChips.map((chip) => /* @__PURE__ */ jsx22(
4208
+ Tag,
4209
+ {
4210
+ variant: chipVariantToTagVariant(chip.variant),
4211
+ label: chip.label,
4212
+ labelClassName: "truncate max-w-[120px]",
4213
+ onClose: onFilterRemove ? () => onFilterRemove(chip.id) : void 0
4214
+ },
4215
+ chip.id
4216
+ )),
4217
+ hiddenCount > 0 && /* @__PURE__ */ jsx22("div", { ref: hiddenTagsRef, className: "shrink-0", children: /* @__PURE__ */ jsx22(
4218
+ "button",
4219
+ {
4220
+ ref: badgeRef,
4221
+ type: "button",
4222
+ onClick: (e) => {
4223
+ e.stopPropagation();
4224
+ setShowHiddenTags((prev) => !prev);
4225
+ },
4226
+ className: cn(
4227
+ "flex items-center h-8 px-2",
4228
+ "bg-ods-card border border-ods-border rounded-[6px]",
4229
+ "font-mono text-[14px] font-medium leading-5 text-ods-text-secondary uppercase tracking-[-0.28px]",
4230
+ "hover:bg-ods-bg-hover transition-colors cursor-pointer"
4231
+ ),
4232
+ "aria-label": `${hiddenCount} more selected filters`,
4233
+ children: getLimitTagsText(hiddenCount)
4234
+ }
4235
+ ) }),
4236
+ /* @__PURE__ */ jsx22(
4237
+ "input",
4238
+ {
4239
+ ref: inputRef,
4240
+ type: "text",
4241
+ value: currentValue,
4242
+ onChange: handleChange,
4243
+ onKeyDown: handleKeyDown,
4244
+ onFocus: () => {
4245
+ setIsOpen(true);
4246
+ setShowHiddenTags(false);
4247
+ },
4248
+ placeholder: currentPlaceholder,
4249
+ className: innerInputStyles
4250
+ }
4251
+ )
4252
+ ] }),
4253
+ /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-1 shrink-0 ml-auto", children: [
4254
+ hasValue && /* @__PURE__ */ jsx22(
4255
+ "button",
4256
+ {
4257
+ type: "button",
4258
+ onClick: handleClear,
4259
+ className: "flex items-center justify-center hover:opacity-70 transition-opacity",
4260
+ "aria-label": "Clear search",
4261
+ children: /* @__PURE__ */ jsx22(XmarkCircleIcon, { className: "text-ods-text-secondary", size: 24 })
4262
+ }
4263
+ ),
4264
+ endAdornment
4265
+ ] })
4266
+ ]
4267
+ }
4268
+ ) }),
4269
+ /* @__PURE__ */ jsx22(
4270
+ PopoverPrimitive.Content,
4271
+ {
4272
+ className: cn(
4273
+ "z-50 w-[var(--radix-popover-trigger-width)] mt-1",
4274
+ "bg-ods-card border border-ods-border rounded-[6px] overflow-hidden shadow-lg",
4275
+ "data-[state=open]:animate-in data-[state=closed]:animate-out",
4276
+ "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
4277
+ "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
4278
+ "data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2",
4279
+ dropdownClassName
4280
+ ),
4281
+ sideOffset: 4,
4282
+ align: "start",
4283
+ onOpenAutoFocus: (e) => {
4284
+ e.preventDefault();
4285
+ inputRef.current?.focus();
4286
+ },
4287
+ onInteractOutside: (e) => {
4288
+ if (containerRef.current?.contains(e.target)) {
4289
+ e.preventDefault();
4290
+ }
4291
+ },
4292
+ children: /* @__PURE__ */ jsxs19(ScrollAreaPrimitive.Root, { className: "overflow-hidden", children: [
4293
+ /* @__PURE__ */ jsx22(ScrollAreaPrimitive.Viewport, { className: "max-h-[320px] w-full", children: /* @__PURE__ */ jsx22("div", { role: "listbox", children: renderDropdownContent() }) }),
4294
+ /* @__PURE__ */ jsx22(ScrollAreaPrimitive.Scrollbar, { className: "hidden", orientation: "vertical", children: /* @__PURE__ */ jsx22(ScrollAreaPrimitive.Thumb, {}) })
4295
+ ] })
4296
+ }
4297
+ )
4298
+ ] }),
4299
+ showHiddenTags && hiddenCount > 0 && /* @__PURE__ */ jsx22(
4300
+ HiddenTagsPopup,
4301
+ {
4302
+ ref: hiddenTagsPopupRef,
4303
+ items: hiddenChips.map((chip) => ({ label: chip.label, value: chip.id })),
4304
+ style: {
4305
+ left: badgeRef.current ? badgeRef.current.getBoundingClientRect().left - (containerRef.current?.getBoundingClientRect().left ?? 0) : 0
4306
+ },
4307
+ onRemove: (value2) => {
4308
+ onFilterRemove?.(value2);
4309
+ if (hiddenCount <= 1) setShowHiddenTags(false);
4310
+ }
4311
+ }
4312
+ ),
4313
+ /* @__PURE__ */ jsx22(
4314
+ "span",
4315
+ {
4316
+ ref: textMeasureRef,
4317
+ "aria-hidden": "true",
4318
+ className: "fixed -left-[9999px] top-0 pointer-events-none whitespace-nowrap text-ods-text-primary",
4319
+ children: currentPlaceholder
4320
+ }
4321
+ ),
4322
+ /* @__PURE__ */ jsx22(
4323
+ "div",
4324
+ {
4325
+ ref: measureRef,
4326
+ "aria-hidden": "true",
4327
+ className: "fixed -left-[9999px] top-0 flex gap-2 pointer-events-none",
4328
+ children: filterChips.map((chip) => /* @__PURE__ */ jsx22(
4329
+ Tag,
4330
+ {
4331
+ variant: chipVariantToTagVariant(chip.variant),
4332
+ label: chip.label,
4333
+ labelClassName: "truncate max-w-[120px]",
4334
+ onClose: () => {
4335
+ }
4336
+ },
4337
+ `m-${chip.id}`
4338
+ ))
4339
+ }
4340
+ )
4341
+ ] });
4342
+ }
4343
+
4344
+ // src/components/ui/filter-pill-row.tsx
4345
+ init_button();
4346
+ import { Filter } from "lucide-react";
4347
+ import { jsx as jsx23, jsxs as jsxs20 } from "react/jsx-runtime";
4348
+ function FilterPillRow({
4349
+ label,
4350
+ selectedValue,
4351
+ onValueChange,
4352
+ options,
4353
+ countLabel,
4354
+ children
4355
+ }) {
4356
+ return /* @__PURE__ */ jsxs20("div", { className: "flex flex-wrap items-center gap-3 p-4 bg-ods-card border border-ods-border rounded-lg", children: [
4357
+ /* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-2", children: [
4358
+ /* @__PURE__ */ jsx23(Filter, { className: "h-4 w-4 text-ods-accent" }),
4359
+ /* @__PURE__ */ jsx23("span", { className: "text-h5 text-ods-text-secondary", children: label })
4360
+ ] }),
4361
+ children || options.map((opt) => /* @__PURE__ */ jsx23(
4362
+ Button,
4363
+ {
4364
+ type: "button",
4365
+ variant: selectedValue === opt.value ? "accent" : "outline",
4366
+ size: "small-legacy",
4367
+ onClick: () => onValueChange(opt.value),
4368
+ className: "text-h3",
4369
+ children: opt.label
4370
+ },
4371
+ opt.value
4372
+ )),
4373
+ countLabel && /* @__PURE__ */ jsx23("div", { className: "ml-auto text-[12px] font-['DM_Sans'] text-ods-text-secondary shrink-0", children: countLabel })
4374
+ ] });
4375
+ }
4376
+
4377
+ export {
4378
+ HiddenTagsPopup,
4379
+ tagVariants,
4380
+ Tag,
4381
+ Card,
4382
+ CardHeader,
4383
+ CardTitle,
4384
+ CardDescription,
4385
+ CardContent,
4386
+ CardFooter,
4387
+ CardHorizontal,
4388
+ Tabs,
4389
+ TabsList,
4390
+ TabsTrigger,
4391
+ TabsContent,
4392
+ statusBadgeVariants,
4393
+ StatusBadge,
4394
+ SimpleMarkdownRenderer,
4395
+ formatDate,
4396
+ formatNumber,
4397
+ formatPrice,
4398
+ formatBytes,
4399
+ formatBytesShort,
4400
+ formatLargeNumber,
4401
+ formatAbbreviatedNumber,
4402
+ getFirstLastInitials,
4403
+ nameInitials,
4404
+ formatDurationMMSS,
4405
+ formatDurationCompact,
4406
+ formatTimeWithTimezone,
4407
+ formatDurationFromRange,
4408
+ formatDateUTC,
4409
+ formatLegalDate,
4410
+ formatCurrency,
4411
+ formatPercent,
4412
+ formatWholeDollars,
4413
+ formatCompactMetric,
4414
+ getTrendColors,
4415
+ formatDateRange,
4416
+ formatDateTimeAt,
4417
+ formatDurationFromMs,
4418
+ formatDuration,
4419
+ formatUnderscoreText,
4420
+ stripHtml,
4421
+ formatClassification,
4422
+ formatPricingModel,
4423
+ formatBioText,
4424
+ SquareAvatar,
4425
+ useControllableState,
4426
+ ActionsMenu,
4427
+ ActionsMenuDropdown,
4428
+ isCrossOriginUrl,
4429
+ decideNewTab,
4430
+ computeIsNewTab,
4431
+ newTabAnchorAttrs,
4432
+ buildAnchorProps,
4433
+ NEW_TAB_FEATURES,
4434
+ isModifierClick,
4435
+ stripSameOriginToPath,
4436
+ resolveHrefForRuntime,
4437
+ resolveExternalNavigation,
4438
+ EMPTY_AUTHOR_PLACEHOLDER,
4439
+ EntityMetadataValueCell,
4440
+ EntityMetadataAuthorCell,
4441
+ EntityAuthorCard,
4442
+ BlogImagePlaceholder,
4443
+ fetchPriorityProp,
4444
+ extractYouTubeId,
4445
+ Video,
4446
+ RATIO_GRID_CLASS,
4447
+ RATIO_DISPLAY_GRID_CLASS,
4448
+ RatioTabs,
4449
+ detectAspectRatio,
4450
+ ratioToCategory,
4451
+ groupByAspectRatio,
4452
+ VideoBitesDisplay,
4453
+ VideoBiteCard,
4454
+ EntityVideoSection,
4455
+ useEntityCardLink,
4456
+ useEntityCardPlaceholder,
4457
+ COMPACT_CARD_OUTER,
4458
+ COMPACT_CARD_OUTER_STATIC,
4459
+ COMPACT_CARD_SKELETON_OUTER,
4460
+ COMPACT_CARD_IMAGE_SLOT,
4461
+ COMPACT_CARD_SKELETON_IMAGE_SLOT,
4462
+ COMPACT_CARD_ICON_SLOT,
4463
+ COMPACT_CARD_TEXT_COL,
4464
+ COMPACT_CARD_TITLE_ROW,
4465
+ COMPACT_CARD_META_ROW_BOX,
4466
+ COMPACT_CARD_TITLE,
4467
+ COMPACT_CARD_SUBTITLE,
4468
+ COMPACT_CARD_SUMMARY,
4469
+ COMPACT_CARD_META_ROW,
4470
+ COMPACT_CARD_ROW_FILLER,
4471
+ safeHref,
4472
+ OnboardingGuideCardSkeleton,
4473
+ OnboardingGuideCard,
4474
+ SOURCE_ICON_NAMES,
4475
+ getSourceIconName,
4476
+ SOURCE_LABELS_BY_TABLE,
4477
+ getSourceLabel,
4478
+ DEFAULT_DOCUMENT_TYPE_TO_TABLE_ID,
4479
+ defaultTableIdForDocumentType,
4480
+ ICON_REGISTRY,
4481
+ normalizeIconKey,
4482
+ getIconComponent,
4483
+ getDynamicIcon,
4484
+ sourceRowCtxFromRuntime,
4485
+ resolveSourceRowCTA,
4486
+ resolveSourceIcon,
4487
+ PageActions,
4488
+ usePageActionsBottomPadding,
4489
+ BackButton,
4490
+ PageContainer,
4491
+ ListPageContainer,
4492
+ DetailPageContainer,
4493
+ FormPageContainer,
4494
+ ContentPageContainer,
4495
+ PageShell,
4496
+ ArticleDetailLayout,
4497
+ DEFAULT_CONTENT_SUFFIXES,
4498
+ makeComposeContentUrl,
4499
+ buildDefaultHref,
4500
+ resolveContentHref,
4501
+ ErrorState,
4502
+ PageError,
4503
+ LoadError,
4504
+ NotFoundError,
4505
+ EntityTagBadges,
4506
+ MUX_STREAM_ORIGIN,
4507
+ MUX_IMAGE_ORIGIN,
4508
+ useVideoOriginPreconnect,
4509
+ useVideoWarmup,
4510
+ getCaptionsUrl,
4511
+ SearchInput,
4512
+ FilterPillRow
4513
+ };
4514
+ //# sourceMappingURL=chunk-LKI3YCAX.js.map