@botiverse/raft-ui 0.21.2 → 0.22.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,21 +1,23 @@
1
1
  import { cn } from "./cn.mjs";
2
2
  import { Dialog } from "@base-ui/react/dialog";
3
- import { Check, Minus, X } from "lucide-react";
3
+ import { ArrowLeft, Check, ChevronRight, Minus, X } from "lucide-react";
4
+ import { Children, Fragment, createContext, createElement, isValidElement, use, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
4
5
  import { tv } from "tailwind-variants/lite";
5
- import { Children, Fragment, createContext, isValidElement, use, useCallback, useContext, useEffect, useMemo, useRef, useSyncExternalStore } from "react";
6
6
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
7
7
  import { Button } from "@base-ui/react/button";
8
8
  import { mergeProps } from "@base-ui/react/merge-props";
9
9
  import { useRender } from "@base-ui/react/use-render";
10
+ import { createPortal, flushSync } from "react-dom";
10
11
  import { Tooltip } from "@base-ui/react/tooltip";
11
12
  import { Checkbox } from "@base-ui/react/checkbox";
12
13
  import { ScrollArea } from "@base-ui/react/scroll-area";
13
14
  import { Avatar } from "@base-ui/react";
14
15
  import { Menu } from "@base-ui/react/menu";
15
- import { flushSync } from "react-dom";
16
+ import { Popover } from "@base-ui/react/popover";
17
+ import { PointerSensor, TouchSensor, useSensor } from "@dnd-kit/core";
18
+ import { PreviewCard } from "@base-ui/react/preview-card";
16
19
  import { Toggle } from "@base-ui/react/toggle";
17
20
  import { Toolbar } from "@base-ui/react/toolbar";
18
- import { PointerSensor, TouchSensor, useSensor } from "@dnd-kit/core";
19
21
  //#region src/lib/decision-width.ts
20
22
  /**
21
23
  * The width of every decision dialog (dialog.md#variants): AlertDialog and a
@@ -54,6 +56,103 @@ const headEdges = ["px-5 max-md:px-4", ...headEdgePull];
54
56
  * as the arithmetic so it follows the control tokens wherever they switch.
55
57
  */
56
58
  const controlIconInset = "[--icon-inset:calc((var(--control-height)-var(--control-icon-size))/2)]";
59
+ /**
60
+ * The row of buttons while `data-stacked` is set: a full-width column. A
61
+ * button alone in a wrapper (a tooltip's trigger) fills the width too.
62
+ */
63
+ const stackedActions = [
64
+ "data-stacked:flex-col-reverse",
65
+ "data-stacked:items-stretch",
66
+ "data-stacked:*:data-[slot=button]:w-full data-stacked:[&>*>[data-slot=button]:only-child]:w-full",
67
+ ...[].map((value) => `data-stacked:${value}`)
68
+ ];
69
+ function assignRef$2(ref, value) {
70
+ if (typeof ref === "function") ref(value);
71
+ else if (ref) ref.current = value;
72
+ }
73
+ function contentWidth(element) {
74
+ const style = getComputedStyle(element);
75
+ return element.getBoundingClientRect().width - (Number.parseFloat(style.paddingLeft) || 0) - (Number.parseFloat(style.paddingRight) || 0) - (Number.parseFloat(style.borderLeftWidth) || 0) - (Number.parseFloat(style.borderRightWidth) || 0);
76
+ }
77
+ /**
78
+ * Whether a row of buttons must stack: laid out as a row (`data-stacked` off
79
+ * for the measure and put back at once, inside one frame, so nothing is
80
+ * painted in between and the layouts never flip back and forth), its children
81
+ * run past the content box of `frame`. Children that may shrink (a label that
82
+ * truncates or wraps) shrink first, so only content that can't, the buttons,
83
+ * counts as not fitting. Undefined while the frame is hidden (a sheet hides
84
+ * its footer), so nothing is decided then.
85
+ */
86
+ function rowOverflows(row, frame) {
87
+ if (!(frame.getBoundingClientRect().width > 0)) return void 0;
88
+ const wasStacked = row.hasAttribute("data-stacked");
89
+ if (wasStacked) row.removeAttribute("data-stacked");
90
+ const available = contentWidth(frame);
91
+ let start = Number.POSITIVE_INFINITY;
92
+ let end = Number.NEGATIVE_INFINITY;
93
+ for (const child of row.children) {
94
+ const box = child.getBoundingClientRect();
95
+ if (box.width === 0 && box.height === 0) continue;
96
+ const style = getComputedStyle(child);
97
+ start = Math.min(start, box.left - (Number.parseFloat(style.marginLeft) || 0));
98
+ end = Math.max(end, box.right + (Number.parseFloat(style.marginRight) || 0));
99
+ }
100
+ if (wasStacked) row.setAttribute("data-stacked", "");
101
+ return end - start > available + .5;
102
+ }
103
+ /**
104
+ * Sets `data-stacked` on a footer's row of buttons when it doesn't fit on one
105
+ * line (rowOverflows). Pass the returned ref to the row. It must fit its own
106
+ * content box, or with `inParent` (the group of buttons beside a note) its
107
+ * parent's. As BannerAction's `data-wrap`, it measures again when the footer
108
+ * resizes, the buttons change or a web font arrives.
109
+ */
110
+ function useStackedActions(ref, inParent = false) {
111
+ const [row, setRow] = useState(null);
112
+ const [stacked, setStacked] = useState(false);
113
+ const callbackRef = useCallback((node) => {
114
+ setRow(node);
115
+ assignRef$2(ref, node);
116
+ }, [ref]);
117
+ useLayoutEffect(() => {
118
+ const frame = inParent ? row?.parentElement : row;
119
+ if (!row || !frame || typeof ResizeObserver === "undefined") return;
120
+ const measure = () => {
121
+ const overflows = rowOverflows(row, frame);
122
+ if (overflows !== void 0) setStacked(overflows);
123
+ };
124
+ let raf = 0;
125
+ const schedule = () => {
126
+ cancelAnimationFrame(raf);
127
+ raf = requestAnimationFrame(measure);
128
+ };
129
+ measure();
130
+ const resize = new ResizeObserver(schedule);
131
+ resize.observe(frame);
132
+ resize.observe(row);
133
+ for (const child of row.children) resize.observe(child);
134
+ const mutation = new MutationObserver(schedule);
135
+ mutation.observe(row, {
136
+ childList: true,
137
+ characterData: true,
138
+ subtree: true
139
+ });
140
+ const fonts = typeof document.fonts === "undefined" ? void 0 : document.fonts;
141
+ let live = true;
142
+ fonts?.ready.then(() => {
143
+ if (live) schedule();
144
+ });
145
+ fonts?.addEventListener("loadingdone", schedule);
146
+ return () => {
147
+ live = false;
148
+ cancelAnimationFrame(raf);
149
+ resize.disconnect();
150
+ mutation.disconnect();
151
+ fonts?.removeEventListener("loadingdone", schedule);
152
+ };
153
+ }, [row, inParent]);
154
+ return [stacked, callbackRef];
155
+ }
57
156
  //#endregion
58
157
  //#region src/lib/tv.ts
59
158
  const tv$1 = ((options) => {
@@ -97,6 +196,16 @@ function useThemeScopeProps(props) {
97
196
  //#endregion
98
197
  //#region src/lib/overlay-depth.tsx
99
198
  const OverlayStackContext = createContext([]);
199
+ /**
200
+ * Tells everything under it that a modal surface is open, so a raft-ui
201
+ * Dialog, AlertDialog or Drawer opened from inside draws the lighter nested
202
+ * scrim (design/foundations/elevation.md#stacked-overlays). raft-ui's own
203
+ * roots add one by themselves; an app's own modal (its own backdrop, Esc and
204
+ * focus) wraps its scrim and its content in one to join the stack, and reads
205
+ * `useParentOverlayKind` inside it to pick its own scrim. Modal overlays only:
206
+ * a non-modal one passes `modal={false}`, adds nothing, and must not call
207
+ * `useParentOverlayKind` (it would read the layer below as its own).
208
+ */
100
209
  function OverlayLayerProvider({ kind, modal, children }) {
101
210
  const stack = useContext(OverlayStackContext);
102
211
  const value = useMemo(() => modal ? [...stack, kind] : stack, [
@@ -111,8 +220,14 @@ function OverlayLayerProvider({ kind, modal, children }) {
111
220
  }
112
221
  /**
113
222
  * The modal surface this overlay's own surface was opened from, or null when
114
- * it is the first. Called inside a surface, so the last entry is the surface
115
- * itself and the one before it is its parent.
223
+ * it is the first. Call it inside the surface's own `OverlayLayerProvider`
224
+ * (for raft-ui's overlays, inside their root): the last entry is the surface
225
+ * itself and the one before it is its parent. Not null means the surface is
226
+ * stacked over another and draws `layer-backdrop-nested` instead of
227
+ * `layer-backdrop`. Modal overlays only: under `modal={false}` nothing is
228
+ * added, so the last entry is the parent and this returns the wrong layer. It reads React context, so it sees the surfaces whose
229
+ * tree this one is rendered in, portals included; one opened from outside
230
+ * that tree is not stacked on it here.
116
231
  */
117
232
  function useParentOverlayKind() {
118
233
  const stack = useContext(OverlayStackContext);
@@ -410,6 +525,141 @@ function Button$1({ variant, size, loading = false, loadingLabel = "Loading", di
410
525
  });
411
526
  }
412
527
  //#endregion
528
+ //#region src/components/dialog/dialog-sheet-context.ts
529
+ const NO_SHEET = {
530
+ sheet: false,
531
+ commitSlot: null,
532
+ setCommitSlot: () => void 0,
533
+ bodyEndSlot: null,
534
+ setBodyEndSlot: () => void 0
535
+ };
536
+ const DialogSheetContext = createContext(NO_SHEET);
537
+ function useDialogSheet() {
538
+ return useContext(DialogSheetContext);
539
+ }
540
+ /** Inside the sheet's head: its × is a pushed page's leading action. */
541
+ const DialogSheetHeadContext = createContext(false);
542
+ function useInDialogSheetHead() {
543
+ return useContext(DialogSheetHeadContext);
544
+ }
545
+ /**
546
+ * Every overlay surface starts outside any sheet: a confirm, a dialog or a
547
+ * drawer opened from inside a sheet keeps its own head, footer and commit
548
+ * button instead of taking the sheet's.
549
+ */
550
+ function DialogSheetReset({ children }) {
551
+ return createElement(DialogSheetContext.Provider, { value: NO_SHEET }, createElement(DialogSheetHeadContext.Provider, { value: false }, children));
552
+ }
553
+ /**
554
+ * A dialog drawn as a phone sheet (design: dialog.md "On a phone"). The
555
+ * surface is DialogContent's frame docked to the bottom, 90% of the screen
556
+ * tall, rounded on its top corners, sliding up as a bottom sheet does; its
557
+ * head is a pushed page's head (× at the start, the title, the commit button
558
+ * at the end); its body keeps the phone's 16px margin and the home indicator's
559
+ * inset. Merged over DialogContent's classes, so these win.
560
+ */
561
+ const dialogSheet = tv$1({ slots: {
562
+ content: [
563
+ "fixed inset-x-0 top-auto bottom-0 left-0 h-[90dvh] max-h-none w-full max-w-none translate-none",
564
+ "rounded-none rounded-t-dialog",
565
+ "pr-[env(safe-area-inset-right,0px)] pl-[env(safe-area-inset-left,0px)]",
566
+ "transition-transform duration-240 ease-slide data-ending-style:duration-180",
567
+ "data-starting-style:translate-y-full data-ending-style:translate-y-full",
568
+ "motion-reduce:transition-none"
569
+ ],
570
+ overlay: [
571
+ "transition-opacity duration-240 ease-slide data-ending-style:duration-180",
572
+ "data-starting-style:opacity-0 data-ending-style:opacity-0",
573
+ "motion-reduce:transition-none"
574
+ ],
575
+ header: ["flex h-14 shrink-0 items-center gap-2 px-4 pt-0", "border-b border-line-seam"],
576
+ title: ["min-w-0 flex-1 truncate"],
577
+ commitSlot: ["flex shrink-0 items-center empty:hidden"],
578
+ body: ["min-h-0 content-start", "px-4 pt-4 first:pt-4 pb-[max(16px,env(safe-area-inset-bottom,0px))]"],
579
+ barBody: ["block overflow-y-auto"],
580
+ bodyEnd: ["mt-4 empty:hidden", "text-start font-sans text-footnote text-foreground-muted *:data-[slot=text-link]:text-footnote"]
581
+ } });
582
+ //#endregion
583
+ //#region src/components/text-link/text-link.tsx
584
+ const textLinkVariants = tv$1({
585
+ base: [
586
+ "rounded-[2px]",
587
+ "decoration-[1.5px] underline-offset-[3px] not-disabled:hover:underline",
588
+ "outline-none focus-visible:shadow-focus",
589
+ "disabled:cursor-not-allowed disabled:text-foreground-disabled"
590
+ ],
591
+ variants: { variant: {
592
+ muted: ["font-medium text-foreground-muted", "hover:text-foreground"],
593
+ strong: ["font-semibold text-foreground"]
594
+ } },
595
+ defaultVariants: { variant: "muted" }
596
+ });
597
+ function TextLink({ variant, className, render, ...props }) {
598
+ const defaultProps = {
599
+ className: textLinkVariants({
600
+ variant,
601
+ className
602
+ }),
603
+ ...render ? {} : { type: "button" }
604
+ };
605
+ return useRender({
606
+ defaultTagName: "button",
607
+ render,
608
+ props: mergeProps(defaultProps, props),
609
+ state: { slot: "text-link" }
610
+ });
611
+ }
612
+ //#endregion
613
+ //#region src/components/panel/panel-action.recipe.ts
614
+ const panelActionBase = tv$1({ base: [
615
+ "relative inline-flex w-auto shrink-0 items-center justify-center",
616
+ "overflow-visible font-sans leading-none tabular-nums select-none outline-none",
617
+ "transition-all duration-100",
618
+ "[&>svg]:pointer-events-none [&>svg]:shrink-0",
619
+ "h-control min-w-control rounded-control border-[1.5px] border-transparent bg-transparent py-0",
620
+ "px-2 md:pointer-fine:px-[5px]",
621
+ "after:absolute after:-inset-control-hit",
622
+ "text-foreground",
623
+ "hover:bg-fill-medium active:bg-fill-medium",
624
+ "focus-visible:shadow-focus",
625
+ "[&>svg]:size-control-icon",
626
+ controlIconInset
627
+ ] });
628
+ const panelAction = tv$1({
629
+ extend: panelActionBase,
630
+ base: [
631
+ "whitespace-nowrap",
632
+ "disabled:pointer-events-none disabled:transform-none",
633
+ "gap-1.5 text-control font-bold",
634
+ "disabled:bg-transparent disabled:text-foreground-disabled disabled:opacity-100",
635
+ "aria-pressed:border-sticker-ink aria-pressed:bg-brand aria-pressed:text-sticker-ink aria-pressed:ring-[1.5px] aria-pressed:ring-sticker-edge",
636
+ "aria-pressed:active:overlay-sticker-medium"
637
+ ]
638
+ });
639
+ const panelToggleAction = tv$1({
640
+ extend: panelActionBase,
641
+ base: [
642
+ "data-disabled:pointer-events-none data-disabled:transform-none",
643
+ "gap-1.5 text-control font-bold",
644
+ "data-disabled:bg-transparent data-disabled:text-foreground-disabled data-disabled:opacity-100",
645
+ "data-pressed:border-sticker-ink data-pressed:bg-brand data-pressed:text-sticker-ink data-pressed:ring-[1.5px] data-pressed:ring-sticker-edge",
646
+ "data-pressed:active:overlay-sticker-medium"
647
+ ]
648
+ });
649
+ //#endregion
650
+ //#region src/components/panel/panel-action.tsx
651
+ function PanelAction({ className, render, nativeButton, ...props }) {
652
+ return /* @__PURE__ */ jsx(Button, {
653
+ ...props,
654
+ render,
655
+ nativeButton: nativeButton ?? defaultNativeButton(render),
656
+ type: "button",
657
+ "data-slot": "panel-action",
658
+ "data-icon-inset": "",
659
+ className: panelAction({ className })
660
+ });
661
+ }
662
+ //#endregion
413
663
  //#region src/components/dialog/dialog.tsx
414
664
  const dialog = tv$1({
415
665
  slots: {
@@ -430,7 +680,18 @@ const dialog = tv$1({
430
680
  "px-5 pt-1.5 pb-5 first:pt-5",
431
681
  "font-sans text-subhead text-foreground-muted"
432
682
  ],
433
- footer: ["flex shrink-0 items-center justify-end", "gap-2 px-5 pb-5 first:pt-5 [[data-slot=dialog-header]+&]:pt-5"],
683
+ footer: [
684
+ "flex shrink-0 items-center justify-end",
685
+ ...stackedActions,
686
+ "gap-2 px-5 pb-5 first:pt-5 [[data-slot=dialog-header]+&]:pt-5",
687
+ "*:data-[slot=text-link]:text-footnote"
688
+ ],
689
+ footerNote: ["m-0 md:me-2 md:flex-1", "text-start font-sans text-footnote text-pretty text-foreground-muted md:whitespace-nowrap"],
690
+ footerActions: [
691
+ "ms-auto flex shrink-0 items-center justify-end gap-2",
692
+ ...stackedActions,
693
+ "data-stacked:ms-0 data-stacked:w-full"
694
+ ],
434
695
  title: ["m-0 min-w-0 text-foreground", "font-sans text-headline font-bold"],
435
696
  description: ["m-0", "font-sans text-subhead text-foreground-muted"]
436
697
  },
@@ -462,6 +723,10 @@ const dialog = tv$1({
462
723
  nested: {
463
724
  false: {},
464
725
  true: { overlay: ["bg-layer-backdrop-nested"] }
726
+ },
727
+ start: {
728
+ false: {},
729
+ true: { footer: ["flex-wrap justify-between"] }
465
730
  }
466
731
  },
467
732
  defaultVariants: {
@@ -502,6 +767,7 @@ function DialogOverlay({ className, layer, ...props }) {
502
767
  ...props
503
768
  });
504
769
  }
770
+ const DialogSizeContext = createContext(void 0);
505
771
  function DialogContent({ className, children, portalProps, overlay, size, layer, ...props }) {
506
772
  const { content } = dialog({
507
773
  size,
@@ -517,11 +783,15 @@ function DialogContent({ className, children, portalProps, overlay, size, layer,
517
783
  "data-slot": "dialog-content",
518
784
  className: content({ className }),
519
785
  ...props,
520
- children
786
+ children: /* @__PURE__ */ jsx(DialogSizeContext.Provider, {
787
+ value: size ?? (/(^|\s)max-w-/.test(className ?? "") ? void 0 : "md"),
788
+ children: /* @__PURE__ */ jsx(DialogSheetReset, { children })
789
+ })
521
790
  })]
522
791
  });
523
792
  }
524
793
  function DialogClose({ className, variant, size, children, render, "aria-label": ariaLabel, ...props }) {
794
+ const inSheetHead = useInDialogSheetHead();
525
795
  if (render) return /* @__PURE__ */ jsx(Dialog.Close, {
526
796
  "data-slot": "dialog-close",
527
797
  render,
@@ -529,6 +799,15 @@ function DialogClose({ className, variant, size, children, render, "aria-label":
529
799
  ...props,
530
800
  children
531
801
  });
802
+ if (inSheetHead) return /* @__PURE__ */ jsx(Dialog.Close, {
803
+ "data-slot": "dialog-close",
804
+ render: /* @__PURE__ */ jsx(PanelAction, {
805
+ "aria-label": ariaLabel ?? "Close",
806
+ className
807
+ }),
808
+ ...props,
809
+ children: children ?? /* @__PURE__ */ jsx(X, {})
810
+ });
532
811
  return /* @__PURE__ */ jsx(Dialog.Close, {
533
812
  "data-slot": "dialog-close",
534
813
  render: /* @__PURE__ */ jsx(Button$1, {
@@ -541,35 +820,149 @@ function DialogClose({ className, variant, size, children, render, "aria-label":
541
820
  children: children ?? /* @__PURE__ */ jsx(X, { className: "size-4.5" })
542
821
  });
543
822
  }
544
- function DialogHeader({ className, bar = false, ...props }) {
823
+ /**
824
+ * Back, in a later step of a flow drawn as a phone sheet: it takes the head's
825
+ * start in place of × (stdrc, 2026-10-22). The sheet is a stack of steps, and
826
+ * Back undoes one, as on a pushed page; Esc still closes. Put it in
827
+ * `DialogHeader` beside the `DialogClose`, from the second step on; it draws
828
+ * nothing outside a sheet, where Back stays in the footer. Its icon is
829
+ * `arrow-left`, as every Back (Alignment, "The ends of a header").
830
+ */
831
+ function DialogSheetBack({ className, children, "aria-label": ariaLabel, ...props }) {
832
+ if (!useInDialogSheetHead()) return null;
833
+ return /* @__PURE__ */ jsx(PanelAction, {
834
+ "data-slot": "dialog-sheet-back",
835
+ "aria-label": ariaLabel ?? "Back",
836
+ className,
837
+ ...props,
838
+ children: children ?? /* @__PURE__ */ jsx(ArrowLeft, {})
839
+ });
840
+ }
841
+ function DialogHeader({ className, bar = false, children, ...props }) {
842
+ const { sheet, setCommitSlot } = useDialogSheet();
843
+ const items = sheet ? Children.toArray(children) : [];
844
+ const backs = items.filter((item) => isValidElement(item) && item.type === DialogSheetBack);
845
+ const closes = items.filter((item) => isValidElement(item) && item.type === DialogClose);
846
+ const missingLeading = sheet && backs.length === 0 && closes.length === 0;
847
+ useEffect(() => {
848
+ if (process.env.NODE_ENV === "production" || !missingLeading) return;
849
+ console.warn("raft-ui: a DialogHeader drawn as a phone sheet has no DialogClose (or DialogSheetBack) as a direct child, so the sheet has no × at the start of its head. Put DialogClose directly in DialogHeader, not inside a Tooltip, a Fragment or a component of your own.");
850
+ }, [missingLeading]);
851
+ if (sheet) {
852
+ const rest = items.filter((item) => !backs.includes(item) && !closes.includes(item));
853
+ const head = dialogSheet();
854
+ return /* @__PURE__ */ jsxs("div", {
855
+ "data-slot": "dialog-header",
856
+ "data-sheet": "",
857
+ className: dialog({ bar: false }).header({ className: cn(head.header(), className) }),
858
+ ...props,
859
+ children: [/* @__PURE__ */ jsxs(DialogSheetHeadContext.Provider, {
860
+ value: true,
861
+ children: [backs.length > 0 ? backs : closes, rest]
862
+ }), /* @__PURE__ */ jsx("div", {
863
+ ref: setCommitSlot,
864
+ "data-slot": "dialog-commit-slot",
865
+ className: head.commitSlot()
866
+ })]
867
+ });
868
+ }
545
869
  const { header } = dialog({ bar });
546
870
  return /* @__PURE__ */ jsx("div", {
547
871
  "data-slot": "dialog-header",
548
872
  className: header({ className }),
549
- ...props
873
+ ...props,
874
+ children
550
875
  });
551
876
  }
552
- function DialogBody({ className, bar = false, ...props }) {
877
+ function DialogBody({ className, bar = false, children, ...props }) {
878
+ const { sheet, setBodyEndSlot } = useDialogSheet();
553
879
  const { body } = dialog({ bar });
554
- return /* @__PURE__ */ jsx("div", {
880
+ const sheetBody = sheet ? cn(bar && dialogSheet().barBody(), dialogSheet().body()) : void 0;
881
+ return /* @__PURE__ */ jsxs("div", {
555
882
  "data-slot": "dialog-body",
556
- className: body({ className }),
557
- ...props
883
+ className: body({ className: sheetBody ? cn(sheetBody, className) : className }),
884
+ ...props,
885
+ children: [children, sheet ? /* @__PURE__ */ jsx("div", {
886
+ ref: setBodyEndSlot,
887
+ "data-slot": "dialog-sheet-body-end",
888
+ className: dialogSheet().bodyEnd()
889
+ }) : null]
558
890
  });
559
891
  }
560
- function DialogFooter({ className, bar = false, ...props }) {
561
- const { footer } = dialog({ bar });
562
- return /* @__PURE__ */ jsx("div", {
892
+ function DialogFooter({ className, bar = false, hidden, children, ref, ...props }) {
893
+ const { sheet, bodyEndSlot } = useDialogSheet();
894
+ const items = Children.toArray(children);
895
+ const start = items.find((item) => isValidElement(item) && item.type === DialogFooterNote && hasNoteContent(item.props.children)) ?? (isValidElement(items[0]) && items[0].type === TextLink ? items[0] : void 0);
896
+ const [stacked, rowRef] = useStackedActions(start == null ? ref : void 0, start != null);
897
+ const { footer, footerActions } = dialog({
898
+ bar,
899
+ start: start != null
900
+ });
901
+ const stackedProps = stacked ? { "data-stacked": "" } : {};
902
+ if (start == null) return /* @__PURE__ */ jsx("div", {
563
903
  "data-slot": "dialog-footer",
904
+ ref: rowRef,
564
905
  className: footer({ className }),
565
- ...props
906
+ hidden: sheet || hidden,
907
+ ...stackedProps,
908
+ ...props,
909
+ children
910
+ });
911
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [sheet && bodyEndSlot ? createPortal(start, bodyEndSlot) : null, /* @__PURE__ */ jsxs("div", {
912
+ "data-slot": "dialog-footer",
913
+ ref,
914
+ className: footer({ className }),
915
+ hidden: sheet || hidden,
916
+ ...props,
917
+ children: [sheet ? null : start, /* @__PURE__ */ jsx("div", {
918
+ "data-slot": "dialog-footer-actions",
919
+ ref: rowRef,
920
+ className: footerActions(),
921
+ ...stackedProps,
922
+ children: items.filter((item) => item !== start)
923
+ })]
924
+ })] });
925
+ }
926
+ /**
927
+ * Whether a note has anything to show: not only null, false or "", but also
928
+ * whitespace and an empty array (`{items.map(...)}` of nothing). Numbers,
929
+ * 0 included, and elements count.
930
+ */
931
+ function hasNoteContent(children) {
932
+ return Children.toArray(children).some((child) => typeof child !== "string" || child.trim() !== "");
933
+ }
934
+ /**
935
+ * One short line at the start of `DialogFooter` that says what the commit
936
+ * button will do ("You'll be billed $8 per member each month"). Muted footnote
937
+ * text, start-aligned and never cut. Only in a `lg` or `wide` dialog, and at
938
+ * most 40 characters: on a desktop it is one line beside the buttons and never
939
+ * wraps; on a phone it goes above the buttons when they don't fit beside it,
940
+ * and may wrap to 2 lines. In a phone sheet, which has no footer, it moves
941
+ * to the end of the body. Longer copy belongs in the body. A footer holds a note or a `TextLink`,
942
+ * never both.
943
+ */
944
+ function DialogFooterNote({ className, children, ...props }) {
945
+ const { footerNote } = dialog();
946
+ const size = useContext(DialogSizeContext);
947
+ useEffect(() => {
948
+ if (process.env.NODE_ENV === "production") return;
949
+ if (size === "sm" || size === "md") console.warn(`raft-ui: a DialogFooterNote is in a size="${size}" dialog. A footer note fits beside the buttons only in a size="lg" or size="wide" dialog; put the sentence in DialogBody instead.`);
950
+ }, [size]);
951
+ if (!hasNoteContent(children)) return null;
952
+ return /* @__PURE__ */ jsx("p", {
953
+ "data-slot": "dialog-footer-note",
954
+ className: footerNote({ className }),
955
+ ...props,
956
+ children
566
957
  });
567
958
  }
568
959
  function DialogTitle({ className, ...props }) {
960
+ const inSheetHead = useInDialogSheetHead();
569
961
  const { title } = dialog();
962
+ const classes = inSheetHead ? cn(dialogSheet().title(), className) : className;
570
963
  return /* @__PURE__ */ jsx(Dialog.Title, {
571
964
  "data-slot": "dialog-title",
572
- className: title({ className }),
965
+ className: title({ className: classes }),
573
966
  ...props
574
967
  });
575
968
  }
@@ -593,7 +986,7 @@ const dialogSurface = tv$1({ slots: { root: [
593
986
  * animates itself: the host centres it, and a centred modal has no motion.
594
987
  * DialogContent draws the same frame for a Base UI dialog.
595
988
  */
596
- function DialogSurface({ className, render, ...props }) {
989
+ function DialogSurface({ className, render, children, ...props }) {
597
990
  const { root } = dialogSurface();
598
991
  return useRender({
599
992
  defaultTagName: "div",
@@ -601,7 +994,7 @@ function DialogSurface({ className, render, ...props }) {
601
994
  props: mergeProps({
602
995
  "data-slot": "dialog-surface",
603
996
  className: root({ className })
604
- }, props)
997
+ }, props, { children: /* @__PURE__ */ jsx(DialogSheetReset, { children }) })
605
998
  });
606
999
  }
607
1000
  const dialogNav = tv$1({ slots: { root: ["flex shrink-0 flex-col overflow-y-auto", "w-52 gap-0.5 border-r border-line-seam p-2 max-md:hidden"] } });
@@ -618,6 +1011,206 @@ function DialogNav({ className, render, ...props }) {
618
1011
  });
619
1012
  }
620
1013
  //#endregion
1014
+ //#region src/lib/use-mobile-viewport.ts
1015
+ const MOBILE_QUERY = "(max-width: 767px)";
1016
+ function subscribeMobileViewport(onStoreChange) {
1017
+ if (typeof window === "undefined") return () => void 0;
1018
+ const media = window.matchMedia(MOBILE_QUERY);
1019
+ media.addEventListener("change", onStoreChange);
1020
+ return () => media.removeEventListener("change", onStoreChange);
1021
+ }
1022
+ function getMobileViewportSnapshot() {
1023
+ return typeof window !== "undefined" && window.matchMedia(MOBILE_QUERY).matches;
1024
+ }
1025
+ function useMobileViewport() {
1026
+ return useSyncExternalStore(subscribeMobileViewport, getMobileViewportSnapshot, () => false);
1027
+ }
1028
+ //#endregion
1029
+ //#region src/components/dialog/dialog-sheet.tsx
1030
+ /**
1031
+ * DialogContent for a dialog with several fields, a long list or steps. On a
1032
+ * desktop and a tablet it is DialogContent, the same DOM and classes. Below
1033
+ * 768px it is a sheet: docked to the bottom, 90% of the screen tall, sliding
1034
+ * up over its scrim; `DialogHeader` becomes a pushed page's head (× at the
1035
+ * start, the title, the `DialogCommit` button at the end), `DialogFooter` is
1036
+ * hidden (× is Cancel) and `DialogBody` scrolls on the phone's 16px margin
1037
+ * above the home indicator. Base UI keeps Esc, focus and close.
1038
+ */
1039
+ function DialogSheetContent({ className, overlay, style, ref, children, ...props }) {
1040
+ const phone = useMobileViewport();
1041
+ const [commitSlot, setCommitSlot] = useState(null);
1042
+ const [bodyEndSlot, setBodyEndSlot] = useState(null);
1043
+ const [popup, setPopup] = useState(null);
1044
+ const keyboardBox = useSheetKeyboardBox(phone);
1045
+ useRevealNewProblems(phone ? popup : null);
1046
+ const state = useMemo(() => ({
1047
+ sheet: phone,
1048
+ commitSlot: phone ? commitSlot : null,
1049
+ setCommitSlot,
1050
+ bodyEndSlot: phone ? bodyEndSlot : null,
1051
+ setBodyEndSlot
1052
+ }), [
1053
+ phone,
1054
+ commitSlot,
1055
+ bodyEndSlot
1056
+ ]);
1057
+ const setRef = useCallback((element) => {
1058
+ setPopup(element);
1059
+ assignRef$1(ref, element);
1060
+ }, [ref]);
1061
+ const sheet = dialogSheet();
1062
+ const sheetOverlay = useMemo(() => overlay === false ? false : {
1063
+ ...typeof overlay === "object" ? overlay : null,
1064
+ className: cn(dialogSheet().overlay(), typeof overlay === "object" ? overlay.className : ""),
1065
+ children: /* @__PURE__ */ jsx("span", {
1066
+ "aria-hidden": true,
1067
+ className: "absolute inset-0"
1068
+ })
1069
+ }, [overlay]);
1070
+ const sheetStyle = useMemo(() => keyboardBox ? {
1071
+ ...style,
1072
+ ...keyboardBox
1073
+ } : style, [style, keyboardBox]);
1074
+ return /* @__PURE__ */ jsx(DialogContent, {
1075
+ ...props,
1076
+ ref: setRef,
1077
+ overlay: phone ? sheetOverlay : overlay,
1078
+ className: phone ? cn(className, sheet.content()) : className,
1079
+ style: phone ? sheetStyle : style,
1080
+ "data-sheet": phone ? "" : void 0,
1081
+ children: /* @__PURE__ */ jsx(DialogSheetContext.Provider, {
1082
+ value: state,
1083
+ children
1084
+ })
1085
+ });
1086
+ }
1087
+ function assignRef$1(ref, value) {
1088
+ if (typeof ref === "function") ref(value);
1089
+ else if (ref) ref.current = value;
1090
+ }
1091
+ /**
1092
+ * The dialog's one commit button: a `Button`, written where it goes on a
1093
+ * desktop (the end of `DialogFooter`). In a `DialogSheetContent` below 768px
1094
+ * it is drawn at the end of the sheet's head instead, the same button (color,
1095
+ * words, disabled and loading state) at `size="md"`. In a flow of steps, write
1096
+ * each step's button as one (Next, then the last step's action).
1097
+ *
1098
+ * A submit button stays its form's default button in the head: it is tied to
1099
+ * the form it was written in by the form's `id` (the `form` attribute), so a
1100
+ * click and Enter in a field submit, and neither does while it is disabled.
1101
+ * Give that form an `id`, or pass `form`. Without one a click still submits
1102
+ * the form, but Enter in a field doesn't.
1103
+ */
1104
+ function DialogCommit({ size, form, onClick, ...props }) {
1105
+ const { commitSlot } = useDialogSheet();
1106
+ const anchorRef = useRef(null);
1107
+ const [writtenIn, setWrittenIn] = useState(null);
1108
+ useLayoutEffect(() => {
1109
+ if (!commitSlot || form) return;
1110
+ const found = anchorRef.current?.closest("form") ?? null;
1111
+ setWrittenIn(found);
1112
+ if (process.env.NODE_ENV === "production" || props.type !== "submit" || found?.id) return;
1113
+ console.warn(found ? "raft-ui: a submit DialogCommit is drawn in a phone sheet's head, outside its form, and the form has no id, so Enter in a field doesn't submit it (a click still does). Give the form an id, or pass `form` to DialogCommit." : "raft-ui: a submit DialogCommit in a phone sheet isn't inside a form and has no `form`, so it submits nothing. Pass `form` with the form's id.");
1114
+ }, [
1115
+ commitSlot,
1116
+ form,
1117
+ props.type
1118
+ ]);
1119
+ const handleClick = useCallback((event) => {
1120
+ onClick?.(event);
1121
+ if (!event.defaultPrevented && props.type === "submit" && writtenIn && !writtenIn.id) {
1122
+ event.preventDefault();
1123
+ writtenIn.requestSubmit();
1124
+ }
1125
+ }, [
1126
+ onClick,
1127
+ props.type,
1128
+ writtenIn
1129
+ ]);
1130
+ if (!commitSlot) return /* @__PURE__ */ jsx(Button$1, {
1131
+ size,
1132
+ form,
1133
+ onClick,
1134
+ ...props
1135
+ });
1136
+ const linkedForm = form ?? (writtenIn?.id || void 0);
1137
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
1138
+ ref: anchorRef,
1139
+ hidden: true
1140
+ }), createPortal(/* @__PURE__ */ jsx(Button$1, {
1141
+ size: "md",
1142
+ form: linkedForm,
1143
+ onClick: handleClick,
1144
+ ...props
1145
+ }), commitSlot)] });
1146
+ }
1147
+ /**
1148
+ * The sheet's box while the on-screen keyboard is up: docked to the top of the
1149
+ * keyboard, as tall as the visual viewport allows up to its usual 90% of the
1150
+ * window. With the keyboard up the visual viewport is shorter than that, so
1151
+ * the sheet fills it: the head stays in view and the body scrolls above the
1152
+ * keyboard. `undefined` otherwise (the CSS box).
1153
+ */
1154
+ function useSheetKeyboardBox(active) {
1155
+ const [box, setBox] = useState(void 0);
1156
+ useEffect(() => {
1157
+ const viewport = typeof window === "undefined" ? void 0 : window.visualViewport;
1158
+ if (!active || !viewport) return;
1159
+ const update = () => {
1160
+ const focused = document.activeElement;
1161
+ const keyboardUp = (focused instanceof HTMLInputElement || focused instanceof HTMLTextAreaElement || focused instanceof HTMLElement && focused.isContentEditable) && window.innerHeight - viewport.height > 100;
1162
+ const height = Math.round(Math.min(viewport.height, window.innerHeight * .9));
1163
+ const top = Math.round(Math.max(viewport.offsetTop, 0) + viewport.height - height);
1164
+ setBox((previous) => {
1165
+ if (!keyboardUp) return void 0;
1166
+ return previous?.top === `${top}px` && previous?.height === `${height}px` ? previous : {
1167
+ top: `${top}px`,
1168
+ bottom: "auto",
1169
+ height: `${height}px`
1170
+ };
1171
+ });
1172
+ };
1173
+ viewport.addEventListener("resize", update);
1174
+ viewport.addEventListener("scroll", update, { passive: true });
1175
+ document.addEventListener("focusin", update);
1176
+ document.addEventListener("focusout", update);
1177
+ return () => {
1178
+ viewport.removeEventListener("resize", update);
1179
+ viewport.removeEventListener("scroll", update);
1180
+ document.removeEventListener("focusin", update);
1181
+ document.removeEventListener("focusout", update);
1182
+ };
1183
+ }, [active]);
1184
+ return active ? box : void 0;
1185
+ }
1186
+ /**
1187
+ * The commit is at the top of a sheet, so an error a failed commit adds near
1188
+ * the bottom of a long form would appear out of sight. A problem added after
1189
+ * the sheet opens (an alert, or a warning or destructive Banner) is scrolled
1190
+ * into view.
1191
+ */
1192
+ const PROBLEM_SELECTOR = "[role=alert], [data-slot=banner][data-status=warning], [data-slot=banner][data-status=destructive]";
1193
+ function useRevealNewProblems(popup) {
1194
+ useEffect(() => {
1195
+ if (!popup || typeof MutationObserver === "undefined") return;
1196
+ const observer = new MutationObserver((records) => {
1197
+ for (const record of records) for (const node of record.addedNodes) {
1198
+ if (!(node instanceof Element)) continue;
1199
+ const problem = node.matches(PROBLEM_SELECTOR) ? node : node.querySelector(PROBLEM_SELECTOR);
1200
+ if (problem) {
1201
+ problem.scrollIntoView?.({ block: "nearest" });
1202
+ return;
1203
+ }
1204
+ }
1205
+ });
1206
+ observer.observe(popup, {
1207
+ childList: true,
1208
+ subtree: true
1209
+ });
1210
+ return () => observer.disconnect();
1211
+ }, [popup]);
1212
+ }
1213
+ //#endregion
621
1214
  //#region src/components/badge/badge.tsx
622
1215
  const badge = tv$1({
623
1216
  slots: { root: [
@@ -799,36 +1392,6 @@ function Experimental(props) {
799
1392
  }
800
1393
  const Badge = Object.assign(BadgeRoot, { Experimental });
801
1394
  //#endregion
802
- //#region src/components/text-link/text-link.tsx
803
- const textLinkVariants = tv$1({
804
- base: [
805
- "rounded-[2px]",
806
- "decoration-[1.5px] underline-offset-[3px] not-disabled:hover:underline",
807
- "outline-none focus-visible:shadow-focus",
808
- "disabled:cursor-not-allowed disabled:text-foreground-disabled"
809
- ],
810
- variants: { variant: {
811
- muted: ["font-medium text-foreground-muted", "hover:text-foreground"],
812
- strong: ["font-semibold text-foreground"]
813
- } },
814
- defaultVariants: { variant: "muted" }
815
- });
816
- function TextLink({ variant, className, render, ...props }) {
817
- const defaultProps = {
818
- className: textLinkVariants({
819
- variant,
820
- className
821
- }),
822
- ...render ? {} : { type: "button" }
823
- };
824
- return useRender({
825
- defaultTagName: "button",
826
- render,
827
- props: mergeProps(defaultProps, props),
828
- state: { slot: "text-link" }
829
- });
830
- }
831
- //#endregion
832
1395
  //#region src/components/tooltip/tooltip.tsx
833
1396
  const tooltip = tv$1({
834
1397
  slots: {
@@ -1004,32 +1567,257 @@ function TooltipContent({ align = "center", alignOffset = 0, anchor, arrowPaddin
1004
1567
  });
1005
1568
  }
1006
1569
  //#endregion
1007
- //#region src/lib/presence.ts
1008
- /**
1009
- * The five status lights of the design system. Each colour is a fixed
1010
- * meaning: online green, busy (an agent at work) yellow, error orange,
1011
- * offline stone, an external agent cyan. One map, read by Status and by the
1012
- * avatar's dot through the same `--status-color`.
1013
- */
1014
- const PRESENCE_COLOR = {
1015
- online: "[--status-color:var(--presence-online)]",
1016
- busy: "[--status-color:var(--presence-busy)]",
1017
- error: "[--status-color:var(--presence-error)]",
1018
- offline: "[--status-color:var(--presence-offline)]",
1019
- external: "[--status-color:var(--presence-external)]"
1020
- };
1021
- //#endregion
1022
- //#region src/components/checkbox/checkbox-indicator.tsx
1023
- const checkboxIndicator = tv$1({
1024
- slots: { root: [
1025
- "inline-flex size-5 shrink-0 items-center justify-center",
1026
- "rounded-sticker border-[1.5px] border-line-strong transition-colors",
1027
- "[&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5 [&_svg]:stroke-2"
1028
- ] },
1570
+ //#region src/components/list-row/list-row.recipe.ts
1571
+ const hairline = [
1572
+ "before:pointer-events-none before:absolute before:top-0 before:right-0 before:hidden",
1573
+ "before:left-(--list-row-rule-start) before:border-t before:border-line-hairline",
1574
+ "[[data-slot=list-row]+&]:before:block [li+li>&]:before:block"
1575
+ ];
1576
+ const listRow = tv$1({
1577
+ slots: {
1578
+ group: ["flex min-w-0 flex-col overflow-hidden", "rounded-container border border-line-hairline bg-layer-card text-foreground"],
1579
+ root: [
1580
+ "group/list-row relative flex w-full min-w-0 items-center gap-3 px-4 py-3 text-left",
1581
+ "font-sans no-underline",
1582
+ "transition-colors duration-100",
1583
+ "outline-none focus-visible:keyboard:inset-ring-2 focus-visible:keyboard:inset-ring-foreground",
1584
+ "data-selected:bg-info-soft",
1585
+ "[--list-row-ink:var(--foreground)] [--list-row-ink-2:var(--foreground-muted)] [--list-row-ink-3:var(--foreground-hint)]",
1586
+ "text-(--list-row-ink)",
1587
+ "not-disabled:not-aria-disabled:data-[tone=unread]:[--list-row-ink-2:var(--foreground)]",
1588
+ "not-disabled:not-aria-disabled:data-[tone=muted]:[--list-row-ink:var(--foreground-muted)]",
1589
+ "not-disabled:not-aria-disabled:data-[tone=away]:[--list-row-ink:var(--foreground-hint)]",
1590
+ "not-disabled:not-aria-disabled:data-[tone=away]:[--list-row-ink-2:var(--foreground-hint)]",
1591
+ "disabled:cursor-not-allowed disabled:[--list-row-ink:var(--foreground-disabled)]",
1592
+ "disabled:[--list-row-ink-2:var(--foreground-disabled)] disabled:[--list-row-ink-3:var(--foreground-disabled)]",
1593
+ "aria-disabled:cursor-not-allowed aria-disabled:[--list-row-ink:var(--foreground-disabled)]",
1594
+ "aria-disabled:[--list-row-ink-2:var(--foreground-disabled)] aria-disabled:[--list-row-ink-3:var(--foreground-disabled)]"
1595
+ ],
1596
+ icon: ["flex shrink-0 items-center justify-center", "text-(--list-row-ink-3) [&>svg]:size-5 [&>svg]:shrink-0"],
1597
+ body: ["flex min-w-0 flex-1 flex-col gap-0.5"],
1598
+ line: ["flex min-w-0 items-center gap-2"],
1599
+ title: [
1600
+ "min-w-0 flex-1 truncate",
1601
+ "text-body font-semibold",
1602
+ "group-data-[tone=unread]/list-row:font-bold"
1603
+ ],
1604
+ description: ["min-w-0 flex-1 truncate", "text-callout text-(--list-row-ink-2)"],
1605
+ meta: ["shrink-0", "font-mono text-mono-meta font-normal tabular-nums text-(--list-row-ink-3)"],
1606
+ value: ["max-w-[45%] shrink-0 truncate", "text-subhead text-(--list-row-ink-3)"],
1607
+ chevron: ["flex shrink-0 items-center justify-center", "text-(--list-row-ink-3) [&>svg]:size-5 [&>svg]:shrink-0"]
1608
+ },
1029
1609
  variants: {
1030
- shape: {
1031
- square: "",
1032
- circle: { root: "rounded-full" }
1610
+ variant: {
1611
+ default: { root: ["min-h-16 [--list-row-rule-start:63px]", hairline] },
1612
+ plain: { root: ["[--list-row-rule-start:16px]", hairline] },
1613
+ entry: { root: "" }
1614
+ },
1615
+ interactive: {
1616
+ true: { root: [
1617
+ "not-disabled:not-aria-disabled:not-data-selected:hover:overlay-subtle",
1618
+ "not-disabled:not-aria-disabled:not-data-selected:active:overlay-medium",
1619
+ "not-data-selected:data-popup-open:overlay-subtle"
1620
+ ] },
1621
+ false: {}
1622
+ },
1623
+ wrap: {
1624
+ true: { description: "whitespace-normal text-pretty" },
1625
+ false: {}
1626
+ }
1627
+ },
1628
+ defaultVariants: {
1629
+ variant: "default",
1630
+ interactive: true,
1631
+ wrap: false
1632
+ }
1633
+ });
1634
+ //#endregion
1635
+ //#region src/components/list-row/list-row.tsx
1636
+ /** An inset group: rows wrapped in a card (surface, hairline, radius-lg). */
1637
+ function ListRowGroup({ className, render, ...props }) {
1638
+ const { group } = listRow();
1639
+ const defaultProps = {
1640
+ "data-slot": "list-row-group",
1641
+ className: group({ className })
1642
+ };
1643
+ return useRender({
1644
+ defaultTagName: "div",
1645
+ render,
1646
+ props: mergeProps(defaultProps, props)
1647
+ });
1648
+ }
1649
+ /**
1650
+ * One row of a list. It renders a `button` by default; pass `render={<a />}`
1651
+ * (or a router link) for a row that goes somewhere. Children are the leading
1652
+ * block (an avatar, a channel or icon tile, `ListRowIcon`), `ListRowBody`, and
1653
+ * anything trailing (a value, a badge, a switch, `ListRowChevron`).
1654
+ */
1655
+ function ListRow({ variant = "default", tone = "default", selected = false, interactive = true, className, render, ...props }) {
1656
+ const { root } = listRow({
1657
+ variant,
1658
+ interactive
1659
+ });
1660
+ const defaultProps = {
1661
+ "data-slot": "list-row",
1662
+ "data-variant": variant,
1663
+ "data-tone": tone === "default" ? void 0 : tone,
1664
+ "data-selected": selected ? "" : void 0,
1665
+ className: root({ className }),
1666
+ ...interactive && !render ? { type: "button" } : {}
1667
+ };
1668
+ return useRender({
1669
+ defaultTagName: interactive ? "button" : "div",
1670
+ render,
1671
+ props: mergeProps(defaultProps, props)
1672
+ });
1673
+ }
1674
+ /** A 20px leading icon in ink-3: entry rows and settings. */
1675
+ function ListRowIcon({ className, render, ...props }) {
1676
+ const { icon } = listRow();
1677
+ const defaultProps = {
1678
+ "data-slot": "list-row-icon",
1679
+ "aria-hidden": true,
1680
+ className: icon({ className })
1681
+ };
1682
+ return useRender({
1683
+ defaultTagName: "span",
1684
+ render,
1685
+ props: mergeProps(defaultProps, props)
1686
+ });
1687
+ }
1688
+ /** The text column: the title and the preview, or a `ListRowTop` and a `ListRowBottom`. */
1689
+ function ListRowBody({ className, render, ...props }) {
1690
+ const { body } = listRow();
1691
+ const defaultProps = {
1692
+ "data-slot": "list-row-body",
1693
+ className: body({ className })
1694
+ };
1695
+ return useRender({
1696
+ defaultTagName: "span",
1697
+ render,
1698
+ props: mergeProps(defaultProps, props)
1699
+ });
1700
+ }
1701
+ /** The first line: the title, then the time. */
1702
+ function ListRowTop({ className, render, ...props }) {
1703
+ const { line } = listRow();
1704
+ const defaultProps = {
1705
+ "data-slot": "list-row-top",
1706
+ className: line({ className })
1707
+ };
1708
+ return useRender({
1709
+ defaultTagName: "span",
1710
+ render,
1711
+ props: mergeProps(defaultProps, props)
1712
+ });
1713
+ }
1714
+ /** The second line: the preview, then at most one badge. */
1715
+ function ListRowBottom({ className, render, ...props }) {
1716
+ const { line } = listRow();
1717
+ const defaultProps = {
1718
+ "data-slot": "list-row-bottom",
1719
+ className: line({ className })
1720
+ };
1721
+ return useRender({
1722
+ defaultTagName: "span",
1723
+ render,
1724
+ props: mergeProps(defaultProps, props)
1725
+ });
1726
+ }
1727
+ /** 16/24 600 on one line; the row's tone sets its weight and ink. */
1728
+ function ListRowTitle({ className, render, ...props }) {
1729
+ const { title } = listRow();
1730
+ const defaultProps = {
1731
+ "data-slot": "list-row-title",
1732
+ className: title({ className })
1733
+ };
1734
+ return useRender({
1735
+ defaultTagName: "span",
1736
+ render,
1737
+ props: mergeProps(defaultProps, props)
1738
+ });
1739
+ }
1740
+ /** The preview (who spoke first: "Wug: …") or a setting's consequence, 14/20 ink-2. */
1741
+ function ListRowDescription({ className, render, wrap = false, ...props }) {
1742
+ const { description } = listRow({ wrap });
1743
+ const defaultProps = {
1744
+ "data-slot": "list-row-description",
1745
+ className: description({ className })
1746
+ };
1747
+ return useRender({
1748
+ defaultTagName: "span",
1749
+ render,
1750
+ props: mergeProps(defaultProps, props)
1751
+ });
1752
+ }
1753
+ /** Machine-written facts in Space Mono 12, ink-3: the time, a gray count. */
1754
+ function ListRowMeta({ className, render, ...props }) {
1755
+ const { meta } = listRow();
1756
+ const defaultProps = {
1757
+ "data-slot": "list-row-meta",
1758
+ className: meta({ className })
1759
+ };
1760
+ return useRender({
1761
+ defaultTagName: "span",
1762
+ render,
1763
+ props: mergeProps(defaultProps, props)
1764
+ });
1765
+ }
1766
+ /** A setting's current value on the right, 15/20 ink-3, at most 45% of the row. */
1767
+ function ListRowValue({ className, render, ...props }) {
1768
+ const { value } = listRow();
1769
+ const defaultProps = {
1770
+ "data-slot": "list-row-value",
1771
+ className: value({ className })
1772
+ };
1773
+ return useRender({
1774
+ defaultTagName: "span",
1775
+ render,
1776
+ props: mergeProps(defaultProps, props)
1777
+ });
1778
+ }
1779
+ /** The ink-3 arrow of a row that opens another page. */
1780
+ function ListRowChevron({ className, render, ...props }) {
1781
+ const { chevron } = listRow();
1782
+ const defaultProps = {
1783
+ "data-slot": "list-row-chevron",
1784
+ "aria-hidden": true,
1785
+ className: chevron({ className }),
1786
+ children: /* @__PURE__ */ jsx(ChevronRight, {})
1787
+ };
1788
+ return useRender({
1789
+ defaultTagName: "span",
1790
+ render,
1791
+ props: mergeProps(defaultProps, props)
1792
+ });
1793
+ }
1794
+ //#endregion
1795
+ //#region src/lib/presence.ts
1796
+ /**
1797
+ * The five status lights of the design system. Each colour is a fixed
1798
+ * meaning: online green, busy (an agent at work) yellow, error orange,
1799
+ * offline stone, an external agent cyan. One map, read by Status and by the
1800
+ * avatar's dot through the same `--status-color`.
1801
+ */
1802
+ const PRESENCE_COLOR = {
1803
+ online: "[--status-color:var(--presence-online)]",
1804
+ busy: "[--status-color:var(--presence-busy)]",
1805
+ error: "[--status-color:var(--presence-error)]",
1806
+ offline: "[--status-color:var(--presence-offline)]",
1807
+ external: "[--status-color:var(--presence-external)]"
1808
+ };
1809
+ //#endregion
1810
+ //#region src/components/checkbox/checkbox-indicator.tsx
1811
+ const checkboxIndicator = tv$1({
1812
+ slots: { root: [
1813
+ "inline-flex size-5 shrink-0 items-center justify-center",
1814
+ "rounded-sticker border-[1.5px] border-line-strong transition-colors",
1815
+ "[&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5 [&_svg]:stroke-2"
1816
+ ] },
1817
+ variants: {
1818
+ shape: {
1819
+ square: "",
1820
+ circle: { root: "rounded-full" }
1033
1821
  },
1034
1822
  checked: {
1035
1823
  true: { root: "border-foreground bg-foreground text-foreground-inverse" },
@@ -1110,21 +1898,6 @@ function Checkbox$1({ shape = "square", className, ...props }) {
1110
1898
  });
1111
1899
  }
1112
1900
  //#endregion
1113
- //#region src/lib/use-mobile-viewport.ts
1114
- const MOBILE_QUERY = "(max-width: 767px)";
1115
- function subscribeMobileViewport(onStoreChange) {
1116
- if (typeof window === "undefined") return () => void 0;
1117
- const media = window.matchMedia(MOBILE_QUERY);
1118
- media.addEventListener("change", onStoreChange);
1119
- return () => media.removeEventListener("change", onStoreChange);
1120
- }
1121
- function getMobileViewportSnapshot() {
1122
- return typeof window !== "undefined" && window.matchMedia(MOBILE_QUERY).matches;
1123
- }
1124
- function useMobileViewport() {
1125
- return useSyncExternalStore(subscribeMobileViewport, getMobileViewportSnapshot, () => false);
1126
- }
1127
- //#endregion
1128
1901
  //#region src/components/scroll-area/scroll-area.recipe.ts
1129
1902
  const scrollArea = tv$1({ slots: {
1130
1903
  root: ["relative min-h-0"],
@@ -1403,44 +2176,926 @@ function DropdownMenu(props) {
1403
2176
  ...props
1404
2177
  });
1405
2178
  }
1406
- function DropdownMenuSubmenu(props) {
1407
- return /* @__PURE__ */ jsx(Menu.SubmenuRoot, {
1408
- "data-slot": "dropdown-menu-submenu",
2179
+ function DropdownMenuSubmenu(props) {
2180
+ return /* @__PURE__ */ jsx(Menu.SubmenuRoot, {
2181
+ "data-slot": "dropdown-menu-submenu",
2182
+ ...props
2183
+ });
2184
+ }
2185
+ function DropdownMenuTrigger({ className, render = /* @__PURE__ */ jsx(Button$1, {
2186
+ variant: "outline",
2187
+ size: "md"
2188
+ }), ...props }) {
2189
+ const { trigger } = dropdownMenu();
2190
+ return /* @__PURE__ */ jsx(Menu.Trigger, {
2191
+ "data-slot": "dropdown-menu-trigger",
2192
+ render,
2193
+ className: trigger({ className }),
2194
+ ...props
2195
+ });
2196
+ }
2197
+ function DropdownMenuPortal(props) {
2198
+ return /* @__PURE__ */ jsx(Menu.Portal, {
2199
+ "data-slot": "dropdown-menu-portal",
2200
+ ...useThemeScopeProps(props)
2201
+ });
2202
+ }
2203
+ function DropdownMenuPopup({ className, ...props }) {
2204
+ const { content } = dropdownMenu();
2205
+ return /* @__PURE__ */ jsx("div", {
2206
+ "data-slot": "dropdown-menu-content",
2207
+ className: content({ className }),
2208
+ ...props
2209
+ });
2210
+ }
2211
+ function DropdownMenuContent({ align = "start", alignOffset = 0, anchor, arrowPadding, collisionAvoidance, collisionBoundary, collisionPadding, disableAnchorTracking, positionMethod = "fixed", side = "bottom", sideOffset, sticky, portalProps, className, ...props }) {
2212
+ const resolvedSideOffset = sideOffset ?? 6;
2213
+ const { positioner, content } = dropdownMenu();
2214
+ return /* @__PURE__ */ jsx(DropdownMenuPortal, {
2215
+ ...portalProps,
2216
+ children: /* @__PURE__ */ jsx(Menu.Positioner, {
2217
+ align,
2218
+ alignOffset,
2219
+ anchor,
2220
+ arrowPadding,
2221
+ collisionAvoidance,
2222
+ collisionBoundary,
2223
+ collisionPadding,
2224
+ disableAnchorTracking,
2225
+ positionMethod,
2226
+ side,
2227
+ sideOffset: resolvedSideOffset,
2228
+ sticky,
2229
+ className: positioner(),
2230
+ children: /* @__PURE__ */ jsx(Menu.Popup, {
2231
+ "data-slot": "dropdown-menu-content",
2232
+ className: content({ className }),
2233
+ ...props
2234
+ })
2235
+ })
2236
+ });
2237
+ }
2238
+ function DropdownMenuItem({ className, ...props }) {
2239
+ const { item } = dropdownMenu();
2240
+ return /* @__PURE__ */ jsx(Menu.Item, {
2241
+ "data-slot": "dropdown-menu-item",
2242
+ className: item({ className }),
2243
+ ...props
2244
+ });
2245
+ }
2246
+ function DropdownMenuItemLabel({ className, ...props }) {
2247
+ const { itemLabel } = dropdownMenu();
2248
+ return /* @__PURE__ */ jsx("span", {
2249
+ "data-slot": "dropdown-menu-item-label",
2250
+ className: itemLabel({ className }),
2251
+ ...props
2252
+ });
2253
+ }
2254
+ function DropdownMenuFooter({ className, ...props }) {
2255
+ const { footer } = dropdownMenu();
2256
+ return /* @__PURE__ */ jsx("div", {
2257
+ "data-slot": "dropdown-menu-footer",
2258
+ className: footer({ className }),
2259
+ ...props
2260
+ });
2261
+ }
2262
+ function DropdownMenuSubmenuTrigger({ children, className, ...props }) {
2263
+ const { submenuTrigger, indicator } = dropdownMenu();
2264
+ return /* @__PURE__ */ jsxs(Menu.SubmenuTrigger, {
2265
+ "data-slot": "dropdown-menu-submenu-trigger",
2266
+ className: submenuTrigger({ className }),
2267
+ ...props,
2268
+ children: [children, /* @__PURE__ */ jsx("span", {
2269
+ className: indicator(),
2270
+ "data-slot": "dropdown-menu-submenu-trigger-indicator",
2271
+ "aria-hidden": "true",
2272
+ children: /* @__PURE__ */ jsx(DropdownMenuSubmenuChevronIcon, {})
2273
+ })]
2274
+ });
2275
+ }
2276
+ function DropdownMenuLabel({ className, ...props }) {
2277
+ const { label } = dropdownMenu();
2278
+ return /* @__PURE__ */ jsx("div", {
2279
+ "data-slot": "dropdown-menu-label",
2280
+ className: label({ className }),
2281
+ ...props
2282
+ });
2283
+ }
2284
+ function DropdownMenuShortcut({ className, ...props }) {
2285
+ const { shortcut } = dropdownMenu();
2286
+ return /* @__PURE__ */ jsx(Kbd, {
2287
+ "data-slot": "dropdown-menu-shortcut",
2288
+ className: shortcut({ className }),
2289
+ ...props
2290
+ });
2291
+ }
2292
+ function DropdownMenuItemCount({ className, ...props }) {
2293
+ const { itemCount } = dropdownMenu();
2294
+ return /* @__PURE__ */ jsx(Badge, {
2295
+ "data-slot": "dropdown-menu-item-count",
2296
+ className: itemCount({ className }),
2297
+ ...props
2298
+ });
2299
+ }
2300
+ function DropdownMenuSeparator({ className, ...props }) {
2301
+ const { separator } = dropdownMenu();
2302
+ return /* @__PURE__ */ jsx(Menu.Separator, {
2303
+ "data-slot": "dropdown-menu-separator",
2304
+ className: separator({ className }),
2305
+ ...props
2306
+ });
2307
+ }
2308
+ function DropdownMenuSubmenuChevronIcon() {
2309
+ return /* @__PURE__ */ jsx("svg", {
2310
+ width: "12",
2311
+ height: "12",
2312
+ viewBox: "0 0 12 12",
2313
+ "aria-hidden": true,
2314
+ className: "size-3 shrink-0",
2315
+ children: /* @__PURE__ */ jsx("g", {
2316
+ fill: "none",
2317
+ strokeLinecap: "round",
2318
+ strokeLinejoin: "round",
2319
+ strokeWidth: "1",
2320
+ stroke: "currentColor",
2321
+ children: /* @__PURE__ */ jsx("path", { d: "M4.25 2.5L7.75 6l-3.5 3.5" })
2322
+ })
2323
+ });
2324
+ }
2325
+ function DropdownMenuCheckIcon() {
2326
+ return /* @__PURE__ */ jsx("svg", {
2327
+ width: "12",
2328
+ height: "12",
2329
+ viewBox: "0 0 12 12",
2330
+ "aria-hidden": true,
2331
+ className: "size-3 shrink-0",
2332
+ children: /* @__PURE__ */ jsx("g", {
2333
+ fill: "none",
2334
+ strokeLinecap: "round",
2335
+ strokeLinejoin: "round",
2336
+ strokeWidth: "1",
2337
+ stroke: "currentColor",
2338
+ children: /* @__PURE__ */ jsx("path", { d: "M1.75 6.41L4.4 9.25L10.25 2.99" })
2339
+ })
2340
+ });
2341
+ }
2342
+ function DropdownMenuRadioGroup({ ...props }) {
2343
+ return /* @__PURE__ */ jsx(Menu.RadioGroup, {
2344
+ "data-slot": "dropdown-menu-radio-group",
2345
+ ...props
2346
+ });
2347
+ }
2348
+ function DropdownMenuRadioItem({ className, children, ...props }) {
2349
+ const { radioItem, radioLabel, indicator } = dropdownMenu();
2350
+ return /* @__PURE__ */ jsxs(Menu.RadioItem, {
2351
+ "data-slot": "dropdown-menu-radio-item",
2352
+ className: radioItem({ className }),
2353
+ ...props,
2354
+ children: [renderRadioItemChildren(children, radioLabel()), /* @__PURE__ */ jsx("span", {
2355
+ className: indicator(),
2356
+ children: /* @__PURE__ */ jsx(Menu.RadioItemIndicator, { children: /* @__PURE__ */ jsx(DropdownMenuCheckIcon, {}) })
2357
+ })]
2358
+ });
2359
+ }
2360
+ function renderRadioItemChildren(children, className) {
2361
+ if (typeof children === "string" || typeof children === "number") return /* @__PURE__ */ jsx("span", {
2362
+ "data-slot": "dropdown-menu-radio-item-label",
2363
+ className,
2364
+ children
2365
+ });
2366
+ if (Array.isArray(children) && children.every((child) => typeof child === "string")) return /* @__PURE__ */ jsx("span", {
2367
+ "data-slot": "dropdown-menu-radio-item-label",
2368
+ className,
2369
+ children
2370
+ });
2371
+ return children;
2372
+ }
2373
+ function DropdownMenuRadioItemIndicator({ className, ...props }) {
2374
+ return /* @__PURE__ */ jsx(Menu.RadioItemIndicator, {
2375
+ "data-slot": "dropdown-menu-radio-item-indicator",
2376
+ className,
2377
+ ...props
2378
+ });
2379
+ }
2380
+ function DropdownMenuCheckboxItem({ className, children, ...props }) {
2381
+ const { item, indicator } = dropdownMenu();
2382
+ return /* @__PURE__ */ jsxs(Menu.CheckboxItem, {
2383
+ "data-slot": "dropdown-menu-checkbox-item",
2384
+ className: item({ className }),
2385
+ ...props,
2386
+ children: [children, /* @__PURE__ */ jsx("span", {
2387
+ className: indicator(),
2388
+ children: /* @__PURE__ */ jsx(Menu.CheckboxItemIndicator, { children: /* @__PURE__ */ jsx(DropdownMenuCheckIcon, {}) })
2389
+ })]
2390
+ });
2391
+ }
2392
+ function DropdownMenuCheckboxItemIndicator({ className, ...props }) {
2393
+ return /* @__PURE__ */ jsx(Menu.CheckboxItemIndicator, {
2394
+ "data-slot": "dropdown-menu-checkbox-item-indicator",
2395
+ className,
2396
+ ...props
2397
+ });
2398
+ }
2399
+ /**
2400
+ * One choice that is another page, such as the same page in another
2401
+ * language: a link, so it opens in a new tab and can be copied. Its
2402
+ * `href` goes on the item; `current` marks the page being shown.
2403
+ */
2404
+ function DropdownMenuLinkItem({ current = false, className, children, ...props }) {
2405
+ const { radioItem, radioLabel, indicator } = dropdownMenu();
2406
+ return /* @__PURE__ */ jsxs(Menu.LinkItem, {
2407
+ "data-slot": "dropdown-menu-link-item",
2408
+ "aria-current": current ? "page" : void 0,
2409
+ "data-checked": current ? "" : void 0,
2410
+ className: radioItem({ className }),
2411
+ ...props,
2412
+ children: [renderRadioItemChildren(children, radioLabel()), current ? /* @__PURE__ */ jsx("span", {
2413
+ className: indicator(),
2414
+ children: /* @__PURE__ */ jsx(DropdownMenuCheckIcon, {})
2415
+ }) : null]
2416
+ });
2417
+ }
2418
+ //#endregion
2419
+ //#region src/components/mobile-nav/mobile-nav.tsx
2420
+ const mobileNav = tv$1({ slots: {
2421
+ root: [
2422
+ "grid w-full flex-none grid-flow-col auto-cols-fr items-start gap-0",
2423
+ "border-t border-line-seam bg-layer-card px-2 pt-1.5 text-foreground-hint",
2424
+ "pb-[max(8px,min(env(safe-area-inset-bottom,0px),34px))]"
2425
+ ],
2426
+ item: [
2427
+ "relative inline-flex shrink-0 items-center outline-none",
2428
+ "disabled:pointer-events-none disabled:opacity-45",
2429
+ "[&_svg]:shrink-0",
2430
+ "isolate min-w-0 flex-col justify-start gap-[7px] border-0 px-0 pt-1 pb-0",
2431
+ "font-sans text-[10px]/3 font-semibold text-foreground-hint",
2432
+ "*:relative *:z-10 [&_svg]:size-[22px] [&_svg]:stroke-2",
2433
+ "before:pointer-events-none before:absolute before:top-0 before:left-1/2 before:z-0 before:-translate-x-1/2",
2434
+ "before:h-[30px] before:w-14 before:rounded-control before:border-[1.5px] before:border-transparent before:content-['']",
2435
+ "active:before:bg-fill-medium",
2436
+ "data-[selected=true]:font-bold data-[selected=true]:text-foreground",
2437
+ "data-[selected=true]:[&_svg]:text-sticker-ink",
2438
+ "data-[selected=true]:before:border-sticker-ink data-[selected=true]:before:bg-brand data-[selected=true]:before:ring-[1.5px] data-[selected=true]:before:ring-sticker-edge",
2439
+ "data-[selected=true]:active:before:overlay-sticker-medium",
2440
+ "rounded-control focus-visible:shadow-focus"
2441
+ ],
2442
+ itemLabel: ["min-w-0 truncate", "max-w-full"]
2443
+ } });
2444
+ function MobileNavRoot({ className, render, ...props }) {
2445
+ const { root } = mobileNav();
2446
+ const defaultProps = {
2447
+ "data-slot": "mobile-nav-root",
2448
+ className: root({ className })
2449
+ };
2450
+ return useRender({
2451
+ defaultTagName: "nav",
2452
+ render,
2453
+ props: mergeProps(defaultProps, props)
2454
+ });
2455
+ }
2456
+ function MobileNavItem({ nativeButton = true, selected, className, render, ...props }) {
2457
+ const { item } = mobileNav();
2458
+ const defaultProps = {
2459
+ type: nativeButton ? "button" : void 0,
2460
+ "aria-pressed": nativeButton ? selected : void 0,
2461
+ "data-slot": "mobile-nav-item",
2462
+ "data-selected": selected ? "true" : void 0,
2463
+ className: item({ className })
2464
+ };
2465
+ return useRender({
2466
+ render: render ?? /* @__PURE__ */ jsx(Button, {}),
2467
+ props: mergeProps(defaultProps, props)
2468
+ });
2469
+ }
2470
+ function MobileNavLabel({ className, render, ...props }) {
2471
+ const { itemLabel } = mobileNav();
2472
+ const defaultProps = {
2473
+ "data-slot": "mobile-nav-item-label",
2474
+ className: itemLabel({ className })
2475
+ };
2476
+ return useRender({
2477
+ defaultTagName: "span",
2478
+ render,
2479
+ props: mergeProps(defaultProps, props)
2480
+ });
2481
+ }
2482
+ const holds = /* @__PURE__ */ new WeakMap();
2483
+ function touchPoint(event) {
2484
+ const touch = event.touches?.[0] ?? event.changedTouches?.[0];
2485
+ return touch ? {
2486
+ x: touch.clientX,
2487
+ y: touch.clientY
2488
+ } : null;
2489
+ }
2490
+ function farther(from, to, limit) {
2491
+ return Math.hypot(to.x - from.x, to.y - from.y) > limit;
2492
+ }
2493
+ var HoldThenMoveTouchSensorImpl = class extends TouchSensor {
2494
+ static activators = TouchSensor.activators;
2495
+ static setup() {
2496
+ return TouchSensor.setup();
2497
+ }
2498
+ attach() {
2499
+ super.attach();
2500
+ const self = this;
2501
+ const { holdMs = 500 } = self.props.options;
2502
+ const state = {
2503
+ heldAt: null,
2504
+ last: null,
2505
+ timer: setTimeout(() => {
2506
+ state.heldAt = state.last ?? self.initialCoordinates;
2507
+ }, holdMs)
2508
+ };
2509
+ holds.set(this, state);
2510
+ }
2511
+ handleMove(event) {
2512
+ const self = this;
2513
+ const state = holds.get(this);
2514
+ if (self.activated || !state) {
2515
+ super.handleMove(event);
2516
+ return;
2517
+ }
2518
+ if ((event.touches?.length ?? 1) > 1) {
2519
+ self.handleCancel();
2520
+ return;
2521
+ }
2522
+ const point = touchPoint(event);
2523
+ const start = self.initialCoordinates;
2524
+ if (!point || !start) return;
2525
+ state.last = point;
2526
+ const { distance = 10, tolerance = 10 } = self.props.options;
2527
+ if (!state.heldAt) {
2528
+ if (farther(start, point, tolerance)) self.handleCancel();
2529
+ return;
2530
+ }
2531
+ if (event.cancelable) event.preventDefault();
2532
+ if (farther(state.heldAt, point, distance)) self.handleStart();
2533
+ }
2534
+ detach() {
2535
+ const state = holds.get(this);
2536
+ if (state) clearTimeout(state.timer);
2537
+ holds.delete(this);
2538
+ super.detach();
2539
+ }
2540
+ };
2541
+ /** A TouchSensor that starts a drag on the first move after a still hold (see above). */
2542
+ const HoldThenMoveTouchSensor = HoldThenMoveTouchSensorImpl;
2543
+ /**
2544
+ * dnd-kit's PointerSensor for a mouse and a pen only: a finger's pointerdown is
2545
+ * left to `HoldThenMoveTouchSensor`.
2546
+ */
2547
+ var MouseOnlyPointerSensor = class extends PointerSensor {
2548
+ static activators = [{
2549
+ eventName: "onPointerDown",
2550
+ handler: (event, options) => event.nativeEvent.pointerType === "touch" ? false : PointerSensor.activators[0].handler(event, options)
2551
+ }];
2552
+ };
2553
+ /**
2554
+ * `useSensor(HoldThenMoveTouchSensor, …)` with the activation the sensor relies
2555
+ * on: no constraint dnd-kit would act on by itself, so only a move after the
2556
+ * hold starts the drag.
2557
+ */
2558
+ function useHoldThenMoveTouchSensor({ holdMs = 500, distance = 10, tolerance = 10 } = {}) {
2559
+ const options = useMemo(() => ({
2560
+ holdMs,
2561
+ distance,
2562
+ tolerance,
2563
+ activationConstraint: { distance: Number.POSITIVE_INFINITY }
2564
+ }), [
2565
+ holdMs,
2566
+ distance,
2567
+ tolerance
2568
+ ]);
2569
+ return useSensor(HoldThenMoveTouchSensor, options);
2570
+ }
2571
+ /** The mouse / pen sensor that goes with it: picks up after `distance` px. */
2572
+ function useMouseOnlyPointerSensor(distance) {
2573
+ const options = useMemo(() => ({ activationConstraint: { distance } }), [distance]);
2574
+ return useSensor(MouseOnlyPointerSensor, options);
2575
+ }
2576
+ //#endregion
2577
+ //#region src/components/popover/popover.tsx
2578
+ const popover = tv$1({ slots: {
2579
+ trigger: ["select-none"],
2580
+ positioner: ["isolate z-50 outline-none"],
2581
+ content: [
2582
+ "max-h-(--available-height) min-w-[220px] overflow-hidden",
2583
+ "outline-none",
2584
+ "rounded-container bg-layer-popover text-foreground",
2585
+ "shadow-float ring-1 ring-line-seam"
2586
+ ],
2587
+ header: ["flex items-center justify-between px-3", "min-h-11 gap-2 py-1.5 pr-1.5 pl-4"],
2588
+ body: ["flex min-w-0 flex-col gap-1.5", "px-4 pt-4 pb-3 last:pb-4"],
2589
+ actions: ["flex flex-wrap items-center justify-end gap-2", "px-4 pb-3 last:pb-4"],
2590
+ separator: ["border-t", "border-line-hairline"],
2591
+ title: ["m-0", "font-sans text-headline font-bold text-foreground"],
2592
+ description: ["m-0", "font-sans text-footnote text-foreground-muted"],
2593
+ close: [
2594
+ "inline-flex items-center justify-center",
2595
+ "outline-none transition-colors",
2596
+ "h-8 min-w-8 rounded-chip px-1.5",
2597
+ "font-sans text-footnote font-bold text-foreground",
2598
+ "hover:bg-fill-medium active:bg-fill-medium",
2599
+ "focus-visible:shadow-focus",
2600
+ "[&_svg]:size-4.5"
2601
+ ],
2602
+ arrow: ["size-2 rotate-45", "border-l border-t border-line-seam bg-layer-popover"],
2603
+ backdrop: ["fixed inset-0", "bg-transparent"],
2604
+ viewport: ["block"]
2605
+ } });
2606
+ function Popover$1(props) {
2607
+ return /* @__PURE__ */ jsx(Popover.Root, {
2608
+ "data-slot": "popover",
2609
+ ...props
2610
+ });
2611
+ }
2612
+ function PopoverTrigger({ className, ...props }) {
2613
+ const { trigger } = popover();
2614
+ return /* @__PURE__ */ jsx(Popover.Trigger, {
2615
+ "data-slot": "popover-trigger",
2616
+ className: trigger({ className }),
2617
+ ...props
2618
+ });
2619
+ }
2620
+ function PopoverPortal(props) {
2621
+ return /* @__PURE__ */ jsx(Popover.Portal, {
2622
+ "data-slot": "popover-portal",
2623
+ ...useThemeScopeProps(props)
2624
+ });
2625
+ }
2626
+ function PopoverPopup({ className, ...props }) {
2627
+ const { content } = popover();
2628
+ return /* @__PURE__ */ jsx("div", {
2629
+ "data-slot": "popover-content",
2630
+ className: content({ className }),
2631
+ ...props
2632
+ });
2633
+ }
2634
+ function PopoverContent({ align = "start", alignOffset = 0, anchor, arrowPadding, collisionAvoidance, collisionBoundary, collisionPadding, disableAnchorTracking, positionMethod = "fixed", side = "bottom", sideOffset = 4, sticky, container, portalProps, className, ...props }) {
2635
+ const { positioner, content } = popover();
2636
+ const portalContainer = container !== void 0 ? container : portalProps?.container;
2637
+ return /* @__PURE__ */ jsx(PopoverPortal, {
2638
+ ...portalProps,
2639
+ container: portalContainer,
2640
+ children: /* @__PURE__ */ jsx(Popover.Positioner, {
2641
+ align,
2642
+ alignOffset,
2643
+ anchor,
2644
+ arrowPadding,
2645
+ collisionAvoidance,
2646
+ collisionBoundary,
2647
+ collisionPadding,
2648
+ disableAnchorTracking,
2649
+ positionMethod,
2650
+ side,
2651
+ sideOffset,
2652
+ sticky,
2653
+ className: positioner(),
2654
+ children: /* @__PURE__ */ jsx(Popover.Popup, {
2655
+ "data-slot": "popover-content",
2656
+ className: content({ className }),
2657
+ ...props
2658
+ })
2659
+ })
2660
+ });
2661
+ }
2662
+ function PopoverViewport({ className, ...props }) {
2663
+ const { viewport } = popover();
2664
+ return /* @__PURE__ */ jsx(Popover.Viewport, {
2665
+ "data-slot": "popover-viewport",
2666
+ className: viewport({ className }),
2667
+ ...props
2668
+ });
2669
+ }
2670
+ function PopoverHeader({ className, ...props }) {
2671
+ const { header } = popover();
2672
+ return /* @__PURE__ */ jsx("div", {
2673
+ "data-slot": "popover-header",
2674
+ className: header({ className }),
2675
+ ...props
2676
+ });
2677
+ }
2678
+ function PopoverSeparator({ className, ...props }) {
2679
+ const { separator } = popover();
2680
+ return /* @__PURE__ */ jsx("div", {
2681
+ role: "separator",
2682
+ "aria-orientation": "horizontal",
2683
+ "data-slot": "popover-separator",
2684
+ className: separator({ className }),
2685
+ ...props
2686
+ });
2687
+ }
2688
+ function PopoverTitle({ className, ...props }) {
2689
+ const { title } = popover();
2690
+ return /* @__PURE__ */ jsx(Popover.Title, {
2691
+ "data-slot": "popover-title",
2692
+ className: title({ className }),
2693
+ ...props
2694
+ });
2695
+ }
2696
+ function PopoverDescription({ className, ...props }) {
2697
+ const { description } = popover();
2698
+ return /* @__PURE__ */ jsx(Popover.Description, {
2699
+ "data-slot": "popover-description",
2700
+ className: description({ className }),
2701
+ ...props
2702
+ });
2703
+ }
2704
+ function PopoverClose({ className, ...props }) {
2705
+ const { close } = popover();
2706
+ return /* @__PURE__ */ jsx(Popover.Close, {
2707
+ "data-slot": "popover-close",
2708
+ className: close({ className }),
2709
+ ...props
2710
+ });
2711
+ }
2712
+ function PopoverArrow({ className, ...props }) {
2713
+ const { arrow } = popover();
2714
+ return /* @__PURE__ */ jsx(Popover.Arrow, {
2715
+ "data-slot": "popover-arrow",
2716
+ className: arrow({ className }),
2717
+ ...props
2718
+ });
2719
+ }
2720
+ function PopoverBackdrop({ className, ...props }) {
2721
+ const { backdrop } = popover();
2722
+ return /* @__PURE__ */ jsx(Popover.Backdrop, {
2723
+ "data-slot": "popover-backdrop",
2724
+ className: backdrop({ className }),
2725
+ ...props
2726
+ });
2727
+ }
2728
+ /** The panel's text: a `PopoverTitle` and a `PopoverDescription`, 16px in from its edges. */
2729
+ function PopoverBody({ className, ...props }) {
2730
+ const { body } = popover();
2731
+ return /* @__PURE__ */ jsx("div", {
2732
+ "data-slot": "popover-body",
2733
+ className: body({ className }),
2734
+ ...props
2735
+ });
2736
+ }
2737
+ /** The panel's buttons, at its end, under `PopoverBody`: the way out first, then the confirm button. */
2738
+ function PopoverActions({ className, ...props }) {
2739
+ const { actions } = popover();
2740
+ return /* @__PURE__ */ jsx("div", {
2741
+ "data-slot": "popover-actions",
2742
+ className: actions({ className }),
2743
+ ...props
2744
+ });
2745
+ }
2746
+ //#endregion
2747
+ //#region src/components/mobile-nav/mobile-nav-server.tsx
2748
+ const mobileNavServer = tv$1({ slots: {
2749
+ item: [
2750
+ "select-none [-webkit-touch-callout:none]",
2751
+ "*:data-[slot=avatar]:size-[22px]",
2752
+ "data-popup-open:before:bg-fill-medium"
2753
+ ],
2754
+ content: [
2755
+ "flex flex-col",
2756
+ "w-[calc(var(--anchor-width)-16px)] min-w-0",
2757
+ "max-h-[min(60dvh,calc(var(--available-height)-env(safe-area-inset-top,0px)))]"
2758
+ ],
2759
+ list: ["flex min-h-0 flex-1 flex-col overflow-y-auto overscroll-contain p-1"],
2760
+ group: ["flex shrink-0 flex-col p-1"],
2761
+ row: [
2762
+ "flex w-full shrink-0 cursor-default select-none items-center",
2763
+ "text-left outline-none transition-colors",
2764
+ "min-h-option gap-2.5 rounded-chip px-2.5 py-option-inset",
2765
+ "font-sans text-callout font-medium text-foreground",
2766
+ "hover:bg-brand-soft active:bg-brand-soft focus-visible:bg-brand-soft",
2767
+ "aria-[current=true]:font-bold",
2768
+ "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&>svg:not([class*='size-'])]:size-4",
2769
+ "[&>svg]:text-foreground-muted",
2770
+ "*:data-[slot=avatar]:size-5 *:data-[slot=avatar]:shrink-0"
2771
+ ],
2772
+ rowLabel: ["min-w-0 flex-1 truncate"],
2773
+ rowBody: ["flex min-w-0 flex-1 flex-col"],
2774
+ rowDescription: ["min-w-0 truncate", "font-mono text-mono-meta font-normal text-foreground-hint"],
2775
+ destinations: ["grid shrink-0 grid-flow-col auto-cols-fr gap-1 p-1"],
2776
+ destination: [
2777
+ "relative flex min-w-0 cursor-default select-none flex-col items-center justify-center",
2778
+ "outline-none transition-colors",
2779
+ "gap-1 rounded-chip px-1 py-2",
2780
+ "font-sans text-footnote font-medium text-foreground",
2781
+ "hover:bg-brand-soft active:bg-brand-soft focus-visible:bg-brand-soft",
2782
+ "[&_svg]:pointer-events-none [&_svg]:size-5 [&_svg]:shrink-0 [&_svg]:text-foreground-muted",
2783
+ "[&>span]:max-w-full [&>span]:truncate",
2784
+ "*:data-[slot=status]:absolute *:data-[slot=status]:top-1 *:data-[slot=status]:left-[calc(50%+6px)]"
2785
+ ]
2786
+ } });
2787
+ const MobileNavServerContext = createContext(null);
2788
+ function useMobileNavServerContext(part) {
2789
+ const context = use(MobileNavServerContext);
2790
+ if (!context) throw new Error(`${part} must be inside MobileNavServerPopover`);
2791
+ return context;
2792
+ }
2793
+ /** The root: owns the popover's open state, like `Popover` (`open`, `onOpenChange`). */
2794
+ function MobileNavServerPopover(props) {
2795
+ const [handle] = useState(() => Popover.createHandle());
2796
+ const triggerRef = useRef(null);
2797
+ const context = useMemo(() => ({
2798
+ handle,
2799
+ triggerRef
2800
+ }), [handle]);
2801
+ return /* @__PURE__ */ jsx(MobileNavServerContext, {
2802
+ value: context,
2803
+ children: /* @__PURE__ */ jsx(Popover.Root, {
2804
+ "data-slot": "mobile-nav-server-popover",
2805
+ handle,
2806
+ ...props
2807
+ })
2808
+ });
2809
+ }
2810
+ /**
2811
+ * The tab: the server's avatar and its name in `MobileNavLabel`, as children.
2812
+ * A press opens the popover, and so does holding it for 500ms (the long press
2813
+ * of raft-ui's menus). Lifting the finger after a hold never closes it: the
2814
+ * click that ends the hold is not a press, whether the hold opened the
2815
+ * popover or found it open.
2816
+ * It is never `selected`: it holds no page.
2817
+ */
2818
+ function MobileNavServerItem({ className, id, ref, onClick, onContextMenu, onPointerDown, onPointerMove, onPointerUp, onPointerCancel, onPointerLeave, ...props }) {
2819
+ const { handle, triggerRef } = useMobileNavServerContext("MobileNavServerItem");
2820
+ const { item } = mobileNavServer();
2821
+ const generatedId = useId();
2822
+ const triggerId = id ?? generatedId;
2823
+ const holdTimer = useRef(void 0);
2824
+ const holdStart = useRef(null);
2825
+ const held = useRef(false);
2826
+ const cancelHold = useCallback(() => {
2827
+ clearTimeout(holdTimer.current);
2828
+ holdTimer.current = void 0;
2829
+ holdStart.current = null;
2830
+ }, []);
2831
+ useEffect(() => cancelHold, [cancelHold]);
2832
+ const setRef = useCallback((node) => {
2833
+ triggerRef.current = node;
2834
+ assignRef(ref, node);
2835
+ }, [ref, triggerRef]);
2836
+ const handlePointerDown = useCallback((event) => {
2837
+ onPointerDown?.(event);
2838
+ cancelHold();
2839
+ held.current = false;
2840
+ if (event.button !== 0) return;
2841
+ holdStart.current = {
2842
+ x: event.clientX,
2843
+ y: event.clientY
2844
+ };
2845
+ holdTimer.current = setTimeout(() => {
2846
+ holdTimer.current = void 0;
2847
+ held.current = true;
2848
+ if (!handle.isOpen) handle.open(triggerId);
2849
+ }, 500);
2850
+ }, [
2851
+ cancelHold,
2852
+ handle,
2853
+ onPointerDown,
2854
+ triggerId
2855
+ ]);
2856
+ const handlePointerMove = useCallback((event) => {
2857
+ onPointerMove?.(event);
2858
+ const start = holdStart.current;
2859
+ if (!start) return;
2860
+ if (Math.hypot(event.clientX - start.x, event.clientY - start.y) > 10) cancelHold();
2861
+ }, [cancelHold, onPointerMove]);
2862
+ const handlePointerUp = useCallback((event) => {
2863
+ onPointerUp?.(event);
2864
+ cancelHold();
2865
+ }, [cancelHold, onPointerUp]);
2866
+ const handlePointerCancel = useCallback((event) => {
2867
+ onPointerCancel?.(event);
2868
+ cancelHold();
2869
+ }, [cancelHold, onPointerCancel]);
2870
+ const handlePointerLeave = useCallback((event) => {
2871
+ onPointerLeave?.(event);
2872
+ cancelHold();
2873
+ }, [cancelHold, onPointerLeave]);
2874
+ const handleClick = useCallback((event) => {
2875
+ onClick?.(event);
2876
+ if (!held.current) return;
2877
+ held.current = false;
2878
+ event.preventBaseUIHandler?.();
2879
+ }, [onClick]);
2880
+ const handleContextMenu = useCallback((event) => {
2881
+ onContextMenu?.(event);
2882
+ event.preventDefault();
2883
+ }, [onContextMenu]);
2884
+ return /* @__PURE__ */ jsx(Popover.Trigger, {
2885
+ handle,
2886
+ id: triggerId,
2887
+ ref: setRef,
2888
+ render: /* @__PURE__ */ jsx(MobileNavItem, {}),
2889
+ "data-slot": "mobile-nav-server-item",
2890
+ className: item({ className }),
2891
+ onPointerDown: handlePointerDown,
2892
+ onPointerMove: handlePointerMove,
2893
+ onPointerUp: handlePointerUp,
2894
+ onPointerCancel: handlePointerCancel,
2895
+ onPointerLeave: handlePointerLeave,
2896
+ onClick: handleClick,
2897
+ onContextMenu: handleContextMenu,
2898
+ ...props
2899
+ });
2900
+ }
2901
+ /**
2902
+ * The popover above the tab bar, 8px from each side of it and 8px above it,
2903
+ * as tall as its content and at most 60% of the screen. It stops below the
2904
+ * top safe area (a notch in a standalone web app): Base UI's collision padding
2905
+ * takes only pixels, so the height cap subtracts `env(safe-area-inset-top)`.
2906
+ * The bottom needs none: the tab bar under the popover already pads the
2907
+ * bottom safe area. A column:
2908
+ * `MobileNavServerPopoverList` takes the room left and scrolls, and the
2909
+ * destinations and the footer after it stay in view.
2910
+ */
2911
+ function MobileNavServerPopoverContent({ anchor, side = "top", align = "center", sideOffset = 8, collisionPadding = 8, className, ...props }) {
2912
+ const { triggerRef } = useMobileNavServerContext("MobileNavServerPopoverContent");
2913
+ const { content } = mobileNavServer();
2914
+ const tabBar = useCallback(() => {
2915
+ const trigger = triggerRef.current;
2916
+ return trigger?.closest("[data-slot=mobile-nav-root]") ?? trigger;
2917
+ }, [triggerRef]);
2918
+ return /* @__PURE__ */ jsx(PopoverContent, {
2919
+ "data-slot": "mobile-nav-server-popover-content",
2920
+ anchor: anchor ?? tabBar,
2921
+ side,
2922
+ align,
2923
+ sideOffset,
2924
+ collisionPadding,
2925
+ className: content({ className }),
2926
+ ...props
2927
+ });
2928
+ }
2929
+ /** The server list: takes the room left in the popover and scrolls inside it. */
2930
+ function MobileNavServerPopoverList({ className, ...props }) {
2931
+ const { list } = mobileNavServer();
2932
+ return /* @__PURE__ */ jsx("div", {
2933
+ "data-slot": "mobile-nav-server-popover-list",
2934
+ className: list({ className }),
2935
+ ...props
2936
+ });
2937
+ }
2938
+ /** Rows that always stay in view, such as the footer actions. */
2939
+ function MobileNavServerPopoverGroup({ className, ...props }) {
2940
+ const { group } = mobileNavServer();
2941
+ return /* @__PURE__ */ jsx("div", {
2942
+ "data-slot": "mobile-nav-server-popover-group",
2943
+ className: group({ className }),
2944
+ ...props
2945
+ });
2946
+ }
2947
+ /**
2948
+ * One row at the option height of every popup list (`py-option-inset`, 36px
2949
+ * for one line; a second line adds its own height and nothing else): a 20px
2950
+ * avatar or a 16px icon, `MobileNavServerPopoverRowLabel` (or a
2951
+ * `MobileNavServerPopoverRowBody` with a description under the label), then
2952
+ * anything trailing (a check, a count), centered on the row.
2953
+ * `aria-current="true"` marks the current server in bold.
2954
+ */
2955
+ function MobileNavServerPopoverRow({ className, type = "button", ...props }) {
2956
+ const { row } = mobileNavServer();
2957
+ return /* @__PURE__ */ jsx("button", {
2958
+ type,
2959
+ "data-slot": "mobile-nav-server-popover-row",
2960
+ className: row({ className }),
2961
+ ...props
2962
+ });
2963
+ }
2964
+ /** A row's label: takes the room and truncates. */
2965
+ function MobileNavServerPopoverRowLabel({ className, ...props }) {
2966
+ const { rowLabel } = mobileNavServer();
2967
+ return /* @__PURE__ */ jsx("span", {
2968
+ "data-slot": "mobile-nav-server-popover-row-label",
2969
+ className: rowLabel({ className }),
2970
+ ...props
2971
+ });
2972
+ }
2973
+ /** The server's own pages, side by side in equal cells; a `group`, named with `aria-label`. */
2974
+ function MobileNavServerPopoverDestinations({ className, ...props }) {
2975
+ const { destinations } = mobileNavServer();
2976
+ return /* @__PURE__ */ jsx("div", {
2977
+ role: "group",
2978
+ "data-slot": "mobile-nav-server-popover-destinations",
2979
+ className: destinations({ className }),
1409
2980
  ...props
1410
2981
  });
1411
2982
  }
1412
- function DropdownMenuTrigger({ className, render = /* @__PURE__ */ jsx(Button$1, {
1413
- variant: "outline",
1414
- size: "md"
1415
- }), ...props }) {
1416
- const { trigger } = dropdownMenu();
1417
- return /* @__PURE__ */ jsx(Menu.Trigger, {
1418
- "data-slot": "dropdown-menu-trigger",
1419
- render,
2983
+ /**
2984
+ * One of the server's pages: a 20px icon over a short label in a `span`, which
2985
+ * truncates. Something new on that page (a feedback reply, a computer that
2986
+ * needs attention) is an `accent` `Status` child, the attention dot at the
2987
+ * icon's top-right corner: the server tab's avatar never carries it.
2988
+ */
2989
+ function MobileNavServerPopoverDestination({ className, type = "button", ...props }) {
2990
+ const { destination } = mobileNavServer();
2991
+ return /* @__PURE__ */ jsx("button", {
2992
+ type,
2993
+ "data-slot": "mobile-nav-server-popover-destination",
2994
+ className: destination({ className }),
2995
+ ...props
2996
+ });
2997
+ }
2998
+ /**
2999
+ * The text of a two-line row: `MobileNavServerPopoverRowLabel`, then
3000
+ * `MobileNavServerPopoverRowDescription` under it. The second line adds its
3001
+ * own height and nothing else; the avatar and the trailing check or count
3002
+ * center on both lines.
3003
+ */
3004
+ function MobileNavServerPopoverRowBody({ className, ...props }) {
3005
+ const { rowBody } = mobileNavServer();
3006
+ return /* @__PURE__ */ jsx("span", {
3007
+ "data-slot": "mobile-nav-server-popover-row-body",
3008
+ className: rowBody({ className }),
3009
+ ...props
3010
+ });
3011
+ }
3012
+ /** A row's second line, such as a server's address: muted Space Mono; truncates. */
3013
+ function MobileNavServerPopoverRowDescription({ className, ...props }) {
3014
+ const { rowDescription } = mobileNavServer();
3015
+ return /* @__PURE__ */ jsx("span", {
3016
+ "data-slot": "mobile-nav-server-popover-row-description",
3017
+ className: rowDescription({ className }),
3018
+ ...props
3019
+ });
3020
+ }
3021
+ function assignRef(ref, value) {
3022
+ if (typeof ref === "function") ref(value);
3023
+ else if (ref) ref.current = value;
3024
+ }
3025
+ //#endregion
3026
+ //#region src/components/preview-card/preview-card.tsx
3027
+ const previewCard = tv$1({ slots: {
3028
+ trigger: [],
3029
+ positioner: ["isolate z-50 outline-none"],
3030
+ popup: [
3031
+ "max-h-(--available-height) min-w-[220px] overflow-hidden",
3032
+ "outline-none",
3033
+ "origin-(--transform-origin) transform-gpu opacity-100 will-change-[transform,opacity]",
3034
+ "transition-[opacity,transform] duration-250 ease-[cubic-bezier(0.22,1,0.36,1)]",
3035
+ "data-starting-style:scale-[0.97] data-starting-style:opacity-0",
3036
+ "data-ending-style:scale-[0.99] data-ending-style:opacity-0 data-ending-style:duration-150",
3037
+ "data-instant:transition-none motion-reduce:transition-none",
3038
+ "w-[300px] rounded-container bg-layer-popover text-foreground",
3039
+ "shadow-float ring-1 ring-line-seam"
3040
+ ],
3041
+ header: ["flex items-start gap-3", "px-4 pt-4 pb-3 last:pb-4"],
3042
+ separator: ["border-t", "border-line-hairline"],
3043
+ title: ["m-0 truncate", "font-sans text-headline font-bold text-foreground"],
3044
+ description: ["m-0 truncate", "px-4 py-3 last:pb-4 font-sans text-callout text-foreground-muted"],
3045
+ arrow: ["size-2 rotate-45", "border-l border-t border-line-seam bg-layer-popover"],
3046
+ body: [
3047
+ "flex min-w-0 flex-col gap-1.5",
3048
+ "px-4 pt-4 pb-3 last:pb-4",
3049
+ "*:data-[slot=preview-card-title]:line-clamp-3 *:data-[slot=preview-card-title]:whitespace-normal"
3050
+ ],
3051
+ eyebrow: ["m-0 truncate", "font-mono text-mono-meta text-foreground-muted"],
3052
+ meta: ["flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1", "m-0 font-sans text-callout text-foreground-muted"],
3053
+ actions: ["flex flex-wrap items-center gap-2", "px-4 pb-3 last:pb-4"],
3054
+ footer: [
3055
+ "m-0 truncate",
3056
+ "border-t border-line-hairline px-4 py-2",
3057
+ "font-sans text-caption text-foreground-muted"
3058
+ ]
3059
+ } });
3060
+ function PreviewCard$1(props) {
3061
+ return /* @__PURE__ */ jsx(PreviewCard.Root, {
3062
+ "data-slot": "preview-card",
3063
+ ...props
3064
+ });
3065
+ }
3066
+ function PreviewCardTrigger({ delay = 80, closeDelay = 40, className, ...props }) {
3067
+ const { trigger } = previewCard();
3068
+ return /* @__PURE__ */ jsx(PreviewCard.Trigger, {
3069
+ "data-slot": "preview-card-trigger",
3070
+ delay,
3071
+ closeDelay,
1420
3072
  className: trigger({ className }),
1421
3073
  ...props
1422
3074
  });
1423
3075
  }
1424
- function DropdownMenuPortal(props) {
1425
- return /* @__PURE__ */ jsx(Menu.Portal, {
1426
- "data-slot": "dropdown-menu-portal",
3076
+ function PreviewCardPortal(props) {
3077
+ return /* @__PURE__ */ jsx(PreviewCard.Portal, {
3078
+ "data-slot": "preview-card-portal",
1427
3079
  ...useThemeScopeProps(props)
1428
3080
  });
1429
3081
  }
1430
- function DropdownMenuPopup({ className, ...props }) {
1431
- const { content } = dropdownMenu();
3082
+ function PreviewCardPopup({ className, ...props }) {
3083
+ const themeState = useTheme();
3084
+ const { popup } = previewCard();
3085
+ const scopeProps = resolveThemeScopeProps({
3086
+ ...props,
3087
+ className: popup({ className })
3088
+ }, themeState);
1432
3089
  return /* @__PURE__ */ jsx("div", {
1433
- "data-slot": "dropdown-menu-content",
1434
- className: content({ className }),
1435
- ...props
3090
+ "data-slot": "preview-card-content",
3091
+ ...scopeProps
1436
3092
  });
1437
3093
  }
1438
- function DropdownMenuContent({ align = "start", alignOffset = 0, anchor, arrowPadding, collisionAvoidance, collisionBoundary, collisionPadding, disableAnchorTracking, positionMethod = "fixed", side = "bottom", sideOffset, sticky, portalProps, className, ...props }) {
1439
- const resolvedSideOffset = sideOffset ?? 6;
1440
- const { positioner, content } = dropdownMenu();
1441
- return /* @__PURE__ */ jsx(DropdownMenuPortal, {
3094
+ function PreviewCardContent({ align = "start", alignOffset = 0, anchor, arrowPadding, collisionAvoidance, collisionBoundary, collisionPadding, disableAnchorTracking, positionMethod = "fixed", side = "bottom", sideOffset = 6, sticky, portalProps, className, ...props }) {
3095
+ const { positioner, popup } = previewCard();
3096
+ return /* @__PURE__ */ jsx(PreviewCardPortal, {
1442
3097
  ...portalProps,
1443
- children: /* @__PURE__ */ jsx(Menu.Positioner, {
3098
+ children: /* @__PURE__ */ jsx(PreviewCard.Positioner, {
1444
3099
  align,
1445
3100
  alignOffset,
1446
3101
  anchor,
@@ -1451,195 +3106,102 @@ function DropdownMenuContent({ align = "start", alignOffset = 0, anchor, arrowPa
1451
3106
  disableAnchorTracking,
1452
3107
  positionMethod,
1453
3108
  side,
1454
- sideOffset: resolvedSideOffset,
3109
+ sideOffset,
1455
3110
  sticky,
1456
3111
  className: positioner(),
1457
- children: /* @__PURE__ */ jsx(Menu.Popup, {
1458
- "data-slot": "dropdown-menu-content",
1459
- className: content({ className }),
3112
+ children: /* @__PURE__ */ jsx(PreviewCard.Popup, {
3113
+ "data-slot": "preview-card-content",
3114
+ className: popup({ className }),
1460
3115
  ...props
1461
3116
  })
1462
3117
  })
1463
3118
  });
1464
3119
  }
1465
- function DropdownMenuItem({ className, ...props }) {
1466
- const { item } = dropdownMenu();
1467
- return /* @__PURE__ */ jsx(Menu.Item, {
1468
- "data-slot": "dropdown-menu-item",
1469
- className: item({ className }),
1470
- ...props
1471
- });
1472
- }
1473
- function DropdownMenuItemLabel({ className, ...props }) {
1474
- const { itemLabel } = dropdownMenu();
1475
- return /* @__PURE__ */ jsx("span", {
1476
- "data-slot": "dropdown-menu-item-label",
1477
- className: itemLabel({ className }),
1478
- ...props
1479
- });
1480
- }
1481
- function DropdownMenuFooter({ className, ...props }) {
1482
- const { footer } = dropdownMenu();
3120
+ function PreviewCardHeader({ className, ...props }) {
3121
+ const { header } = previewCard();
1483
3122
  return /* @__PURE__ */ jsx("div", {
1484
- "data-slot": "dropdown-menu-footer",
1485
- className: footer({ className }),
3123
+ "data-slot": "preview-card-header",
3124
+ className: header({ className }),
1486
3125
  ...props
1487
3126
  });
1488
3127
  }
1489
- function DropdownMenuSubmenuTrigger({ children, className, ...props }) {
1490
- const { submenuTrigger, indicator } = dropdownMenu();
1491
- return /* @__PURE__ */ jsxs(Menu.SubmenuTrigger, {
1492
- "data-slot": "dropdown-menu-submenu-trigger",
1493
- className: submenuTrigger({ className }),
1494
- ...props,
1495
- children: [children, /* @__PURE__ */ jsx("span", {
1496
- className: indicator(),
1497
- "data-slot": "dropdown-menu-submenu-trigger-indicator",
1498
- "aria-hidden": "true",
1499
- children: /* @__PURE__ */ jsx(DropdownMenuSubmenuChevronIcon, {})
1500
- })]
1501
- });
1502
- }
1503
- function DropdownMenuLabel({ className, ...props }) {
1504
- const { label } = dropdownMenu();
3128
+ function PreviewCardSeparator({ className, ...props }) {
3129
+ const { separator } = previewCard();
1505
3130
  return /* @__PURE__ */ jsx("div", {
1506
- "data-slot": "dropdown-menu-label",
1507
- className: label({ className }),
3131
+ role: "separator",
3132
+ "aria-orientation": "horizontal",
3133
+ "data-slot": "preview-card-separator",
3134
+ className: separator({ className }),
1508
3135
  ...props
1509
3136
  });
1510
3137
  }
1511
- function DropdownMenuShortcut({ className, ...props }) {
1512
- const { shortcut } = dropdownMenu();
1513
- return /* @__PURE__ */ jsx(Kbd, {
1514
- "data-slot": "dropdown-menu-shortcut",
1515
- className: shortcut({ className }),
3138
+ function PreviewCardTitle({ className, ...props }) {
3139
+ const { title } = previewCard();
3140
+ return /* @__PURE__ */ jsx("p", {
3141
+ "data-slot": "preview-card-title",
3142
+ className: title({ className }),
1516
3143
  ...props
1517
3144
  });
1518
3145
  }
1519
- function DropdownMenuItemCount({ className, ...props }) {
1520
- const { itemCount } = dropdownMenu();
1521
- return /* @__PURE__ */ jsx(Badge, {
1522
- "data-slot": "dropdown-menu-item-count",
1523
- className: itemCount({ className }),
3146
+ function PreviewCardDescription({ className, ...props }) {
3147
+ const { description } = previewCard();
3148
+ return /* @__PURE__ */ jsx("p", {
3149
+ "data-slot": "preview-card-description",
3150
+ className: description({ className }),
1524
3151
  ...props
1525
3152
  });
1526
3153
  }
1527
- function DropdownMenuSeparator({ className, ...props }) {
1528
- const { separator } = dropdownMenu();
1529
- return /* @__PURE__ */ jsx(Menu.Separator, {
1530
- "data-slot": "dropdown-menu-separator",
1531
- className: separator({ className }),
3154
+ function PreviewCardArrow({ className, ...props }) {
3155
+ const { arrow } = previewCard();
3156
+ return /* @__PURE__ */ jsx(PreviewCard.Arrow, {
3157
+ "data-slot": "preview-card-arrow",
3158
+ className: arrow({ className }),
1532
3159
  ...props
1533
3160
  });
1534
3161
  }
1535
- function DropdownMenuSubmenuChevronIcon() {
1536
- return /* @__PURE__ */ jsx("svg", {
1537
- width: "12",
1538
- height: "12",
1539
- viewBox: "0 0 12 12",
1540
- "aria-hidden": true,
1541
- className: "size-3 shrink-0",
1542
- children: /* @__PURE__ */ jsx("g", {
1543
- fill: "none",
1544
- strokeLinecap: "round",
1545
- strokeLinejoin: "round",
1546
- strokeWidth: "1",
1547
- stroke: "currentColor",
1548
- children: /* @__PURE__ */ jsx("path", { d: "M4.25 2.5L7.75 6l-3.5 3.5" })
1549
- })
1550
- });
1551
- }
1552
- function DropdownMenuCheckIcon() {
1553
- return /* @__PURE__ */ jsx("svg", {
1554
- width: "12",
1555
- height: "12",
1556
- viewBox: "0 0 12 12",
1557
- "aria-hidden": true,
1558
- className: "size-3 shrink-0",
1559
- children: /* @__PURE__ */ jsx("g", {
1560
- fill: "none",
1561
- strokeLinecap: "round",
1562
- strokeLinejoin: "round",
1563
- strokeWidth: "1",
1564
- stroke: "currentColor",
1565
- children: /* @__PURE__ */ jsx("path", { d: "M1.75 6.41L4.4 9.25L10.25 2.99" })
1566
- })
1567
- });
1568
- }
1569
- function DropdownMenuRadioGroup({ ...props }) {
1570
- return /* @__PURE__ */ jsx(Menu.RadioGroup, {
1571
- "data-slot": "dropdown-menu-radio-group",
3162
+ /** The item card's block: the id line, the title and the details row, in that order. */
3163
+ function PreviewCardBody({ className, ...props }) {
3164
+ const { body } = previewCard();
3165
+ return /* @__PURE__ */ jsx("div", {
3166
+ "data-slot": "preview-card-body",
3167
+ className: body({ className }),
1572
3168
  ...props
1573
3169
  });
1574
3170
  }
1575
- function DropdownMenuRadioItem({ className, children, ...props }) {
1576
- const { radioItem, radioLabel, indicator } = dropdownMenu();
1577
- return /* @__PURE__ */ jsxs(Menu.RadioItem, {
1578
- "data-slot": "dropdown-menu-radio-item",
1579
- className: radioItem({ className }),
1580
- ...props,
1581
- children: [renderRadioItemChildren(children, radioLabel()), /* @__PURE__ */ jsx("span", {
1582
- className: indicator(),
1583
- children: /* @__PURE__ */ jsx(Menu.RadioItemIndicator, { children: /* @__PURE__ */ jsx(DropdownMenuCheckIcon, {}) })
1584
- })]
1585
- });
1586
- }
1587
- function renderRadioItemChildren(children, className) {
1588
- if (typeof children === "string" || typeof children === "number") return /* @__PURE__ */ jsx("span", {
1589
- "data-slot": "dropdown-menu-radio-item-label",
1590
- className,
1591
- children
1592
- });
1593
- if (Array.isArray(children) && children.every((child) => typeof child === "string")) return /* @__PURE__ */ jsx("span", {
1594
- "data-slot": "dropdown-menu-radio-item-label",
1595
- className,
1596
- children
1597
- });
1598
- return children;
1599
- }
1600
- function DropdownMenuRadioItemIndicator({ className, ...props }) {
1601
- return /* @__PURE__ */ jsx(Menu.RadioItemIndicator, {
1602
- "data-slot": "dropdown-menu-radio-item-indicator",
1603
- className,
3171
+ /** The muted line above the title that identifies the item, such as its id. One line, truncated. */
3172
+ function PreviewCardEyebrow({ className, ...props }) {
3173
+ const { eyebrow } = previewCard();
3174
+ return /* @__PURE__ */ jsx("p", {
3175
+ "data-slot": "preview-card-eyebrow",
3176
+ className: eyebrow({ className }),
1604
3177
  ...props
1605
3178
  });
1606
3179
  }
1607
- function DropdownMenuCheckboxItem({ className, children, ...props }) {
1608
- const { item, indicator } = dropdownMenu();
1609
- return /* @__PURE__ */ jsxs(Menu.CheckboxItem, {
1610
- "data-slot": "dropdown-menu-checkbox-item",
1611
- className: item({ className }),
1612
- ...props,
1613
- children: [children, /* @__PURE__ */ jsx("span", {
1614
- className: indicator(),
1615
- children: /* @__PURE__ */ jsx(Menu.CheckboxItemIndicator, { children: /* @__PURE__ */ jsx(DropdownMenuCheckIcon, {}) })
1616
- })]
3180
+ /** The row under the title: one state chip, then muted details. Wraps when it runs out of room. */
3181
+ function PreviewCardMeta({ className, ...props }) {
3182
+ const { meta } = previewCard();
3183
+ return /* @__PURE__ */ jsx("div", {
3184
+ "data-slot": "preview-card-meta",
3185
+ className: meta({ className }),
3186
+ ...props
1617
3187
  });
1618
3188
  }
1619
- function DropdownMenuCheckboxItemIndicator({ className, ...props }) {
1620
- return /* @__PURE__ */ jsx(Menu.CheckboxItemIndicator, {
1621
- "data-slot": "dropdown-menu-checkbox-item-indicator",
1622
- className,
3189
+ /** The row of at most two buttons. */
3190
+ function PreviewCardActions({ className, ...props }) {
3191
+ const { actions } = previewCard();
3192
+ return /* @__PURE__ */ jsx("div", {
3193
+ "data-slot": "preview-card-actions",
3194
+ className: actions({ className }),
1623
3195
  ...props
1624
3196
  });
1625
3197
  }
1626
- /**
1627
- * One choice that is another page, such as the same page in another
1628
- * language: a link, so it opens in a new tab and can be copied. Its
1629
- * `href` goes on the item; `current` marks the page being shown.
1630
- */
1631
- function DropdownMenuLinkItem({ current = false, className, children, ...props }) {
1632
- const { radioItem, radioLabel, indicator } = dropdownMenu();
1633
- return /* @__PURE__ */ jsxs(Menu.LinkItem, {
1634
- "data-slot": "dropdown-menu-link-item",
1635
- "aria-current": current ? "page" : void 0,
1636
- "data-checked": current ? "" : void 0,
1637
- className: radioItem({ className }),
1638
- ...props,
1639
- children: [renderRadioItemChildren(children, radioLabel()), current ? /* @__PURE__ */ jsx("span", {
1640
- className: indicator(),
1641
- children: /* @__PURE__ */ jsx(DropdownMenuCheckIcon, {})
1642
- }) : null]
3198
+ /** The caption under a hairline at the bottom of the card: where the item comes from. */
3199
+ function PreviewCardFooter({ className, ...props }) {
3200
+ const { footer } = previewCard();
3201
+ return /* @__PURE__ */ jsx("p", {
3202
+ "data-slot": "preview-card-footer",
3203
+ className: footer({ className }),
3204
+ ...props
1643
3205
  });
1644
3206
  }
1645
3207
  function contentBox(element) {
@@ -2131,8 +3693,8 @@ const messageItem = tv$1({
2131
3693
  "has-data-popup-open:[--mi-toolbar-opacity:1] has-data-popup-open:[--mi-toolbar-pe:auto]",
2132
3694
  "min-h-0 px-2 py-timeline-row border-t-(length:--spacing-timeline-turn) border-transparent bg-clip-padding",
2133
3695
  "hover:[--mi-ct-color:var(--foreground-hint)] has-data-popup-open:[--mi-ct-color:var(--foreground-hint)]",
2134
- "bg-(--mi-row-fill) [--mi-row-fill:initial]",
2135
- "hover:[--mi-row-fill:var(--fill-subtle)]",
3696
+ "bg-(--mi-row-fill) [--mi-row-fill:var(--located-fill)]",
3697
+ "not-data-located:hover:[--mi-row-fill:var(--fill-subtle)]",
2136
3698
  "data-popup-open:[--mi-row-fill:var(--fill-subtle)] has-data-popup-open:[--mi-row-fill:var(--fill-subtle)]",
2137
3699
  "has-data-[slot=message-item-search-match]:[--mi-row-fill:var(--info-soft)] has-data-[slot=message-item-search-match]:hover:[--mi-row-fill:var(--info-soft)]",
2138
3700
  "has-[[data-slot=message-multi-select-checkbox][data-checked]]:[--mi-row-fill:var(--info-soft)]",
@@ -2225,11 +3787,12 @@ const messageItem = tv$1({
2225
3787
  });
2226
3788
  //#endregion
2227
3789
  //#region src/components/message-item/message-item-root.tsx
2228
- function MessageItem({ className, compact, render, ...props }) {
3790
+ function MessageItem({ className, compact, located = false, render, ...props }) {
2229
3791
  const { root } = messageItem({ compact });
2230
3792
  const defaultProps = {
2231
3793
  "data-message-item": "",
2232
3794
  "data-compact": compact ? "true" : void 0,
3795
+ "data-located": located ? "" : void 0,
2233
3796
  className: root({ className })
2234
3797
  };
2235
3798
  return useRender({
@@ -2882,10 +4445,38 @@ function MessageReactionTooltipMeta({ className, render, ...props }) {
2882
4445
  });
2883
4446
  }
2884
4447
  //#endregion
4448
+ //#region src/lib/inline-chip.ts
4449
+ /**
4450
+ * The one box every chip in message text shares: a reference chip (any
4451
+ * variant, loading or unavailable too), the yellow sticker of a mention of the
4452
+ * person reading, and an inline task chip. The rule and its numbers are in
4453
+ * message-item.md, Chips in running text.
4454
+ *
4455
+ * - Type: 0.875em of the sentence, semibold.
4456
+ * - Height: the line box of its text, the body line less 2px, with no padding
4457
+ * above or below: 20px on a desktop (body 14/22), 22px on a phone (16/24). A
4458
+ * chip is always 2px shorter than its line, so it never makes the line taller.
4459
+ * `--chip-line-height` overrides it for text that isn't on the body line.
4460
+ * - Alignment: the chip's text on the sentence's baseline, a leading mark
4461
+ * centred on the chip (alignment.md, Marks in running text).
4462
+ * - Sides: 6px at each end. A chip with a leading mark (a 14px icon, an app's
4463
+ * image or avatar in `MessageReferenceIcon`'s 14px box, the loading spinner,
4464
+ * a task's 10px status pixel, each in its own box) takes
4465
+ * 4.5px at each end, so the ink at its two ends sits equally far in
4466
+ * (alignment.md, Chips), and the mark is 4px from the text. A note after the
4467
+ * label keeps the same 4px.
4468
+ * - Corners: `rounded-sticker`.
4469
+ */
4470
+ const inlineChip = [
4471
+ "items-baseline gap-1 px-1.5 py-0 align-baseline",
4472
+ "has-[>svg,>[data-slot=spinner],>[data-slot=message-reference-icon]]:px-[4.5px]",
4473
+ "[font-size:0.875em] leading-[var(--chip-line-height,calc(var(--text-body--line-height)-2px))] font-semibold",
4474
+ "rounded-sticker"
4475
+ ];
4476
+ //#endregion
2885
4477
  //#region src/components/message-item/message-reference.recipe.ts
2886
4478
  const chip = (fill) => [
2887
- "items-baseline gap-0.5 rounded-sticker px-1.5 py-px has-[>svg]:px-[4.5px]",
2888
- "[font-size:0.875em] leading-5 font-semibold",
4479
+ ...inlineChip,
2889
4480
  "[--message-reference-em:1em]",
2890
4481
  "[&_svg]:size-3.5 [&_svg]:text-foreground-muted",
2891
4482
  fill
@@ -2915,23 +4506,36 @@ const messageReference = tv$1({
2915
4506
  SUFFIX_LIFT,
2916
4507
  "font-mono text-label leading-none font-normal text-foreground-hint"
2917
4508
  ],
2918
- indicator: ["pointer-events-none absolute -right-1.5 top-0 inline-flex size-2 shrink-0 items-center justify-center", "*:data-[slot=status]:size-full"]
4509
+ icon: ["inline-flex size-3.5 shrink-0 items-center justify-center self-center", "*:size-full *:shrink-0 [&>img]:rounded-[2px] [&>img]:object-cover"],
4510
+ indicator: ["pointer-events-none absolute -right-1.5 top-0 inline-flex size-2 shrink-0 items-center justify-center", "*:data-[slot=status]:size-full"],
4511
+ readMark: [
4512
+ "relative inline-block shrink-0 self-center",
4513
+ "-mr-[calc(0.857em*2/24)]",
4514
+ "align-[calc(0.7em/2-0.857em/2)] leading-none",
4515
+ "text-foreground-hint",
4516
+ "[&>svg]:block [&>svg]:size-[0.857em]"
4517
+ ]
4518
+ },
4519
+ variants: {
4520
+ variant: {
4521
+ primary: { root: [
4522
+ "inline-flex cursor-pointer",
4523
+ ...inlineChip,
4524
+ "[--message-reference-em:1em]",
4525
+ "text-sticker-ink no-underline",
4526
+ "bg-brand inset-ring-[1.5px] inset-ring-sticker-ink",
4527
+ "hover:overlay-sticker-medium active:overlay-sticker-medium"
4528
+ ] },
4529
+ secondary: { root: ["inline cursor-pointer font-bold text-foreground", "decoration-foreground decoration-[1.5px] underline-offset-[3px] hover:underline"] },
4530
+ accent: { root: linkChip("bg-attention-soft") },
4531
+ muted: { root: linkChip("bg-inactive-soft") },
4532
+ info: { root: linkChip("bg-info-soft") },
4533
+ link: { root: linkChip("bg-info-soft") },
4534
+ pending: { root: chip("cursor-wait bg-inactive-soft text-foreground-muted") },
4535
+ unavailable: { root: chip("cursor-default bg-inactive-soft text-foreground-hint") }
4536
+ },
4537
+ capped: { true: { root: "max-w-[min(100%,24em)]" } }
2919
4538
  },
2920
- variants: { variant: {
2921
- primary: { root: [
2922
- "inline-flex cursor-pointer rounded-sticker px-[3px]",
2923
- "font-semibold text-sticker-ink no-underline",
2924
- "bg-brand inset-ring-[1.5px] inset-ring-sticker-ink ring-[1.5px] ring-sticker-edge",
2925
- "hover:overlay-sticker-medium active:overlay-sticker-medium"
2926
- ] },
2927
- secondary: { root: ["inline cursor-pointer font-bold text-foreground", "decoration-foreground decoration-[1.5px] underline-offset-[3px] hover:underline"] },
2928
- accent: { root: linkChip("bg-attention-soft") },
2929
- muted: { root: linkChip("bg-inactive-soft") },
2930
- info: { root: linkChip("bg-info-soft") },
2931
- link: { root: linkChip("bg-info-soft") },
2932
- pending: { root: chip("cursor-wait bg-inactive-soft text-foreground-muted") },
2933
- unavailable: { root: chip("cursor-default bg-inactive-soft text-foreground-hint") }
2934
- } },
2935
4539
  defaultVariants: { variant: "muted" }
2936
4540
  });
2937
4541
  //#endregion
@@ -2958,8 +4562,36 @@ function MessageReferenceIndicator({ className, render, ...props }) {
2958
4562
  state: { slot: "message-reference-indicator" }
2959
4563
  });
2960
4564
  }
2961
- function MessageReferenceChip({ className, render, variant = "muted", ...props }) {
2962
- const { root } = messageReference({ variant });
4565
+ /**
4566
+ * The read receipt on a mention: a muted check after the mention's text, once
4567
+ * the one mentioned has read the message. Put it last inside the mention, and
4568
+ * render nothing until then. The check is decorative; `children` is what a
4569
+ * screen reader says instead ("Read"), visually hidden.
4570
+ */
4571
+ function MessageReferenceReadMark({ children, className, render, ...props }) {
4572
+ const { readMark } = messageReference();
4573
+ const defaultProps = {
4574
+ className: readMark({ className }),
4575
+ children: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Check, {
4576
+ "aria-hidden": true,
4577
+ strokeWidth: 2
4578
+ }), children == null ? null : /* @__PURE__ */ jsx("span", {
4579
+ className: "sr-only",
4580
+ children
4581
+ })] })
4582
+ };
4583
+ return useRender({
4584
+ defaultTagName: "span",
4585
+ render,
4586
+ props: mergeProps(defaultProps, props),
4587
+ state: { slot: "message-reference-read-mark" }
4588
+ });
4589
+ }
4590
+ function MessageReferenceChip({ className, render, variant = "muted", capped, ...props }) {
4591
+ const { root } = messageReference({
4592
+ variant,
4593
+ capped
4594
+ });
2963
4595
  return useReferenceRender(root({ className }), render, props);
2964
4596
  }
2965
4597
  function MessageReferenceLabel({ className, render, ...props }) {
@@ -2981,11 +4613,29 @@ function MessageReferenceSuffix({ className, render, ...props }) {
2981
4613
  defaultTagName: "span",
2982
4614
  render,
2983
4615
  props: mergeProps(defaultProps, props),
2984
- state: { slot: "message-reference-suffix" }
4616
+ state: { slot: "message-reference-suffix" }
4617
+ });
4618
+ }
4619
+ /**
4620
+ * The chip's leading icon when it is not a plain svg: an image or a `3xs`
4621
+ * Avatar, such as an app's registered icon. It holds one child at 14px,
4622
+ * centred on the chip. Put it first in the chip.
4623
+ */
4624
+ function MessageReferenceIcon({ className, render, ...props }) {
4625
+ const { icon } = messageReference();
4626
+ const defaultProps = { className: icon({ className }) };
4627
+ return useRender({
4628
+ defaultTagName: "span",
4629
+ render,
4630
+ props: mergeProps(defaultProps, props),
4631
+ state: { slot: "message-reference-icon" }
2985
4632
  });
2986
4633
  }
2987
- function MessageReferencePending({ className, render, ...props }) {
2988
- const { root } = messageReference({ variant: "pending" });
4634
+ function MessageReferencePending({ className, render, capped, ...props }) {
4635
+ const { root } = messageReference({
4636
+ variant: "pending",
4637
+ capped
4638
+ });
2989
4639
  return useReferenceRender(root({ className }), render, props);
2990
4640
  }
2991
4641
  function MessageReferenceUnavailable({ className, render, ...props }) {
@@ -3551,99 +5201,31 @@ function MessageListJumpPillDismiss({ className, children, render, nativeButton,
3551
5201
  })
3552
5202
  });
3553
5203
  }
3554
- const holds = /* @__PURE__ */ new WeakMap();
3555
- function touchPoint(event) {
3556
- const touch = event.touches?.[0] ?? event.changedTouches?.[0];
3557
- return touch ? {
3558
- x: touch.clientX,
3559
- y: touch.clientY
3560
- } : null;
3561
- }
3562
- function farther(from, to, limit) {
3563
- return Math.hypot(to.x - from.x, to.y - from.y) > limit;
3564
- }
3565
- var HoldThenMoveTouchSensorImpl = class extends TouchSensor {
3566
- static activators = TouchSensor.activators;
3567
- static setup() {
3568
- return TouchSensor.setup();
3569
- }
3570
- attach() {
3571
- super.attach();
3572
- const self = this;
3573
- const { holdMs = 500 } = self.props.options;
3574
- const state = {
3575
- heldAt: null,
3576
- last: null,
3577
- timer: setTimeout(() => {
3578
- state.heldAt = state.last ?? self.initialCoordinates;
3579
- }, holdMs)
3580
- };
3581
- holds.set(this, state);
3582
- }
3583
- handleMove(event) {
3584
- const self = this;
3585
- const state = holds.get(this);
3586
- if (self.activated || !state) {
3587
- super.handleMove(event);
3588
- return;
3589
- }
3590
- if ((event.touches?.length ?? 1) > 1) {
3591
- self.handleCancel();
3592
- return;
3593
- }
3594
- const point = touchPoint(event);
3595
- const start = self.initialCoordinates;
3596
- if (!point || !start) return;
3597
- state.last = point;
3598
- const { distance = 10, tolerance = 10 } = self.props.options;
3599
- if (!state.heldAt) {
3600
- if (farther(start, point, tolerance)) self.handleCancel();
3601
- return;
3602
- }
3603
- if (event.cancelable) event.preventDefault();
3604
- if (farther(state.heldAt, point, distance)) self.handleStart();
3605
- }
3606
- detach() {
3607
- const state = holds.get(this);
3608
- if (state) clearTimeout(state.timer);
3609
- holds.delete(this);
3610
- super.detach();
3611
- }
3612
- };
3613
- /** A TouchSensor that starts a drag on the first move after a still hold (see above). */
3614
- const HoldThenMoveTouchSensor = HoldThenMoveTouchSensorImpl;
3615
- /**
3616
- * dnd-kit's PointerSensor for a mouse and a pen only: a finger's pointerdown is
3617
- * left to `HoldThenMoveTouchSensor`.
3618
- */
3619
- var MouseOnlyPointerSensor = class extends PointerSensor {
3620
- static activators = [{
3621
- eventName: "onPointerDown",
3622
- handler: (event, options) => event.nativeEvent.pointerType === "touch" ? false : PointerSensor.activators[0].handler(event, options)
3623
- }];
3624
- };
3625
- /**
3626
- * `useSensor(HoldThenMoveTouchSensor, …)` with the activation the sensor relies
3627
- * on: no constraint dnd-kit would act on by itself, so only a move after the
3628
- * hold starts the drag.
3629
- */
3630
- function useHoldThenMoveTouchSensor({ holdMs = 500, distance = 10, tolerance = 10 } = {}) {
3631
- const options = useMemo(() => ({
3632
- holdMs,
3633
- distance,
3634
- tolerance,
3635
- activationConstraint: { distance: Number.POSITIVE_INFINITY }
3636
- }), [
3637
- holdMs,
3638
- distance,
3639
- tolerance
3640
- ]);
3641
- return useSensor(HoldThenMoveTouchSensor, options);
3642
- }
3643
- /** The mouse / pen sensor that goes with it: picks up after `distance` px. */
3644
- function useMouseOnlyPointerSensor(distance) {
3645
- const options = useMemo(() => ({ activationConstraint: { distance } }), [distance]);
3646
- return useSensor(MouseOnlyPointerSensor, options);
5204
+ //#endregion
5205
+ //#region src/components/skeleton/skeleton.tsx
5206
+ const skeleton = tv$1({
5207
+ slots: { root: [
5208
+ "animate-pulse motion-reduce:animate-none",
5209
+ "pointer-events-none",
5210
+ "data-[variant=line]:rounded-sticker data-[variant=line]:bg-fill-muted",
5211
+ "data-[variant=block]:rounded-sticker data-[variant=block]:bg-fill-muted"
5212
+ ] },
5213
+ variants: { variant: {
5214
+ line: { root: "h-3" },
5215
+ block: { root: "" },
5216
+ circle: { root: ["rounded-sticker bg-fill-muted"] }
5217
+ } },
5218
+ defaultVariants: { variant: "block" }
5219
+ });
5220
+ function Skeleton({ variant = "block", className, ...props }) {
5221
+ const { root } = skeleton({ variant });
5222
+ return /* @__PURE__ */ jsx("div", {
5223
+ "aria-hidden": "true",
5224
+ "data-slot": "skeleton",
5225
+ "data-variant": variant,
5226
+ className: root({ className }),
5227
+ ...props
5228
+ });
3647
5229
  }
3648
5230
  //#endregion
3649
5231
  //#region src/components/inbox-item/inbox-item.recipe.ts
@@ -3710,7 +5292,7 @@ const inboxItem = tv$1({
3710
5292
  "flex shrink-0",
3711
5293
  "mt-0 flex-col items-end gap-2 pt-px",
3712
5294
  "max-md:flex-col max-md:items-end max-md:gap-2",
3713
- "after:hidden after:size-[9px] after:shrink-0 after:rounded-[2px] after:border-[1.5px] after:border-sticker-ink after:bg-attention-400",
5295
+ "after:hidden after:size-2.5 after:-mb-px after:shrink-0 after:rounded-[2px] after:border-[1.5px] after:border-sticker-ink after:bg-attention-400",
3714
5296
  "group-data-[unread=true]/inbox-item:after:block",
3715
5297
  "*:not-data-[reveal=hover]:opacity-[calc(1-var(--inbox-item-reveal-aside,0)*var(--inbox-item-revealed,0))] after:opacity-[calc(1-var(--inbox-item-reveal-aside,0)*var(--inbox-item-revealed,0))]",
3716
5298
  "*:not-data-[reveal=hover]:mr-(--inbox-item-aside-room,0px) after:mr-(--inbox-item-aside-room,0px)",
@@ -3785,4 +5367,274 @@ const inboxItem = tv$1({
3785
5367
  }]
3786
5368
  });
3787
5369
  //#endregion
3788
- export { MessageReactionPickerStatic as $, DialogTitle as $n, DropdownMenuFooter as $t, MessageEmbedFooter as A, useMobileViewport as An, MessageItemEmbedded as At, MessageReferenceChip as B, textLinkVariants as Bn, AvatarFallback as Bt, MessageQuotedPreviewLoading as C, PanelSection as Cn, MessageItemSenderButton as Ct, MessageQuotedPreviewUnavailable as D, ScrollAreaScrollbar as Dn, MessageItemBody as Dt, MessageQuotedPreviewTimestamp as E, ScrollAreaCorner as En, MessageItemAttachments as Et, MessageTextSelectionToolbarSeparator as F, TooltipContent as Fn, MessageItemGutter as Ft, MessageReferenceText as G, DialogContent as Gn, measureContent as Gt, MessageReferenceLabel as H, Dialog$1 as Hn, AvatarGroupCount as Ht, MessageMultiSelect as I, TooltipPortal as In, MessageItem as It, MessageReactionChip as J, DialogHeader as Jn, readNow as Jt, MessageReferenceUnavailable as K, DialogDescription as Kn, measureElement as Kt, MessageMultiSelectCheckbox as L, TooltipProvider as Ln, searchHit as Lt, messageEmbed as M, CheckboxIndicator as Mn, MessageItemSearchMatch as Mt, MessageTextSelectionToolbar as N, PRESENCE_COLOR as Nn, MessageItemAvatarSlot as Nt, MessageEmbed as O, ScrollAreaThumb as On, MessageItemContent as Ot, MessageTextSelectionToolbarButton as P, Tooltip$1 as Pn, MessageItemContinuationTime as Pt, MessageReactionPickerContent as Q, DialogSurface as Qn, DropdownMenuContent as Qt, MessageMultiSelectToolbar as R, TooltipTrigger as Rn, Avatar$1 as Rt, MessageQuotedPreviewHeader as S, PanelScrollViewport as Sn, MessageItemSenderBadges as St, MessageQuotedPreviewThread as T, ScrollAreaContent as Tn, MessageItemTime as Tt, MessageReferencePending as U, DialogBody as Un, AvatarImage as Ut, MessageReferenceIndicator as V, Badge as Vn, AvatarGroup as Vt, MessageReferenceSuffix as W, DialogClose as Wn, useOverflowTitle as Wt, MessageReactionErrorIndicator as X, DialogOverlay as Xn, DropdownMenuCheckboxItem as Xt, MessageReactionCount as Y, DialogNav as Yn, DropdownMenu as Yt, MessageReactionGlyph as Z, DialogPortal as Zn, DropdownMenuCheckboxItemIndicator as Zt, MessageQuotedPreviewAuthorDelegatedBy as _, Panel as _n, MessageItemDelegatedByName as _t, MessageListJumpPill as a, DropdownMenuPopup as an, useParentOverlayKind as ar, MessageReactionTooltipNameText as at, MessageQuotedPreviewChannel as b, PanelHeaderRoot as bn, MessageItemMeta as bt, MessageListDateDivider as c, DropdownMenuRadioItem as cn, useThemeScopeProps as cr, MessageItemBadge as ct, MessageListNotice as d, DropdownMenuShortcut as dn, tv$1 as dr, MessageItemSavedIndicator as dt, DropdownMenuItem as en, DialogTrigger as er, MessageReactionQuickButton as et, MessageList as f, DropdownMenuSubmenu as fn, controlIconInset as fr, MessageItemToolbar as ft, MessageQuotedPreviewAuthor as g, KbdGroup as gn, MessageItemDelegatedByButton as gt, MessageQuotedPreviewAttachments as h, Kbd as hn, decisionWidth as hr, MessageItemDelegatedBy as ht, MessageListJumpControls as i, DropdownMenuLinkItem as in, defaultNativeButton as ir, MessageReactionTooltipName as it, MessageEmbedHeader as j, Checkbox$1 as jn, MessageItemFooter as jt, MessageEmbedContent as k, ScrollAreaViewport as kn, MessageItemEdited as kt, MessageListEndFade as l, DropdownMenuRadioItemIndicator as ln, useTheme as lr, MessageItemBadgeSeparator as lt, MessageQuotedPreviewAttachment as m, DropdownMenuTrigger as mn, headEdges as mr, MessageItemDelegatedAvatar as mt, useHoldThenMoveTouchSensor as n, DropdownMenuItemLabel as nn, buttonVariants as nr, MessageReactionTooltipContent as nt, MessageListJumpPillDismiss as o, DropdownMenuPortal as on, withOverlayLayer as or, MessageReactionTooltipNames as ot, MessageQuotedPreview as p, DropdownMenuSubmenuTrigger as pn, headEdgePull as pr, MessageItemToolbarButton as pt, MessageReactionAddButton as q, DialogFooter as qn, observeTruncation as qt, useMouseOnlyPointerSensor as r, DropdownMenuLabel as rn, Spinner as rr, MessageReactionTooltipMeta as rt, MessageListJumpPillGroup as s, DropdownMenuRadioGroup as sn, resolveThemeScopeProps as sr, MessageReactionTooltipSeparator as st, inboxItem as t, DropdownMenuItemCount as tn, Button$1 as tr, MessageReactionQuickRow as tt, MessageListHistoryLimited as u, DropdownMenuSeparator as un, ThemeStateContext as ur, MessageItemReadReceipt as ut, MessageQuotedPreviewAuthorMeta as v, PanelBody as vn, MessageItemHeader as vt, MessageQuotedPreviewText as w, ScrollArea$1 as wn, MessageItemSenderGroup as wt, MessageQuotedPreviewContent as x, PanelScrollContent as xn, MessageItemSender as xt, MessageQuotedPreviewAuthorName as y, PanelFooter as yn, MessageItemInlineStatus as yt, MessageMultiSelectToolbarActions as z, TextLink as zn, AvatarBadge as zt };
5370
+ //#region src/components/inbox-item/inbox-item.tsx
5371
+ const InboxItemDisabledContext = createContext(false);
5372
+ const InboxItemDensityContext = createContext(void 0);
5373
+ function InboxItem({ active = false, className, density, disabled = false, render, ...props }) {
5374
+ const { item } = inboxItem({ density });
5375
+ const defaultProps = {
5376
+ "aria-disabled": disabled || void 0,
5377
+ "data-active": active ? "true" : void 0,
5378
+ "data-density": density,
5379
+ "data-disabled": disabled ? "true" : void 0,
5380
+ "data-slot": "inbox-item",
5381
+ className: item({ className: `group/inbox-item ${className ?? ""}`.trim() })
5382
+ };
5383
+ const element = useRender({
5384
+ defaultTagName: "div",
5385
+ render,
5386
+ props: mergeProps(defaultProps, props)
5387
+ });
5388
+ return /* @__PURE__ */ jsx(InboxItemDisabledContext.Provider, {
5389
+ value: disabled,
5390
+ children: /* @__PURE__ */ jsx(InboxItemDensityContext.Provider, {
5391
+ value: density,
5392
+ children: element
5393
+ })
5394
+ });
5395
+ }
5396
+ function InboxItemTrigger({ className, onClick, render, ...props }) {
5397
+ const disabled = useContext(InboxItemDisabledContext);
5398
+ const { itemTrigger } = inboxItem();
5399
+ const defaultProps = {
5400
+ type: render ? void 0 : "button",
5401
+ "aria-disabled": disabled || void 0,
5402
+ "data-disabled": disabled ? "true" : void 0,
5403
+ "data-slot": "inbox-item-open",
5404
+ disabled: render ? void 0 : disabled,
5405
+ tabIndex: disabled && render ? -1 : void 0,
5406
+ className: itemTrigger({ className }),
5407
+ onClick: (event) => {
5408
+ if (disabled) {
5409
+ event.preventDefault();
5410
+ event.stopPropagation();
5411
+ return;
5412
+ }
5413
+ onClick?.(event);
5414
+ }
5415
+ };
5416
+ return useRender({
5417
+ defaultTagName: "button",
5418
+ render,
5419
+ props: mergeProps(defaultProps, props)
5420
+ });
5421
+ }
5422
+ function InboxItemLeading({ className, render, ...props }) {
5423
+ const { itemLeading } = inboxItem();
5424
+ const defaultProps = {
5425
+ "data-slot": "inbox-item-leading",
5426
+ className: itemLeading({ className })
5427
+ };
5428
+ return useRender({
5429
+ defaultTagName: "div",
5430
+ render,
5431
+ props: mergeProps(defaultProps, props)
5432
+ });
5433
+ }
5434
+ function InboxItemSelection({ className, render, ...props }) {
5435
+ const { itemSelection } = inboxItem();
5436
+ const defaultProps = {
5437
+ "data-slot": "inbox-item-selection",
5438
+ className: itemSelection({ className })
5439
+ };
5440
+ return useRender({
5441
+ defaultTagName: "div",
5442
+ render,
5443
+ props: mergeProps(defaultProps, props)
5444
+ });
5445
+ }
5446
+ function InboxItemContent({ className, render, ...props }) {
5447
+ const { itemContent } = inboxItem();
5448
+ const defaultProps = {
5449
+ "data-slot": "inbox-item-content",
5450
+ className: itemContent({ className })
5451
+ };
5452
+ return useRender({
5453
+ defaultTagName: "div",
5454
+ render,
5455
+ props: mergeProps(defaultProps, props)
5456
+ });
5457
+ }
5458
+ function InboxItemContext({ className, render, ...props }) {
5459
+ const { itemContext } = inboxItem();
5460
+ const defaultProps = {
5461
+ "data-slot": "inbox-item-context",
5462
+ className: itemContext({ className })
5463
+ };
5464
+ return useRender({
5465
+ defaultTagName: "div",
5466
+ render,
5467
+ props: mergeProps(defaultProps, props)
5468
+ });
5469
+ }
5470
+ function InboxItemSubtitle({ className, render, ...props }) {
5471
+ const { itemSubtitle } = inboxItem();
5472
+ const defaultProps = {
5473
+ "data-slot": "inbox-item-subtitle",
5474
+ className: itemSubtitle({ className })
5475
+ };
5476
+ return useRender({
5477
+ defaultTagName: "div",
5478
+ render,
5479
+ props: mergeProps(defaultProps, props)
5480
+ });
5481
+ }
5482
+ function InboxItemHeading({ className, render, ...props }) {
5483
+ const { itemHeading } = inboxItem();
5484
+ const defaultProps = {
5485
+ "data-slot": "inbox-item-heading",
5486
+ className: itemHeading({ className })
5487
+ };
5488
+ return useRender({
5489
+ defaultTagName: "div",
5490
+ render,
5491
+ props: mergeProps(defaultProps, props)
5492
+ });
5493
+ }
5494
+ function InboxItemTitle({ className, render, ...props }) {
5495
+ const density = useContext(InboxItemDensityContext);
5496
+ const { itemTitle } = inboxItem({ density });
5497
+ const defaultProps = {
5498
+ "data-slot": "inbox-item-title",
5499
+ className: itemTitle({ className })
5500
+ };
5501
+ return useRender({
5502
+ defaultTagName: "div",
5503
+ render,
5504
+ props: mergeProps(defaultProps, props)
5505
+ });
5506
+ }
5507
+ function InboxItemTrailing({ className, render, ...props }) {
5508
+ const density = useContext(InboxItemDensityContext);
5509
+ const { itemTrailing } = inboxItem({ density });
5510
+ const defaultProps = {
5511
+ "data-slot": "inbox-item-trailing",
5512
+ className: itemTrailing({ className })
5513
+ };
5514
+ return useRender({
5515
+ defaultTagName: "div",
5516
+ render,
5517
+ props: mergeProps(defaultProps, props)
5518
+ });
5519
+ }
5520
+ function InboxItemTimestamp({ className, render, ...props }) {
5521
+ const density = useContext(InboxItemDensityContext);
5522
+ const { itemTimestamp } = inboxItem({ density });
5523
+ const defaultProps = {
5524
+ "data-slot": "inbox-item-timestamp",
5525
+ className: itemTimestamp({ className })
5526
+ };
5527
+ return useRender({
5528
+ defaultTagName: "span",
5529
+ render,
5530
+ props: mergeProps(defaultProps, props)
5531
+ });
5532
+ }
5533
+ function InboxItemMarks({ className, render, ...props }) {
5534
+ const { itemMarks } = inboxItem();
5535
+ const defaultProps = {
5536
+ "data-slot": "inbox-item-marks",
5537
+ className: itemMarks({ className })
5538
+ };
5539
+ return useRender({
5540
+ defaultTagName: "div",
5541
+ render,
5542
+ props: mergeProps(defaultProps, props)
5543
+ });
5544
+ }
5545
+ function InboxItemHoverTarget({ className, onClick, render, ...props }) {
5546
+ const { itemHoverTarget } = inboxItem();
5547
+ const defaultProps = {
5548
+ "data-slot": "inbox-item-hover-target",
5549
+ className: itemHoverTarget({ className }),
5550
+ onClick: (event) => {
5551
+ onClick?.(event);
5552
+ if (event.defaultPrevented) return;
5553
+ const trigger = event.currentTarget.closest("[data-slot=inbox-item]")?.querySelector("[data-slot=inbox-item-open]");
5554
+ if (!trigger) return;
5555
+ trigger.dispatchEvent(new MouseEvent("click", {
5556
+ bubbles: true,
5557
+ cancelable: true,
5558
+ detail: event.detail,
5559
+ altKey: event.altKey,
5560
+ ctrlKey: event.ctrlKey,
5561
+ metaKey: event.metaKey,
5562
+ shiftKey: event.shiftKey
5563
+ }));
5564
+ }
5565
+ };
5566
+ return useRender({
5567
+ defaultTagName: "span",
5568
+ render,
5569
+ props: mergeProps(defaultProps, props)
5570
+ });
5571
+ }
5572
+ function InboxItemBody({ className, render, ...props }) {
5573
+ const density = useContext(InboxItemDensityContext);
5574
+ const { itemBody } = inboxItem({ density });
5575
+ const defaultProps = {
5576
+ "data-slot": "inbox-item-body",
5577
+ className: itemBody({ className })
5578
+ };
5579
+ return useRender({
5580
+ defaultTagName: "p",
5581
+ render,
5582
+ props: mergeProps(defaultProps, props)
5583
+ });
5584
+ }
5585
+ function InboxItemMessage({ className, render, ...props }) {
5586
+ const density = useContext(InboxItemDensityContext);
5587
+ const { itemMessage } = inboxItem({ density });
5588
+ const defaultProps = {
5589
+ "data-slot": "inbox-item-message",
5590
+ className: itemMessage({ className })
5591
+ };
5592
+ return useRender({
5593
+ defaultTagName: "p",
5594
+ render,
5595
+ props: mergeProps(defaultProps, props)
5596
+ });
5597
+ }
5598
+ function InboxItemSender({ className, render, ...props }) {
5599
+ const { itemSender } = inboxItem();
5600
+ const defaultProps = {
5601
+ "data-slot": "inbox-item-sender",
5602
+ className: itemSender({ className })
5603
+ };
5604
+ return useRender({
5605
+ defaultTagName: "span",
5606
+ render,
5607
+ props: mergeProps(defaultProps, props)
5608
+ });
5609
+ }
5610
+ function InboxItemMetadata({ className, render, ...props }) {
5611
+ const { itemMetadata } = inboxItem();
5612
+ const defaultProps = {
5613
+ "data-slot": "inbox-item-metadata",
5614
+ className: itemMetadata({ className })
5615
+ };
5616
+ return useRender({
5617
+ defaultTagName: "div",
5618
+ render,
5619
+ props: mergeProps(defaultProps, props)
5620
+ });
5621
+ }
5622
+ function InboxItemActions({ className, render, reveal, ...props }) {
5623
+ const density = useContext(InboxItemDensityContext);
5624
+ const { itemActions } = inboxItem({
5625
+ density,
5626
+ reveal
5627
+ });
5628
+ const defaultProps = {
5629
+ "data-reveal": reveal,
5630
+ "data-slot": "inbox-item-actions",
5631
+ className: itemActions({ className })
5632
+ };
5633
+ return useRender({
5634
+ defaultTagName: "div",
5635
+ render,
5636
+ props: mergeProps(defaultProps, props)
5637
+ });
5638
+ }
5639
+ //#endregion
5640
+ export { MessageMultiSelect as $, PopoverTitle as $n, ListRowTitle as $r, MessageItemAvatarSlot as $t, MessageQuotedPreview as A, DialogSheetReset as Ai, MobileNavServerItem as An, PanelBody as Ar, MessageItemReadReceipt as At, MessageQuotedPreviewLoading as B, ThemeStateContext as Bi, MobileNavServerPopoverRowLabel as Bn, ScrollAreaThumb as Br, MessageItemMeta as Bt, MessageListJumpPillDismiss as C, DialogSurface as Ci, PreviewCardHeader as Cn, DropdownMenuShortcut as Cr, MessageReactionTooltipMeta as Ct, MessageListHistoryLimited as D, panelToggleAction as Di, PreviewCardSeparator as Dn, Kbd as Dr, MessageReactionTooltipSeparator as Dt, MessageListEndFade as E, PanelAction as Ei, PreviewCardPortal as En, DropdownMenuTrigger as Er, MessageReactionTooltipNames as Et, MessageQuotedPreviewAuthorMeta as F, OverlayLayerProvider as Fi, MobileNavServerPopoverGroup as Fn, PanelSection as Fr, MessageItemDelegatedBy as Ft, MessageEmbed as G, headEdgePull as Gi, PopoverBody as Gn, ListRow as Gr, MessageItemTime as Gt, MessageQuotedPreviewThread as H, stackedActions as Hi, PopoverActions as Hn, Checkbox$1 as Hr, MessageItemSenderBadges as Ht, MessageQuotedPreviewAuthorName as I, useParentOverlayKind as Ii, MobileNavServerPopoverList as In, ScrollArea$1 as Ir, MessageItemDelegatedByButton as It, MessageEmbedHeader as J, PopoverDescription as Jn, ListRowChevron as Jr, MessageItemContent as Jt, MessageEmbedContent as K, headEdges as Ki, PopoverClose as Kn, ListRowBody as Kr, MessageItemAttachments as Kt, MessageQuotedPreviewChannel as L, withOverlayLayer as Li, MobileNavServerPopoverRow as Ln, ScrollAreaContent as Lr, MessageItemDelegatedByName as Lt, MessageQuotedPreviewAttachments as M, buttonVariants as Mi, MobileNavServerPopoverContent as Mn, PanelHeaderRoot as Mr, MessageItemToolbar as Mt, MessageQuotedPreviewAuthor as N, Spinner as Ni, MobileNavServerPopoverDestination as Nn, PanelScrollContent as Nr, MessageItemToolbarButton as Nt, MessageListNotice as O, TextLink as Oi, PreviewCardTitle as On, KbdGroup as Or, MessageItemBadge as Ot, MessageQuotedPreviewAuthorDelegatedBy as P, defaultNativeButton as Pi, MobileNavServerPopoverDestinations as Pn, PanelScrollViewport as Pr, MessageItemDelegatedAvatar as Pt, MessageTextSelectionToolbarSeparator as Q, PopoverSeparator as Qn, ListRowMeta as Qr, MessageItemSearchMatch as Qt, MessageQuotedPreviewContent as R, useThemeScopeProps as Ri, MobileNavServerPopoverRowBody as Rn, ScrollAreaCorner as Rr, MessageItemHeader as Rt, MessageListJumpPill as S, DialogSheetBack as Si, PreviewCardFooter as Sn, DropdownMenuSeparator as Sr, MessageReactionTooltipContent as St, MessageListDateDivider as T, DialogTrigger as Ti, PreviewCardPopup as Tn, DropdownMenuSubmenuTrigger as Tr, MessageReactionTooltipNameText as Tt, MessageQuotedPreviewTimestamp as U, useStackedActions as Ui, PopoverArrow as Un, CheckboxIndicator as Ur, MessageItemSenderButton as Ut, MessageQuotedPreviewText as V, tv$1 as Vi, Popover$1 as Vn, ScrollAreaViewport as Vr, MessageItemSender as Vt, MessageQuotedPreviewUnavailable as W, controlIconInset as Wi, PopoverBackdrop as Wn, PRESENCE_COLOR as Wr, MessageItemSenderGroup as Wt, MessageTextSelectionToolbar as X, PopoverPopup as Xn, ListRowGroup as Xr, MessageItemEmbedded as Xt, messageEmbed as Y, PopoverHeader as Yn, ListRowDescription as Yr, MessageItemEdited as Yt, MessageTextSelectionToolbarButton as Z, PopoverPortal as Zn, ListRowIcon as Zr, MessageItemFooter as Zt, InboxItemTrailing as _, DialogFooterNote as _i, PreviewCardArrow as _n, DropdownMenuPopup as _r, MessageReactionGlyph as _t, InboxItemContext as a, TooltipProvider as ai, AvatarBadge as an, MobileNavLabel as ar, MessageReferenceIndicator as at, Skeleton as b, DialogOverlay as bi, PreviewCardDescription as bn, DropdownMenuRadioItem as br, MessageReactionQuickButton as bt, InboxItemLeading as c, DialogCommit as ci, AvatarGroupCount as cn, DropdownMenuCheckboxItem as cr, MessageReferenceReadMark as ct, InboxItemMetadata as d, Dialog$1 as di, measureContent as dn, DropdownMenuFooter as dr, MessageReferenceUnavailable as dt, ListRowTop as ei, MessageItemContinuationTime as en, PopoverTrigger as er, MessageMultiSelectCheckbox as et, InboxItemSelection as f, DialogBody as fi, measureElement as fn, DropdownMenuItem as fr, inlineChip as ft, InboxItemTitle as g, DialogFooter as gi, PreviewCardActions as gn, DropdownMenuLinkItem as gr, MessageReactionErrorIndicator as gt, InboxItemTimestamp as h, DialogDescription as hi, PreviewCard$1 as hn, DropdownMenuLabel as hr, MessageReactionCount as ht, InboxItemContent as i, TooltipPortal as ii, Avatar$1 as in, MobileNavItem as ir, MessageReferenceIcon as it, MessageQuotedPreviewAttachment as j, Button$1 as ji, MobileNavServerPopover as jn, PanelFooter as jr, MessageItemSavedIndicator as jt, MessageList as k, textLinkVariants as ki, PreviewCardTrigger as kn, Panel as kr, MessageItemBadgeSeparator as kt, InboxItemMarks as l, DialogSheetContent as li, AvatarImage as ln, DropdownMenuCheckboxItemIndicator as lr, MessageReferenceSuffix as lt, InboxItemSubtitle as m, DialogContent as mi, readNow as mn, DropdownMenuItemLabel as mr, MessageReactionChip as mt, InboxItemActions as n, Tooltip$1 as ni, MessageItem as nn, useHoldThenMoveTouchSensor as nr, MessageMultiSelectToolbarActions as nt, InboxItemHeading as o, TooltipTrigger as oi, AvatarFallback as on, MobileNavRoot as or, MessageReferenceLabel as ot, InboxItemSender as p, DialogClose as pi, observeTruncation as pn, DropdownMenuItemCount as pr, MessageReactionAddButton as pt, MessageEmbedFooter as q, decisionWidth as qi, PopoverContent as qn, ListRowBottom as qr, MessageItemBody as qt, InboxItemBody as r, TooltipContent as ri, searchHit as rn, useMouseOnlyPointerSensor as rr, MessageReferenceChip as rt, InboxItemHoverTarget as s, Badge as si, AvatarGroup as sn, DropdownMenu as sr, MessageReferencePending as st, InboxItem as t, ListRowValue as ti, MessageItemGutter as tn, PopoverViewport as tr, MessageMultiSelectToolbar as tt, InboxItemMessage as u, useMobileViewport as ui, useOverflowTitle as un, DropdownMenuContent as ur, MessageReferenceText as ut, InboxItemTrigger as v, DialogHeader as vi, PreviewCardBody as vn, DropdownMenuPortal as vr, MessageReactionPickerContent as vt, MessageListJumpPillGroup as w, DialogTitle as wi, PreviewCardMeta as wn, DropdownMenuSubmenu as wr, MessageReactionTooltipName as wt, MessageListJumpControls as x, DialogPortal as xi, PreviewCardEyebrow as xn, DropdownMenuRadioItemIndicator as xr, MessageReactionQuickRow as xt, inboxItem as y, DialogNav as yi, PreviewCardContent as yn, DropdownMenuRadioGroup as yr, MessageReactionPickerStatic as yt, MessageQuotedPreviewHeader as z, useTheme as zi, MobileNavServerPopoverRowDescription as zn, ScrollAreaScrollbar as zr, MessageItemInlineStatus as zt };