@fanvue/ui 3.15.0 → 3.17.0

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 (72) hide show
  1. package/dist/cjs/components/AudioPlayer/AudioPlayer.cjs +16 -89
  2. package/dist/cjs/components/AudioPlayer/AudioPlayer.cjs.map +1 -1
  3. package/dist/cjs/components/AudioRecordButton/AudioRecordButton.cjs +54 -0
  4. package/dist/cjs/components/AudioRecordButton/AudioRecordButton.cjs.map +1 -0
  5. package/dist/cjs/components/AudioUpload/AudioUpload.cjs +5 -5
  6. package/dist/cjs/components/AudioUpload/AudioUpload.cjs.map +1 -1
  7. package/dist/cjs/components/ChatInput/ChatInput.cjs +6 -11
  8. package/dist/cjs/components/ChatInput/ChatInput.cjs.map +1 -1
  9. package/dist/cjs/components/ChatMessage/ChatMessage.cjs +228 -0
  10. package/dist/cjs/components/ChatMessage/ChatMessage.cjs.map +1 -0
  11. package/dist/cjs/components/DropdownMenu/DropdownMenu.cjs +160 -71
  12. package/dist/cjs/components/DropdownMenu/DropdownMenu.cjs.map +1 -1
  13. package/dist/cjs/components/Icons/DenseGridViewIcon.cjs +52 -0
  14. package/dist/cjs/components/Icons/DenseGridViewIcon.cjs.map +1 -0
  15. package/dist/cjs/components/MediaStatusIndicator/MediaStatusIndicator.cjs +66 -0
  16. package/dist/cjs/components/MediaStatusIndicator/MediaStatusIndicator.cjs.map +1 -0
  17. package/dist/cjs/components/Select/Select.cjs +61 -17
  18. package/dist/cjs/components/Select/Select.cjs.map +1 -1
  19. package/dist/cjs/components/SubscribeButton/SubscribeButton.cjs +53 -0
  20. package/dist/cjs/components/SubscribeButton/SubscribeButton.cjs.map +1 -0
  21. package/dist/cjs/components/VoiceNote/VoiceNote.cjs +305 -0
  22. package/dist/cjs/components/VoiceNote/VoiceNote.cjs.map +1 -0
  23. package/dist/cjs/index.cjs +12 -0
  24. package/dist/cjs/index.cjs.map +1 -1
  25. package/dist/cjs/utils/audioWaveform.cjs +76 -0
  26. package/dist/cjs/utils/audioWaveform.cjs.map +1 -0
  27. package/dist/cjs/utils/useAudioPlayback.cjs +123 -0
  28. package/dist/cjs/utils/useAudioPlayback.cjs.map +1 -0
  29. package/dist/cjs/utils/useFittedBarCount.cjs +42 -0
  30. package/dist/cjs/utils/useFittedBarCount.cjs.map +1 -0
  31. package/dist/cjs/utils/useWaveformPeaks.cjs +48 -0
  32. package/dist/cjs/utils/useWaveformPeaks.cjs.map +1 -0
  33. package/dist/cjs/utils/useWaveformSeek.cjs +100 -0
  34. package/dist/cjs/utils/useWaveformSeek.cjs.map +1 -0
  35. package/dist/components/AudioPlayer/AudioPlayer.mjs +7 -80
  36. package/dist/components/AudioPlayer/AudioPlayer.mjs.map +1 -1
  37. package/dist/components/AudioRecordButton/AudioRecordButton.mjs +37 -0
  38. package/dist/components/AudioRecordButton/AudioRecordButton.mjs.map +1 -0
  39. package/dist/components/AudioUpload/AudioUpload.mjs +5 -5
  40. package/dist/components/AudioUpload/AudioUpload.mjs.map +1 -1
  41. package/dist/components/ChatInput/ChatInput.mjs +6 -11
  42. package/dist/components/ChatInput/ChatInput.mjs.map +1 -1
  43. package/dist/components/ChatMessage/ChatMessage.mjs +211 -0
  44. package/dist/components/ChatMessage/ChatMessage.mjs.map +1 -0
  45. package/dist/components/DropdownMenu/DropdownMenu.mjs +161 -72
  46. package/dist/components/DropdownMenu/DropdownMenu.mjs.map +1 -1
  47. package/dist/components/Icons/DenseGridViewIcon.mjs +35 -0
  48. package/dist/components/Icons/DenseGridViewIcon.mjs.map +1 -0
  49. package/dist/components/MediaStatusIndicator/MediaStatusIndicator.mjs +49 -0
  50. package/dist/components/MediaStatusIndicator/MediaStatusIndicator.mjs.map +1 -0
  51. package/dist/components/Select/Select.mjs +61 -17
  52. package/dist/components/Select/Select.mjs.map +1 -1
  53. package/dist/components/SubscribeButton/SubscribeButton.mjs +36 -0
  54. package/dist/components/SubscribeButton/SubscribeButton.mjs.map +1 -0
  55. package/dist/components/VoiceNote/VoiceNote.mjs +288 -0
  56. package/dist/components/VoiceNote/VoiceNote.mjs.map +1 -0
  57. package/dist/index.d.ts +321 -4
  58. package/dist/index.mjs +12 -0
  59. package/dist/index.mjs.map +1 -1
  60. package/dist/styles/base.css +11 -2
  61. package/dist/styles/theme.css +1 -1
  62. package/dist/utils/audioWaveform.mjs +76 -0
  63. package/dist/utils/audioWaveform.mjs.map +1 -0
  64. package/dist/utils/useAudioPlayback.mjs +106 -0
  65. package/dist/utils/useAudioPlayback.mjs.map +1 -0
  66. package/dist/utils/useFittedBarCount.mjs +25 -0
  67. package/dist/utils/useFittedBarCount.mjs.map +1 -0
  68. package/dist/utils/useWaveformPeaks.mjs +31 -0
  69. package/dist/utils/useWaveformPeaks.mjs.map +1 -0
  70. package/dist/utils/useWaveformSeek.mjs +83 -0
  71. package/dist/utils/useWaveformSeek.mjs.map +1 -0
  72. package/package.json +1 -1
package/dist/index.d.ts CHANGED
@@ -380,6 +380,37 @@ export declare interface AudioPlayerProps extends Omit<React_2.HTMLAttributes<HT
380
380
  /** Height of the audio player row, in pixels. Matches both the Vault card overlay and the "Generated Audio" modal row in Figma — both use an identical 32px play button and 40px row. */
381
381
  export declare type AudioPlayerSize = "40";
382
382
 
383
+ /**
384
+ * A circular icon button that toggles audio recording. It owns the record
385
+ * lifecycle so consumers flip a single `status` prop instead of swapping an
386
+ * {@link IconButton}'s icon and colour: `idle` shows the microphone to start,
387
+ * `recording` turns destructive with a stop glyph to end. Used by
388
+ * {@link AudioUpload} for its in-progress stop control.
389
+ *
390
+ * @example
391
+ * ```tsx
392
+ * <AudioRecordButton status={recording ? "recording" : "idle"} onClick={toggle} />
393
+ * ```
394
+ */
395
+ export declare const AudioRecordButton: React_2.ForwardRefExoticComponent<AudioRecordButtonProps & React_2.RefAttributes<HTMLButtonElement>>;
396
+
397
+ export declare interface AudioRecordButtonProps extends Omit<IconButtonProps, "icon" | "variant" | "aria-label"> {
398
+ /** Recording state; drives the icon and colour. @default "idle" */
399
+ status?: AudioRecordButtonStatus;
400
+ /**
401
+ * Accessible name. Falls back to a status-aware default ("Record" /
402
+ * "Stop recording") when omitted so screen readers announce the action.
403
+ */
404
+ "aria-label"?: string;
405
+ }
406
+
407
+ /**
408
+ * Recording state of the button. Drives the icon and colour: `idle` shows the
409
+ * microphone to start a recording, `recording` shows the stop glyph with the
410
+ * destructive treatment to end it.
411
+ */
412
+ export declare type AudioRecordButtonStatus = "idle" | "recording";
413
+
383
414
  /**
384
415
  * Audio file upload with drag-and-drop and optional in-browser recording.
385
416
  * Supports file validation, multiple files, and real-time waveform visualization during recording.
@@ -1255,6 +1286,82 @@ export declare interface ChatInputSelectOption {
1255
1286
  icon?: React_2.ReactNode;
1256
1287
  }
1257
1288
 
1289
+ /**
1290
+ * A single chat message rendered as a bubble. Sender messages sit on the right
1291
+ * with a green bubble and a delivery tick; receiver messages sit on the left
1292
+ * with a grey bubble and an avatar. Supports plain text, a typing indicator, a
1293
+ * voice message with waveform, and a deleted-message placeholder.
1294
+ *
1295
+ * Text bubbles place the timestamp inline after short messages and drop it to
1296
+ * the bottom-right corner once the text wraps, so no variant switch is needed
1297
+ * for short versus long content.
1298
+ *
1299
+ * @example
1300
+ * ```tsx
1301
+ * <ChatMessage user="sender" message="On my way!" time="16:00" status="read" />
1302
+ * <ChatMessage user="receiver" message="See you soon" time="16:01" avatarSrc="/jane.jpg" online />
1303
+ * <ChatMessage user="receiver" variant="typing" avatarSrc="/jane.jpg" />
1304
+ * ```
1305
+ */
1306
+ export declare const ChatMessage: React_2.ForwardRefExoticComponent<ChatMessageProps & React_2.RefAttributes<HTMLDivElement>>;
1307
+
1308
+ export declare interface ChatMessageProps extends React_2.HTMLAttributes<HTMLDivElement> {
1309
+ /**
1310
+ * Who sent the message. `"sender"` is the current user (right-aligned, green
1311
+ * bubble, delivery tick). `"receiver"` is the other party (left-aligned, grey
1312
+ * bubble, with avatar). @default "receiver"
1313
+ */
1314
+ user?: ChatMessageUser;
1315
+ /** The kind of content the message carries. @default "text" */
1316
+ variant?: ChatMessageVariant;
1317
+ /** Message body for the `"text"` variant. Keep it to inline content. */
1318
+ message?: React_2.ReactNode;
1319
+ /** Timestamp shown with the message, e.g. `"16:00"`. */
1320
+ time?: string;
1321
+ /**
1322
+ * Delivery status shown on sender messages (a double tick). Ignored for
1323
+ * receiver messages. `"read"` renders the tick in the read colour. @default "delivered"
1324
+ */
1325
+ status?: ChatMessageStatus;
1326
+ /** Duration label for the `"audio"` variant, e.g. `"0:05"`. @default "0:00" */
1327
+ audioDuration?: string;
1328
+ /**
1329
+ * Relative bar heights (values `0`–`1`) for the `"audio"` waveform. Defaults
1330
+ * to a flat row of dots matching the unplayed design state.
1331
+ */
1332
+ waveform?: number[];
1333
+ /** Whether the audio is playing (controlled). Pairs with {@link onPlayingChange}. */
1334
+ playing?: boolean;
1335
+ /** Initial playing state when uncontrolled. @default false */
1336
+ defaultPlaying?: boolean;
1337
+ /** Fired when the audio play/pause button is pressed, with the next playing state. */
1338
+ onPlayingChange?: (playing: boolean) => void;
1339
+ /** Render the receiver avatar. Reserves the avatar space when `false` so grouped bubbles stay aligned. @default true */
1340
+ showAvatar?: boolean;
1341
+ /** Avatar image URL for receiver messages. */
1342
+ avatarSrc?: string;
1343
+ /** Avatar alt text. @default "Avatar" */
1344
+ avatarAlt?: string;
1345
+ /** Avatar fallback (initials or icon) shown before the image loads. */
1346
+ avatarFallback?: React_2.ReactNode;
1347
+ /** Show the online indicator on the receiver avatar. @default false */
1348
+ online?: boolean;
1349
+ /** Accessible label for the typing indicator. @default "Typing" */
1350
+ typingLabel?: string;
1351
+ /** Text shown for the `"deleted"` variant. @default "Message deleted" */
1352
+ deletedLabel?: string;
1353
+ /** Accessible label for the audio play button. @default "Play" */
1354
+ playLabel?: string;
1355
+ /** Accessible label for the audio pause button. @default "Pause" */
1356
+ pauseLabel?: string;
1357
+ }
1358
+
1359
+ export declare type ChatMessageStatus = "delivered" | "read";
1360
+
1361
+ export declare type ChatMessageUser = "sender" | "receiver";
1362
+
1363
+ export declare type ChatMessageVariant = "text" | "typing" | "audio" | "deleted";
1364
+
1258
1365
  /**
1259
1366
  * A checkbox input with optional label and helper text. Supports checked,
1260
1367
  * unchecked, and indeterminate states.
@@ -1779,6 +1886,20 @@ export declare interface CyclingTextProps extends Omit<React_2.HTMLAttributes<HT
1779
1886
  /** How the wrapper should be sized to accommodate variable-length items. */
1780
1887
  export declare type CyclingTextSizing = "longest" | "current";
1781
1888
 
1889
+ /**
1890
+ * Dense grid view icon (3×3 tiles) — the compact-grid counterpart of
1891
+ * {@link GridViewIcon}. Renders at sizes 16, 24, or 32 px.
1892
+ *
1893
+ * @example
1894
+ * ```tsx
1895
+ * <DenseGridViewIcon size={24} />
1896
+ * ```
1897
+ */
1898
+ export declare const DenseGridViewIcon: React_2.ForwardRefExoticComponent<BaseIconProps & React_2.RefAttributes<SVGSVGElement>>;
1899
+
1900
+ /** Props for {@link DenseGridViewIcon}. See {@link BaseIconProps} for the shared shape. */
1901
+ export declare type DenseGridViewIconProps = BaseIconProps;
1902
+
1782
1903
  /** Root component that manages open/close state for a dialog. */
1783
1904
  export declare const Dialog: React_2.FC<DialogPrimitive.DialogProps>;
1784
1905
 
@@ -2221,7 +2342,7 @@ export declare type DrawerTriggerProps = React_2.ComponentPropsWithoutRef<typeof
2221
2342
  export declare type DrawerVariant = "panel" | "sheet";
2222
2343
 
2223
2344
  /** Root component that manages open/close state for a dropdown menu. */
2224
- export declare function DropdownMenu({ open: openProp, defaultOpen, onOpenChange, children, ...props }: DropdownMenuProps): JSX.Element;
2345
+ export declare function DropdownMenu({ open: openProp, defaultOpen, onOpenChange, variant, children, ...props }: DropdownMenuProps): JSX.Element;
2225
2346
 
2226
2347
  /**
2227
2348
  * The positioned content panel rendered inside a portal.
@@ -2248,7 +2369,11 @@ export declare const DropdownMenuContent: React_2.ForwardRefExoticComponent<Drop
2248
2369
  export declare interface DropdownMenuContentProps extends React_2.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> {
2249
2370
  }
2250
2371
 
2251
- /** Groups related menu items. Accepts an optional `DropdownMenuLabel`. */
2372
+ /**
2373
+ * Groups related menu items. Accepts an optional `DropdownMenuLabel`.
2374
+ *
2375
+ * Requires Radix menu context — not supported inside a `variant="sheet"` menu.
2376
+ */
2252
2377
  export declare const DropdownMenuGroup: React_2.ForwardRefExoticComponent<DropdownMenuPrimitive.DropdownMenuGroupProps & React_2.RefAttributes<HTMLDivElement>>;
2253
2378
 
2254
2379
  /** Props for the {@link DropdownMenuGroup} component. */
@@ -2345,7 +2470,13 @@ export declare interface DropdownMenuItemProps extends React_2.ComponentPropsWit
2345
2470
  * Takes precedence over `leadingIcon`.
2346
2471
  */
2347
2472
  avatar?: React_2.ReactNode;
2348
- /** Icon (or other node) rendered after the label. */
2473
+ /**
2474
+ * Icon (or other node) rendered after the label. When
2475
+ * {@link DropdownMenuItemProps.selected} is true and no `trailingIcon` is
2476
+ * given, the built-in selected check indicator renders in this slot
2477
+ * instead — pass a `trailingIcon` to use a custom selected indicator (e.g.
2478
+ * a themed tick) rather than the default one.
2479
+ */
2349
2480
  trailingIcon?: React_2.ReactNode;
2350
2481
  /** Trailing count or number (e.g. an unread total) rendered before {@link DropdownMenuItemProps.trailingIcon}. */
2351
2482
  count?: React_2.ReactNode;
@@ -2390,6 +2521,8 @@ export declare interface DropdownMenuLabelProps extends React_2.ComponentPropsWi
2390
2521
 
2391
2522
  /** Props for the {@link DropdownMenu} root component. */
2392
2523
  export declare interface DropdownMenuProps extends React_2.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Root> {
2524
+ /** How the menu presents its content. @default "menu" */
2525
+ variant?: DropdownMenuVariant;
2393
2526
  }
2394
2527
 
2395
2528
  /**
@@ -2403,6 +2536,8 @@ export declare interface DropdownMenuProps extends React_2.ComponentPropsWithout
2403
2536
  * <DropdownMenuRadioItem value="oldest">Oldest first</DropdownMenuRadioItem>
2404
2537
  * </DropdownMenuRadioGroup>
2405
2538
  * ```
2539
+ *
2540
+ * Requires Radix menu context — not supported inside a `variant="sheet"` menu.
2406
2541
  */
2407
2542
  export declare const DropdownMenuRadioGroup: React_2.ForwardRefExoticComponent<DropdownMenuPrimitive.DropdownMenuRadioGroupProps & React_2.RefAttributes<HTMLDivElement>>;
2408
2543
 
@@ -2447,6 +2582,16 @@ export declare const DropdownMenuTrigger: React_2.ForwardRefExoticComponent<Omit
2447
2582
  /** Props for the {@link DropdownMenuTrigger} component. */
2448
2583
  export declare type DropdownMenuTriggerProps = React_2.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Trigger>;
2449
2584
 
2585
+ /**
2586
+ * How a {@link DropdownMenu} presents its content.
2587
+ * - `"menu"` (default) — a Radix-positioned panel anchored to the trigger.
2588
+ * - `"sheet"` — a bottom drawer (via {@link Drawer}), for mobile/touch viewports.
2589
+ *
2590
+ * The viewport decision belongs to the consumer (it owns the breakpoint
2591
+ * source of truth), so pass e.g. `variant={isDesktop ? "menu" : "sheet"}`.
2592
+ */
2593
+ declare type DropdownMenuVariant = "menu" | "sheet";
2594
+
2450
2595
  /**
2451
2596
  * Edit icon. Renders at sizes 16, 24, or 32 px with outlined and filled variants.
2452
2597
  *
@@ -3326,6 +3471,40 @@ export declare const MassMessageIcon: React_2.ForwardRefExoticComponent<BaseIcon
3326
3471
  /** Props for {@link MassMessageIcon}. See {@link BaseIconProps} for the shared shape. */
3327
3472
  export declare type MassMessageIconProps = BaseIconProps;
3328
3473
 
3474
+ /**
3475
+ * A compact circular indicator that surfaces the moderation state of a piece of
3476
+ * media — flagged, removed, or sensitive. Typically overlaid on a thumbnail or
3477
+ * attachment.
3478
+ *
3479
+ * Renders as `role="img"` with a descriptive `aria-label`; the inner glyph is
3480
+ * decorative.
3481
+ *
3482
+ * @example
3483
+ * ```tsx
3484
+ * <MediaStatusIndicator status="sensitive" />
3485
+ * <MediaStatusIndicator status="removed" label="Removed for violating guidelines" />
3486
+ * ```
3487
+ */
3488
+ export declare const MediaStatusIndicator: React_2.ForwardRefExoticComponent<MediaStatusIndicatorProps & React_2.RefAttributes<HTMLSpanElement>>;
3489
+
3490
+ export declare interface MediaStatusIndicatorProps extends Omit<React_2.HTMLAttributes<HTMLSpanElement>, "children"> {
3491
+ /** Which media state to represent. @default "default" */
3492
+ status?: MediaStatusIndicatorStatus;
3493
+ /**
3494
+ * Accessible label announced by assistive tech. Defaults to a per-status
3495
+ * description; pass your own for localisation or extra context.
3496
+ */
3497
+ label?: string;
3498
+ }
3499
+
3500
+ /**
3501
+ * State represented by the indicator.
3502
+ * - `"default"`: media flagged / pending review (amber).
3503
+ * - `"removed"`: media removed for a policy violation (red).
3504
+ * - `"sensitive"`: sensitive content hidden behind an overlay (dark).
3505
+ */
3506
+ export declare type MediaStatusIndicatorStatus = "default" | "removed" | "sensitive";
3507
+
3329
3508
  /**
3330
3509
  * Megaphone icon. Renders at sizes 16, 24, or 32 px with outlined and filled variants.
3331
3510
  *
@@ -4298,13 +4477,38 @@ export declare const SelectGroup: React_2.ForwardRefExoticComponent<SelectPrimit
4298
4477
  export declare type SelectGroupProps = React_2.ComponentPropsWithoutRef<typeof SelectPrimitive.Group>;
4299
4478
 
4300
4479
  /**
4301
- * An individual option inside {@link SelectContent}.
4480
+ * An individual option inside {@link SelectContent}, following the V2 Menu Item spec.
4481
+ *
4482
+ * Supports a leading icon or avatar, an optional two-line layout via `description`,
4483
+ * and the standard hover / selected / disabled states. The selected row is marked
4484
+ * with a trailing check indicator.
4485
+ *
4486
+ * @example
4487
+ * ```tsx
4488
+ * <SelectItem value="jane" avatar={<Avatar size={24} fallback="JD" />} description="Product designer">
4489
+ * Jane Doe
4490
+ * </SelectItem>
4491
+ * ```
4302
4492
  */
4303
4493
  export declare const SelectItem: React_2.ForwardRefExoticComponent<SelectItemProps & React_2.RefAttributes<HTMLDivElement>>;
4304
4494
 
4305
4495
  export declare interface SelectItemProps extends React_2.ComponentPropsWithoutRef<typeof SelectPrimitive.Item> {
4496
+ /** Row height. Defaults to the parent {@link Select} size (`48`/`40` → `40`, `32` → `32`). */
4497
+ size?: SelectItemSize;
4498
+ /** Icon (or other node) rendered before the label. Ignored when {@link SelectItemProps.avatar} is set. */
4499
+ leadingIcon?: React_2.ReactNode;
4500
+ /**
4501
+ * Leading avatar rendered in place of {@link SelectItemProps.leadingIcon}, for rows
4502
+ * representing a person or account. Pass an `Avatar` sized to `24`. Takes precedence over `leadingIcon`.
4503
+ */
4504
+ avatar?: React_2.ReactNode;
4505
+ /** Optional secondary text rendered on a second line below the label. */
4506
+ description?: React_2.ReactNode;
4306
4507
  }
4307
4508
 
4509
+ /** Dropdown row height in pixels, matching the V2 Menu Item spec. */
4510
+ export declare type SelectItemSize = "40" | "32";
4511
+
4308
4512
  /**
4309
4513
  * A non-interactive label shown above a {@link SelectGroup}.
4310
4514
  */
@@ -4632,6 +4836,43 @@ export declare const StopIcon: React_2.ForwardRefExoticComponent<BaseIconProps &
4632
4836
  /** Props for {@link StopIcon}. See {@link BaseIconProps} for the shared shape. */
4633
4837
  export declare type StopIconProps = BaseIconProps;
4634
4838
 
4839
+ /**
4840
+ * A subscription / purchase button pairing an action label with the current
4841
+ * price and an optional struck-through previous price. Built on {@link Button},
4842
+ * so it inherits the same variants, sizes, `negative` dark-surface treatment,
4843
+ * `fullWidth`, loading, and disabled behaviour.
4844
+ *
4845
+ * The accessible name defaults to the label plus pricing (e.g. `"Join now, $9.99,
4846
+ * was $19.99"`); pass `aria-label` to override.
4847
+ *
4848
+ * @example
4849
+ * ```tsx
4850
+ * <SubscribeButton price="$9.99/mo" discount="$19.99" variant="brand" fullWidth>
4851
+ * Join now
4852
+ * </SubscribeButton>
4853
+ * ```
4854
+ */
4855
+ export declare const SubscribeButton: React_2.ForwardRefExoticComponent<SubscribeButtonProps & React_2.RefAttributes<HTMLButtonElement>>;
4856
+
4857
+ export declare interface SubscribeButtonProps extends Omit<ButtonProps, "variant" | "size" | "price" | "discount" | "leftIcon" | "rightIcon" | "asChild"> {
4858
+ /** Visual style variant. `brand` is the Figma "Upsell" (green) type. @default "primary" */
4859
+ variant?: SubscribeButtonVariant;
4860
+ /** Height of the button in pixels. @default "48" */
4861
+ size?: SubscribeButtonSize;
4862
+ /** Current price shown on the trailing side (e.g. `"$9.99/mo"`). */
4863
+ price: string;
4864
+ /** Previous price rendered struck-through before {@link SubscribeButtonProps.price}. */
4865
+ discount?: string;
4866
+ /** Leading action label. @default "Join now" */
4867
+ children?: React_2.ReactNode;
4868
+ }
4869
+
4870
+ /** Height of the subscribe button in pixels. */
4871
+ export declare type SubscribeButtonSize = "48" | "40" | "32";
4872
+
4873
+ /** Visual style variant of the subscribe button. Mirrors the V2 Subscribe Button "Type" set (`brand` is Figma's "Upsell"). */
4874
+ export declare type SubscribeButtonVariant = "primary" | "secondary" | "tertiary" | "outline" | "brand";
4875
+
4635
4876
  /** A checkmark inside a filled circle icon for success states (20 × 20). */
4636
4877
  export declare const SuccessIcon: default_2.ForwardRefExoticComponent<default_2.SVGAttributes<SVGSVGElement> & {
4637
4878
  className?: string;
@@ -5812,6 +6053,82 @@ export declare const VipBadgeIcon: React_2.ForwardRefExoticComponent<React_2.SVG
5812
6053
  className?: string;
5813
6054
  } & React_2.RefAttributes<SVGSVGElement>>;
5814
6055
 
6056
+ /**
6057
+ * A voice-note audio player: a play/pause control, an amplitude waveform, and a
6058
+ * timestamp — for voice messages and audio attachments in a conversation.
6059
+ *
6060
+ * Two modes: pass `src` for a self-contained player that decodes the waveform,
6061
+ * plays real audio, tracks live progress and is seekable; or omit `src` and
6062
+ * drive it with `playing`/`progress` + `onPlayPause` for a presentational,
6063
+ * fully-controlled voice note. `flat` renders a compact dotted preview and
6064
+ * `negative` adapts it to dark message bubbles.
6065
+ *
6066
+ * @example
6067
+ * ```tsx
6068
+ * <VoiceNote src="https://example.com/note.mp3" duration={5} />
6069
+ * <VoiceNote time="0:05" progress={0.4} playing onPlayPause={toggle} />
6070
+ * ```
6071
+ */
6072
+ export declare const VoiceNote: React_2.ForwardRefExoticComponent<VoiceNoteProps & React_2.RefAttributes<HTMLDivElement>>;
6073
+
6074
+ export declare interface VoiceNoteProps extends Omit<React_2.HTMLAttributes<HTMLDivElement>, "onPlay" | "onPause"> {
6075
+ /**
6076
+ * URL of the audio to play. When set, the component manages a real `<audio>`
6077
+ * element: it decodes the waveform, plays/pauses, tracks live progress and is
6078
+ * seekable — `waveform`/`progress` are derived automatically. Leave unset for
6079
+ * a presentational, fully-controlled voice note.
6080
+ */
6081
+ src?: string;
6082
+ /** Fallback total duration (seconds), used until the media's metadata loads. */
6083
+ duration?: number;
6084
+ /** Amplitude values (0–1), one per bar. Ignored when `src` is set. Falls back to a built-in pattern. */
6085
+ waveform?: number[];
6086
+ /** Visual style; `flat` renders a simplified dotted preview. @default "default" */
6087
+ variant?: VoiceNoteVariant;
6088
+ /** Size preset. @default "default" */
6089
+ size?: VoiceNoteSize;
6090
+ /** Dark-surface treatment for use on message bubbles. @default false */
6091
+ negative?: boolean;
6092
+ /**
6093
+ * Playback progress (0–1) for the presentational mode. When set, the waveform
6094
+ * splits into played/unplayed bars (the "Listening" state). Ignored when `src`
6095
+ * is set (progress comes from the media element).
6096
+ */
6097
+ progress?: number;
6098
+ /** Whether audio is playing (controlled) — toggles the play/pause icon. */
6099
+ playing?: boolean;
6100
+ /** Initial playing state (uncontrolled). @default false */
6101
+ defaultPlaying?: boolean;
6102
+ /** Called with the next playing state when the play/pause control is pressed. */
6103
+ onPlayPause?: (playing: boolean) => void;
6104
+ /** Called when audio playback reaches the end (only in `src` mode). */
6105
+ onEnded?: () => void;
6106
+ /** Timestamp or duration label, e.g. "0:05". Ignored when `src` is set (derived from the media). */
6107
+ time?: string;
6108
+ /** File name shown when the audio is an uploaded file rather than a voice note. */
6109
+ fileName?: string;
6110
+ /** Show the play/pause control. @default true */
6111
+ showControls?: boolean;
6112
+ /** Show the timestamp label. @default true */
6113
+ showTimestamp?: boolean;
6114
+ /** Show a remove button (calls {@link VoiceNoteProps.onRemove}). @default false */
6115
+ showRemove?: boolean;
6116
+ /** Called when the remove button is pressed. */
6117
+ onRemove?: () => void;
6118
+ /** Accessible name for the play/pause control. Defaults to "Play"/"Pause". */
6119
+ playButtonLabel?: string;
6120
+ /** Accessible name for the remove button. @default "Remove" */
6121
+ removeButtonLabel?: string;
6122
+ /** Accessible name for the whole player. @default "Voice note" */
6123
+ "aria-label"?: string;
6124
+ }
6125
+
6126
+ /** Size preset for the voice note. */
6127
+ export declare type VoiceNoteSize = "default" | "small";
6128
+
6129
+ /** Visual style of the waveform. */
6130
+ export declare type VoiceNoteVariant = "default" | "flat";
6131
+
5815
6132
  /**
5816
6133
  * Wallet icon. Renders at sizes 16, 24, or 32 px with outlined and filled variants.
5817
6134
  *
package/dist/index.mjs CHANGED
@@ -6,6 +6,7 @@ import { AccordionItem } from "./components/Accordion/AccordionItem.mjs";
6
6
  import { AccordionTrigger } from "./components/Accordion/AccordionTrigger.mjs";
7
7
  import { Alert } from "./components/Alert/Alert.mjs";
8
8
  import { AudioPlayer } from "./components/AudioPlayer/AudioPlayer.mjs";
9
+ import { AudioRecordButton } from "./components/AudioRecordButton/AudioRecordButton.mjs";
9
10
  import { AudioUpload } from "./components/AudioUpload/AudioUpload.mjs";
10
11
  import { useAudioRecorder } from "./components/AudioUpload/useAudioRecorder.mjs";
11
12
  import { Autocomplete } from "./components/Autocomplete/Autocomplete.mjs";
@@ -18,6 +19,7 @@ import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbP
18
19
  import { Button } from "./components/Button/Button.mjs";
19
20
  import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "./components/Card/Card.mjs";
20
21
  import { ChatInput } from "./components/ChatInput/ChatInput.mjs";
22
+ import { ChatMessage } from "./components/ChatMessage/ChatMessage.mjs";
21
23
  import { Checkbox } from "./components/Checkbox/Checkbox.mjs";
22
24
  import { Chip } from "./components/Chip/Chip.mjs";
23
25
  import { Count } from "./components/Count/Count.mjs";
@@ -77,6 +79,7 @@ import { CopyIcon } from "./components/Icons/CopyIcon.mjs";
77
79
  import { CrossCircleIcon } from "./components/Icons/CrossCircleIcon.mjs";
78
80
  import { CrossIcon } from "./components/Icons/CrossIcon.mjs";
79
81
  import { CrownIcon } from "./components/Icons/CrownIcon.mjs";
82
+ import { DenseGridViewIcon } from "./components/Icons/DenseGridViewIcon.mjs";
80
83
  import { DiamondIcon } from "./components/Icons/DiamondIcon.mjs";
81
84
  import { DiscordIcon } from "./components/Icons/DiscordIcon.mjs";
82
85
  import { DiscountIcon } from "./components/Icons/DiscountIcon.mjs";
@@ -204,6 +207,7 @@ import { InlineEdit } from "./components/InlineEdit/InlineEdit.mjs";
204
207
  import { Link } from "./components/Link/Link.mjs";
205
208
  import { Loader } from "./components/Loader/Loader.mjs";
206
209
  import { Logo } from "./components/Logo/Logo.mjs";
210
+ import { MediaStatusIndicator } from "./components/MediaStatusIndicator/MediaStatusIndicator.mjs";
207
211
  import { MobileStepper } from "./components/MobileStepper/MobileStepper.mjs";
208
212
  import { OnlineBlinkingIcon } from "./components/OnlineBlinkingIcon/OnlineBlinkingIcon.mjs";
209
213
  import { PageSelector } from "./components/PageSelector/PageSelector.mjs";
@@ -227,6 +231,7 @@ import { Slider } from "./components/Slider/Slider.mjs";
227
231
  import { Snackbar } from "./components/Snackbar/Snackbar.mjs";
228
232
  import { Stepper } from "./components/Stepper/Stepper.mjs";
229
233
  import { StepperStep } from "./components/Stepper/StepperStep.mjs";
234
+ import { SubscribeButton } from "./components/SubscribeButton/SubscribeButton.mjs";
230
235
  import { Switch } from "./components/Switch/Switch.mjs";
231
236
  import { SwitchField } from "./components/SwitchField/SwitchField.mjs";
232
237
  import { SwitchToggle } from "./components/SwitchToggle/SwitchToggle.mjs";
@@ -243,6 +248,7 @@ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./comp
243
248
  import { UserDisplayName } from "./components/UserDisplayName/UserDisplayName.mjs";
244
249
  import { UserHandle } from "./components/UserHandle/UserHandle.mjs";
245
250
  import { UserItem } from "./components/UserItem/UserItem.mjs";
251
+ import { VoiceNote } from "./components/VoiceNote/VoiceNote.mjs";
246
252
  import { cn } from "./utils/cn.mjs";
247
253
  import { getInitials } from "./utils/getInitials.mjs";
248
254
  import { useSuppressClickAfterDrag } from "./utils/useSuppressClickAfterDrag.mjs";
@@ -267,6 +273,7 @@ export {
267
273
  ArrowUpRightIcon,
268
274
  AtSignIcon,
269
275
  AudioPlayer,
276
+ AudioRecordButton,
270
277
  AudioUpload,
271
278
  AutoMessageIcon,
272
279
  Autocomplete,
@@ -302,6 +309,7 @@ export {
302
309
  CardTitle,
303
310
  ChartIcon,
304
311
  ChatInput,
312
+ ChatMessage,
305
313
  CheckBoxOffIcon,
306
314
  CheckBoxOnIcon,
307
315
  CheckCircleIcon,
@@ -328,6 +336,7 @@ export {
328
336
  CrossIcon,
329
337
  CrownIcon,
330
338
  CyclingText,
339
+ DenseGridViewIcon,
331
340
  Dialog,
332
341
  DialogBody,
333
342
  DialogClose,
@@ -418,6 +427,7 @@ export {
418
427
  LogoutIcon,
419
428
  LoveIcon,
420
429
  MassMessageIcon,
430
+ MediaStatusIndicator,
421
431
  MegaphoneIcon,
422
432
  MenuCloseIcon,
423
433
  MenuIcon,
@@ -482,6 +492,7 @@ export {
482
492
  Stepper,
483
493
  StepperStep,
484
494
  StopIcon,
495
+ SubscribeButton,
485
496
  SuccessIcon,
486
497
  SunIcon,
487
498
  Support2Icon,
@@ -554,6 +565,7 @@ export {
554
565
  VerifiedIcon,
555
566
  VideoIcon,
556
567
  VipBadgeIcon,
568
+ VoiceNote,
557
569
  WalletIcon,
558
570
  WarningIcon,
559
571
  WarningTriangleIcon,
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -13,6 +13,15 @@
13
13
  scrollbar-gutter: stable;
14
14
  }
15
15
 
16
+ /*
17
+ * Neutralise the browser's autofill highlight with the inset box-shadow
18
+ * hack. The shadow colour must match the input container background
19
+ * (TextField/TextArea render a transparent <input> inside a
20
+ * bg-inputs-inputs-primary wrapper) — a translucent tint here paints a
21
+ * visibly lighter band inside the field in dark mode. Note this rule
22
+ * cannot be overridden by unlayered !important declarations downstream:
23
+ * for !important the cascade reverses, so @layer base wins.
24
+ */
16
25
  input:-webkit-autofill,
17
26
  input:-webkit-autofill:hover,
18
27
  input:-webkit-autofill:focus,
@@ -26,8 +35,8 @@
26
35
  textarea:autofill:hover,
27
36
  textarea:autofill:focus {
28
37
  -webkit-text-fill-color: var(--color-content-primary);
29
- -webkit-box-shadow: inset 0 0 0 1000px var(--color-neutral-alphas-50) !important;
30
- box-shadow: inset 0 0 0 1000px var(--color-neutral-alphas-50) !important;
38
+ -webkit-box-shadow: inset 0 0 0 1000px var(--color-inputs-inputs-primary) !important;
39
+ box-shadow: inset 0 0 0 1000px var(--color-inputs-inputs-primary) !important;
31
40
  background-clip: padding-box !important;
32
41
  transition: background-color 9999s ease-in-out 0s;
33
42
  }
@@ -637,7 +637,7 @@
637
637
 
638
638
  .dark {
639
639
  --color-neutral-alphas-50: var(--primitives-color-whitealpha-100);
640
- --color-neutral-alphas-100: var(--primitives-color-whitealpha-100);
640
+ --color-neutral-alphas-100: #ffffff26;
641
641
  --color-neutral-alphas-150: #ffffffcc;
642
642
  --color-neutral-alphas-200: var(--primitives-color-whitealpha-200);
643
643
  --color-neutral-alphas-300: var(--primitives-color-whitealpha-300);
@@ -0,0 +1,76 @@
1
+ "use client";
2
+ const RAW_PEAK_COUNT = 128;
3
+ function formatTime(seconds) {
4
+ if (seconds === void 0 || !Number.isFinite(seconds)) return "--:--";
5
+ const totalSeconds = Math.max(0, Math.floor(seconds));
6
+ const minutes = Math.floor(totalSeconds / 60);
7
+ const remainingSeconds = totalSeconds % 60;
8
+ return `${minutes}:${String(remainingSeconds).padStart(2, "0")}`;
9
+ }
10
+ function hashString(value) {
11
+ let hash = 2166136261;
12
+ for (let i = 0; i < value.length; i++) {
13
+ hash ^= value.charCodeAt(i);
14
+ hash = Math.imul(hash, 16777619);
15
+ }
16
+ return hash >>> 0;
17
+ }
18
+ function createSeededRandom(seed) {
19
+ let state = seed;
20
+ return () => {
21
+ state = state + 1831565813 | 0;
22
+ let t = Math.imul(state ^ state >>> 15, 1 | state);
23
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
24
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
25
+ };
26
+ }
27
+ function generateFallbackPeaks(src, count) {
28
+ const random = createSeededRandom(hashString(src));
29
+ return Array.from({ length: count }, () => 0.2 + random() * 0.8);
30
+ }
31
+ function computePeaksFromChannelData(channelData, count) {
32
+ const blockSize = Math.max(1, Math.floor(channelData.length / count));
33
+ const peaks = [];
34
+ for (let i = 0; i < count; i++) {
35
+ const start = i * blockSize;
36
+ let max = 0;
37
+ for (let j = 0; j < blockSize && start + j < channelData.length; j++) {
38
+ max = Math.max(max, Math.abs(channelData[start + j] ?? 0));
39
+ }
40
+ peaks.push(max);
41
+ }
42
+ return peaks;
43
+ }
44
+ function resamplePeaks(peaks, barCount) {
45
+ if (peaks.length === 0 || barCount <= 0) return [];
46
+ const step = peaks.length / barCount;
47
+ return Array.from(
48
+ { length: barCount },
49
+ (_, i) => peaks[Math.min(peaks.length - 1, Math.floor(i * step))] ?? 0
50
+ );
51
+ }
52
+ async function decodeAudioPeaks(src, signal) {
53
+ const AudioContextCtor = window.AudioContext ?? window.webkitAudioContext;
54
+ if (!AudioContextCtor) throw new Error("WebAudio unsupported");
55
+ const response = await fetch(src, { signal });
56
+ const arrayBuffer = await response.arrayBuffer();
57
+ const audioContext = new AudioContextCtor();
58
+ try {
59
+ const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
60
+ return computePeaksFromChannelData(audioBuffer.getChannelData(0), RAW_PEAK_COUNT);
61
+ } finally {
62
+ if (audioContext.state !== "closed") audioContext.close().catch(() => {
63
+ });
64
+ }
65
+ }
66
+ export {
67
+ RAW_PEAK_COUNT,
68
+ computePeaksFromChannelData,
69
+ createSeededRandom,
70
+ decodeAudioPeaks,
71
+ formatTime,
72
+ generateFallbackPeaks,
73
+ hashString,
74
+ resamplePeaks
75
+ };
76
+ //# sourceMappingURL=audioWaveform.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audioWaveform.mjs","sources":["../../src/utils/audioWaveform.ts"],"sourcesContent":["/** Number of amplitude samples decoded/kept internally, resampled to the rendered bar count. */\nexport const RAW_PEAK_COUNT = 128;\n\n/** Formats a duration in seconds as `m:ss`, or `--:--` when unknown. */\nexport function formatTime(seconds: number | undefined): string {\n if (seconds === undefined || !Number.isFinite(seconds)) return \"--:--\";\n const totalSeconds = Math.max(0, Math.floor(seconds));\n const minutes = Math.floor(totalSeconds / 60);\n const remainingSeconds = totalSeconds % 60;\n return `${minutes}:${String(remainingSeconds).padStart(2, \"0\")}`;\n}\n\n/** Deterministic 32-bit string hash (FNV-1a), used to seed the fallback waveform. */\nexport function hashString(value: string): number {\n let hash = 2166136261;\n for (let i = 0; i < value.length; i++) {\n hash ^= value.charCodeAt(i);\n hash = Math.imul(hash, 16777619);\n }\n return hash >>> 0;\n}\n\n/** Seeded PRNG (mulberry32) — deterministic across runs, unlike `Math.random`. */\nexport function createSeededRandom(seed: number): () => number {\n let state = seed;\n return () => {\n state = (state + 0x6d2b79f5) | 0;\n let t = Math.imul(state ^ (state >>> 15), 1 | state);\n t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\n/** Deterministic placeholder amplitudes used when audio can't be decoded (network/CORS/format failure). */\nexport function generateFallbackPeaks(src: string, count: number): number[] {\n const random = createSeededRandom(hashString(src));\n return Array.from({ length: count }, () => 0.2 + random() * 0.8);\n}\n\n/** Downsamples decoded PCM data to `count` peak (max-abs) amplitudes. */\nexport function computePeaksFromChannelData(channelData: Float32Array, count: number): number[] {\n const blockSize = Math.max(1, Math.floor(channelData.length / count));\n const peaks: number[] = [];\n for (let i = 0; i < count; i++) {\n const start = i * blockSize;\n let max = 0;\n for (let j = 0; j < blockSize && start + j < channelData.length; j++) {\n max = Math.max(max, Math.abs(channelData[start + j] ?? 0));\n }\n peaks.push(max);\n }\n return peaks;\n}\n\n/** Resamples a peaks array to `barCount` values (nearest-neighbour). */\nexport function resamplePeaks(peaks: number[], barCount: number): number[] {\n if (peaks.length === 0 || barCount <= 0) return [];\n const step = peaks.length / barCount;\n return Array.from(\n { length: barCount },\n (_, i) => peaks[Math.min(peaks.length - 1, Math.floor(i * step))] ?? 0,\n );\n}\n\n/** Decodes `src` via WebAudio and returns downsampled peak amplitudes. Throws on any failure. */\nexport async function decodeAudioPeaks(src: string, signal: AbortSignal): Promise<number[]> {\n const AudioContextCtor =\n window.AudioContext ??\n (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n if (!AudioContextCtor) throw new Error(\"WebAudio unsupported\");\n\n const response = await fetch(src, { signal });\n const arrayBuffer = await response.arrayBuffer();\n const audioContext = new AudioContextCtor();\n try {\n const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);\n return computePeaksFromChannelData(audioBuffer.getChannelData(0), RAW_PEAK_COUNT);\n } finally {\n if (audioContext.state !== \"closed\") audioContext.close().catch(() => {});\n }\n}\n"],"names":[],"mappings":";AACO,MAAM,iBAAiB;AAGvB,SAAS,WAAW,SAAqC;AAC9D,MAAI,YAAY,UAAa,CAAC,OAAO,SAAS,OAAO,EAAG,QAAO;AAC/D,QAAM,eAAe,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,CAAC;AACpD,QAAM,UAAU,KAAK,MAAM,eAAe,EAAE;AAC5C,QAAM,mBAAmB,eAAe;AACxC,SAAO,GAAG,OAAO,IAAI,OAAO,gBAAgB,EAAE,SAAS,GAAG,GAAG,CAAC;AAChE;AAGO,SAAS,WAAW,OAAuB;AAChD,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAQ,MAAM,WAAW,CAAC;AAC1B,WAAO,KAAK,KAAK,MAAM,QAAQ;AAAA,EACjC;AACA,SAAO,SAAS;AAClB;AAGO,SAAS,mBAAmB,MAA4B;AAC7D,MAAI,QAAQ;AACZ,SAAO,MAAM;AACX,YAAS,QAAQ,aAAc;AAC/B,QAAI,IAAI,KAAK,KAAK,QAAS,UAAU,IAAK,IAAI,KAAK;AACnD,QAAK,IAAI,KAAK,KAAK,IAAK,MAAM,GAAI,KAAK,CAAC,IAAK;AAC7C,aAAS,IAAK,MAAM,QAAS,KAAK;AAAA,EACpC;AACF;AAGO,SAAS,sBAAsB,KAAa,OAAyB;AAC1E,QAAM,SAAS,mBAAmB,WAAW,GAAG,CAAC;AACjD,SAAO,MAAM,KAAK,EAAE,QAAQ,MAAA,GAAS,MAAM,MAAM,OAAA,IAAW,GAAG;AACjE;AAGO,SAAS,4BAA4B,aAA2B,OAAyB;AAC9F,QAAM,YAAY,KAAK,IAAI,GAAG,KAAK,MAAM,YAAY,SAAS,KAAK,CAAC;AACpE,QAAM,QAAkB,CAAA;AACxB,WAAS,IAAI,GAAG,IAAI,OAAO,KAAK;AAC9B,UAAM,QAAQ,IAAI;AAClB,QAAI,MAAM;AACV,aAAS,IAAI,GAAG,IAAI,aAAa,QAAQ,IAAI,YAAY,QAAQ,KAAK;AACpE,YAAM,KAAK,IAAI,KAAK,KAAK,IAAI,YAAY,QAAQ,CAAC,KAAK,CAAC,CAAC;AAAA,IAC3D;AACA,UAAM,KAAK,GAAG;AAAA,EAChB;AACA,SAAO;AACT;AAGO,SAAS,cAAc,OAAiB,UAA4B;AACzE,MAAI,MAAM,WAAW,KAAK,YAAY,UAAU,CAAA;AAChD,QAAM,OAAO,MAAM,SAAS;AAC5B,SAAO,MAAM;AAAA,IACX,EAAE,QAAQ,SAAA;AAAA,IACV,CAAC,GAAG,MAAM,MAAM,KAAK,IAAI,MAAM,SAAS,GAAG,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,KAAK;AAAA,EAAA;AAEzE;AAGA,eAAsB,iBAAiB,KAAa,QAAwC;AAC1F,QAAM,mBACJ,OAAO,gBACN,OAAmE;AACtE,MAAI,CAAC,iBAAkB,OAAM,IAAI,MAAM,sBAAsB;AAE7D,QAAM,WAAW,MAAM,MAAM,KAAK,EAAE,QAAQ;AAC5C,QAAM,cAAc,MAAM,SAAS,YAAA;AACnC,QAAM,eAAe,IAAI,iBAAA;AACzB,MAAI;AACF,UAAM,cAAc,MAAM,aAAa,gBAAgB,WAAW;AAClE,WAAO,4BAA4B,YAAY,eAAe,CAAC,GAAG,cAAc;AAAA,EAClF,UAAA;AACE,QAAI,aAAa,UAAU,uBAAuB,MAAA,EAAQ,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EAC1E;AACF;"}