@octaviaflow/core 3.1.0-beta.117 → 3.1.0-beta.119

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 (31) hide show
  1. package/dist/chunk-FM34SY5X.js +3691 -0
  2. package/dist/chunk-FM34SY5X.js.map +1 -0
  3. package/dist/chunk-JHIV4QHZ.js +3707 -0
  4. package/dist/chunk-JHIV4QHZ.js.map +1 -0
  5. package/dist/chunk-MSRO4K25.js +3691 -0
  6. package/dist/chunk-MSRO4K25.js.map +1 -0
  7. package/dist/chunk-ZHTMPKJN.js +3704 -0
  8. package/dist/chunk-ZHTMPKJN.js.map +1 -0
  9. package/dist/components/AssistantConversation/AssistantConversation.d.ts +5 -1
  10. package/dist/components/AssistantConversation/AssistantConversation.d.ts.map +1 -1
  11. package/dist/components/AssistantConversation/assistant.fixtures.d.ts +2 -0
  12. package/dist/components/AssistantConversation/assistant.fixtures.d.ts.map +1 -1
  13. package/dist/components/AssistantConversation/citations.d.ts +3 -0
  14. package/dist/components/AssistantConversation/citations.d.ts.map +1 -0
  15. package/dist/components/AssistantConversation/index.d.ts +2 -1
  16. package/dist/components/AssistantConversation/index.d.ts.map +1 -1
  17. package/dist/components/AssistantDock/AssistantDock.d.ts +1 -1
  18. package/dist/components/AssistantDock/AssistantDock.d.ts.map +1 -1
  19. package/dist/components/ChatToolTrace/ChatToolTrace.d.ts +3 -0
  20. package/dist/components/ChatToolTrace/ChatToolTrace.d.ts.map +1 -1
  21. package/dist/index.cjs +62 -17
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.d.ts +2 -2
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +18 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/marketing.cjs +44 -17
  28. package/dist/marketing.cjs.map +1 -1
  29. package/dist/marketing.js +1 -1
  30. package/dist/styles.css +1 -1
  31. package/package.json +1 -1
@@ -0,0 +1,3704 @@
1
+ import {
2
+ $384704861d32dbed$export$bca9d026f8e704eb,
3
+ Button,
4
+ Checkbox,
5
+ Input,
6
+ Slot,
7
+ Spinner,
8
+ Tooltip,
9
+ resolveAccessibleName
10
+ } from "./chunk-V22HCPVO.js";
11
+ import {
12
+ cn
13
+ } from "./chunk-ZAUUGK2Y.js";
14
+
15
+ // src/components/Card/Card.tsx
16
+ import { motion, useReducedMotion } from "framer-motion";
17
+ import {
18
+ forwardRef
19
+ } from "react";
20
+ import { jsx } from "react/jsx-runtime";
21
+ var Card = forwardRef(function Card2({
22
+ variant = "default",
23
+ padding = "md",
24
+ radius = "md",
25
+ hoverable = false,
26
+ disabled = false,
27
+ loading = false,
28
+ asChild = false,
29
+ onClick,
30
+ onKeyDown,
31
+ className,
32
+ children,
33
+ ...rest
34
+ }, ref) {
35
+ const reducedMotion = useReducedMotion();
36
+ const isInteractive = Boolean(onClick) && !disabled && !loading;
37
+ const handleKeyDown = (e) => {
38
+ onKeyDown?.(e);
39
+ if (e.defaultPrevented) return;
40
+ if (!isInteractive) return;
41
+ if ((e.key === "Enter" || e.key === " ") && !e.repeat) {
42
+ e.preventDefault();
43
+ onClick?.(e);
44
+ }
45
+ };
46
+ const rootClassName = cn(
47
+ "ods-card",
48
+ `ods-card--${variant}`,
49
+ `ods-card--pad-${padding}`,
50
+ `ods-card--radius-${radius}`,
51
+ (hoverable || isInteractive) && !loading && !disabled && "ods-card--hoverable",
52
+ isInteractive && "ods-card--clickable",
53
+ disabled && "ods-card--disabled",
54
+ loading && "ods-card--loading",
55
+ className
56
+ );
57
+ if (asChild) {
58
+ return /* @__PURE__ */ jsx(
59
+ Slot,
60
+ {
61
+ ...rest,
62
+ ref,
63
+ className: rootClassName,
64
+ onClick: isInteractive ? onClick : void 0,
65
+ onKeyDown: isInteractive ? handleKeyDown : onKeyDown,
66
+ "aria-disabled": disabled || void 0,
67
+ children
68
+ }
69
+ );
70
+ }
71
+ if (loading) {
72
+ return /* @__PURE__ */ jsx("div", { ...rest, ref, className: rootClassName, "aria-busy": "true", "aria-label": "Loading", children: /* @__PURE__ */ jsx("div", { className: "ods-card__skeleton", "data-ods-animate": "shimmer" }) });
73
+ }
74
+ return /* @__PURE__ */ jsx(
75
+ motion.div,
76
+ {
77
+ ...rest,
78
+ ref,
79
+ onClick: isInteractive ? onClick : void 0,
80
+ onKeyDown: isInteractive ? handleKeyDown : onKeyDown,
81
+ className: rootClassName,
82
+ whileHover: (hoverable || isInteractive) && !reducedMotion ? { y: -2, transition: { duration: 0.15 } } : void 0,
83
+ whileTap: isInteractive && !reducedMotion ? { scale: 0.99 } : void 0,
84
+ role: isInteractive ? "button" : rest.role,
85
+ tabIndex: isInteractive ? rest.tabIndex ?? 0 : rest.tabIndex,
86
+ "aria-disabled": disabled || void 0,
87
+ children
88
+ }
89
+ );
90
+ });
91
+ Card.displayName = "Card";
92
+ var CardHeader = forwardRef(function CardHeader2({ className, children, ...rest }, ref) {
93
+ return /* @__PURE__ */ jsx("div", { ...rest, ref, className: cn("ods-card__header", className), children });
94
+ });
95
+ CardHeader.displayName = "Card.Header";
96
+ var CardBody = forwardRef(function CardBody2({ className, children, ...rest }, ref) {
97
+ return /* @__PURE__ */ jsx("div", { ...rest, ref, className: cn("ods-card__body", className), children });
98
+ });
99
+ CardBody.displayName = "Card.Body";
100
+ var CardFooter = forwardRef(function CardFooter2({ className, children, ...rest }, ref) {
101
+ return /* @__PURE__ */ jsx("div", { ...rest, ref, className: cn("ods-card__footer", className), children });
102
+ });
103
+ CardFooter.displayName = "Card.Footer";
104
+ var CardTitle = forwardRef(function CardTitle2({ as: Comp = "h3", className, children, ...rest }, ref) {
105
+ return /* @__PURE__ */ jsx(
106
+ Comp,
107
+ {
108
+ ...rest,
109
+ ref,
110
+ className: cn("ods-card__title", className),
111
+ children
112
+ }
113
+ );
114
+ });
115
+ CardTitle.displayName = "Card.Title";
116
+ var CardDescription = forwardRef(
117
+ function CardDescription2({ className, children, ...rest }, ref) {
118
+ return /* @__PURE__ */ jsx("p", { ...rest, ref, className: cn("ods-card__description", className), children });
119
+ }
120
+ );
121
+ CardDescription.displayName = "Card.Description";
122
+ Card.Header = CardHeader;
123
+ Card.Body = CardBody;
124
+ Card.Footer = CardFooter;
125
+ Card.Title = CardTitle;
126
+ Card.Description = CardDescription;
127
+
128
+ // src/components/Avatar/Avatar.tsx
129
+ import {
130
+ forwardRef as forwardRef2,
131
+ useCallback,
132
+ useMemo,
133
+ useState
134
+ } from "react";
135
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
136
+ var PALETTE_SIZE = 12;
137
+ function djb2(str) {
138
+ let h = 5381;
139
+ for (let i = 0; i < str.length; i++) h = (h << 5) + h + str.charCodeAt(i);
140
+ return h >>> 0;
141
+ }
142
+ function paletteIndexFor(seed) {
143
+ return djb2(seed) % PALETTE_SIZE;
144
+ }
145
+ var AVATAR_PALETTE_SIZE = PALETTE_SIZE;
146
+ function avatarPaletteIndex(seed) {
147
+ return paletteIndexFor(seed);
148
+ }
149
+ function normalizeInitials(raw) {
150
+ const trimmed = raw.trim();
151
+ if (!trimmed) return "";
152
+ const tokens = trimmed.split(/\s+/).filter(Boolean);
153
+ if (tokens.length >= 2) {
154
+ return (tokens[0].charAt(0) + tokens[1].charAt(0)).toUpperCase();
155
+ }
156
+ return trimmed.slice(0, 2).toUpperCase();
157
+ }
158
+ var STATUS_DEFAULT_LABEL = {
159
+ online: "Online",
160
+ offline: "Offline",
161
+ busy: "Busy",
162
+ away: "Away"
163
+ };
164
+ var Avatar = forwardRef2(function Avatar2({
165
+ src,
166
+ alt,
167
+ initials,
168
+ icon,
169
+ fallback,
170
+ seed,
171
+ palette,
172
+ size = "md",
173
+ shape = "circle",
174
+ status,
175
+ statusLabel,
176
+ onClick,
177
+ imgLoading = "lazy",
178
+ referrerPolicy,
179
+ className,
180
+ ...rest
181
+ }, ref) {
182
+ const [imgFailed, setImgFailed] = useState(false);
183
+ const handleImgError = useCallback(() => setImgFailed(true), []);
184
+ const showImage = Boolean(src) && !imgFailed;
185
+ const normalizedInitials = useMemo(
186
+ () => initials ? normalizeInitials(initials) : "",
187
+ [initials]
188
+ );
189
+ const paletteIdx = useMemo(() => {
190
+ if (palette !== void 0) {
191
+ return (Math.trunc(palette) % PALETTE_SIZE + PALETTE_SIZE) % PALETTE_SIZE;
192
+ }
193
+ const resolved = seed ?? alt ?? initials ?? "?";
194
+ return paletteIndexFor(resolved);
195
+ }, [palette, seed, alt, initials]);
196
+ const baseName = alt || normalizedInitials || "Avatar";
197
+ const announcedStatus = status ? statusLabel ?? STATUS_DEFAULT_LABEL[status] : "";
198
+ const accessibleName = announcedStatus ? `${baseName}, ${announcedStatus}` : baseName;
199
+ const isClickable = Boolean(onClick);
200
+ const handleKeyDown = (e) => {
201
+ if (!isClickable) return;
202
+ if ((e.key === "Enter" || e.key === " ") && !e.repeat) {
203
+ e.preventDefault();
204
+ onClick?.();
205
+ }
206
+ };
207
+ const rootClassName = cn(
208
+ "ods-avatar",
209
+ `ods-avatar--${size}`,
210
+ `ods-avatar--${shape}`,
211
+ isClickable && "ods-avatar--clickable",
212
+ className
213
+ );
214
+ return /* @__PURE__ */ jsxs(
215
+ "div",
216
+ {
217
+ ...rest,
218
+ ref,
219
+ className: rootClassName,
220
+ role: isClickable ? "button" : "img",
221
+ "aria-label": accessibleName,
222
+ tabIndex: isClickable ? 0 : void 0,
223
+ onClick,
224
+ onKeyDown: isClickable ? handleKeyDown : void 0,
225
+ "data-status": status,
226
+ children: [
227
+ /* @__PURE__ */ jsx2("span", { className: "ods-avatar__inner", "data-palette": paletteIdx, children: showImage ? /* @__PURE__ */ jsx2(
228
+ "img",
229
+ {
230
+ src,
231
+ alt: "",
232
+ className: "ods-avatar__image",
233
+ loading: imgLoading,
234
+ decoding: "async",
235
+ referrerPolicy,
236
+ onError: handleImgError,
237
+ draggable: false
238
+ }
239
+ ) : icon ? /* @__PURE__ */ jsx2("span", { className: "ods-avatar__icon", "aria-hidden": "true", children: icon }) : normalizedInitials ? /* @__PURE__ */ jsx2("span", { className: "ods-avatar__initials", "aria-hidden": "true", children: normalizedInitials }) : /* @__PURE__ */ jsx2("span", { className: "ods-avatar__initials", "aria-hidden": "true", children: fallback ?? "?" }) }),
240
+ status && // Decorative — the textual status is folded into the wrapper's
241
+ // aria-label above. `aria-hidden` keeps AT from double-announcing.
242
+ /* @__PURE__ */ jsx2(
243
+ "span",
244
+ {
245
+ className: cn("ods-avatar__status", `ods-avatar__status--${status}`),
246
+ "aria-hidden": "true"
247
+ }
248
+ )
249
+ ]
250
+ }
251
+ );
252
+ });
253
+ Avatar.displayName = "Avatar";
254
+ var AvatarStack = forwardRef2(function AvatarStack2({ children, max, size = "md", renderOverflow, direction = "ltr", className, ...rest }, ref) {
255
+ const arr = Array.isArray(children) ? children : [children];
256
+ const visible = max && arr.length > max ? arr.slice(0, max) : arr;
257
+ const overflow = max && arr.length > max ? arr.length - max : 0;
258
+ return /* @__PURE__ */ jsxs(
259
+ "div",
260
+ {
261
+ ...rest,
262
+ ref,
263
+ className: cn("ods-avatar-stack", `ods-avatar-stack--${direction}`, className),
264
+ children: [
265
+ visible,
266
+ overflow > 0 && (renderOverflow ? renderOverflow(overflow) : /* @__PURE__ */ jsx2(
267
+ "div",
268
+ {
269
+ className: cn(
270
+ "ods-avatar",
271
+ `ods-avatar--${size}`,
272
+ "ods-avatar--circle",
273
+ "ods-avatar-stack__overflow"
274
+ ),
275
+ role: "img",
276
+ "aria-label": `${overflow} more`,
277
+ children: /* @__PURE__ */ jsx2("span", { className: "ods-avatar__inner", children: /* @__PURE__ */ jsxs("span", { className: "ods-avatar__initials", "aria-hidden": "true", children: [
278
+ "+",
279
+ overflow
280
+ ] }) })
281
+ }
282
+ ))
283
+ ]
284
+ }
285
+ );
286
+ });
287
+ AvatarStack.displayName = "AvatarStack";
288
+
289
+ // src/components/BlogCard/BlogCard.tsx
290
+ import { forwardRef as forwardRef3, useId } from "react";
291
+ import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
292
+ var BlogCard = forwardRef3(function BlogCard2({
293
+ cover,
294
+ coverAspect = "16/9",
295
+ category,
296
+ tags,
297
+ meta,
298
+ readingTime,
299
+ title,
300
+ titleAs = "h3",
301
+ description,
302
+ author,
303
+ footer,
304
+ radius = "md",
305
+ orientation = "vertical",
306
+ href,
307
+ onClick,
308
+ id: providedId,
309
+ className,
310
+ ...rest
311
+ }, ref) {
312
+ const reactId = useId();
313
+ const baseId = providedId ?? `ods-blog-card-${reactId}`;
314
+ const titleId = `${baseId}-title`;
315
+ const descId = description ? `${baseId}-desc` : void 0;
316
+ const tagList = tags ?? (category ? [category] : []);
317
+ const body = /* @__PURE__ */ jsxs2(Fragment, { children: [
318
+ cover && /* @__PURE__ */ jsx3(
319
+ "div",
320
+ {
321
+ className: cn(
322
+ "ods-blog-card__cover",
323
+ `ods-blog-card__cover--aspect-${coverAspect.replace("/", "-")}`
324
+ ),
325
+ "aria-hidden": "true",
326
+ children: cover
327
+ }
328
+ ),
329
+ /* @__PURE__ */ jsxs2("div", { className: "ods-blog-card__body", children: [
330
+ (tagList.length > 0 || meta || readingTime) && /* @__PURE__ */ jsxs2("div", { className: "ods-blog-card__meta", children: [
331
+ tagList.length > 0 && /* @__PURE__ */ jsx3("span", { className: "ods-blog-card__tags", children: tagList.map((t, i) => /* @__PURE__ */ jsx3("span", { className: "ods-blog-card__tag", children: t }, i)) }),
332
+ meta && /* @__PURE__ */ jsx3("span", { className: "ods-blog-card__date", children: meta }),
333
+ readingTime && /* @__PURE__ */ jsx3("span", { className: "ods-blog-card__reading", children: readingTime })
334
+ ] }),
335
+ /* @__PURE__ */ jsx3(Card.Title, { as: titleAs, id: titleId, className: "ods-blog-card__title", children: title }),
336
+ description && /* @__PURE__ */ jsx3(Card.Description, { id: descId, className: "ods-blog-card__desc", children: description }),
337
+ (author || footer) && /* @__PURE__ */ jsxs2("div", { className: "ods-blog-card__footer", children: [
338
+ author && /* @__PURE__ */ jsxs2("div", { className: "ods-blog-card__author", children: [
339
+ author.avatar && /* @__PURE__ */ jsx3(
340
+ Avatar,
341
+ {
342
+ size: "sm",
343
+ src: author.avatar.src,
344
+ initials: author.avatar.initials,
345
+ seed: author.avatar.seed,
346
+ palette: author.avatar.palette,
347
+ alt: author.avatar.alt ?? (typeof author.name === "string" ? author.name : void 0)
348
+ }
349
+ ),
350
+ /* @__PURE__ */ jsxs2("div", { className: "ods-blog-card__author-text", children: [
351
+ /* @__PURE__ */ jsx3("span", { className: "ods-blog-card__author-name", children: author.name }),
352
+ author.byline && /* @__PURE__ */ jsx3("span", { className: "ods-blog-card__author-byline", children: author.byline })
353
+ ] })
354
+ ] }),
355
+ footer && /* @__PURE__ */ jsx3("div", { className: "ods-blog-card__action", children: footer })
356
+ ] })
357
+ ] })
358
+ ] });
359
+ if (href) {
360
+ return /* @__PURE__ */ jsx3(
361
+ Card,
362
+ {
363
+ asChild: true,
364
+ radius,
365
+ padding: "none",
366
+ className: cn(
367
+ "ods-blog-card",
368
+ `ods-blog-card--${orientation}`,
369
+ "ods-blog-card--interactive",
370
+ className
371
+ ),
372
+ "aria-labelledby": titleId,
373
+ "aria-describedby": descId,
374
+ children: /* @__PURE__ */ jsx3(
375
+ "a",
376
+ {
377
+ ...rest,
378
+ ref,
379
+ href,
380
+ id: baseId,
381
+ onClick,
382
+ children: body
383
+ }
384
+ )
385
+ }
386
+ );
387
+ }
388
+ return /* @__PURE__ */ jsx3(
389
+ Card,
390
+ {
391
+ ...rest,
392
+ ref,
393
+ id: baseId,
394
+ radius,
395
+ padding: "none",
396
+ onClick,
397
+ className: cn(
398
+ "ods-blog-card",
399
+ `ods-blog-card--${orientation}`,
400
+ onClick && "ods-blog-card--interactive",
401
+ className
402
+ ),
403
+ "aria-labelledby": titleId,
404
+ "aria-describedby": descId,
405
+ children: body
406
+ }
407
+ );
408
+ });
409
+ BlogCard.displayName = "BlogCard";
410
+
411
+ // src/components/ChatBubble/ChatBubble.tsx
412
+ import {
413
+ forwardRef as forwardRef4
414
+ } from "react";
415
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
416
+ var STATUS_LABEL = {
417
+ sent: "Sent",
418
+ delivered: "Delivered",
419
+ read: "Read",
420
+ failed: "Failed to send"
421
+ };
422
+ function StatusGlyph({ status }) {
423
+ switch (status) {
424
+ case "sent":
425
+ return /* @__PURE__ */ jsx4("svg", { viewBox: "0 0 10 10", width: "10", height: "10", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx4(
426
+ "path",
427
+ {
428
+ d: "M2 5l2 2 4-4",
429
+ stroke: "currentColor",
430
+ strokeWidth: "1.4",
431
+ strokeLinecap: "round",
432
+ strokeLinejoin: "round"
433
+ }
434
+ ) });
435
+ case "delivered":
436
+ return /* @__PURE__ */ jsx4("svg", { viewBox: "0 0 12 10", width: "12", height: "10", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx4(
437
+ "path",
438
+ {
439
+ d: "M1 5l2 2 3.5-3.5 M5 5l2 2 4-4",
440
+ stroke: "currentColor",
441
+ strokeWidth: "1.4",
442
+ strokeLinecap: "round",
443
+ strokeLinejoin: "round"
444
+ }
445
+ ) });
446
+ case "read":
447
+ return /* @__PURE__ */ jsx4("svg", { viewBox: "0 0 12 10", width: "12", height: "10", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx4(
448
+ "path",
449
+ {
450
+ d: "M1 5l2 2 3.5-3.5 M5 5l2 2 4-4",
451
+ stroke: "currentColor",
452
+ strokeWidth: "1.4",
453
+ strokeLinecap: "round",
454
+ strokeLinejoin: "round"
455
+ }
456
+ ) });
457
+ case "failed":
458
+ return /* @__PURE__ */ jsxs3("svg", { viewBox: "0 0 10 10", width: "10", height: "10", fill: "none", "aria-hidden": "true", children: [
459
+ /* @__PURE__ */ jsx4("circle", { cx: "5", cy: "5", r: "4", stroke: "currentColor", strokeWidth: "1.4" }),
460
+ /* @__PURE__ */ jsx4("path", { d: "M5 3v3M5 6.8v.2", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round" })
461
+ ] });
462
+ }
463
+ }
464
+ var ChatBubble = forwardRef4(function ChatBubble2({
465
+ variant = "bot",
466
+ size = "md",
467
+ author,
468
+ timestamp,
469
+ avatar,
470
+ initial,
471
+ hideAvatar = false,
472
+ continuation = false,
473
+ metaPlacement = "bottom",
474
+ isTyping = false,
475
+ status,
476
+ actions,
477
+ attachments,
478
+ asChild = false,
479
+ className,
480
+ children,
481
+ ...rest
482
+ }, ref) {
483
+ const isUser = variant === "user";
484
+ const reserveAvatar = hideAvatar || continuation;
485
+ const rootClassName = cn(
486
+ "ods-chat",
487
+ `ods-chat--${variant}`,
488
+ `ods-chat--${size}`,
489
+ isTyping && "ods-chat--typing",
490
+ continuation && "ods-chat--continuation",
491
+ status === "failed" && "ods-chat--failed",
492
+ className
493
+ );
494
+ if (asChild) {
495
+ return /* @__PURE__ */ jsx4(
496
+ Slot,
497
+ {
498
+ ...rest,
499
+ ref,
500
+ className: rootClassName,
501
+ children
502
+ }
503
+ );
504
+ }
505
+ let resolvedAvatar;
506
+ if (avatar !== void 0) {
507
+ resolvedAvatar = avatar;
508
+ } else {
509
+ const resolvedInitial = initial ?? (typeof author === "string" ? author : isUser ? "You" : variant === "system" ? "System" : "AI");
510
+ resolvedAvatar = /* @__PURE__ */ jsx4(
511
+ Avatar,
512
+ {
513
+ initials: resolvedInitial,
514
+ alt: typeof author === "string" ? author : variant,
515
+ seed: typeof author === "string" ? author : variant,
516
+ size: size === "sm" ? "sm" : size === "lg" ? "lg" : "md"
517
+ }
518
+ );
519
+ }
520
+ const metaEl = author || timestamp || status ? /* @__PURE__ */ jsxs3("div", { className: "ods-chat__meta", children: [
521
+ author && /* @__PURE__ */ jsx4("span", { className: "ods-chat__author", children: author }),
522
+ timestamp && /* @__PURE__ */ jsx4("span", { className: "ods-chat__time", children: timestamp }),
523
+ status && /* @__PURE__ */ jsx4(
524
+ "span",
525
+ {
526
+ className: cn("ods-chat__status", `ods-chat__status--${status}`),
527
+ role: "img",
528
+ "aria-label": STATUS_LABEL[status],
529
+ children: /* @__PURE__ */ jsx4(StatusGlyph, { status })
530
+ }
531
+ )
532
+ ] }) : null;
533
+ return /* @__PURE__ */ jsxs3("div", { ...rest, ref, className: rootClassName, children: [
534
+ /* @__PURE__ */ jsx4(
535
+ "div",
536
+ {
537
+ className: cn("ods-chat__avatar-slot", reserveAvatar && "ods-chat__avatar-slot--hidden"),
538
+ "aria-hidden": reserveAvatar || void 0,
539
+ children: resolvedAvatar
540
+ }
541
+ ),
542
+ /* @__PURE__ */ jsxs3("div", { className: "ods-chat__column", children: [
543
+ metaPlacement === "top" && metaEl,
544
+ /* @__PURE__ */ jsxs3(
545
+ "div",
546
+ {
547
+ className: "ods-chat__bubble",
548
+ role: isTyping ? "status" : void 0,
549
+ "aria-live": isTyping ? "polite" : void 0,
550
+ children: [
551
+ isTyping ? (
552
+ // role="img" gives the dots cluster an accessible name without
553
+ // tripping axe's "aria-label on a non-roleful span" rule. The
554
+ // outer bubble already carries role="status" + aria-live, so
555
+ // AT users hear "Typing" the moment the indicator appears.
556
+ /* @__PURE__ */ jsxs3("span", { className: "ods-chat__typing", role: "img", "aria-label": "Typing", children: [
557
+ /* @__PURE__ */ jsx4("span", { className: "ods-chat__typing-dot" }),
558
+ /* @__PURE__ */ jsx4("span", { className: "ods-chat__typing-dot" }),
559
+ /* @__PURE__ */ jsx4("span", { className: "ods-chat__typing-dot" })
560
+ ] })
561
+ ) : /* @__PURE__ */ jsx4("div", { className: "ods-chat__body", children }),
562
+ !isTyping && attachments && /* @__PURE__ */ jsx4("div", { className: "ods-chat__attachments", children: attachments })
563
+ ]
564
+ }
565
+ ),
566
+ metaPlacement === "bottom" && metaEl,
567
+ actions && /* @__PURE__ */ jsx4("div", { className: "ods-chat__actions", children: actions })
568
+ ] })
569
+ ] });
570
+ });
571
+ ChatBubble.displayName = "ChatBubble";
572
+
573
+ // src/components/ChatComposer/ChatComposer.tsx
574
+ import {
575
+ forwardRef as forwardRef5,
576
+ useEffect,
577
+ useId as useId2,
578
+ useRef,
579
+ useState as useState2
580
+ } from "react";
581
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
582
+ function SendGlyph() {
583
+ return /* @__PURE__ */ jsx5("svg", { viewBox: "0 0 16 16", width: "16", height: "16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx5(
584
+ "path",
585
+ {
586
+ d: "M14.5 1.5 7 9M14.5 1.5l-4.6 13-2.9-5.5L1.5 6.1l13-4.6Z",
587
+ stroke: "currentColor",
588
+ strokeWidth: "1.3",
589
+ strokeLinecap: "round",
590
+ strokeLinejoin: "round"
591
+ }
592
+ ) });
593
+ }
594
+ function SpinnerGlyph() {
595
+ return /* @__PURE__ */ jsxs4("svg", { viewBox: "0 0 16 16", width: "16", height: "16", fill: "none", "aria-hidden": "true", children: [
596
+ /* @__PURE__ */ jsx5("circle", { cx: "8", cy: "8", r: "6", stroke: "currentColor", strokeOpacity: "0.3", strokeWidth: "2" }),
597
+ /* @__PURE__ */ jsx5("path", { d: "M8 2a6 6 0 0 1 6 6", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })
598
+ ] });
599
+ }
600
+ function StopGlyph() {
601
+ return /* @__PURE__ */ jsx5("svg", { viewBox: "0 0 16 16", width: "16", height: "16", "aria-hidden": "true", children: /* @__PURE__ */ jsx5("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1", fill: "currentColor" }) });
602
+ }
603
+ var ChatComposer = forwardRef5(
604
+ function ChatComposer2({
605
+ value,
606
+ defaultValue,
607
+ onChange,
608
+ onSend,
609
+ placeholder = "Write a message\u2026",
610
+ disabled = false,
611
+ sending = false,
612
+ allowEmpty = false,
613
+ size = "md",
614
+ minRows = 1,
615
+ maxRows = 6,
616
+ grow = "up",
617
+ leading,
618
+ trailing,
619
+ onStop,
620
+ stopLabel = "Stop",
621
+ sendIcon,
622
+ sendLabel = "Send message",
623
+ fieldLabel = "Message",
624
+ hint,
625
+ className,
626
+ onKeyDown,
627
+ ...rest
628
+ }, forwardedRef) {
629
+ const innerRef = useRef(null);
630
+ const setRef = (node) => {
631
+ innerRef.current = node;
632
+ if (typeof forwardedRef === "function") forwardedRef(node);
633
+ else if (forwardedRef)
634
+ forwardedRef.current = node;
635
+ };
636
+ const isControlled = value !== void 0;
637
+ const [internal, setInternal] = useState2(defaultValue ?? "");
638
+ const text = isControlled ? value : internal;
639
+ const baseId = useId2();
640
+ const setText = (next) => {
641
+ if (!isControlled) setInternal(next);
642
+ onChange?.(next);
643
+ };
644
+ useEffect(() => {
645
+ const el = innerRef.current;
646
+ if (!el) return;
647
+ el.style.height = "auto";
648
+ const cs = window.getComputedStyle(el);
649
+ const fontSize = parseFloat(cs.fontSize) || 14;
650
+ const lh = cs.lineHeight === "normal" ? fontSize * 1.4 : cs.lineHeight.endsWith("px") ? parseFloat(cs.lineHeight) : parseFloat(cs.lineHeight) * fontSize;
651
+ const padV = parseFloat(cs.paddingTop) + parseFloat(cs.paddingBottom);
652
+ const max = lh * maxRows + padV;
653
+ const next = Math.min(el.scrollHeight, max);
654
+ el.style.height = `${next}px`;
655
+ const overflowing = el.scrollHeight > max;
656
+ el.style.overflowY = overflowing ? "auto" : "hidden";
657
+ if (overflowing) el.scrollTop = el.scrollHeight;
658
+ }, [text, maxRows]);
659
+ const canSend = !disabled && !sending && (allowEmpty || text.trim().length > 0);
660
+ const stoppable = sending && Boolean(onStop);
661
+ const submit = () => {
662
+ if (!canSend) return;
663
+ onSend?.(text);
664
+ if (!isControlled) setInternal("");
665
+ };
666
+ const handleKeyDown = (e) => {
667
+ onKeyDown?.(e);
668
+ if (e.defaultPrevented) return;
669
+ if (e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing) {
670
+ e.preventDefault();
671
+ submit();
672
+ }
673
+ };
674
+ return /* @__PURE__ */ jsxs4(
675
+ "div",
676
+ {
677
+ className: cn(
678
+ "ods-composer",
679
+ `ods-composer--${size}`,
680
+ `ods-composer--grow-${grow}`,
681
+ disabled && "ods-composer--disabled",
682
+ sending && "ods-composer--sending",
683
+ stoppable && "ods-composer--stoppable",
684
+ className
685
+ ),
686
+ children: [
687
+ /* @__PURE__ */ jsxs4("div", { className: "ods-composer__row", children: [
688
+ leading && /* @__PURE__ */ jsx5("div", { className: "ods-composer__leading", children: leading }),
689
+ /* @__PURE__ */ jsx5(
690
+ "textarea",
691
+ {
692
+ ...rest,
693
+ ref: setRef,
694
+ id: `ods-composer-${baseId}`,
695
+ className: "ods-composer__field",
696
+ rows: minRows,
697
+ value: text,
698
+ placeholder,
699
+ disabled,
700
+ "aria-label": fieldLabel,
701
+ onChange: (e) => setText(e.target.value),
702
+ onKeyDown: handleKeyDown
703
+ }
704
+ ),
705
+ /* @__PURE__ */ jsxs4("div", { className: "ods-composer__trailing", children: [
706
+ trailing,
707
+ /* @__PURE__ */ jsxs4(
708
+ "button",
709
+ {
710
+ type: "button",
711
+ className: "ods-composer__send",
712
+ "aria-label": stoppable ? stopLabel : sending ? "Sending\u2026" : sendLabel,
713
+ "aria-busy": sending && !stoppable || void 0,
714
+ onClick: stoppable ? onStop : submit,
715
+ disabled: stoppable ? false : !canSend,
716
+ children: [
717
+ /* @__PURE__ */ jsx5("span", { className: "ods-composer__send-icon", children: sendIcon ?? /* @__PURE__ */ jsx5(SendGlyph, {}) }),
718
+ /* @__PURE__ */ jsx5("span", { className: "ods-composer__send-spinner", "aria-hidden": "true", children: /* @__PURE__ */ jsx5(SpinnerGlyph, {}) }),
719
+ /* @__PURE__ */ jsx5("span", { className: "ods-composer__send-stop", "aria-hidden": "true", children: /* @__PURE__ */ jsx5(StopGlyph, {}) })
720
+ ]
721
+ }
722
+ )
723
+ ] })
724
+ ] }),
725
+ hint && /* @__PURE__ */ jsx5("div", { className: "ods-composer__hint", children: hint })
726
+ ]
727
+ }
728
+ );
729
+ }
730
+ );
731
+ ChatComposer.displayName = "ChatComposer";
732
+
733
+ // src/components/ChatLinkPreview/ChatLinkPreview.tsx
734
+ import { forwardRef as forwardRef6 } from "react";
735
+
736
+ // src/components/TruncatedText/TruncatedText.tsx
737
+ import { useLayoutEffect, useRef as useRef2, useState as useState3 } from "react";
738
+ import { jsx as jsx6 } from "react/jsx-runtime";
739
+ function TruncatedText({ children, className }) {
740
+ const ref = useRef2(null);
741
+ const [cut, setCut] = useState3(false);
742
+ useLayoutEffect(() => {
743
+ const el = ref.current;
744
+ if (!el) return;
745
+ const measure = () => setCut(el.scrollWidth > el.clientWidth);
746
+ measure();
747
+ if (typeof ResizeObserver === "undefined") return;
748
+ const observer = new ResizeObserver(measure);
749
+ observer.observe(el);
750
+ return () => observer.disconnect();
751
+ }, [children]);
752
+ return /* @__PURE__ */ jsx6(Tooltip, { content: children, disabled: !cut, position: "top", children: /* @__PURE__ */ jsx6("span", { ref, className: cn("ods-truncated-text", className), children }) });
753
+ }
754
+
755
+ // src/components/ChatLinkPreview/ChatLinkPreview.tsx
756
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
757
+ function hostOf(url) {
758
+ try {
759
+ return new URL(url).hostname.replace(/^www\./, "");
760
+ } catch {
761
+ return url;
762
+ }
763
+ }
764
+ function linkTargetProps(target) {
765
+ return { target, rel: target === "_blank" ? "noopener noreferrer" : void 0 };
766
+ }
767
+ var ChatLinkPreview = forwardRef6(
768
+ function ChatLinkPreview2({
769
+ url,
770
+ title,
771
+ description,
772
+ siteName,
773
+ image,
774
+ favicon,
775
+ linkTarget = "_blank",
776
+ compact = false,
777
+ className,
778
+ ...rest
779
+ }, ref) {
780
+ const host = hostOf(url);
781
+ return /* @__PURE__ */ jsxs5(
782
+ "a",
783
+ {
784
+ ...rest,
785
+ ref,
786
+ href: url,
787
+ ...linkTargetProps(linkTarget),
788
+ className: cn(
789
+ "ods-chat-link-preview",
790
+ compact && "ods-chat-link-preview--compact",
791
+ className
792
+ ),
793
+ children: [
794
+ /* @__PURE__ */ jsxs5("span", { className: "ods-chat-link-preview__body", children: [
795
+ /* @__PURE__ */ jsxs5("span", { className: "ods-chat-link-preview__site", children: [
796
+ favicon && /* @__PURE__ */ jsx7(
797
+ "img",
798
+ {
799
+ className: "ods-chat-link-preview__favicon",
800
+ src: favicon,
801
+ alt: "",
802
+ width: 14,
803
+ height: 14
804
+ }
805
+ ),
806
+ /* @__PURE__ */ jsx7("span", { className: "ods-chat-link-preview__site-name", children: siteName ?? host })
807
+ ] }),
808
+ /* @__PURE__ */ jsx7(TruncatedText, { className: "ods-chat-link-preview__title", children: title ?? host }),
809
+ description && !compact && /* @__PURE__ */ jsx7("span", { className: "ods-chat-link-preview__description", children: description })
810
+ ] }),
811
+ image && /* @__PURE__ */ jsx7(
812
+ "span",
813
+ {
814
+ className: "ods-chat-link-preview__image",
815
+ style: { backgroundImage: `url(${JSON.stringify(image)})` },
816
+ "aria-hidden": "true"
817
+ }
818
+ )
819
+ ]
820
+ }
821
+ );
822
+ }
823
+ );
824
+ ChatLinkPreview.displayName = "ChatLinkPreview";
825
+
826
+ // src/components/ChatDocPreview/ChatDocPreview.tsx
827
+ import { forwardRef as forwardRef7 } from "react";
828
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
829
+ function PageGlyph() {
830
+ return /* @__PURE__ */ jsxs6("svg", { viewBox: "0 0 16 16", width: "16", height: "16", fill: "none", "aria-hidden": "true", children: [
831
+ /* @__PURE__ */ jsx8(
832
+ "path",
833
+ {
834
+ d: "M4 2.5h5L13 6.5V13a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1Z",
835
+ stroke: "currentColor",
836
+ strokeWidth: "1.1",
837
+ strokeLinejoin: "round"
838
+ }
839
+ ),
840
+ /* @__PURE__ */ jsx8(
841
+ "path",
842
+ {
843
+ d: "M9 2.5V6h3.5M5.5 9h5M5.5 11h3.5",
844
+ stroke: "currentColor",
845
+ strokeWidth: "1.1",
846
+ strokeLinecap: "round"
847
+ }
848
+ )
849
+ ] });
850
+ }
851
+ var ChatDocPreview = forwardRef7(
852
+ function ChatDocPreview2({ url, title, path, excerpt, linkTarget = "_blank", className, ...rest }, ref) {
853
+ return /* @__PURE__ */ jsxs6(
854
+ "a",
855
+ {
856
+ ...rest,
857
+ ref,
858
+ href: url,
859
+ ...linkTargetProps(linkTarget),
860
+ className: cn("ods-chat-doc-preview", className),
861
+ children: [
862
+ /* @__PURE__ */ jsxs6("span", { className: "ods-chat-doc-preview__head", children: [
863
+ /* @__PURE__ */ jsx8("span", { className: "ods-chat-doc-preview__icon", children: /* @__PURE__ */ jsx8(PageGlyph, {}) }),
864
+ /* @__PURE__ */ jsx8(TruncatedText, { className: "ods-chat-doc-preview__title", children: title })
865
+ ] }),
866
+ excerpt && /* @__PURE__ */ jsx8("span", { className: "ods-chat-doc-preview__excerpt", children: excerpt }),
867
+ path && path.length > 0 && /* @__PURE__ */ jsx8("span", { className: "ods-chat-doc-preview__path", children: path.map((segment, i) => (
868
+ // Keyed by the path up to here: unique even when a name repeats.
869
+ /* @__PURE__ */ jsx8("span", { className: "ods-chat-doc-preview__segment", children: segment }, path.slice(0, i + 1).join("/"))
870
+ )) })
871
+ ]
872
+ }
873
+ );
874
+ }
875
+ );
876
+ ChatDocPreview.displayName = "ChatDocPreview";
877
+
878
+ // src/components/Radio/RadioGroup.tsx
879
+ import {
880
+ createContext,
881
+ forwardRef as forwardRef8,
882
+ useContext,
883
+ useId as useId3,
884
+ useRef as useRef3
885
+ } from "react";
886
+ import { useRadioGroup } from "react-aria";
887
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
888
+ var RadioGroupContext = createContext(null);
889
+ function useRadioGroupContext() {
890
+ const context = useContext(RadioGroupContext);
891
+ if (!context) {
892
+ throw new Error("Radio must be used within a RadioGroup");
893
+ }
894
+ return context;
895
+ }
896
+ var RadioGroup = forwardRef8(function RadioGroup2({
897
+ value,
898
+ defaultValue,
899
+ onChange,
900
+ label,
901
+ orientation = "vertical",
902
+ disabled = false,
903
+ children,
904
+ className,
905
+ name,
906
+ required,
907
+ id: providedId,
908
+ helperText,
909
+ error,
910
+ "aria-label": ariaLabel,
911
+ "aria-labelledby": ariaLabelledBy,
912
+ "aria-describedby": consumerDescribedBy,
913
+ ...rest
914
+ }, ref) {
915
+ const reactId = useId3();
916
+ const baseId = providedId ?? `ods-radio-group-${reactId}`;
917
+ const hintId = error || helperText ? `${baseId}-hint` : void 0;
918
+ const innerRef = useRef3(null);
919
+ const describedBy = [consumerDescribedBy, hintId].filter(Boolean).join(" ") || void 0;
920
+ const ariaProps = {
921
+ // react-aria wants a string label for its `<label>` association,
922
+ // even if our public API also takes ReactNode. Fall back to the
923
+ // explicit aria-label when label isn't a string.
924
+ label: typeof label === "string" ? label : ariaLabel || "radio group",
925
+ value,
926
+ defaultValue,
927
+ onChange,
928
+ isDisabled: disabled,
929
+ isInvalid: !!error,
930
+ isRequired: required,
931
+ orientation,
932
+ name,
933
+ "aria-label": ariaLabel,
934
+ "aria-labelledby": ariaLabelledBy,
935
+ "aria-describedby": describedBy
936
+ };
937
+ const state = $384704861d32dbed$export$bca9d026f8e704eb(ariaProps);
938
+ const { radioGroupProps, labelProps } = useRadioGroup(ariaProps, state);
939
+ const setRef = (node) => {
940
+ innerRef.current = node;
941
+ if (typeof ref === "function") ref(node);
942
+ else if (ref) ref.current = node;
943
+ };
944
+ return /* @__PURE__ */ jsxs7(
945
+ "div",
946
+ {
947
+ ...rest,
948
+ ...radioGroupProps,
949
+ ref: setRef,
950
+ id: baseId,
951
+ className: cn(
952
+ "ods-radio-group",
953
+ `ods-radio-group--${orientation}`,
954
+ disabled && "ods-radio-group--disabled",
955
+ error && "ods-radio-group--error",
956
+ className
957
+ ),
958
+ children: [
959
+ label && /* @__PURE__ */ jsx9("span", { ...labelProps, className: "ods-radio-group__label", children: label }),
960
+ /* @__PURE__ */ jsx9(RadioGroupContext.Provider, { value: state, children: /* @__PURE__ */ jsx9("div", { className: "ods-radio-group__items", children }) }),
961
+ error ? /* @__PURE__ */ jsx9(
962
+ "div",
963
+ {
964
+ id: hintId,
965
+ className: "ods-radio-group__hint ods-radio-group__hint--error",
966
+ role: "alert",
967
+ children: error
968
+ }
969
+ ) : helperText ? /* @__PURE__ */ jsx9("div", { id: hintId, className: "ods-radio-group__hint", children: helperText }) : null
970
+ ]
971
+ }
972
+ );
973
+ });
974
+ RadioGroup.displayName = "RadioGroup";
975
+
976
+ // src/components/Radio/Radio.tsx
977
+ import { forwardRef as forwardRef9, useRef as useRef4 } from "react";
978
+ import { useRadio } from "react-aria";
979
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
980
+ var Radio = forwardRef9(function Radio2({ value, label, description, size = "md", disabled = false, className, ...props }, forwardedRef) {
981
+ const state = useRadioGroupContext();
982
+ const innerRef = useRef4(null);
983
+ const setRef = (node) => {
984
+ innerRef.current = node;
985
+ if (typeof forwardedRef === "function") forwardedRef(node);
986
+ else if (forwardedRef) forwardedRef.current = node;
987
+ };
988
+ const ariaNameProps = resolveAccessibleName({
989
+ label: typeof label === "string" ? label : void 0,
990
+ ariaLabel: props["aria-label"],
991
+ ariaLabelledby: props["aria-labelledby"],
992
+ componentName: "Radio",
993
+ fallbacks: [value]
994
+ });
995
+ const { inputProps } = useRadio(
996
+ {
997
+ value,
998
+ isDisabled: disabled,
999
+ ...ariaNameProps
1000
+ },
1001
+ state,
1002
+ innerRef
1003
+ );
1004
+ const isSelected = state.selectedValue === value;
1005
+ const {
1006
+ onClick: _onClick,
1007
+ onKeyDown: _onKeyDown,
1008
+ onKeyUp: _onKeyUp,
1009
+ onFocus: _onFocus,
1010
+ onBlur: _onBlur,
1011
+ ...passthroughProps
1012
+ } = props;
1013
+ return /* @__PURE__ */ jsxs8(
1014
+ "label",
1015
+ {
1016
+ className: cn(
1017
+ "ods-radio",
1018
+ `ods-radio--${size}`,
1019
+ isSelected && "ods-radio--selected",
1020
+ disabled && "ods-radio--disabled",
1021
+ description && "ods-radio--with-description",
1022
+ className
1023
+ ),
1024
+ children: [
1025
+ /* @__PURE__ */ jsx10(
1026
+ "input",
1027
+ {
1028
+ ...passthroughProps,
1029
+ ...inputProps,
1030
+ ref: setRef,
1031
+ className: "ods-radio__input"
1032
+ }
1033
+ ),
1034
+ /* @__PURE__ */ jsx10("div", { className: "ods-radio__circle", "aria-hidden": "true", children: /* @__PURE__ */ jsx10("div", { className: cn("ods-radio__dot", isSelected && "ods-radio__dot--visible") }) }),
1035
+ (label || description) && /* @__PURE__ */ jsxs8("div", { className: "ods-radio__text", children: [
1036
+ label && /* @__PURE__ */ jsx10("span", { className: "ods-radio__label", children: label }),
1037
+ description && /* @__PURE__ */ jsx10("span", { className: "ods-radio__description", children: description })
1038
+ ] })
1039
+ ]
1040
+ }
1041
+ );
1042
+ });
1043
+ Radio.displayName = "Radio";
1044
+
1045
+ // src/components/ChatInputRequest/ChatInputRequest.tsx
1046
+ import {
1047
+ forwardRef as forwardRef10,
1048
+ useId as useId4,
1049
+ useState as useState4
1050
+ } from "react";
1051
+ import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
1052
+ var EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1053
+ function optionsOf(field) {
1054
+ return (field.options ?? []).map((o) => typeof o === "string" ? { value: o, label: o } : o);
1055
+ }
1056
+ function emptyValue(field) {
1057
+ if (field.type === "multichoice") return [];
1058
+ return null;
1059
+ }
1060
+ function isEmpty(value) {
1061
+ if (Array.isArray(value)) return value.length === 0;
1062
+ return value === null || value === "";
1063
+ }
1064
+ function fieldProblem(field, value) {
1065
+ if (isEmpty(value)) return field.required ? "Required." : null;
1066
+ if (field.type === "email" && typeof value === "string" && !EMAIL.test(value.trim())) {
1067
+ return "Enter an email address, like name@company.com.";
1068
+ }
1069
+ if (field.type === "number" && typeof value === "string" && Number.isNaN(Number(value)))
1070
+ return "Enter a number.";
1071
+ return null;
1072
+ }
1073
+ function problemsOf(fields, values, typed) {
1074
+ return Object.fromEntries(
1075
+ fields.map((f) => {
1076
+ const value = values[f.name] ?? null;
1077
+ return [f.name, typed && isEmpty(value) ? null : fieldProblem(f, value)];
1078
+ })
1079
+ );
1080
+ }
1081
+ function displayValue(field, value) {
1082
+ if (value === null || isEmpty(value)) return "";
1083
+ if (typeof value === "boolean") return value ? "Yes" : "No";
1084
+ const labels = new Map(optionsOf(field).map((o) => [o.value, o.label]));
1085
+ if (Array.isArray(value)) return value.map((v) => labels.get(v) ?? v).join(", ");
1086
+ return labels.get(value) ?? value;
1087
+ }
1088
+ function ChoiceField({ field, value, error, disabled, onChange }) {
1089
+ const options = field.type === "boolean" ? [
1090
+ { value: "yes", label: "Yes" },
1091
+ { value: "no", label: "No" }
1092
+ ] : optionsOf(field);
1093
+ let selected = "";
1094
+ if (field.type === "boolean" && typeof value === "boolean") selected = value ? "yes" : "no";
1095
+ else if (typeof value === "string") selected = value;
1096
+ return /* @__PURE__ */ jsx11(
1097
+ RadioGroup,
1098
+ {
1099
+ label: field.label,
1100
+ value: selected,
1101
+ onChange: (v) => onChange(field.type === "boolean" ? v === "yes" : v),
1102
+ orientation: options.length > 3 ? "vertical" : "horizontal",
1103
+ required: field.required,
1104
+ disabled,
1105
+ helperText: field.helperText,
1106
+ error: error ?? void 0,
1107
+ children: options.map((o) => /* @__PURE__ */ jsx11(Radio, { value: o.value, label: o.label, size: "sm" }, o.value))
1108
+ }
1109
+ );
1110
+ }
1111
+ function MultiChoiceField({ field, value, error, disabled, onChange }) {
1112
+ const selected = Array.isArray(value) ? value : [];
1113
+ const toggle = (v, on) => onChange(on ? [...selected, v] : selected.filter((s) => s !== v));
1114
+ return /* @__PURE__ */ jsxs9("fieldset", { className: "ods-chat-input-request__group", disabled, children: [
1115
+ /* @__PURE__ */ jsx11("legend", { className: "ods-chat-input-request__legend", children: field.label }),
1116
+ optionsOf(field).map((o) => /* @__PURE__ */ jsx11(
1117
+ Checkbox,
1118
+ {
1119
+ size: "sm",
1120
+ label: o.label,
1121
+ checked: selected.includes(o.value),
1122
+ onChange: (on) => toggle(o.value, on)
1123
+ },
1124
+ o.value
1125
+ )),
1126
+ error && /* @__PURE__ */ jsx11("span", { className: "ods-chat-input-request__error", role: "alert", children: error })
1127
+ ] });
1128
+ }
1129
+ function TextField({ field, value, error, disabled, onChange }) {
1130
+ return /* @__PURE__ */ jsx11(
1131
+ Input,
1132
+ {
1133
+ size: "sm",
1134
+ type: field.type === "choice" || field.type === "multichoice" || field.type === "boolean" ? "text" : field.type,
1135
+ label: field.label,
1136
+ value: typeof value === "string" ? value : "",
1137
+ onChange: (e) => onChange(e.target.value),
1138
+ placeholder: field.placeholder,
1139
+ helperText: field.helperText,
1140
+ required: field.required,
1141
+ disabled,
1142
+ error: Boolean(error),
1143
+ errorMessage: error ?? void 0
1144
+ }
1145
+ );
1146
+ }
1147
+ function Field(props) {
1148
+ const { type } = props.field;
1149
+ if (type === "choice" || type === "boolean") return /* @__PURE__ */ jsx11(ChoiceField, { ...props });
1150
+ if (type === "multichoice") return /* @__PURE__ */ jsx11(MultiChoiceField, { ...props });
1151
+ return /* @__PURE__ */ jsx11(TextField, { ...props });
1152
+ }
1153
+ function AnswerSummary({
1154
+ fields,
1155
+ answer
1156
+ }) {
1157
+ if (answer === "skipped") return /* @__PURE__ */ jsx11("p", { className: "ods-chat-input-request__answer", children: "Skipped" });
1158
+ const parts = fields.map((f) => displayValue(f, answer.values[f.name] ?? null)).filter(Boolean);
1159
+ if (answer.text) parts.push(answer.text);
1160
+ return /* @__PURE__ */ jsxs9("p", { className: "ods-chat-input-request__answer", children: [
1161
+ /* @__PURE__ */ jsx11("svg", { viewBox: "0 0 16 16", width: "14", height: "14", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx11(
1162
+ "path",
1163
+ {
1164
+ d: "M3.5 8.5l3 3 6-7",
1165
+ stroke: "currentColor",
1166
+ strokeWidth: "1.6",
1167
+ strokeLinecap: "round",
1168
+ strokeLinejoin: "round"
1169
+ }
1170
+ ) }),
1171
+ /* @__PURE__ */ jsx11("span", { children: parts.length > 0 ? parts.join(" \xB7 ") : "Sent" })
1172
+ ] });
1173
+ }
1174
+ var ChatInputRequest = forwardRef10(
1175
+ function ChatInputRequest2({
1176
+ prompt,
1177
+ description,
1178
+ fields,
1179
+ onSubmit,
1180
+ onSkip,
1181
+ answer = null,
1182
+ submitting = false,
1183
+ submitLabel = "Send",
1184
+ skipLabel = "Skip",
1185
+ textPlaceholder = "Or type your answer\u2026",
1186
+ className,
1187
+ ...rest
1188
+ }, ref) {
1189
+ const promptId = useId4();
1190
+ const [values, setValues] = useState4(
1191
+ () => Object.fromEntries(fields.map((f) => [f.name, emptyValue(f)]))
1192
+ );
1193
+ const [errors, setErrors] = useState4({});
1194
+ const [text, setText] = useState4("");
1195
+ const [textError, setTextError] = useState4(null);
1196
+ const answered = answer !== null;
1197
+ const locked = answered || submitting;
1198
+ const submit = (e) => {
1199
+ e.preventDefault();
1200
+ if (locked) return;
1201
+ const typed = text.trim();
1202
+ if (fields.length === 0 && !typed) {
1203
+ setTextError("Type an answer.");
1204
+ return;
1205
+ }
1206
+ const found = problemsOf(fields, values, typed);
1207
+ setErrors(found);
1208
+ if (Object.values(found).some(Boolean)) return;
1209
+ onSubmit(values, typed || void 0);
1210
+ };
1211
+ return /* @__PURE__ */ jsxs9(
1212
+ "form",
1213
+ {
1214
+ ...rest,
1215
+ ref,
1216
+ noValidate: true,
1217
+ "aria-labelledby": promptId,
1218
+ onSubmit: submit,
1219
+ className: cn(
1220
+ "ods-chat-input-request",
1221
+ answered && "ods-chat-input-request--answered",
1222
+ className
1223
+ ),
1224
+ children: [
1225
+ /* @__PURE__ */ jsxs9("div", { className: "ods-chat-input-request__head", children: [
1226
+ /* @__PURE__ */ jsx11("p", { id: promptId, className: "ods-chat-input-request__prompt", children: prompt }),
1227
+ description && !answered && /* @__PURE__ */ jsx11("p", { className: "ods-chat-input-request__description", children: description })
1228
+ ] }),
1229
+ answered ? /* @__PURE__ */ jsx11(AnswerSummary, { fields, answer }) : /* @__PURE__ */ jsxs9(Fragment2, { children: [
1230
+ fields.length > 0 && /* @__PURE__ */ jsx11("div", { className: "ods-chat-input-request__fields", children: fields.map((f) => /* @__PURE__ */ jsx11(
1231
+ Field,
1232
+ {
1233
+ field: f,
1234
+ value: values[f.name] ?? null,
1235
+ error: errors[f.name] ?? null,
1236
+ disabled: locked,
1237
+ onChange: (v) => {
1238
+ setValues((prev) => ({ ...prev, [f.name]: v }));
1239
+ if (errors[f.name]) setErrors((prev) => ({ ...prev, [f.name]: null }));
1240
+ }
1241
+ },
1242
+ f.name
1243
+ )) }),
1244
+ /* @__PURE__ */ jsx11("div", { className: "ods-chat-input-request__reply", children: /* @__PURE__ */ jsx11(
1245
+ Input,
1246
+ {
1247
+ size: "sm",
1248
+ "aria-label": "Type your answer",
1249
+ className: "ods-chat-input-request__text",
1250
+ value: text,
1251
+ onChange: (e) => {
1252
+ setText(e.target.value);
1253
+ if (textError) setTextError(null);
1254
+ },
1255
+ placeholder: textPlaceholder,
1256
+ disabled: locked,
1257
+ error: Boolean(textError),
1258
+ errorMessage: textError ?? void 0
1259
+ }
1260
+ ) }),
1261
+ /* @__PURE__ */ jsxs9("div", { className: "ods-chat-input-request__actions", children: [
1262
+ onSkip && /* @__PURE__ */ jsx11(
1263
+ Button,
1264
+ {
1265
+ type: "button",
1266
+ variant: "ghost",
1267
+ size: "sm",
1268
+ onClick: onSkip,
1269
+ disabled: submitting,
1270
+ children: skipLabel
1271
+ }
1272
+ ),
1273
+ /* @__PURE__ */ jsx11(Button, { type: "submit", variant: "primary", size: "sm", loading: submitting, children: submitLabel })
1274
+ ] })
1275
+ ] })
1276
+ ]
1277
+ }
1278
+ );
1279
+ }
1280
+ );
1281
+ ChatInputRequest.displayName = "ChatInputRequest";
1282
+
1283
+ // src/components/ChatFeedback/ChatFeedback.tsx
1284
+ import { forwardRef as forwardRef11, useState as useState5 } from "react";
1285
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1286
+ function ThumbUp() {
1287
+ return /* @__PURE__ */ jsx12("svg", { viewBox: "0 0 16 16", width: "15", height: "15", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx12(
1288
+ "path",
1289
+ {
1290
+ d: "M5 7v6H3a1 1 0 0 1-1-1V8a1 1 0 0 1 1-1h2Zm0 0 3-4.5A1.5 1.5 0 0 1 10.4 4l-.6 3h3.1a1.5 1.5 0 0 1 1.47 1.8l-.8 4A1.5 1.5 0 0 1 12.1 14H5",
1291
+ stroke: "currentColor",
1292
+ strokeWidth: "1.2",
1293
+ strokeLinejoin: "round"
1294
+ }
1295
+ ) });
1296
+ }
1297
+ function ThumbDown() {
1298
+ return /* @__PURE__ */ jsx12("svg", { viewBox: "0 0 16 16", width: "15", height: "15", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx12(
1299
+ "path",
1300
+ {
1301
+ d: "M11 9V3h2a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1h-2Zm0 0-3 4.5A1.5 1.5 0 0 1 5.6 12l.6-3H3.1A1.5 1.5 0 0 1 1.63 7.2l.8-4A1.5 1.5 0 0 1 3.9 2H11",
1302
+ stroke: "currentColor",
1303
+ strokeWidth: "1.2",
1304
+ strokeLinejoin: "round"
1305
+ }
1306
+ ) });
1307
+ }
1308
+ function CopyGlyph() {
1309
+ return /* @__PURE__ */ jsxs10("svg", { viewBox: "0 0 16 16", width: "14", height: "14", fill: "none", "aria-hidden": "true", children: [
1310
+ /* @__PURE__ */ jsx12("rect", { x: "5.5", y: "5.5", width: "8", height: "8", rx: "1.5", stroke: "currentColor", strokeWidth: "1.2" }),
1311
+ /* @__PURE__ */ jsx12(
1312
+ "path",
1313
+ {
1314
+ d: "M10.5 5.5V4A1.5 1.5 0 0 0 9 2.5H4A1.5 1.5 0 0 0 2.5 4v5A1.5 1.5 0 0 0 4 10.5h1.5",
1315
+ stroke: "currentColor",
1316
+ strokeWidth: "1.2"
1317
+ }
1318
+ )
1319
+ ] });
1320
+ }
1321
+ function CheckGlyph() {
1322
+ return /* @__PURE__ */ jsx12("svg", { viewBox: "0 0 16 16", width: "14", height: "14", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx12(
1323
+ "path",
1324
+ {
1325
+ d: "M3 8.5l3 3 7-7",
1326
+ stroke: "currentColor",
1327
+ strokeWidth: "1.4",
1328
+ strokeLinecap: "round",
1329
+ strokeLinejoin: "round"
1330
+ }
1331
+ ) });
1332
+ }
1333
+ function RegenGlyph() {
1334
+ return /* @__PURE__ */ jsx12("svg", { viewBox: "0 0 16 16", width: "14", height: "14", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx12(
1335
+ "path",
1336
+ {
1337
+ d: "M13 8a5 5 0 1 1-1.5-3.5M13 2v3h-3",
1338
+ stroke: "currentColor",
1339
+ strokeWidth: "1.2",
1340
+ strokeLinecap: "round",
1341
+ strokeLinejoin: "round"
1342
+ }
1343
+ ) });
1344
+ }
1345
+ var ChatFeedback = forwardRef11(function ChatFeedback2({
1346
+ rating,
1347
+ defaultRating = null,
1348
+ onRate,
1349
+ onCopy,
1350
+ onRegenerate,
1351
+ children,
1352
+ size = "sm",
1353
+ className,
1354
+ ...rest
1355
+ }, ref) {
1356
+ const isControlled = rating !== void 0;
1357
+ const [internal, setInternal] = useState5(defaultRating);
1358
+ const current = isControlled ? rating : internal;
1359
+ const [copied, setCopied] = useState5(false);
1360
+ const rate = (next) => {
1361
+ const resolved = current === next ? null : next;
1362
+ if (!isControlled) setInternal(resolved);
1363
+ onRate?.(resolved);
1364
+ };
1365
+ const copy = () => {
1366
+ onCopy?.();
1367
+ setCopied(true);
1368
+ window.setTimeout(() => setCopied(false), 1600);
1369
+ };
1370
+ const btnSize = size === "md" ? "md" : "sm";
1371
+ return /* @__PURE__ */ jsxs10(
1372
+ "div",
1373
+ {
1374
+ ...rest,
1375
+ ref,
1376
+ className: cn("ods-chat-feedback", `ods-chat-feedback--${size}`, className),
1377
+ role: "group",
1378
+ "aria-label": "Message feedback",
1379
+ children: [
1380
+ /* @__PURE__ */ jsx12(
1381
+ Button,
1382
+ {
1383
+ variant: "icon",
1384
+ size: btnSize,
1385
+ "aria-label": "Good response",
1386
+ pressed: current === "up",
1387
+ onClick: () => rate("up"),
1388
+ children: /* @__PURE__ */ jsx12(ThumbUp, {})
1389
+ }
1390
+ ),
1391
+ /* @__PURE__ */ jsx12(
1392
+ Button,
1393
+ {
1394
+ variant: "icon",
1395
+ size: btnSize,
1396
+ "aria-label": "Bad response",
1397
+ pressed: current === "down",
1398
+ onClick: () => rate("down"),
1399
+ children: /* @__PURE__ */ jsx12(ThumbDown, {})
1400
+ }
1401
+ ),
1402
+ onCopy && /* @__PURE__ */ jsx12(
1403
+ Button,
1404
+ {
1405
+ variant: "icon",
1406
+ size: btnSize,
1407
+ "aria-label": copied ? "Copied" : "Copy",
1408
+ onClick: copy,
1409
+ children: copied ? /* @__PURE__ */ jsx12(CheckGlyph, {}) : /* @__PURE__ */ jsx12(CopyGlyph, {})
1410
+ }
1411
+ ),
1412
+ onRegenerate && /* @__PURE__ */ jsx12(
1413
+ Button,
1414
+ {
1415
+ variant: "icon",
1416
+ size: btnSize,
1417
+ "aria-label": "Regenerate response",
1418
+ onClick: onRegenerate,
1419
+ children: /* @__PURE__ */ jsx12(RegenGlyph, {})
1420
+ }
1421
+ ),
1422
+ children
1423
+ ]
1424
+ }
1425
+ );
1426
+ });
1427
+ ChatFeedback.displayName = "ChatFeedback";
1428
+
1429
+ // src/components/ChatScrollToBottom/ChatScrollToBottom.tsx
1430
+ import { forwardRef as forwardRef12 } from "react";
1431
+ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
1432
+ var ChatScrollToBottom = forwardRef12(
1433
+ function ChatScrollToBottom2({ count, label = "Jump to latest", showLabel, className, ...rest }, ref) {
1434
+ const hasCount = typeof count === "number" && count > 0;
1435
+ const withLabel = showLabel ?? hasCount;
1436
+ const text = hasCount ? `${count} new` : withLabel ? label : null;
1437
+ return /* @__PURE__ */ jsxs11(
1438
+ "button",
1439
+ {
1440
+ ...rest,
1441
+ ref,
1442
+ type: "button",
1443
+ className: cn(
1444
+ "ods-chat-scroll-bottom",
1445
+ !withLabel && "ods-chat-scroll-bottom--icon",
1446
+ className
1447
+ ),
1448
+ "aria-label": label,
1449
+ children: [
1450
+ /* @__PURE__ */ jsx13("svg", { viewBox: "0 0 16 16", width: "14", height: "14", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx13(
1451
+ "path",
1452
+ {
1453
+ d: "M8 3v8.5M4.5 8L8 11.5 11.5 8",
1454
+ stroke: "currentColor",
1455
+ strokeWidth: "1.5",
1456
+ strokeLinecap: "round",
1457
+ strokeLinejoin: "round"
1458
+ }
1459
+ ) }),
1460
+ text && /* @__PURE__ */ jsx13("span", { className: "ods-chat-scroll-bottom__text", children: text })
1461
+ ]
1462
+ }
1463
+ );
1464
+ }
1465
+ );
1466
+ ChatScrollToBottom.displayName = "ChatScrollToBottom";
1467
+
1468
+ // src/components/ChatThread/ChatThread.tsx
1469
+ import {
1470
+ Children,
1471
+ forwardRef as forwardRef13,
1472
+ useEffect as useEffect2,
1473
+ useRef as useRef5
1474
+ } from "react";
1475
+ import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
1476
+ var STICK_THRESHOLD = 120;
1477
+ var ChatThread = forwardRef13(function ChatThread2({
1478
+ children,
1479
+ emptyState,
1480
+ loading = false,
1481
+ header,
1482
+ footer,
1483
+ stickToBottom = true,
1484
+ gap = "md",
1485
+ className,
1486
+ onScroll,
1487
+ ...rest
1488
+ }, forwardedRef) {
1489
+ const innerRef = useRef5(null);
1490
+ const setRef = (node) => {
1491
+ innerRef.current = node;
1492
+ if (typeof forwardedRef === "function") forwardedRef(node);
1493
+ else if (forwardedRef) forwardedRef.current = node;
1494
+ };
1495
+ const following = useRef5(true);
1496
+ const handleScroll = (e) => {
1497
+ const el = e.currentTarget;
1498
+ following.current = el.scrollHeight - el.scrollTop - el.clientHeight < STICK_THRESHOLD;
1499
+ onScroll?.(e);
1500
+ };
1501
+ useEffect2(() => {
1502
+ if (!stickToBottom || loading) return;
1503
+ const el = innerRef.current;
1504
+ if (el && following.current) el.scrollTop = el.scrollHeight;
1505
+ });
1506
+ const isEmpty2 = Children.count(children) === 0;
1507
+ const showEmpty = !loading && isEmpty2 && !footer && emptyState != null;
1508
+ return /* @__PURE__ */ jsx14(
1509
+ "div",
1510
+ {
1511
+ ...rest,
1512
+ ref: setRef,
1513
+ className: cn("ods-chat-thread", `ods-chat-thread--gap-${gap}`, className),
1514
+ role: "log",
1515
+ "aria-live": "polite",
1516
+ "aria-relevant": "additions",
1517
+ onScroll: handleScroll,
1518
+ children: loading ? /* @__PURE__ */ jsx14("div", { className: "ods-chat-thread__loading", children: /* @__PURE__ */ jsx14(Spinner, { label: "Loading messages" }) }) : showEmpty ? /* @__PURE__ */ jsx14("div", { className: "ods-chat-thread__empty", children: emptyState }) : /* @__PURE__ */ jsxs12("div", { className: "ods-chat-thread__list", children: [
1519
+ header && /* @__PURE__ */ jsx14("div", { className: "ods-chat-thread__header", children: header }),
1520
+ children,
1521
+ footer && /* @__PURE__ */ jsx14("div", { className: "ods-chat-thread__footer", children: footer })
1522
+ ] })
1523
+ }
1524
+ );
1525
+ });
1526
+ ChatThread.displayName = "ChatThread";
1527
+
1528
+ // src/components/ChatPanel/ChatPanel.tsx
1529
+ import {
1530
+ forwardRef as forwardRef14,
1531
+ useCallback as useCallback2,
1532
+ useRef as useRef6,
1533
+ useState as useState6
1534
+ } from "react";
1535
+ import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
1536
+ var JUMP_THRESHOLD = 200;
1537
+ var ChatPanel = forwardRef14(function ChatPanel2({
1538
+ header,
1539
+ children,
1540
+ emptyState,
1541
+ loading = false,
1542
+ thinking,
1543
+ threadHeader,
1544
+ suggestions,
1545
+ composer,
1546
+ onSend,
1547
+ composerProps,
1548
+ threadProps,
1549
+ showScrollToBottom = true,
1550
+ className,
1551
+ ...rest
1552
+ }, ref) {
1553
+ const threadRef = useRef6(null);
1554
+ const [showJump, setShowJump] = useState6(false);
1555
+ const handleScroll = useCallback2(() => {
1556
+ const el = threadRef.current;
1557
+ if (!el) return;
1558
+ setShowJump(el.scrollHeight - el.scrollTop - el.clientHeight > JUMP_THRESHOLD);
1559
+ }, []);
1560
+ const jumpToBottom = useCallback2(() => {
1561
+ const el = threadRef.current;
1562
+ if (el) el.scrollTo({ top: el.scrollHeight, behavior: "smooth" });
1563
+ }, []);
1564
+ return /* @__PURE__ */ jsxs13("div", { ...rest, ref, className: cn("ods-chat-panel", className), children: [
1565
+ header && /* @__PURE__ */ jsx15("div", { className: "ods-chat-panel__header", children: header }),
1566
+ /* @__PURE__ */ jsxs13("div", { className: "ods-chat-panel__body", children: [
1567
+ /* @__PURE__ */ jsx15(
1568
+ ChatThread,
1569
+ {
1570
+ ...threadProps,
1571
+ ref: threadRef,
1572
+ loading,
1573
+ emptyState,
1574
+ header: threadHeader,
1575
+ footer: thinking,
1576
+ onScroll: handleScroll,
1577
+ children
1578
+ }
1579
+ ),
1580
+ showScrollToBottom && showJump && /* @__PURE__ */ jsx15("div", { className: "ods-chat-panel__jump", children: /* @__PURE__ */ jsx15(ChatScrollToBottom, { showLabel: true, onClick: jumpToBottom }) })
1581
+ ] }),
1582
+ suggestions && /* @__PURE__ */ jsx15("div", { className: "ods-chat-panel__suggestions", children: suggestions }),
1583
+ (composer || onSend) && /* @__PURE__ */ jsx15("div", { className: "ods-chat-panel__composer", children: composer ?? /* @__PURE__ */ jsx15(ChatComposer, { onSend, ...composerProps }) })
1584
+ ] });
1585
+ });
1586
+ ChatPanel.displayName = "ChatPanel";
1587
+
1588
+ // src/components/AssistantAura/AssistantAura.tsx
1589
+ import { jsx as jsx16 } from "react/jsx-runtime";
1590
+ var ASSISTANT_STATES = [
1591
+ "idle",
1592
+ "listening",
1593
+ "thinking",
1594
+ "processing",
1595
+ "speaking",
1596
+ "done",
1597
+ "stopped",
1598
+ "error",
1599
+ "offline"
1600
+ ];
1601
+ function AssistantAura({
1602
+ state = "idle",
1603
+ intensity = "normal",
1604
+ thickness = 1,
1605
+ className,
1606
+ style,
1607
+ children,
1608
+ ...rest
1609
+ }) {
1610
+ return /* @__PURE__ */ jsx16(
1611
+ "div",
1612
+ {
1613
+ ...rest,
1614
+ "data-state": state,
1615
+ className: cn("ods-aura", `ods-aura--${intensity}`, `ods-aura--${state}`, className),
1616
+ style: { ...style, ["--aura-thickness"]: `${thickness}px` },
1617
+ children: /* @__PURE__ */ jsx16("div", { className: "ods-aura__content", children })
1618
+ }
1619
+ );
1620
+ }
1621
+
1622
+ // src/components/AssistantOrb/AssistantOrb.tsx
1623
+ import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
1624
+ function CubeMark() {
1625
+ return /* @__PURE__ */ jsxs14("svg", { className: "ods-orb__mark", viewBox: "0 0 32 32", "aria-hidden": "true", focusable: "false", children: [
1626
+ /* @__PURE__ */ jsx17("path", { className: "ods-orb__hex", d: "M16 7 9 11v8l7 4 7-4v-8l-7-4Z" }),
1627
+ /* @__PURE__ */ jsxs14("g", { className: "ods-orb__edges", children: [
1628
+ /* @__PURE__ */ jsx17("path", { className: "ods-orb__edge", pathLength: 1, d: "M16 15V7" }),
1629
+ /* @__PURE__ */ jsx17("path", { className: "ods-orb__edge", pathLength: 1, d: "M16 15l7-4" }),
1630
+ /* @__PURE__ */ jsx17("path", { className: "ods-orb__edge", pathLength: 1, d: "M16 15 9 11" })
1631
+ ] }),
1632
+ /* @__PURE__ */ jsx17("circle", { className: "ods-orb__node", cx: "16", cy: "15", r: "1.75" }),
1633
+ /* @__PURE__ */ jsx17("rect", { className: "ods-orb__stop", x: "14", y: "13", width: "4", height: "4" }),
1634
+ /* @__PURE__ */ jsxs14("g", { className: "ods-orb__alert", children: [
1635
+ /* @__PURE__ */ jsx17("path", { d: "M16 10.5v5" }),
1636
+ /* @__PURE__ */ jsx17("circle", { cx: "16", cy: "18.5", r: "1.1" })
1637
+ ] })
1638
+ ] });
1639
+ }
1640
+ function AssistantOrb({
1641
+ state = "idle",
1642
+ size = "md",
1643
+ label,
1644
+ className,
1645
+ children,
1646
+ ...rest
1647
+ }) {
1648
+ return /* @__PURE__ */ jsxs14(
1649
+ "span",
1650
+ {
1651
+ ...rest,
1652
+ ...label ? { role: "img", "aria-label": label } : { "aria-hidden": true },
1653
+ "data-state": state,
1654
+ className: cn("ods-orb", `ods-orb--${size}`, `ods-orb--${state}`, className),
1655
+ children: [
1656
+ /* @__PURE__ */ jsx17("span", { className: "ods-orb__ring" }),
1657
+ /* @__PURE__ */ jsx17("span", { className: "ods-orb__core", children: children ?? /* @__PURE__ */ jsx17(CubeMark, {}) })
1658
+ ]
1659
+ }
1660
+ );
1661
+ }
1662
+
1663
+ // src/components/ChatSources/ChatSources.tsx
1664
+ import { forwardRef as forwardRef15, useState as useState7 } from "react";
1665
+ import { jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
1666
+ function hostOf2(url) {
1667
+ try {
1668
+ return new URL(url).hostname.replace(/^www\./, "");
1669
+ } catch {
1670
+ return url;
1671
+ }
1672
+ }
1673
+ var ChatSources = forwardRef15(function ChatSources2({
1674
+ sources,
1675
+ label = "Sources",
1676
+ display = "links",
1677
+ numbered = true,
1678
+ defaultOpen = false,
1679
+ linkTarget = "_blank",
1680
+ className,
1681
+ ...rest
1682
+ }, ref) {
1683
+ const [open, setOpen] = useState7(defaultOpen);
1684
+ if (!sources || sources.length === 0) return null;
1685
+ const count = sources.length;
1686
+ const isChips = display === "chips" || display === "plain-chips";
1687
+ return /* @__PURE__ */ jsxs15(
1688
+ "details",
1689
+ {
1690
+ ...rest,
1691
+ ref,
1692
+ open,
1693
+ onToggle: (e) => setOpen(e.currentTarget.open),
1694
+ className: cn("ods-chat-sources", `ods-chat-sources--${display}`, className),
1695
+ children: [
1696
+ /* @__PURE__ */ jsxs15("summary", { className: "ods-chat-sources__trigger", children: [
1697
+ /* @__PURE__ */ jsx18("span", { className: "ods-chat-sources__caret", "aria-hidden": "true", children: /* @__PURE__ */ jsx18("svg", { viewBox: "0 0 12 12", width: "11", height: "11", fill: "none", children: /* @__PURE__ */ jsx18(
1698
+ "path",
1699
+ {
1700
+ d: "M4 2.5L8 6l-4 3.5",
1701
+ stroke: "currentColor",
1702
+ strokeWidth: "1.4",
1703
+ strokeLinecap: "round",
1704
+ strokeLinejoin: "round"
1705
+ }
1706
+ ) }) }),
1707
+ /* @__PURE__ */ jsx18("span", { className: "ods-chat-sources__trigger-label", children: label }),
1708
+ /* @__PURE__ */ jsx18("span", { className: "ods-chat-sources__count", children: count })
1709
+ ] }),
1710
+ /* @__PURE__ */ jsx18("div", { className: cn("ods-chat-sources__panel", isChips && "ods-chat-sources__panel--chips"), children: sources.map((s, i) => {
1711
+ const key = s.id ?? `${i}`;
1712
+ const idx = numbered ? /* @__PURE__ */ jsxs15("span", { className: "ods-chat-sources__index", children: [
1713
+ "[",
1714
+ i + 1,
1715
+ "]"
1716
+ ] }) : null;
1717
+ const linkProps = s.url ? {
1718
+ href: s.url,
1719
+ target: linkTarget,
1720
+ rel: linkTarget === "_blank" ? "noopener noreferrer" : void 0
1721
+ } : null;
1722
+ if (isChips) {
1723
+ const chipClass = cn(
1724
+ "ods-chat-sources__chip",
1725
+ s.url && display === "chips" && "ods-chat-sources__chip--link"
1726
+ );
1727
+ return display === "chips" && linkProps ? /* @__PURE__ */ jsx18("a", { className: chipClass, ...linkProps, children: s.title }, key) : /* @__PURE__ */ jsx18("span", { className: chipClass, children: s.title }, key);
1728
+ }
1729
+ if (display === "urls") {
1730
+ const shown = s.url ?? "";
1731
+ return /* @__PURE__ */ jsxs15("a", { className: "ods-chat-sources__url", ...linkProps ?? {}, children: [
1732
+ idx,
1733
+ /* @__PURE__ */ jsx18("span", { className: "ods-chat-sources__url-text", children: shown || s.title })
1734
+ ] }, key);
1735
+ }
1736
+ const Row = linkProps ? "a" : "div";
1737
+ return /* @__PURE__ */ jsxs15(
1738
+ Row,
1739
+ {
1740
+ className: cn("ods-chat-sources__row", s.url && "ods-chat-sources__row--link"),
1741
+ ...linkProps ?? {},
1742
+ children: [
1743
+ idx,
1744
+ /* @__PURE__ */ jsxs15("span", { className: "ods-chat-sources__body", children: [
1745
+ /* @__PURE__ */ jsx18("span", { className: "ods-chat-sources__title", children: s.title }),
1746
+ s.snippet && /* @__PURE__ */ jsx18("span", { className: "ods-chat-sources__snippet", children: s.snippet })
1747
+ ] }),
1748
+ (s.meta ?? (s.url ? hostOf2(s.url) : null)) && /* @__PURE__ */ jsx18("span", { className: "ods-chat-sources__meta", children: s.meta ?? hostOf2(s.url) })
1749
+ ]
1750
+ },
1751
+ key
1752
+ );
1753
+ }) })
1754
+ ]
1755
+ }
1756
+ );
1757
+ });
1758
+ ChatSources.displayName = "ChatSources";
1759
+
1760
+ // src/components/ChatSuggestions/ChatSuggestions.tsx
1761
+ import { forwardRef as forwardRef16 } from "react";
1762
+ import { jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
1763
+ function normalize(item) {
1764
+ return typeof item === "string" ? { label: item, value: item } : item;
1765
+ }
1766
+ function resolveValue(s) {
1767
+ if (s.value != null) return s.value;
1768
+ return typeof s.label === "string" ? s.label : "";
1769
+ }
1770
+ var ChatSuggestions = forwardRef16(
1771
+ function ChatSuggestions2({ items, onSelect, label, size = "md", layout = "wrap", disabled = false, className, ...rest }, ref) {
1772
+ return /* @__PURE__ */ jsxs16(
1773
+ "div",
1774
+ {
1775
+ ...rest,
1776
+ ref,
1777
+ className: cn(
1778
+ "ods-chat-suggestions",
1779
+ `ods-chat-suggestions--${size}`,
1780
+ `ods-chat-suggestions--${layout}`,
1781
+ className
1782
+ ),
1783
+ role: "group",
1784
+ "aria-label": typeof label === "string" ? label : "Suggested replies",
1785
+ children: [
1786
+ label && /* @__PURE__ */ jsx19("span", { className: "ods-chat-suggestions__label", children: label }),
1787
+ /* @__PURE__ */ jsx19("div", { className: "ods-chat-suggestions__track", children: items.map((raw, i) => {
1788
+ const s = normalize(raw);
1789
+ const isDisabled = disabled || s.disabled;
1790
+ return /* @__PURE__ */ jsxs16(
1791
+ "button",
1792
+ {
1793
+ type: "button",
1794
+ className: "ods-chat-suggestions__chip",
1795
+ disabled: isDisabled,
1796
+ onClick: () => onSelect?.(resolveValue(s), s),
1797
+ children: [
1798
+ s.icon && /* @__PURE__ */ jsx19("span", { className: "ods-chat-suggestions__chip-icon", children: s.icon }),
1799
+ /* @__PURE__ */ jsx19("span", { className: "ods-chat-suggestions__chip-label", children: s.label })
1800
+ ]
1801
+ },
1802
+ s.id ?? `${i}`
1803
+ );
1804
+ }) })
1805
+ ]
1806
+ }
1807
+ );
1808
+ }
1809
+ );
1810
+ ChatSuggestions.displayName = "ChatSuggestions";
1811
+
1812
+ // src/components/ChatThinking/ChatThinking.tsx
1813
+ import {
1814
+ forwardRef as forwardRef17,
1815
+ useEffect as useEffect3,
1816
+ useState as useState8
1817
+ } from "react";
1818
+ import { Fragment as Fragment3, jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
1819
+ var ChatThinking = forwardRef17(function ChatThinking2({
1820
+ variant = "dots",
1821
+ label,
1822
+ labels,
1823
+ cycleMs = 2200,
1824
+ size = "md",
1825
+ srLabel = "Assistant is thinking",
1826
+ className,
1827
+ ...rest
1828
+ }, ref) {
1829
+ const cycling = Array.isArray(labels) && labels.length > 0;
1830
+ const [idx, setIdx] = useState8(0);
1831
+ useEffect3(() => {
1832
+ if (!cycling || labels.length < 2) return;
1833
+ const id = window.setInterval(() => setIdx((i) => (i + 1) % labels.length), cycleMs);
1834
+ return () => window.clearInterval(id);
1835
+ }, [cycling, labels, cycleMs]);
1836
+ const activeLabel = cycling ? labels[idx % labels.length] : label;
1837
+ const fadeKey = cycling ? idx : void 0;
1838
+ let indicator;
1839
+ switch (variant) {
1840
+ case "wave":
1841
+ indicator = /* @__PURE__ */ jsx20("span", { className: "ods-thinking__wave", children: [0, 1, 2, 3, 4].map((i) => /* @__PURE__ */ jsx20("span", { className: "ods-thinking__wave-dot" }, i)) });
1842
+ break;
1843
+ case "pulse":
1844
+ indicator = /* @__PURE__ */ jsx20("span", { className: "ods-thinking__pulse" });
1845
+ break;
1846
+ case "bars":
1847
+ indicator = /* @__PURE__ */ jsx20("span", { className: "ods-thinking__bars", children: [0, 1, 2, 3].map((i) => /* @__PURE__ */ jsx20("span", { className: "ods-thinking__bar" }, i)) });
1848
+ break;
1849
+ case "shimmer":
1850
+ indicator = /* @__PURE__ */ jsx20("span", { className: "ods-thinking__shimmer", children: activeLabel ?? "Thinking\u2026" }, fadeKey);
1851
+ break;
1852
+ case "cursor":
1853
+ indicator = /* @__PURE__ */ jsxs17(Fragment3, { children: [
1854
+ /* @__PURE__ */ jsx20("span", { className: "ods-thinking__cursor-label ods-thinking__fade", children: activeLabel ?? "Thinking" }, fadeKey),
1855
+ /* @__PURE__ */ jsx20("span", { className: "ods-thinking__cursor" })
1856
+ ] });
1857
+ break;
1858
+ case "dots":
1859
+ default:
1860
+ indicator = /* @__PURE__ */ jsx20("span", { className: "ods-thinking__dots", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx20("span", { className: "ods-thinking__dot" }, i)) });
1861
+ }
1862
+ const labelInline = variant !== "shimmer" && variant !== "cursor";
1863
+ const inlineLabel = cycling ? activeLabel : label;
1864
+ return /* @__PURE__ */ jsxs17(
1865
+ "div",
1866
+ {
1867
+ ...rest,
1868
+ ref,
1869
+ role: "status",
1870
+ "aria-live": "polite",
1871
+ className: cn("ods-thinking", `ods-thinking--${variant}`, `ods-thinking--${size}`, className),
1872
+ children: [
1873
+ /* @__PURE__ */ jsx20("span", { className: "ods-thinking__sr", children: srLabel }),
1874
+ /* @__PURE__ */ jsx20("span", { className: "ods-thinking__indicator", "aria-hidden": "true", children: indicator }),
1875
+ labelInline && inlineLabel && /* @__PURE__ */ jsx20("span", { className: "ods-thinking__label ods-thinking__fade", "aria-hidden": "true", children: inlineLabel }, fadeKey)
1876
+ ]
1877
+ }
1878
+ );
1879
+ });
1880
+ ChatThinking.displayName = "ChatThinking";
1881
+
1882
+ // src/components/ChatToolTrace/ChatToolTrace.tsx
1883
+ import { forwardRef as forwardRef18, useState as useState9 } from "react";
1884
+ import { jsx as jsx21, jsxs as jsxs18 } from "react/jsx-runtime";
1885
+ function NodeGlyph({ status }) {
1886
+ switch (status) {
1887
+ case "success":
1888
+ return /* @__PURE__ */ jsx21("svg", { viewBox: "0 0 12 12", width: "9", height: "9", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx21(
1889
+ "path",
1890
+ {
1891
+ d: "M2.5 6.2l2.2 2.2L9.5 3.6",
1892
+ stroke: "currentColor",
1893
+ strokeWidth: "1.6",
1894
+ strokeLinecap: "round",
1895
+ strokeLinejoin: "round"
1896
+ }
1897
+ ) });
1898
+ case "error":
1899
+ return /* @__PURE__ */ jsx21("svg", { viewBox: "0 0 12 12", width: "9", height: "9", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx21(
1900
+ "path",
1901
+ {
1902
+ d: "M3.5 3.5l5 5M8.5 3.5l-5 5",
1903
+ stroke: "currentColor",
1904
+ strokeWidth: "1.6",
1905
+ strokeLinecap: "round"
1906
+ }
1907
+ ) });
1908
+ case "running":
1909
+ return /* @__PURE__ */ jsxs18(
1910
+ "svg",
1911
+ {
1912
+ viewBox: "0 0 12 12",
1913
+ width: "11",
1914
+ height: "11",
1915
+ fill: "none",
1916
+ "aria-hidden": "true",
1917
+ className: "ods-chat-tools__spin",
1918
+ children: [
1919
+ /* @__PURE__ */ jsx21(
1920
+ "circle",
1921
+ {
1922
+ cx: "6",
1923
+ cy: "6",
1924
+ r: "4.4",
1925
+ stroke: "currentColor",
1926
+ strokeOpacity: "0.25",
1927
+ strokeWidth: "1.6"
1928
+ }
1929
+ ),
1930
+ /* @__PURE__ */ jsx21(
1931
+ "path",
1932
+ {
1933
+ d: "M6 1.6a4.4 4.4 0 0 1 4.4 4.4",
1934
+ stroke: "currentColor",
1935
+ strokeWidth: "1.6",
1936
+ strokeLinecap: "round"
1937
+ }
1938
+ )
1939
+ ]
1940
+ }
1941
+ );
1942
+ case "pending":
1943
+ return /* @__PURE__ */ jsx21("span", { className: "ods-chat-tools__dot", "aria-hidden": "true" });
1944
+ }
1945
+ }
1946
+ var ChatToolTrace = forwardRef18(
1947
+ function ChatToolTrace2({ calls, summary, defaultOpen = false, autoOpen = true, className, ...rest }, ref) {
1948
+ const count = calls?.length ?? 0;
1949
+ const running = (calls ?? []).some((c) => c.status === "running" || c.status === "pending");
1950
+ const [open, setOpen] = useState9(defaultOpen || autoOpen && running);
1951
+ const defaultSummary = running ? "Working\u2026" : `Used ${count} ${count === 1 ? "tool" : "tools"}`;
1952
+ return /* @__PURE__ */ jsxs18(
1953
+ "details",
1954
+ {
1955
+ ...rest,
1956
+ ref,
1957
+ open,
1958
+ onToggle: (e) => setOpen(e.currentTarget.open),
1959
+ className: cn("ods-chat-tools", running && "ods-chat-tools--running", className),
1960
+ children: [
1961
+ /* @__PURE__ */ jsxs18("summary", { className: "ods-chat-tools__summary", children: [
1962
+ /* @__PURE__ */ jsx21("span", { className: "ods-chat-tools__caret", "aria-hidden": "true", children: /* @__PURE__ */ jsx21("svg", { viewBox: "0 0 12 12", width: "11", height: "11", fill: "none", children: /* @__PURE__ */ jsx21(
1963
+ "path",
1964
+ {
1965
+ d: "M4 2.5L8 6l-4 3.5",
1966
+ stroke: "currentColor",
1967
+ strokeWidth: "1.4",
1968
+ strokeLinecap: "round",
1969
+ strokeLinejoin: "round"
1970
+ }
1971
+ ) }) }),
1972
+ running && /* @__PURE__ */ jsx21("span", { className: "ods-chat-tools__live", "aria-hidden": "true" }),
1973
+ /* @__PURE__ */ jsx21("span", { className: "ods-chat-tools__summary-text", children: summary ?? defaultSummary }),
1974
+ !running && count > 0 && /* @__PURE__ */ jsx21("span", { className: "ods-chat-tools__count", children: count })
1975
+ ] }),
1976
+ /* @__PURE__ */ jsx21("ol", { className: "ods-chat-tools__steps", children: calls.map((c, i) => {
1977
+ const status = c.status ?? "success";
1978
+ return /* @__PURE__ */ jsxs18(
1979
+ "li",
1980
+ {
1981
+ className: cn("ods-chat-tools__step", `ods-chat-tools__step--${status}`),
1982
+ children: [
1983
+ /* @__PURE__ */ jsx21("span", { className: "ods-chat-tools__rail", "aria-hidden": "true", children: /* @__PURE__ */ jsx21("span", { className: "ods-chat-tools__node", children: /* @__PURE__ */ jsx21(NodeGlyph, { status }) }) }),
1984
+ /* @__PURE__ */ jsxs18("span", { className: "ods-chat-tools__detail", children: [
1985
+ /* @__PURE__ */ jsxs18("span", { className: "ods-chat-tools__head", children: [
1986
+ /* @__PURE__ */ jsx21("code", { className: "ods-chat-tools__name", children: c.name }),
1987
+ c.args != null && /* @__PURE__ */ jsx21("code", { className: "ods-chat-tools__args", children: c.args }),
1988
+ typeof c.durationMs === "number" && /* @__PURE__ */ jsxs18("span", { className: "ods-chat-tools__duration", children: [
1989
+ c.durationMs,
1990
+ "ms"
1991
+ ] })
1992
+ ] }),
1993
+ c.result != null && /* @__PURE__ */ jsx21("span", { className: "ods-chat-tools__result", children: c.result })
1994
+ ] })
1995
+ ]
1996
+ },
1997
+ c.id ?? `${i}`
1998
+ );
1999
+ }) })
2000
+ ]
2001
+ }
2002
+ );
2003
+ }
2004
+ );
2005
+ ChatToolTrace.displayName = "ChatToolTrace";
2006
+
2007
+ // src/components/ChatVideoPreview/ChatVideoPreview.tsx
2008
+ import { forwardRef as forwardRef19, useState as useState10 } from "react";
2009
+ import { jsx as jsx22, jsxs as jsxs19 } from "react/jsx-runtime";
2010
+ var PROVIDER_LABEL = {
2011
+ youtube: "YouTube",
2012
+ vimeo: "Vimeo",
2013
+ loom: "Loom",
2014
+ file: "Video"
2015
+ };
2016
+ var ID = /^[\w-]+$/;
2017
+ function youtubeId(u) {
2018
+ const host = u.hostname.replace(/^www\.|^m\./, "");
2019
+ if (host === "youtu.be") return u.pathname.slice(1).split("/")[0] || null;
2020
+ if (host !== "youtube.com" && host !== "youtube-nocookie.com") return null;
2021
+ if (u.pathname === "/watch") return u.searchParams.get("v");
2022
+ const [, kind, id] = u.pathname.split("/");
2023
+ return kind === "embed" || kind === "shorts" || kind === "live" ? id ?? null : null;
2024
+ }
2025
+ function lastSegment(u, prefixes) {
2026
+ const parts = u.pathname.split("/").filter(Boolean);
2027
+ if (parts.length === 0) return null;
2028
+ if (prefixes.length > 0 && !prefixes.includes(parts[0] ?? "")) return null;
2029
+ return parts[parts.length - 1] ?? null;
2030
+ }
2031
+ function videoEmbedOf(url) {
2032
+ let u;
2033
+ try {
2034
+ u = new URL(url);
2035
+ } catch {
2036
+ return null;
2037
+ }
2038
+ if (u.protocol !== "https:" && u.protocol !== "http:") return null;
2039
+ const host = u.hostname.replace(/^www\./, "");
2040
+ const yt = youtubeId(u);
2041
+ if (yt && ID.test(yt))
2042
+ return { provider: "youtube", src: `https://www.youtube-nocookie.com/embed/${yt}?autoplay=1` };
2043
+ if (host === "vimeo.com" || host === "player.vimeo.com") {
2044
+ const id = lastSegment(u, []);
2045
+ if (id && /^\d+$/.test(id))
2046
+ return { provider: "vimeo", src: `https://player.vimeo.com/video/${id}?autoplay=1` };
2047
+ }
2048
+ if (host === "loom.com") {
2049
+ const id = lastSegment(u, ["share", "embed"]);
2050
+ if (id && ID.test(id))
2051
+ return { provider: "loom", src: `https://www.loom.com/embed/${id}?autoplay=1` };
2052
+ }
2053
+ if (/\.(mp4|webm|mov)$/i.test(u.pathname)) return { provider: "file", src: u.toString() };
2054
+ return null;
2055
+ }
2056
+ function PlayGlyph() {
2057
+ return /* @__PURE__ */ jsx22("svg", { viewBox: "0 0 24 24", width: "22", height: "22", "aria-hidden": "true", children: /* @__PURE__ */ jsx22("path", { d: "M8 5.5v13l10.5-6.5z", fill: "currentColor" }) });
2058
+ }
2059
+ function seedOf(text) {
2060
+ let h = 2166136261;
2061
+ for (let i = 0; i < text.length; i++) {
2062
+ h ^= text.charCodeAt(i);
2063
+ h = Math.imul(h, 16777619);
2064
+ }
2065
+ return h >>> 0;
2066
+ }
2067
+ function pick(seed, n, min, max) {
2068
+ const v = (seed >>> n * 3) % 1e3;
2069
+ return min + Math.round(v / 999 * (max - min));
2070
+ }
2071
+ function PosterArt({ url, source }) {
2072
+ const seed = seedOf(url);
2073
+ const holes = Array.from({ length: 16 }, (_, i) => i * 20 + 6);
2074
+ const frames = [0, 1, 2].map((i) => ({ x: 24 + i * 94, h: pick(seed, i, 44, 62) }));
2075
+ return /* @__PURE__ */ jsxs19("span", { className: "ods-chat-video-preview__art", "aria-hidden": "true", children: [
2076
+ /* @__PURE__ */ jsxs19(
2077
+ "svg",
2078
+ {
2079
+ viewBox: "0 0 320 180",
2080
+ preserveAspectRatio: "xMidYMid slice",
2081
+ width: "100%",
2082
+ height: "100%",
2083
+ "aria-hidden": "true",
2084
+ children: [
2085
+ /* @__PURE__ */ jsx22("rect", { className: "ods-chat-video-preview__art-strip", x: "0", y: "0", width: "320", height: "16" }),
2086
+ /* @__PURE__ */ jsx22("rect", { className: "ods-chat-video-preview__art-strip", x: "0", y: "164", width: "320", height: "16" }),
2087
+ holes.map((x) => /* @__PURE__ */ jsxs19("g", { children: [
2088
+ /* @__PURE__ */ jsx22("rect", { className: "ods-chat-video-preview__art-hole", x, y: "5", width: "8", height: "6" }),
2089
+ /* @__PURE__ */ jsx22("rect", { className: "ods-chat-video-preview__art-hole", x, y: "169", width: "8", height: "6" })
2090
+ ] }, x)),
2091
+ frames.map((f, i) => /* @__PURE__ */ jsx22(
2092
+ "rect",
2093
+ {
2094
+ className: `ods-chat-video-preview__art-frame ods-chat-video-preview__art-frame--${i}`,
2095
+ x: f.x,
2096
+ y: 90 - f.h / 2 - 8,
2097
+ width: "84",
2098
+ height: f.h
2099
+ },
2100
+ f.x
2101
+ )),
2102
+ /* @__PURE__ */ jsx22(
2103
+ "rect",
2104
+ {
2105
+ className: "ods-chat-video-preview__art-line",
2106
+ x: "24",
2107
+ y: "134",
2108
+ width: pick(seed, 3, 120, 220),
2109
+ height: "5"
2110
+ }
2111
+ ),
2112
+ /* @__PURE__ */ jsx22(
2113
+ "rect",
2114
+ {
2115
+ className: "ods-chat-video-preview__art-line",
2116
+ x: "24",
2117
+ y: "145",
2118
+ width: pick(seed, 4, 60, 150),
2119
+ height: "5"
2120
+ }
2121
+ )
2122
+ ]
2123
+ }
2124
+ ),
2125
+ /* @__PURE__ */ jsx22("span", { className: "ods-chat-video-preview__art-label", children: source })
2126
+ ] });
2127
+ }
2128
+ function Player({ embed, title }) {
2129
+ if (embed.provider === "file") {
2130
+ return (
2131
+ // biome-ignore lint/a11y/useMediaCaption: captions come with the source file, which the card does not own.
2132
+ /* @__PURE__ */ jsx22(
2133
+ "video",
2134
+ {
2135
+ className: "ods-chat-video-preview__player",
2136
+ src: embed.src,
2137
+ controls: true,
2138
+ autoPlay: true,
2139
+ playsInline: true
2140
+ }
2141
+ )
2142
+ );
2143
+ }
2144
+ return /* @__PURE__ */ jsx22(
2145
+ "iframe",
2146
+ {
2147
+ className: "ods-chat-video-preview__player",
2148
+ src: embed.src,
2149
+ title,
2150
+ allow: "autoplay; encrypted-media; picture-in-picture; fullscreen",
2151
+ allowFullScreen: true,
2152
+ referrerPolicy: "strict-origin-when-cross-origin"
2153
+ }
2154
+ );
2155
+ }
2156
+ var ChatVideoPreview = forwardRef19(
2157
+ function ChatVideoPreview2({ url, title, thumbnail, duration, linkTarget = "_blank", className, ...rest }, ref) {
2158
+ const [playing, setPlaying] = useState10(false);
2159
+ const [failedPoster, setFailedPoster] = useState10(null);
2160
+ const embed = videoEmbedOf(url);
2161
+ const label = typeof title === "string" ? title : "video";
2162
+ const source = embed ? PROVIDER_LABEL[embed.provider] : hostOf(url);
2163
+ const showImage = Boolean(thumbnail) && failedPoster !== thumbnail;
2164
+ const poster = /* @__PURE__ */ jsxs19("span", { className: "ods-chat-video-preview__poster", children: [
2165
+ /* @__PURE__ */ jsx22(PosterArt, { url, source }),
2166
+ showImage && /* @__PURE__ */ jsx22(
2167
+ "img",
2168
+ {
2169
+ className: "ods-chat-video-preview__poster-img",
2170
+ src: thumbnail,
2171
+ alt: "",
2172
+ onError: () => setFailedPoster(thumbnail ?? null)
2173
+ }
2174
+ ),
2175
+ /* @__PURE__ */ jsx22("span", { className: "ods-chat-video-preview__play", children: /* @__PURE__ */ jsx22(PlayGlyph, {}) }),
2176
+ duration && /* @__PURE__ */ jsx22("span", { className: "ods-chat-video-preview__duration", children: duration })
2177
+ ] });
2178
+ let stage;
2179
+ if (embed && playing) {
2180
+ stage = /* @__PURE__ */ jsx22(Player, { embed, title: label });
2181
+ } else if (embed) {
2182
+ stage = /* @__PURE__ */ jsx22(
2183
+ "button",
2184
+ {
2185
+ type: "button",
2186
+ className: "ods-chat-video-preview__stage",
2187
+ onClick: () => setPlaying(true),
2188
+ "aria-label": `Play ${label}`,
2189
+ children: poster
2190
+ }
2191
+ );
2192
+ } else {
2193
+ stage = /* @__PURE__ */ jsx22(
2194
+ "a",
2195
+ {
2196
+ className: "ods-chat-video-preview__stage",
2197
+ href: url,
2198
+ ...linkTargetProps(linkTarget),
2199
+ "aria-label": `Open ${label}`,
2200
+ children: poster
2201
+ }
2202
+ );
2203
+ }
2204
+ return /* @__PURE__ */ jsxs19(
2205
+ "div",
2206
+ {
2207
+ ...rest,
2208
+ ref,
2209
+ className: cn(
2210
+ "ods-chat-video-preview",
2211
+ playing && "ods-chat-video-preview--playing",
2212
+ className
2213
+ ),
2214
+ children: [
2215
+ /* @__PURE__ */ jsx22("div", { className: "ods-chat-video-preview__frame", children: stage }),
2216
+ /* @__PURE__ */ jsxs19("div", { className: "ods-chat-video-preview__meta", children: [
2217
+ /* @__PURE__ */ jsx22(TruncatedText, { className: "ods-chat-video-preview__title", children: title }),
2218
+ /* @__PURE__ */ jsxs19("span", { className: "ods-chat-video-preview__source", children: [
2219
+ source,
2220
+ /* @__PURE__ */ jsx22("a", { className: "ods-chat-video-preview__open", href: url, ...linkTargetProps(linkTarget), children: "Open" })
2221
+ ] })
2222
+ ] })
2223
+ ]
2224
+ }
2225
+ );
2226
+ }
2227
+ );
2228
+ ChatVideoPreview.displayName = "ChatVideoPreview";
2229
+
2230
+ // src/components/AssistantConversation/AssistantConversation.tsx
2231
+ import { useState as useState11 } from "react";
2232
+ import { Fragment as Fragment4, jsx as jsx23, jsxs as jsxs20 } from "react/jsx-runtime";
2233
+ function assistantStateOf(messages, busy, typing = false, flags = {}) {
2234
+ if (flags.offline) return "offline";
2235
+ const last = messages[messages.length - 1];
2236
+ const reply = last?.role === "assistant" ? last : void 0;
2237
+ if (busy) {
2238
+ if (reply?.content) return "speaking";
2239
+ if (reply?.steps?.some((s) => s.status === "running")) return "processing";
2240
+ return "thinking";
2241
+ }
2242
+ if (flags.error) return "error";
2243
+ if (typing) return "listening";
2244
+ return reply?.outcome ?? "idle";
2245
+ }
2246
+ var STATE_LINE = {
2247
+ idle: "",
2248
+ listening: "",
2249
+ thinking: "Thinking\u2026",
2250
+ processing: "Working\u2026",
2251
+ speaking: "Writing\u2026",
2252
+ done: "",
2253
+ stopped: "Stopped",
2254
+ error: "Couldn't answer",
2255
+ offline: "Offline"
2256
+ };
2257
+ var STATUS_TONE = {
2258
+ thinking: "active",
2259
+ processing: "active",
2260
+ speaking: "active",
2261
+ error: "error"
2262
+ };
2263
+ function AssistantHeaderButton({
2264
+ label,
2265
+ children,
2266
+ pressed,
2267
+ className,
2268
+ type = "button",
2269
+ ...rest
2270
+ }) {
2271
+ return /* @__PURE__ */ jsx23(
2272
+ "button",
2273
+ {
2274
+ ...rest,
2275
+ type,
2276
+ "aria-label": label,
2277
+ title: label,
2278
+ "aria-pressed": pressed,
2279
+ className: cn("ods-assistant__icon-btn", className),
2280
+ children
2281
+ }
2282
+ );
2283
+ }
2284
+ function stepSummary(steps) {
2285
+ const running = steps.find((s) => s.status === "running" || s.status === "pending");
2286
+ if (running) return running.name;
2287
+ const only = steps.length === 1 ? steps[0] : void 0;
2288
+ if (!only) return void 0;
2289
+ return only.result == null ? only.name : /* @__PURE__ */ jsxs20(Fragment4, { children: [
2290
+ only.name,
2291
+ " \xB7 ",
2292
+ only.result
2293
+ ] });
2294
+ }
2295
+ function ReplyText({ message }) {
2296
+ return typeof message.content === "string" ? /* @__PURE__ */ jsx23(
2297
+ "span",
2298
+ {
2299
+ className: cn("ods-assistant__text", message.streaming && "ods-assistant__text--streaming"),
2300
+ children: message.content
2301
+ }
2302
+ ) : /* @__PURE__ */ jsx23(
2303
+ "div",
2304
+ {
2305
+ className: cn(
2306
+ "ods-assistant__text",
2307
+ "ods-assistant__text--rich",
2308
+ message.streaming && "ods-assistant__text--streaming"
2309
+ ),
2310
+ children: message.content
2311
+ }
2312
+ );
2313
+ }
2314
+ function Block({
2315
+ block,
2316
+ busy,
2317
+ onAnswer,
2318
+ onSkipAsk
2319
+ }) {
2320
+ if (block.type === "doc") {
2321
+ return /* @__PURE__ */ jsx23(
2322
+ ChatDocPreview,
2323
+ {
2324
+ url: block.url,
2325
+ title: block.title,
2326
+ path: block.path,
2327
+ excerpt: block.excerpt
2328
+ }
2329
+ );
2330
+ }
2331
+ if (block.type === "video") {
2332
+ return /* @__PURE__ */ jsx23(
2333
+ ChatVideoPreview,
2334
+ {
2335
+ url: block.url,
2336
+ title: block.title,
2337
+ thumbnail: block.thumbnail,
2338
+ duration: block.duration
2339
+ }
2340
+ );
2341
+ }
2342
+ return /* @__PURE__ */ jsx23(
2343
+ ChatInputRequest,
2344
+ {
2345
+ prompt: block.prompt,
2346
+ description: block.description,
2347
+ fields: block.fields,
2348
+ answer: block.answer ?? null,
2349
+ submitting: busy,
2350
+ onSubmit: (values, text) => onAnswer?.(block.askId, values, text),
2351
+ ...block.skippable && onSkipAsk ? { onSkip: () => onSkipAsk(block.askId) } : {}
2352
+ }
2353
+ );
2354
+ }
2355
+ function AssistantConversation({
2356
+ name = "Ova",
2357
+ subtitle = "Octaviaflow assistant",
2358
+ avatar,
2359
+ actions,
2360
+ context,
2361
+ aura = "normal",
2362
+ hideHeader = false,
2363
+ layout = "comfortable",
2364
+ state: stateProp,
2365
+ greeting,
2366
+ suggestions,
2367
+ messages,
2368
+ replacement,
2369
+ busy = false,
2370
+ onSend,
2371
+ onStop,
2372
+ onRate,
2373
+ onAnswer,
2374
+ onSkipAsk,
2375
+ placeholder = "Ask anything\u2026",
2376
+ maxLength,
2377
+ composerDisabled = false,
2378
+ error,
2379
+ notice,
2380
+ nameId,
2381
+ className,
2382
+ ...rest
2383
+ }) {
2384
+ const [typing, setTyping] = useState11(false);
2385
+ const state = stateProp ?? assistantStateOf(messages, busy, typing, { error: Boolean(error) });
2386
+ const statusLine = STATE_LINE[state] || subtitle;
2387
+ const tone = STATUS_TONE[state];
2388
+ const compact = layout === "compact";
2389
+ const said = (body) => compact ? /* @__PURE__ */ jsx23("div", { className: "ods-assistant__answer", children: body }) : /* @__PURE__ */ jsx23(ChatBubble, { variant: "bot", hideAvatar: true, children: body });
2390
+ const header = hideHeader ? void 0 : /* @__PURE__ */ jsxs20("div", { className: "ods-assistant__header", children: [
2391
+ /* @__PURE__ */ jsx23(AssistantOrb, { state, size: compact ? "sm" : "md", children: avatar }),
2392
+ /* @__PURE__ */ jsxs20("div", { className: "ods-assistant__heading", children: [
2393
+ /* @__PURE__ */ jsx23("span", { id: nameId, className: "ods-assistant__name", children: name }),
2394
+ /* @__PURE__ */ jsx23(
2395
+ "span",
2396
+ {
2397
+ className: cn("ods-assistant__status", tone && `ods-assistant__status--${tone}`),
2398
+ "aria-live": "polite",
2399
+ children: statusLine
2400
+ }
2401
+ )
2402
+ ] }),
2403
+ actions && /* @__PURE__ */ jsx23("div", { className: "ods-assistant__actions", children: actions })
2404
+ ] });
2405
+ const thread = /* @__PURE__ */ jsxs20(Fragment4, { children: [
2406
+ greeting && said(greeting),
2407
+ messages.map(
2408
+ (m) => m.role === "user" ? /* @__PURE__ */ jsx23(ChatBubble, { variant: "user", hideAvatar: true, children: m.content }, m.id) : /* @__PURE__ */ jsxs20("div", { className: "ods-assistant__reply", children: [
2409
+ m.steps && m.steps.length > 0 && /* @__PURE__ */ jsx23(ChatToolTrace, { calls: m.steps, summary: stepSummary(m.steps), autoOpen: false }),
2410
+ m.content ? said(/* @__PURE__ */ jsx23(ReplyText, { message: m })) : (
2411
+ // While a step runs, its line already says what is happening.
2412
+ !m.steps?.some((st) => st.status === "running") && said(/* @__PURE__ */ jsx23(ChatThinking, { variant: "shimmer", label: m.status || "Thinking\u2026", size: "sm" }))
2413
+ ),
2414
+ m.blocks && m.blocks.length > 0 && /* @__PURE__ */ jsx23("div", { className: "ods-assistant__blocks", children: m.blocks.map((b, i) => /* @__PURE__ */ jsx23(
2415
+ Block,
2416
+ {
2417
+ block: b,
2418
+ busy,
2419
+ onAnswer,
2420
+ onSkipAsk
2421
+ },
2422
+ b.type === "ask" ? b.askId : `${b.type}:${b.url}:${i}`
2423
+ )) }),
2424
+ m.sources && m.sources.length > 0 && /* @__PURE__ */ jsx23(
2425
+ ChatSources,
2426
+ {
2427
+ sources: m.sources,
2428
+ display: "chips",
2429
+ label: "Sources",
2430
+ linkTarget: "_blank"
2431
+ }
2432
+ ),
2433
+ (m.note || !m.streaming && onRate && m.content) && /* @__PURE__ */ jsxs20("div", { className: "ods-assistant__meta", children: [
2434
+ m.note ? /* @__PURE__ */ jsx23("span", { className: "ods-assistant__note", children: m.note }) : /* @__PURE__ */ jsx23("span", {}),
2435
+ !m.streaming && onRate && m.content && /* @__PURE__ */ jsx23(
2436
+ ChatFeedback,
2437
+ {
2438
+ size: "sm",
2439
+ rating: m.rating ?? null,
2440
+ onRate: (r) => onRate(m.id, r)
2441
+ }
2442
+ )
2443
+ ] })
2444
+ ] }, m.id)
2445
+ )
2446
+ ] });
2447
+ const showSuggestions = !replacement && messages.length === 0 && (suggestions?.length ?? 0) > 0;
2448
+ return /* @__PURE__ */ jsx23(
2449
+ AssistantAura,
2450
+ {
2451
+ ...rest,
2452
+ state,
2453
+ intensity: aura,
2454
+ className: cn("ods-assistant", compact && "ods-assistant--compact", className),
2455
+ children: replacement ? /* @__PURE__ */ jsxs20("div", { className: "ods-assistant__replacement", children: [
2456
+ header,
2457
+ context && /* @__PURE__ */ jsx23("div", { className: "ods-assistant__context", children: context }),
2458
+ /* @__PURE__ */ jsx23("div", { className: "ods-assistant__replacement-body", children: replacement })
2459
+ ] }) : /* @__PURE__ */ jsx23(
2460
+ ChatPanel,
2461
+ {
2462
+ header: header || context ? /* @__PURE__ */ jsxs20(Fragment4, { children: [
2463
+ header,
2464
+ context && /* @__PURE__ */ jsx23("div", { className: "ods-assistant__context", children: context })
2465
+ ] }) : void 0,
2466
+ suggestions: showSuggestions ? /* @__PURE__ */ jsx23(
2467
+ ChatSuggestions,
2468
+ {
2469
+ items: suggestions ?? [],
2470
+ onSelect: (v) => onSend?.(v),
2471
+ size: "sm",
2472
+ layout: "wrap",
2473
+ disabled: busy
2474
+ }
2475
+ ) : void 0,
2476
+ composer: /* @__PURE__ */ jsxs20("div", { className: "ods-assistant__composer", children: [
2477
+ error && /* @__PURE__ */ jsx23("div", { className: "ods-assistant__error", role: "alert", children: error }),
2478
+ /* @__PURE__ */ jsx23(
2479
+ ChatComposer,
2480
+ {
2481
+ onSend: (v) => {
2482
+ setTyping(false);
2483
+ onSend?.(v);
2484
+ },
2485
+ onChange: (v) => setTyping(v.trim().length > 0),
2486
+ onBlur: () => setTyping(false),
2487
+ ...onStop ? { onStop } : {},
2488
+ placeholder,
2489
+ sending: busy,
2490
+ disabled: composerDisabled,
2491
+ maxRows: 5,
2492
+ ...maxLength ? { maxLength } : {}
2493
+ }
2494
+ ),
2495
+ notice && /* @__PURE__ */ jsx23("div", { className: "ods-assistant__notice", children: notice })
2496
+ ] }),
2497
+ children: thread
2498
+ }
2499
+ )
2500
+ }
2501
+ );
2502
+ }
2503
+
2504
+ // src/components/CountUp/CountUp.tsx
2505
+ import {
2506
+ forwardRef as forwardRef20,
2507
+ useEffect as useEffect4,
2508
+ useRef as useRef7,
2509
+ useState as useState12
2510
+ } from "react";
2511
+ import { jsxs as jsxs21 } from "react/jsx-runtime";
2512
+ var easeLinear = (t) => t;
2513
+ var easeOutCubic = (t) => 1 - (1 - t) ** 3;
2514
+ var easeOutQuart = (t) => 1 - (1 - t) ** 4;
2515
+ var easeOutExpo = (t) => t === 1 ? 1 : 1 - 2 ** (-10 * t);
2516
+ var CountUp = forwardRef20(function CountUp2({
2517
+ value,
2518
+ from = 0,
2519
+ duration = 800,
2520
+ easing = easeOutCubic,
2521
+ decimals = 0,
2522
+ formatValue,
2523
+ prefix,
2524
+ suffix,
2525
+ delay = 0,
2526
+ disableAnimation = false,
2527
+ onComplete,
2528
+ className,
2529
+ ...rest
2530
+ }, ref) {
2531
+ const [n, setN] = useState12(disableAnimation ? value : from);
2532
+ const lastRendered = useRef7(disableAnimation ? value : from);
2533
+ useEffect4(() => {
2534
+ const reduced = typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
2535
+ if (disableAnimation || reduced) {
2536
+ setN(value);
2537
+ lastRendered.current = value;
2538
+ onComplete?.();
2539
+ return;
2540
+ }
2541
+ const startValue = lastRendered.current;
2542
+ const delta = value - startValue;
2543
+ if (delta === 0) return;
2544
+ let raf = null;
2545
+ let timeoutId = null;
2546
+ let startTime = null;
2547
+ let cancelled = false;
2548
+ const tick = (ts) => {
2549
+ if (cancelled) return;
2550
+ if (startTime === null) startTime = ts;
2551
+ const t = Math.min(1, (ts - startTime) / Math.max(1, duration));
2552
+ const eased = easing(t);
2553
+ const current = startValue + delta * eased;
2554
+ setN(current);
2555
+ lastRendered.current = current;
2556
+ if (t < 1) {
2557
+ raf = requestAnimationFrame(tick);
2558
+ } else {
2559
+ lastRendered.current = value;
2560
+ setN(value);
2561
+ onComplete?.();
2562
+ }
2563
+ };
2564
+ const start = () => {
2565
+ raf = requestAnimationFrame(tick);
2566
+ };
2567
+ if (delay > 0) {
2568
+ timeoutId = window.setTimeout(start, delay);
2569
+ } else {
2570
+ start();
2571
+ }
2572
+ return () => {
2573
+ cancelled = true;
2574
+ if (raf !== null) cancelAnimationFrame(raf);
2575
+ if (timeoutId !== null) window.clearTimeout(timeoutId);
2576
+ };
2577
+ }, [value, duration, delay, disableAnimation]);
2578
+ const display = formatValue ? formatValue(n) : n.toFixed(decimals);
2579
+ return /* @__PURE__ */ jsxs21(
2580
+ "span",
2581
+ {
2582
+ ...rest,
2583
+ ref,
2584
+ className: cn("ods-count-up", className),
2585
+ "aria-live": "polite",
2586
+ "aria-atomic": "true",
2587
+ children: [
2588
+ prefix,
2589
+ display,
2590
+ suffix
2591
+ ]
2592
+ }
2593
+ );
2594
+ });
2595
+ CountUp.displayName = "CountUp";
2596
+
2597
+ // src/hooks/useTextareaCommands.ts
2598
+ import {
2599
+ useCallback as useCallback3,
2600
+ useEffect as useEffect5,
2601
+ useMemo as useMemo2,
2602
+ useRef as useRef8,
2603
+ useState as useState13
2604
+ } from "react";
2605
+ function findOpenTrigger(value, caret, trigger) {
2606
+ let i = caret - 1;
2607
+ while (i >= 0) {
2608
+ const ch = value[i];
2609
+ if (ch === "\n" || ch === " " || ch === " ") return null;
2610
+ if (value.startsWith(trigger, i)) {
2611
+ const before = i === 0 ? "" : value[i - 1];
2612
+ if (i === 0 || before === " " || before === "\n" || before === " ") {
2613
+ return {
2614
+ triggerIndex: i,
2615
+ query: value.slice(i + trigger.length, caret)
2616
+ };
2617
+ }
2618
+ return null;
2619
+ }
2620
+ i--;
2621
+ }
2622
+ return null;
2623
+ }
2624
+ function getCaretRect(textarea, caret) {
2625
+ if (typeof document === "undefined") return null;
2626
+ const mirror = document.createElement("div");
2627
+ const styles = window.getComputedStyle(textarea);
2628
+ for (const prop of [
2629
+ "boxSizing",
2630
+ "width",
2631
+ "fontFamily",
2632
+ "fontSize",
2633
+ "fontWeight",
2634
+ "fontStyle",
2635
+ "letterSpacing",
2636
+ "lineHeight",
2637
+ "paddingTop",
2638
+ "paddingRight",
2639
+ "paddingBottom",
2640
+ "paddingLeft",
2641
+ "borderTopWidth",
2642
+ "borderRightWidth",
2643
+ "borderBottomWidth",
2644
+ "borderLeftWidth",
2645
+ "textTransform",
2646
+ "wordSpacing",
2647
+ "tabSize",
2648
+ "whiteSpace"
2649
+ ]) {
2650
+ mirror.style[prop] = styles[prop];
2651
+ }
2652
+ mirror.style.position = "absolute";
2653
+ mirror.style.visibility = "hidden";
2654
+ mirror.style.overflow = "hidden";
2655
+ mirror.style.top = "0";
2656
+ mirror.style.left = "0";
2657
+ mirror.style.whiteSpace = "pre-wrap";
2658
+ mirror.style.wordWrap = "break-word";
2659
+ mirror.textContent = textarea.value.slice(0, caret);
2660
+ const marker = document.createElement("span");
2661
+ marker.textContent = "\u200B";
2662
+ mirror.appendChild(marker);
2663
+ document.body.appendChild(mirror);
2664
+ const taRect = textarea.getBoundingClientRect();
2665
+ const markerRect = marker.getBoundingClientRect();
2666
+ const mirrorRect = mirror.getBoundingClientRect();
2667
+ const x = taRect.left + (markerRect.left - mirrorRect.left) - textarea.scrollLeft;
2668
+ const y = taRect.top + (markerRect.top - mirrorRect.top) - textarea.scrollTop;
2669
+ const out = new DOMRect(x, y, 0, markerRect.height || 16);
2670
+ document.body.removeChild(mirror);
2671
+ return out;
2672
+ }
2673
+ function useTextareaCommands({
2674
+ textareaRef,
2675
+ value,
2676
+ onChange,
2677
+ commands,
2678
+ trigger = "/",
2679
+ openOnEmptyTrigger = true,
2680
+ maxItems = 8
2681
+ }) {
2682
+ const [isOpen, setIsOpen] = useState13(false);
2683
+ const [query, setQuery] = useState13("");
2684
+ const [activeIndex, setActiveIndex] = useState13(0);
2685
+ const [triggerIndex, setTriggerIndex] = useState13(null);
2686
+ const [caretRect, setCaretRect] = useState13(null);
2687
+ const dismissedAtRef = useRef8(null);
2688
+ const items = useMemo2(() => {
2689
+ const q = query.trim().toLowerCase();
2690
+ if (!q) return commands.slice(0, maxItems);
2691
+ return commands.filter((c) => {
2692
+ const hay = [
2693
+ typeof c.label === "string" ? c.label : "",
2694
+ typeof c.description === "string" ? c.description : "",
2695
+ c.id,
2696
+ ...c.keywords ?? []
2697
+ ].join(" ").toLowerCase();
2698
+ return hay.includes(q);
2699
+ }).slice(0, maxItems);
2700
+ }, [commands, query, maxItems]);
2701
+ useEffect5(() => {
2702
+ const ta = textareaRef.current;
2703
+ if (!ta) return;
2704
+ const caret = ta.selectionStart ?? 0;
2705
+ const dismissedAt = dismissedAtRef.current;
2706
+ if (dismissedAt && dismissedAt.value === value && dismissedAt.caret === caret) {
2707
+ return;
2708
+ }
2709
+ const found = findOpenTrigger(value, caret, trigger);
2710
+ if (!found) {
2711
+ if (isOpen) setIsOpen(false);
2712
+ setTriggerIndex(null);
2713
+ return;
2714
+ }
2715
+ if (dismissedAt) dismissedAtRef.current = null;
2716
+ if (found.query.length > 0 || openOnEmptyTrigger) {
2717
+ setQuery(found.query);
2718
+ setTriggerIndex(found.triggerIndex);
2719
+ setIsOpen(true);
2720
+ setActiveIndex(0);
2721
+ setCaretRect(getCaretRect(ta, caret));
2722
+ }
2723
+ }, [value, trigger, openOnEmptyTrigger, isOpen, textareaRef]);
2724
+ const commit = useCallback3(
2725
+ (cmd) => {
2726
+ const ta = textareaRef.current;
2727
+ if (!ta || triggerIndex == null) return;
2728
+ const insertText = typeof cmd.insert === "function" ? cmd.insert(query) : cmd.insert;
2729
+ const caret = ta.selectionStart ?? value.length;
2730
+ const before = value.slice(0, triggerIndex);
2731
+ const after = value.slice(caret);
2732
+ const next = `${before}${insertText}${after}`;
2733
+ dismissedAtRef.current = {
2734
+ value,
2735
+ caret
2736
+ };
2737
+ onChange(next);
2738
+ setIsOpen(false);
2739
+ setTriggerIndex(null);
2740
+ requestAnimationFrame(() => {
2741
+ const pos = before.length + insertText.length;
2742
+ ta.focus();
2743
+ ta.setSelectionRange(pos, pos);
2744
+ });
2745
+ },
2746
+ [textareaRef, triggerIndex, query, value, onChange]
2747
+ );
2748
+ const dismiss = useCallback3(() => {
2749
+ const ta = textareaRef.current;
2750
+ dismissedAtRef.current = {
2751
+ value,
2752
+ caret: ta?.selectionStart ?? value.length
2753
+ };
2754
+ setIsOpen(false);
2755
+ setTriggerIndex(null);
2756
+ }, [textareaRef, value]);
2757
+ const onKeyDown = useCallback3(
2758
+ (e) => {
2759
+ if (!isOpen) return false;
2760
+ if (e.key === "ArrowDown") {
2761
+ e.preventDefault();
2762
+ setActiveIndex((i) => Math.min(items.length - 1, i + 1));
2763
+ return true;
2764
+ }
2765
+ if (e.key === "ArrowUp") {
2766
+ e.preventDefault();
2767
+ setActiveIndex((i) => Math.max(0, i - 1));
2768
+ return true;
2769
+ }
2770
+ if (e.key === "Enter" || e.key === "Tab") {
2771
+ const pick2 = items[activeIndex];
2772
+ if (pick2) {
2773
+ e.preventDefault();
2774
+ commit(pick2);
2775
+ return true;
2776
+ }
2777
+ }
2778
+ if (e.key === "Escape") {
2779
+ e.preventDefault();
2780
+ dismiss();
2781
+ return true;
2782
+ }
2783
+ return false;
2784
+ },
2785
+ [isOpen, items, activeIndex, commit, dismiss]
2786
+ );
2787
+ return {
2788
+ isOpen,
2789
+ query,
2790
+ activeIndex,
2791
+ items,
2792
+ onKeyDown,
2793
+ dismiss,
2794
+ commit,
2795
+ caretRect
2796
+ };
2797
+ }
2798
+
2799
+ // src/hooks/useTextareaSelection.ts
2800
+ import { useCallback as useCallback4, useEffect as useEffect6, useState as useState14 } from "react";
2801
+ function getSelectionRect(textarea, start, end) {
2802
+ if (typeof document === "undefined") return null;
2803
+ if (start === end) return null;
2804
+ const mirror = document.createElement("div");
2805
+ const styles = window.getComputedStyle(textarea);
2806
+ for (const prop of [
2807
+ "boxSizing",
2808
+ "width",
2809
+ "fontFamily",
2810
+ "fontSize",
2811
+ "fontWeight",
2812
+ "fontStyle",
2813
+ "letterSpacing",
2814
+ "lineHeight",
2815
+ "paddingTop",
2816
+ "paddingRight",
2817
+ "paddingBottom",
2818
+ "paddingLeft",
2819
+ "borderTopWidth",
2820
+ "borderRightWidth",
2821
+ "borderBottomWidth",
2822
+ "borderLeftWidth",
2823
+ "textTransform",
2824
+ "wordSpacing",
2825
+ "tabSize",
2826
+ "whiteSpace"
2827
+ ]) {
2828
+ mirror.style[prop] = styles[prop];
2829
+ }
2830
+ mirror.style.position = "absolute";
2831
+ mirror.style.visibility = "hidden";
2832
+ mirror.style.overflow = "hidden";
2833
+ mirror.style.top = "0";
2834
+ mirror.style.left = "0";
2835
+ mirror.style.whiteSpace = "pre-wrap";
2836
+ mirror.style.wordWrap = "break-word";
2837
+ const pre = document.createTextNode(textarea.value.slice(0, start));
2838
+ const range = document.createElement("span");
2839
+ range.textContent = textarea.value.slice(start, end) || "\u200B";
2840
+ const post = document.createTextNode(textarea.value.slice(end));
2841
+ mirror.appendChild(pre);
2842
+ mirror.appendChild(range);
2843
+ mirror.appendChild(post);
2844
+ document.body.appendChild(mirror);
2845
+ const taRect = textarea.getBoundingClientRect();
2846
+ const rangeRect = range.getBoundingClientRect();
2847
+ const mirrorRect = mirror.getBoundingClientRect();
2848
+ const out = new DOMRect(
2849
+ taRect.left + (rangeRect.left - mirrorRect.left) - textarea.scrollLeft,
2850
+ taRect.top + (rangeRect.top - mirrorRect.top) - textarea.scrollTop,
2851
+ rangeRect.width,
2852
+ rangeRect.height
2853
+ );
2854
+ document.body.removeChild(mirror);
2855
+ return out;
2856
+ }
2857
+ function useTextareaSelection({
2858
+ textareaRef,
2859
+ value,
2860
+ onChange
2861
+ }) {
2862
+ const [start, setStart] = useState14(0);
2863
+ const [end, setEnd] = useState14(0);
2864
+ const [rect, setRect] = useState14(null);
2865
+ useEffect6(() => {
2866
+ if (typeof document === "undefined") return;
2867
+ const handler = () => {
2868
+ const ta = textareaRef.current;
2869
+ if (!ta || document.activeElement !== ta) {
2870
+ setStart(0);
2871
+ setEnd(0);
2872
+ setRect(null);
2873
+ return;
2874
+ }
2875
+ const s = ta.selectionStart ?? 0;
2876
+ const e = ta.selectionEnd ?? 0;
2877
+ setStart(s);
2878
+ setEnd(e);
2879
+ setRect(s !== e ? getSelectionRect(ta, s, e) : null);
2880
+ };
2881
+ document.addEventListener("selectionchange", handler);
2882
+ return () => document.removeEventListener("selectionchange", handler);
2883
+ }, [textareaRef]);
2884
+ const active = start !== end;
2885
+ const text = active ? value.slice(start, end) : "";
2886
+ const writeToClipboard = useCallback4(async (str) => {
2887
+ try {
2888
+ if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
2889
+ await navigator.clipboard.writeText(str);
2890
+ return true;
2891
+ }
2892
+ } catch {
2893
+ }
2894
+ try {
2895
+ const tmp = document.createElement("textarea");
2896
+ tmp.value = str;
2897
+ tmp.style.position = "fixed";
2898
+ tmp.style.left = "-9999px";
2899
+ document.body.appendChild(tmp);
2900
+ tmp.select();
2901
+ const ok = document.execCommand("copy");
2902
+ document.body.removeChild(tmp);
2903
+ return ok;
2904
+ } catch {
2905
+ return false;
2906
+ }
2907
+ }, []);
2908
+ const copy = useCallback4(async () => {
2909
+ if (!active) return false;
2910
+ return writeToClipboard(text);
2911
+ }, [active, text, writeToClipboard]);
2912
+ const replace = useCallback4(
2913
+ (str) => {
2914
+ const ta = textareaRef.current;
2915
+ if (!ta) return;
2916
+ const next = value.slice(0, start) + str + value.slice(end);
2917
+ onChange(next);
2918
+ requestAnimationFrame(() => {
2919
+ ta.focus();
2920
+ const caret = start + str.length;
2921
+ ta.setSelectionRange(caret, caret);
2922
+ });
2923
+ },
2924
+ [textareaRef, value, start, end, onChange]
2925
+ );
2926
+ const cut = useCallback4(async () => {
2927
+ if (!active) return false;
2928
+ const ok = await writeToClipboard(text);
2929
+ if (ok) replace("");
2930
+ return ok;
2931
+ }, [active, text, writeToClipboard, replace]);
2932
+ const wrap = useCallback4(
2933
+ (open, close) => {
2934
+ const ta = textareaRef.current;
2935
+ if (!ta) return;
2936
+ const closing = close ?? open;
2937
+ const before = value.slice(0, start);
2938
+ const sel = value.slice(start, end);
2939
+ const after = value.slice(end);
2940
+ const next = `${before}${open}${sel}${closing}${after}`;
2941
+ onChange(next);
2942
+ requestAnimationFrame(() => {
2943
+ ta.focus();
2944
+ const newStart = start;
2945
+ const newEnd = end + open.length + closing.length;
2946
+ ta.setSelectionRange(newStart, newEnd);
2947
+ });
2948
+ },
2949
+ [textareaRef, value, start, end, onChange]
2950
+ );
2951
+ return {
2952
+ active,
2953
+ text,
2954
+ start,
2955
+ end,
2956
+ rect,
2957
+ copy,
2958
+ cut,
2959
+ wrap,
2960
+ replace
2961
+ };
2962
+ }
2963
+
2964
+ // src/hooks/useTextareaTools.tsx
2965
+ import {
2966
+ CodeIcon,
2967
+ HeadingIcon,
2968
+ ListBulletedIcon,
2969
+ ListNumberedIcon,
2970
+ QuotesIcon,
2971
+ RedoIcon,
2972
+ TextBoldIcon,
2973
+ TextItalicIcon,
2974
+ TextLinkIcon,
2975
+ TextStrikethroughIcon,
2976
+ UndoIcon
2977
+ } from "@octaviaflow/icons";
2978
+ import { useCallback as useCallback5, useMemo as useMemo3, useRef as useRef9 } from "react";
2979
+ import { jsx as jsx24 } from "react/jsx-runtime";
2980
+ function useTextareaTools({
2981
+ textareaRef,
2982
+ value,
2983
+ onChange,
2984
+ tools
2985
+ }) {
2986
+ const valueRef = useRef9(value);
2987
+ valueRef.current = value;
2988
+ const helpers = useMemo3(() => {
2989
+ const getTa = () => textareaRef.current;
2990
+ const getSelection = () => {
2991
+ const ta = getTa();
2992
+ const s = ta?.selectionStart ?? 0;
2993
+ const e = ta?.selectionEnd ?? 0;
2994
+ return {
2995
+ text: valueRef.current.slice(s, e),
2996
+ start: s,
2997
+ end: e,
2998
+ active: s !== e
2999
+ };
3000
+ };
3001
+ const setValue = (next) => onChange(next);
3002
+ const replace = (text) => {
3003
+ const ta = getTa();
3004
+ if (!ta) return;
3005
+ const { start, end } = getSelection();
3006
+ const next = valueRef.current.slice(0, start) + text + valueRef.current.slice(end);
3007
+ onChange(next);
3008
+ requestAnimationFrame(() => {
3009
+ ta.focus();
3010
+ const caret = start + text.length;
3011
+ ta.setSelectionRange(caret, caret);
3012
+ });
3013
+ };
3014
+ const insert = (text) => replace(text);
3015
+ const wrap = (open, close) => {
3016
+ const ta = getTa();
3017
+ if (!ta) return;
3018
+ const closing = close ?? open;
3019
+ const { start, end } = getSelection();
3020
+ const sel = valueRef.current.slice(start, end);
3021
+ const next = valueRef.current.slice(0, start) + open + sel + closing + valueRef.current.slice(end);
3022
+ onChange(next);
3023
+ requestAnimationFrame(() => {
3024
+ ta.focus();
3025
+ if (start === end) {
3026
+ const caret = start + open.length;
3027
+ ta.setSelectionRange(caret, caret);
3028
+ } else {
3029
+ ta.setSelectionRange(start, end + open.length + closing.length);
3030
+ }
3031
+ });
3032
+ };
3033
+ const prependLines = (prefix) => {
3034
+ const ta = getTa();
3035
+ if (!ta) return;
3036
+ const { start, end } = getSelection();
3037
+ const v = valueRef.current;
3038
+ const lineStart = v.lastIndexOf("\n", start - 1) + 1;
3039
+ const lineEnd = (() => {
3040
+ const idx = v.indexOf("\n", end);
3041
+ return idx === -1 ? v.length : idx;
3042
+ })();
3043
+ const block = v.slice(lineStart, lineEnd);
3044
+ const updated = block.split("\n").map((line) => `${prefix}${line}`).join("\n");
3045
+ const next = v.slice(0, lineStart) + updated + v.slice(lineEnd);
3046
+ onChange(next);
3047
+ requestAnimationFrame(() => {
3048
+ ta.focus();
3049
+ ta.setSelectionRange(lineStart, lineStart + updated.length);
3050
+ });
3051
+ };
3052
+ const undo = () => {
3053
+ try {
3054
+ getTa()?.focus();
3055
+ document.execCommand("undo");
3056
+ } catch {
3057
+ }
3058
+ };
3059
+ const redo = () => {
3060
+ try {
3061
+ getTa()?.focus();
3062
+ document.execCommand("redo");
3063
+ } catch {
3064
+ }
3065
+ };
3066
+ return {
3067
+ get value() {
3068
+ return valueRef.current;
3069
+ },
3070
+ get selection() {
3071
+ return getSelection();
3072
+ },
3073
+ setValue,
3074
+ replace,
3075
+ insert,
3076
+ wrap,
3077
+ prependLines,
3078
+ undo,
3079
+ redo,
3080
+ getTextarea: getTa
3081
+ };
3082
+ }, [textareaRef, onChange]);
3083
+ const visibleTools = useMemo3(
3084
+ () => tools.filter((t) => t.isVisible ? t.isVisible(helpers) : true),
3085
+ [tools, helpers]
3086
+ );
3087
+ const runTool = useCallback5(
3088
+ async (id) => {
3089
+ const tool = tools.find((t) => t.id === id);
3090
+ if (!tool || tool.kind === "divider") return;
3091
+ await tool.onAction?.(helpers);
3092
+ },
3093
+ [tools, helpers]
3094
+ );
3095
+ return { tools: visibleTools, runTool, helpers };
3096
+ }
3097
+ var wrapTool = (id, open, close, label, icon, title) => ({
3098
+ id,
3099
+ label,
3100
+ icon,
3101
+ title,
3102
+ onAction: (h) => h.wrap(open, close)
3103
+ });
3104
+ var prependLineTool = (id, prefix, label, icon, title) => ({
3105
+ id,
3106
+ label,
3107
+ icon,
3108
+ title,
3109
+ onAction: (h) => h.prependLines(prefix)
3110
+ });
3111
+ var textareaTools = {
3112
+ bold: () => wrapTool("bold", "**", "**", "Bold", /* @__PURE__ */ jsx24(TextBoldIcon, {}), "Bold (\u2318B)"),
3113
+ italic: () => wrapTool("italic", "*", "*", "Italic", /* @__PURE__ */ jsx24(TextItalicIcon, {}), "Italic (\u2318I)"),
3114
+ strikethrough: () => wrapTool("strike", "~~", "~~", "Strikethrough", /* @__PURE__ */ jsx24(TextStrikethroughIcon, {}), "Strikethrough"),
3115
+ code: () => wrapTool("code", "`", "`", "Code", /* @__PURE__ */ jsx24(CodeIcon, {}), "Inline code"),
3116
+ codeBlock: () => ({
3117
+ id: "code-block",
3118
+ label: "Code block",
3119
+ icon: /* @__PURE__ */ jsx24(CodeIcon, {}),
3120
+ title: "Code block",
3121
+ onAction: (h) => h.wrap("```\n", "\n```")
3122
+ }),
3123
+ link: () => ({
3124
+ id: "link",
3125
+ label: "Link",
3126
+ icon: /* @__PURE__ */ jsx24(TextLinkIcon, {}),
3127
+ title: "Insert link",
3128
+ onAction: (h) => {
3129
+ const sel = h.selection;
3130
+ const text = sel.active ? sel.text : "link text";
3131
+ h.replace(`[${text}](https://)`);
3132
+ }
3133
+ }),
3134
+ /** Heading. Defaults to H2. Pass a `level` (1–6) to override. */
3135
+ heading: (level = 2) => prependLineTool(
3136
+ `heading-${level}`,
3137
+ `${"#".repeat(level)} `,
3138
+ `H${level}`,
3139
+ /* @__PURE__ */ jsx24(HeadingIcon, {}),
3140
+ `Heading ${level}`
3141
+ ),
3142
+ bulletList: () => prependLineTool("ul", "- ", "Bullet list", /* @__PURE__ */ jsx24(ListBulletedIcon, {}), "Bullet list"),
3143
+ numberedList: () => prependLineTool("ol", "1. ", "Numbered list", /* @__PURE__ */ jsx24(ListNumberedIcon, {}), "Numbered list"),
3144
+ quote: () => prependLineTool("quote", "> ", "Quote", /* @__PURE__ */ jsx24(QuotesIcon, {}), "Quote"),
3145
+ undo: () => ({
3146
+ id: "undo",
3147
+ label: "Undo",
3148
+ icon: /* @__PURE__ */ jsx24(UndoIcon, {}),
3149
+ title: "Undo (\u2318Z)",
3150
+ onAction: (h) => h.undo()
3151
+ }),
3152
+ redo: () => ({
3153
+ id: "redo",
3154
+ label: "Redo",
3155
+ icon: /* @__PURE__ */ jsx24(RedoIcon, {}),
3156
+ title: "Redo (\u2318\u21E7Z)",
3157
+ onAction: (h) => h.redo()
3158
+ }),
3159
+ divider: (id = "divider") => ({
3160
+ id: `${id}-${Math.random().toString(36).slice(2, 8)}`,
3161
+ label: "",
3162
+ kind: "divider"
3163
+ })
3164
+ };
3165
+
3166
+ // src/components/Textarea/Textarea.tsx
3167
+ import { CopyIcon, CutIcon } from "@octaviaflow/icons";
3168
+ import {
3169
+ forwardRef as forwardRef21,
3170
+ useEffect as useEffect7,
3171
+ useId as useId5,
3172
+ useRef as useRef10,
3173
+ useState as useState15
3174
+ } from "react";
3175
+ import { useTextField } from "react-aria";
3176
+ import { createPortal } from "react-dom";
3177
+ import { jsx as jsx25, jsxs as jsxs22 } from "react/jsx-runtime";
3178
+ var Textarea = forwardRef21(function Textarea2({
3179
+ label,
3180
+ error = false,
3181
+ errorMessage,
3182
+ helperText,
3183
+ resize = "vertical",
3184
+ autoResize = false,
3185
+ minRows,
3186
+ maxRows,
3187
+ maxHeight,
3188
+ maxWidth,
3189
+ minHeight,
3190
+ minWidth,
3191
+ maxLength,
3192
+ size = "md",
3193
+ rows = 3,
3194
+ disabled = false,
3195
+ className,
3196
+ style: consumerStyle,
3197
+ defaultValue,
3198
+ value,
3199
+ onChange,
3200
+ id: providedId,
3201
+ commands,
3202
+ commandTrigger = "/",
3203
+ selectionToolbar = false,
3204
+ selectionActions,
3205
+ tools,
3206
+ toolbarPosition = "top",
3207
+ ...props
3208
+ }, forwardedRef) {
3209
+ const innerRef = useRef10(null);
3210
+ const setRef = (node) => {
3211
+ innerRef.current = node;
3212
+ if (typeof forwardedRef === "function") forwardedRef(node);
3213
+ else if (forwardedRef) forwardedRef.current = node;
3214
+ };
3215
+ const reactId = useId5();
3216
+ const baseId = providedId ?? `ods-textarea-${reactId}`;
3217
+ const hintId = error && errorMessage || helperText ? `${baseId}-hint` : void 0;
3218
+ const [charCount, setCharCount] = useState15(() => String(value ?? defaultValue ?? "").length);
3219
+ const ariaNameProps = resolveAccessibleName({
3220
+ label: typeof label === "string" ? label : void 0,
3221
+ ariaLabel: props["aria-label"],
3222
+ ariaLabelledby: props["aria-labelledby"],
3223
+ componentName: "Textarea"
3224
+ });
3225
+ const { labelProps, inputProps } = useTextField(
3226
+ {
3227
+ label: typeof label === "string" ? label : void 0,
3228
+ ...ariaNameProps,
3229
+ isDisabled: disabled,
3230
+ errorMessage: typeof errorMessage === "string" ? errorMessage : void 0,
3231
+ validationState: error ? "invalid" : void 0,
3232
+ inputElementType: "textarea",
3233
+ value,
3234
+ defaultValue
3235
+ },
3236
+ innerRef
3237
+ );
3238
+ const describedBy = [props["aria-describedby"], hintId].filter(Boolean).join(" ") || void 0;
3239
+ const handleChange = (e) => {
3240
+ setCharCount(e.target.value.length);
3241
+ onChange?.(e);
3242
+ };
3243
+ const [liveValue, setLiveValue] = useState15(String(value ?? defaultValue ?? ""));
3244
+ useEffect7(() => {
3245
+ if (value != null) setLiveValue(String(value));
3246
+ }, [value]);
3247
+ const handleChangeWithMirror = (e) => {
3248
+ setLiveValue(e.target.value);
3249
+ handleChange(e);
3250
+ };
3251
+ const setLiveAndCommit = (next) => {
3252
+ setLiveValue(next);
3253
+ if (innerRef.current) innerRef.current.value = next;
3254
+ onChange?.({
3255
+ target: innerRef.current,
3256
+ currentTarget: innerRef.current
3257
+ });
3258
+ setCharCount(next.length);
3259
+ };
3260
+ const cmdPalette = useTextareaCommands({
3261
+ textareaRef: innerRef,
3262
+ value: liveValue,
3263
+ onChange: setLiveAndCommit,
3264
+ commands: commands ?? [],
3265
+ trigger: commandTrigger
3266
+ });
3267
+ const cmdEnabled = (commands?.length ?? 0) > 0;
3268
+ const sel = useTextareaSelection({
3269
+ textareaRef: innerRef,
3270
+ value: liveValue,
3271
+ onChange: setLiveAndCommit
3272
+ });
3273
+ const toolbar = useTextareaTools({
3274
+ textareaRef: innerRef,
3275
+ value: liveValue,
3276
+ onChange: setLiveAndCommit,
3277
+ tools: tools ?? []
3278
+ });
3279
+ const toolbarEnabled = (tools?.length ?? 0) > 0;
3280
+ const handleKeyDownWithCommands = (e) => {
3281
+ if (cmdEnabled) {
3282
+ const consumed = cmdPalette.onKeyDown(e);
3283
+ if (consumed) return;
3284
+ }
3285
+ props.onKeyDown?.(e);
3286
+ };
3287
+ useEffect7(() => {
3288
+ if (!autoResize) return;
3289
+ const el = innerRef.current;
3290
+ if (!el) return;
3291
+ const computed = window.getComputedStyle(el);
3292
+ const fontSize = parseFloat(computed.fontSize) || 16;
3293
+ const lhRaw = computed.lineHeight;
3294
+ const lh = lhRaw === "normal" ? fontSize * 1.2 : lhRaw.endsWith("px") ? parseFloat(lhRaw) : parseFloat(lhRaw) * fontSize;
3295
+ const padding = parseFloat(computed.paddingTop) + parseFloat(computed.paddingBottom);
3296
+ const rowCap = maxRows != null && Number.isFinite(maxRows) ? lh * maxRows + padding : Number.POSITIVE_INFINITY;
3297
+ const pixelCap = typeof maxHeight === "number" ? maxHeight : Number.POSITIVE_INFINITY;
3298
+ const effectiveMax = Math.min(rowCap, pixelCap);
3299
+ const rowFloor = lh * (minRows ?? rows) + padding;
3300
+ el.style.height = "auto";
3301
+ const target = Math.max(rowFloor, Math.min(effectiveMax, el.scrollHeight));
3302
+ el.style.height = `${target}px`;
3303
+ el.style.overflowY = el.scrollHeight > effectiveMax ? "auto" : "hidden";
3304
+ }, [autoResize, value, defaultValue, minRows, maxRows, maxHeight, rows]);
3305
+ const resolvedResize = typeof resize === "boolean" ? resize ? "vertical" : "none" : resize;
3306
+ const toCss = (v) => v == null ? void 0 : typeof v === "number" ? `${v}px` : v;
3307
+ const fieldStyle = {
3308
+ maxHeight: toCss(maxHeight),
3309
+ maxWidth: toCss(maxWidth),
3310
+ minHeight: toCss(minHeight),
3311
+ minWidth: toCss(minWidth),
3312
+ resize: resolvedResize
3313
+ };
3314
+ return /* @__PURE__ */ jsxs22(
3315
+ "div",
3316
+ {
3317
+ className: cn(
3318
+ "ods-textarea",
3319
+ `ods-textarea--${size}`,
3320
+ `ods-textarea--resize-${resolvedResize}`,
3321
+ autoResize && "ods-textarea--auto-resize",
3322
+ error && "ods-textarea--error",
3323
+ disabled && "ods-textarea--disabled",
3324
+ className
3325
+ ),
3326
+ style: consumerStyle,
3327
+ children: [
3328
+ label && /* @__PURE__ */ jsx25("label", { ...labelProps, htmlFor: baseId, className: "ods-textarea__label", children: label }),
3329
+ toolbarEnabled && toolbarPosition === "top" && /* @__PURE__ */ jsx25(TextareaToolbar, { tools: toolbar.tools, onRun: toolbar.runTool }),
3330
+ /* @__PURE__ */ jsxs22("div", { className: "ods-textarea__wrapper", children: [
3331
+ /* @__PURE__ */ jsx25(
3332
+ "textarea",
3333
+ {
3334
+ ...props,
3335
+ ...inputProps,
3336
+ ref: setRef,
3337
+ id: baseId,
3338
+ rows: autoResize ? minRows ?? rows : rows,
3339
+ disabled,
3340
+ maxLength,
3341
+ defaultValue,
3342
+ value,
3343
+ onChange: handleChangeWithMirror,
3344
+ onKeyDown: handleKeyDownWithCommands,
3345
+ className: "ods-textarea__field",
3346
+ style: fieldStyle,
3347
+ "aria-invalid": error || void 0,
3348
+ "aria-describedby": describedBy,
3349
+ "aria-autocomplete": cmdEnabled ? "list" : void 0,
3350
+ "aria-expanded": cmdEnabled ? cmdPalette.isOpen : void 0,
3351
+ "aria-controls": cmdEnabled && cmdPalette.isOpen ? `${baseId}-cmd-list` : void 0,
3352
+ "aria-activedescendant": cmdEnabled && cmdPalette.isOpen && cmdPalette.items[cmdPalette.activeIndex] ? `${baseId}-cmd-${cmdPalette.items[cmdPalette.activeIndex].id}` : void 0
3353
+ }
3354
+ ),
3355
+ cmdEnabled && cmdPalette.isOpen && cmdPalette.items.length > 0 && typeof document !== "undefined" && createPortal(
3356
+ /* @__PURE__ */ jsx25(
3357
+ "ul",
3358
+ {
3359
+ id: `${baseId}-cmd-list`,
3360
+ role: "listbox",
3361
+ "aria-label": "Inline commands",
3362
+ className: "ods-textarea__cmd-popover",
3363
+ style: {
3364
+ position: "fixed",
3365
+ top: (cmdPalette.caretRect?.bottom ?? 0) + 6,
3366
+ left: cmdPalette.caretRect?.left ?? 0
3367
+ },
3368
+ children: cmdPalette.items.map((c, i) => /* @__PURE__ */ jsxs22(
3369
+ "li",
3370
+ {
3371
+ id: `${baseId}-cmd-${c.id}`,
3372
+ role: "option",
3373
+ "aria-selected": i === cmdPalette.activeIndex,
3374
+ className: cn(
3375
+ "ods-textarea__cmd-item",
3376
+ i === cmdPalette.activeIndex && "ods-textarea__cmd-item--active"
3377
+ ),
3378
+ onMouseDown: (e) => {
3379
+ e.preventDefault();
3380
+ cmdPalette.commit(c);
3381
+ },
3382
+ onMouseEnter: () => {
3383
+ },
3384
+ children: [
3385
+ c.icon && /* @__PURE__ */ jsx25("span", { className: "ods-textarea__cmd-icon", children: c.icon }),
3386
+ /* @__PURE__ */ jsxs22("span", { className: "ods-textarea__cmd-text", children: [
3387
+ /* @__PURE__ */ jsx25("span", { className: "ods-textarea__cmd-label", children: c.label }),
3388
+ c.description && /* @__PURE__ */ jsx25("span", { className: "ods-textarea__cmd-desc", children: c.description })
3389
+ ] })
3390
+ ]
3391
+ },
3392
+ c.id
3393
+ ))
3394
+ }
3395
+ ),
3396
+ document.body
3397
+ ),
3398
+ selectionToolbar && sel.active && sel.rect && typeof document !== "undefined" && createPortal(
3399
+ /* @__PURE__ */ jsxs22(
3400
+ "div",
3401
+ {
3402
+ role: "toolbar",
3403
+ "aria-label": "Selection actions",
3404
+ className: "ods-textarea__sel-toolbar",
3405
+ style: {
3406
+ position: "fixed",
3407
+ top: sel.rect.top - 36,
3408
+ left: sel.rect.left
3409
+ },
3410
+ onMouseDown: (e) => e.preventDefault(),
3411
+ children: [
3412
+ /* @__PURE__ */ jsx25(
3413
+ "button",
3414
+ {
3415
+ type: "button",
3416
+ className: "ods-textarea__sel-btn",
3417
+ onClick: () => sel.copy(),
3418
+ "aria-label": "Copy selection",
3419
+ title: "Copy",
3420
+ children: /* @__PURE__ */ jsx25(CopyIcon, { size: "xs" })
3421
+ }
3422
+ ),
3423
+ /* @__PURE__ */ jsx25(
3424
+ "button",
3425
+ {
3426
+ type: "button",
3427
+ className: "ods-textarea__sel-btn",
3428
+ onClick: () => sel.cut(),
3429
+ "aria-label": "Cut selection",
3430
+ title: "Cut",
3431
+ children: /* @__PURE__ */ jsx25(CutIcon, { size: "xs" })
3432
+ }
3433
+ ),
3434
+ selectionActions?.map((a) => /* @__PURE__ */ jsxs22(
3435
+ "button",
3436
+ {
3437
+ type: "button",
3438
+ className: "ods-textarea__sel-btn",
3439
+ onClick: () => a.onAction(sel.text, {
3440
+ replace: sel.replace,
3441
+ wrap: sel.wrap,
3442
+ copy: sel.copy,
3443
+ cut: sel.cut
3444
+ }),
3445
+ title: typeof a.label === "string" ? a.label : void 0,
3446
+ children: [
3447
+ a.icon,
3448
+ a.label && /* @__PURE__ */ jsx25("span", { className: "ods-textarea__sel-btn-label", children: a.label })
3449
+ ]
3450
+ },
3451
+ a.id
3452
+ ))
3453
+ ]
3454
+ }
3455
+ ),
3456
+ document.body
3457
+ ),
3458
+ maxLength != null && /* @__PURE__ */ jsxs22(
3459
+ "span",
3460
+ {
3461
+ className: "ods-textarea__count",
3462
+ "aria-live": charCount > maxLength ? "polite" : "off",
3463
+ children: [
3464
+ charCount,
3465
+ "/",
3466
+ maxLength
3467
+ ]
3468
+ }
3469
+ )
3470
+ ] }),
3471
+ toolbarEnabled && toolbarPosition === "bottom" && /* @__PURE__ */ jsx25(TextareaToolbar, { tools: toolbar.tools, onRun: toolbar.runTool }),
3472
+ error && errorMessage ? /* @__PURE__ */ jsx25("div", { id: hintId, className: "ods-textarea__error-message", role: "alert", children: errorMessage }) : helperText ? /* @__PURE__ */ jsx25("div", { id: hintId, className: "ods-textarea__hint", children: helperText }) : null
3473
+ ]
3474
+ }
3475
+ );
3476
+ });
3477
+ Textarea.displayName = "Textarea";
3478
+ function TextareaToolbar({
3479
+ tools,
3480
+ onRun
3481
+ }) {
3482
+ return /* @__PURE__ */ jsx25(
3483
+ "div",
3484
+ {
3485
+ role: "toolbar",
3486
+ "aria-label": "Formatting",
3487
+ className: "ods-textarea__toolbar",
3488
+ onMouseDown: (e) => {
3489
+ if (e.target.closest(".ods-textarea__toolbar-btn")) {
3490
+ e.preventDefault();
3491
+ }
3492
+ },
3493
+ children: tools.map((t) => {
3494
+ if (t.kind === "divider") {
3495
+ return /* @__PURE__ */ jsx25("span", { className: "ods-textarea__toolbar-divider", "aria-hidden": "true" }, t.id);
3496
+ }
3497
+ return /* @__PURE__ */ jsx25(
3498
+ "button",
3499
+ {
3500
+ type: "button",
3501
+ className: "ods-textarea__toolbar-btn",
3502
+ title: t.title ?? (typeof t.label === "string" ? t.label : void 0),
3503
+ "aria-label": typeof t.label === "string" ? t.label : t.id,
3504
+ onClick: () => onRun(t.id),
3505
+ children: t.icon ? /* @__PURE__ */ jsx25("span", { className: "ods-textarea__toolbar-icon", children: t.icon }) : /* @__PURE__ */ jsx25("span", { className: "ods-textarea__toolbar-text", children: t.label })
3506
+ },
3507
+ t.id
3508
+ );
3509
+ })
3510
+ }
3511
+ );
3512
+ }
3513
+
3514
+ // src/components/TestimonialCard/TestimonialCard.tsx
3515
+ import { QuotesIcon as QuotesIcon2, StarFilledIcon, StarIcon } from "@octaviaflow/icons";
3516
+ import { forwardRef as forwardRef22, useId as useId6 } from "react";
3517
+ import { jsx as jsx26, jsxs as jsxs23 } from "react/jsx-runtime";
3518
+ function isAuthorObject(v) {
3519
+ return typeof v === "object" && v !== null && !("type" in v) && // exclude React elements
3520
+ "name" in v;
3521
+ }
3522
+ var TestimonialCard = forwardRef22(
3523
+ function TestimonialCard2({
3524
+ quote,
3525
+ author,
3526
+ role: legacyRole,
3527
+ avatar: legacyAvatar,
3528
+ initial: legacyInitial,
3529
+ rating,
3530
+ logo,
3531
+ size = "md",
3532
+ radius = "md",
3533
+ id: providedId,
3534
+ className,
3535
+ ...rest
3536
+ }, ref) {
3537
+ const reactId = useId6();
3538
+ const baseId = providedId ?? `ods-testimonial-${reactId}`;
3539
+ const quoteId = `${baseId}-quote`;
3540
+ const authorId = `${baseId}-author`;
3541
+ const resolved = isAuthorObject(author) ? author : {
3542
+ name: author,
3543
+ role: legacyRole,
3544
+ avatar: legacyAvatar ? void 0 : {
3545
+ initials: typeof legacyInitial === "string" ? legacyInitial : typeof author === "string" ? author.charAt(0).toUpperCase() : void 0
3546
+ }
3547
+ };
3548
+ const renderAvatar = () => {
3549
+ if (!isAuthorObject(author) && legacyAvatar) {
3550
+ return /* @__PURE__ */ jsx26(
3551
+ "span",
3552
+ {
3553
+ className: "ods-testimonial__avatar ods-testimonial__avatar--legacy",
3554
+ "aria-hidden": "true",
3555
+ children: legacyAvatar
3556
+ }
3557
+ );
3558
+ }
3559
+ return /* @__PURE__ */ jsx26(
3560
+ Avatar,
3561
+ {
3562
+ size: size === "sm" ? "xs" : size === "lg" ? "md" : "sm",
3563
+ src: resolved.avatar?.src,
3564
+ initials: resolved.avatar?.initials,
3565
+ seed: resolved.avatar?.seed,
3566
+ palette: resolved.avatar?.palette,
3567
+ alt: resolved.avatar?.alt ?? (typeof resolved.name === "string" ? resolved.name : void 0),
3568
+ className: "ods-testimonial__avatar"
3569
+ }
3570
+ );
3571
+ };
3572
+ return /* @__PURE__ */ jsxs23(
3573
+ Card,
3574
+ {
3575
+ ...rest,
3576
+ ref,
3577
+ id: baseId,
3578
+ radius,
3579
+ padding: "none",
3580
+ variant: "subtle",
3581
+ className: cn("ods-testimonial", `ods-testimonial--${size}`, className),
3582
+ "aria-labelledby": authorId,
3583
+ "aria-describedby": quoteId,
3584
+ children: [
3585
+ /* @__PURE__ */ jsxs23("div", { className: "ods-testimonial__head", children: [
3586
+ /* @__PURE__ */ jsx26("span", { className: "ods-testimonial__quote", "aria-hidden": "true", children: /* @__PURE__ */ jsx26(QuotesIcon2, { size: "md" }) }),
3587
+ logo && /* @__PURE__ */ jsx26("span", { className: "ods-testimonial__logo", "aria-hidden": "true", children: logo })
3588
+ ] }),
3589
+ /* @__PURE__ */ jsx26("blockquote", { id: quoteId, className: "ods-testimonial__text", children: quote }),
3590
+ typeof rating === "number" && /* @__PURE__ */ jsx26(
3591
+ "div",
3592
+ {
3593
+ className: "ods-testimonial__rating",
3594
+ role: "img",
3595
+ "aria-label": `${rating} out of 5 stars`,
3596
+ children: Array.from({ length: 5 }).map(
3597
+ (_, i) => i < Math.round(rating) ? /* @__PURE__ */ jsx26(
3598
+ StarFilledIcon,
3599
+ {
3600
+ size: "xs",
3601
+ className: "ods-testimonial__star ods-testimonial__star--on"
3602
+ },
3603
+ i
3604
+ ) : /* @__PURE__ */ jsx26(StarIcon, { size: "xs", className: "ods-testimonial__star" }, i)
3605
+ )
3606
+ }
3607
+ ),
3608
+ /* @__PURE__ */ jsxs23("figcaption", { className: "ods-testimonial__author", id: authorId, children: [
3609
+ renderAvatar(),
3610
+ /* @__PURE__ */ jsxs23("span", { className: "ods-testimonial__author-info", children: [
3611
+ /* @__PURE__ */ jsx26("span", { className: "ods-testimonial__author-name", children: resolved.name }),
3612
+ resolved.role && /* @__PURE__ */ jsx26("span", { className: "ods-testimonial__author-role", children: resolved.role })
3613
+ ] })
3614
+ ] })
3615
+ ]
3616
+ }
3617
+ );
3618
+ }
3619
+ );
3620
+ TestimonialCard.displayName = "TestimonialCard";
3621
+
3622
+ // src/components/AssistantConversation/assistantGlyphs.tsx
3623
+ import { jsx as jsx27 } from "react/jsx-runtime";
3624
+ var stroke = {
3625
+ fill: "none",
3626
+ stroke: "currentColor",
3627
+ strokeWidth: 1.5,
3628
+ strokeLinecap: "round",
3629
+ strokeLinejoin: "round"
3630
+ };
3631
+ function CloseGlyph() {
3632
+ return /* @__PURE__ */ jsx27("svg", { viewBox: "0 0 16 16", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx27("path", { d: "M4 4l8 8M12 4l-8 8", ...stroke }) });
3633
+ }
3634
+ function NewChatGlyph() {
3635
+ return /* @__PURE__ */ jsx27("svg", { viewBox: "0 0 16 16", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx27("path", { d: "M8 3.5v9M3.5 8h9", ...stroke }) });
3636
+ }
3637
+ function ExpandGlyph() {
3638
+ return /* @__PURE__ */ jsx27("svg", { viewBox: "0 0 16 16", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx27("path", { d: "M9.5 2.5h4v4M6.5 13.5h-4v-4M13.5 2.5 9 7M2.5 13.5 7 9", ...stroke }) });
3639
+ }
3640
+ function CollapseGlyph() {
3641
+ return /* @__PURE__ */ jsx27("svg", { viewBox: "0 0 16 16", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx27("path", { d: "M13.5 6.5h-4v-4M2.5 9.5h4v4M9.5 6.5 14 2M6.5 9.5 2 14", ...stroke }) });
3642
+ }
3643
+ function PinGlyph() {
3644
+ return /* @__PURE__ */ jsx27("svg", { viewBox: "0 0 16 16", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx27("path", { d: "M2.5 2.5h11v11h-11zM10 2.5v11", ...stroke }) });
3645
+ }
3646
+ function HistoryGlyph() {
3647
+ return /* @__PURE__ */ jsx27("svg", { viewBox: "0 0 16 16", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx27("path", { d: "M2.5 8a5.5 5.5 0 1 0 1.6-3.9M2.5 2.5v2.6h2.6M8 5v3.2l2 1.3", ...stroke }) });
3648
+ }
3649
+
3650
+ export {
3651
+ TruncatedText,
3652
+ Card,
3653
+ CardHeader,
3654
+ CardBody,
3655
+ CardFooter,
3656
+ CardTitle,
3657
+ CardDescription,
3658
+ AVATAR_PALETTE_SIZE,
3659
+ avatarPaletteIndex,
3660
+ Avatar,
3661
+ AvatarStack,
3662
+ BlogCard,
3663
+ ChatBubble,
3664
+ ChatComposer,
3665
+ ChatLinkPreview,
3666
+ ChatDocPreview,
3667
+ RadioGroup,
3668
+ Radio,
3669
+ ChatInputRequest,
3670
+ ChatFeedback,
3671
+ ChatScrollToBottom,
3672
+ ChatThread,
3673
+ ChatPanel,
3674
+ ASSISTANT_STATES,
3675
+ AssistantAura,
3676
+ AssistantOrb,
3677
+ ChatSources,
3678
+ ChatSuggestions,
3679
+ ChatThinking,
3680
+ ChatToolTrace,
3681
+ videoEmbedOf,
3682
+ ChatVideoPreview,
3683
+ assistantStateOf,
3684
+ AssistantHeaderButton,
3685
+ AssistantConversation,
3686
+ CloseGlyph,
3687
+ NewChatGlyph,
3688
+ ExpandGlyph,
3689
+ CollapseGlyph,
3690
+ PinGlyph,
3691
+ HistoryGlyph,
3692
+ easeLinear,
3693
+ easeOutCubic,
3694
+ easeOutQuart,
3695
+ easeOutExpo,
3696
+ CountUp,
3697
+ useTextareaCommands,
3698
+ useTextareaSelection,
3699
+ useTextareaTools,
3700
+ textareaTools,
3701
+ Textarea,
3702
+ TestimonialCard
3703
+ };
3704
+ //# sourceMappingURL=chunk-ZHTMPKJN.js.map