@proteos/ui 0.31.0 → 0.32.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.
Files changed (25) hide show
  1. package/dist/index.cjs +1302 -516
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +259 -1
  4. package/dist/index.d.ts +259 -1
  5. package/dist/index.js +1292 -520
  6. package/dist/index.js.map +1 -1
  7. package/package.json +2 -2
  8. package/src/components/draft-message-composer/create-draft-message-client.ts +27 -0
  9. package/src/components/draft-message-composer/draft-message-actions-store.test.ts +62 -0
  10. package/src/components/draft-message-composer/draft-message-actions-store.ts +81 -0
  11. package/src/components/draft-message-composer/draft-message-composer.stories.tsx +175 -0
  12. package/src/components/draft-message-composer/draft-message-composer.test.tsx +276 -0
  13. package/src/components/draft-message-composer/draft-message-composer.tsx +470 -0
  14. package/src/components/draft-message-composer/draft-message-outcome-store.test.ts +59 -0
  15. package/src/components/draft-message-composer/draft-message-outcome-store.ts +56 -0
  16. package/src/components/draft-message-composer/index.ts +19 -0
  17. package/src/components/draft-message-composer/types.ts +32 -0
  18. package/src/components/message-composer/index.ts +13 -0
  19. package/src/components/message-composer/message-composer.stories.tsx +155 -0
  20. package/src/components/message-composer/message-composer.test.tsx +320 -0
  21. package/src/components/message-composer/message-composer.tsx +480 -0
  22. package/src/components/recipient-select/index.ts +2 -0
  23. package/src/components/recipient-select/recipient-select.test.tsx +139 -0
  24. package/src/components/recipient-select/recipient-select.tsx +94 -0
  25. package/src/index.ts +3 -0
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { cn, Button, Skeleton } from './chunk-IKAX76MB.js';
2
2
  export { Button, Skeleton, buttonStyles, cn, skeletonStyles } from './chunk-IKAX76MB.js';
3
- import { Paperclip, BookText, Banknote, User, Link2, Braces, Brackets, ToggleLeft, CircleDot, Calendar, Hash, Type, File, FileImage, FileText, FileSpreadsheet, FileArchive, Loader2, X, ChevronDown, GripVertical, CircleX, Download, ChevronRight as ChevronRight$1, CircleCheck } from 'lucide-react';
3
+ import { Paperclip, BookText, Banknote, User, Link2, Braces, Brackets, ToggleLeft, CircleDot, Calendar, Hash, Type, File, FileImage, FileText, FileSpreadsheet, FileArchive, Loader2, X, ChevronDown, GripVertical, SendHorizontal, CircleX, Download, ChevronRight as ChevronRight$1, CircleCheck } from 'lucide-react';
4
4
  import { tv } from 'tailwind-variants';
5
- import { Tabs as Tabs$1, TabPanel, Tab, TabList, MenuTrigger as MenuTrigger$1, SubmenuTrigger as SubmenuTrigger$1, DialogTrigger, Popover as Popover$1, Dialog, DropZone, Text, FileTrigger, Heading, Label, ListBox, ListBoxItem, Breadcrumbs as Breadcrumbs$1, Breadcrumb, Link, Button as Button$1, Checkbox as Checkbox$1, CheckboxGroup as CheckboxGroup$1, FieldError, ComboBox, Input as Input$1, ComboBoxStateContext, Select as Select$1, DatePicker as DatePicker$1, Group, DateInput, DateSegment, ModalOverlay, Modal as Modal$1, TextField, Menu as Menu$1, MenuItem as MenuItem$1, Disclosure, DisclosurePanel, ProgressBar as ProgressBar$1, RadioGroup as RadioGroup$1, Radio as Radio$1, SelectValue, Separator as Separator$1, Switch as Switch$1, TextArea, TimeField as TimeField$1, TooltipTrigger as TooltipTrigger$1, Tooltip as Tooltip$1, Calendar as Calendar$1, CalendarGrid, CalendarGridHeader, CalendarHeaderCell, CalendarGridBody, CalendarCell, DatePickerStateContext } from 'react-aria-components';
5
+ import { Tabs as Tabs$1, TabPanel, Tab, TabList, MenuTrigger as MenuTrigger$1, SubmenuTrigger as SubmenuTrigger$1, DialogTrigger, Popover as Popover$1, Dialog, DropZone, Text, FileTrigger, Heading, Label, ListBox, ListBoxItem, Breadcrumbs as Breadcrumbs$1, Breadcrumb, Link, Button as Button$1, Checkbox as Checkbox$1, CheckboxGroup as CheckboxGroup$1, FieldError, ComboBox, Input as Input$1, ComboBoxStateContext, Select as Select$1, DatePicker as DatePicker$1, Group, DateInput, DateSegment, SelectValue, ModalOverlay, Modal as Modal$1, TextField, Menu as Menu$1, MenuItem as MenuItem$1, Disclosure, DisclosurePanel, ProgressBar as ProgressBar$1, RadioGroup as RadioGroup$1, Radio as Radio$1, Separator as Separator$1, Switch as Switch$1, TextArea, TimeField as TimeField$1, TooltipTrigger as TooltipTrigger$1, Tooltip as Tooltip$1, Calendar as Calendar$1, CalendarGrid, CalendarGridHeader, CalendarHeaderCell, CalendarGridBody, CalendarCell, DatePickerStateContext } from 'react-aria-components';
6
6
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
- import { lazy, createContext, useState, useRef, useEffect, useContext, useSyncExternalStore, useMemo, useLayoutEffect, Suspense, useId, Fragment as Fragment$1 } from 'react';
7
+ import { lazy, createContext, useState, useRef, useEffect, useContext, useSyncExternalStore, useMemo, useLayoutEffect, useCallback, Suspense, useId, Fragment as Fragment$1 } from 'react';
8
8
  import { formatAmount, parseAmount } from '@proteos/sdk';
9
9
  import { draggable, dropTargetForElements, monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
10
10
  import { extractClosestEdge, attachClosestEdge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';
@@ -573,7 +573,7 @@ function BadgeMultiSelect({
573
573
  autoFocus = false,
574
574
  className
575
575
  }) {
576
- const styles = fieldControlStyles({ size, invalid: isInvalid, appearance });
576
+ const styles2 = fieldControlStyles({ size, invalid: isInvalid, appearance });
577
577
  const focusWithin = appearance === "cell" ? "focus-within:border-accent focus-within:shadow-none" : "focus-within:border-accent focus-within:shadow-[var(--shadow-focus)]";
578
578
  const minHeight = appearance === "cell" ? "min-h-[29px]" : fieldMinHeight[size];
579
579
  const [isOpen, setIsOpen] = useState(false);
@@ -613,7 +613,7 @@ function BadgeMultiSelect({
613
613
  "data-invalid": isInvalid || void 0,
614
614
  "data-disabled": isDisabled || void 0,
615
615
  className: cn(
616
- styles.box(),
616
+ styles2.box(),
617
617
  "flex flex-wrap items-center gap-1.5 cursor-pointer",
618
618
  minHeight,
619
619
  fieldChipPadding[size],
@@ -1007,7 +1007,7 @@ function Combobox({
1007
1007
  allowsEmptyCollection = true,
1008
1008
  ...props
1009
1009
  }) {
1010
- const styles = fieldControlStyles({ size, appearance });
1010
+ const styles2 = fieldControlStyles({ size, appearance });
1011
1011
  const focusWithin = appearance === "cell" ? "focus-within:border-accent focus-within:shadow-none" : "focus-within:border-accent focus-within:shadow-[var(--shadow-focus)]";
1012
1012
  return /* @__PURE__ */ jsxs(
1013
1013
  ComboBox,
@@ -1026,7 +1026,7 @@ function Combobox({
1026
1026
  className: cn(
1027
1027
  // Box chrome on the wrapper; the inner <input> carries the padding
1028
1028
  // + font so the chevron button sits inside the same border + ring.
1029
- styles.box(),
1029
+ styles2.box(),
1030
1030
  "flex items-center gap-1 pr-2",
1031
1031
  focusWithin,
1032
1032
  inputWrapperClassName
@@ -1038,7 +1038,7 @@ function Combobox({
1038
1038
  placeholder,
1039
1039
  className: cn(
1040
1040
  "flex-1 min-w-0 bg-transparent outline-none",
1041
- styles.content(),
1041
+ styles2.content(),
1042
1042
  inputClassName
1043
1043
  )
1044
1044
  }
@@ -1478,16 +1478,16 @@ function CurrencyInput({
1478
1478
  onChange({ amount: nextAmount, currency_code: nextCode });
1479
1479
  };
1480
1480
  const selectedSymbol = currencyOptions.find((option) => option.code === code)?.symbol ?? code;
1481
- const styles = fieldControlStyles({ size, appearance, ...isInvalid ? { invalid: true } : {} });
1481
+ const styles2 = fieldControlStyles({ size, appearance, ...isInvalid ? { invalid: true } : {} });
1482
1482
  const focusWithin = appearance === "cell" ? "focus-within:border-accent focus-within:shadow-none" : "focus-within:border-accent focus-within:shadow-[var(--shadow-focus)]";
1483
1483
  return /* @__PURE__ */ jsxs("div", { className: cn("grid grid-cols-1 gap-1.5", className), children: [
1484
- /* @__PURE__ */ jsxs("div", { className: cn(styles.box(), "flex items-center gap-1 pr-1", focusWithin), children: [
1484
+ /* @__PURE__ */ jsxs("div", { className: cn(styles2.box(), "flex items-center gap-1 pr-1", focusWithin), children: [
1485
1485
  /* @__PURE__ */ jsx(
1486
1486
  "input",
1487
1487
  {
1488
1488
  className: cn(
1489
1489
  "min-w-0 flex-1 bg-transparent text-ink outline-none tabular-nums",
1490
- styles.content(),
1490
+ styles2.content(),
1491
1491
  // Sit a touch lower than a standard field by trimming the vertical
1492
1492
  // padding (cell mode keeps the fixed 29px row-editor height).
1493
1493
  appearance === "cell" ? "" : size === "sm" ? "py-1" : "py-1.5"
@@ -2561,7 +2561,7 @@ function DatePicker({
2561
2561
  className,
2562
2562
  ...props
2563
2563
  }) {
2564
- const styles = fieldControlStyles({ size, appearance });
2564
+ const styles2 = fieldControlStyles({ size, appearance });
2565
2565
  const focusWithin = appearance === "cell" ? "focus-within:border-accent focus-within:shadow-none" : "focus-within:border-accent focus-within:shadow-[var(--shadow-focus)]";
2566
2566
  return /* @__PURE__ */ jsxs(
2567
2567
  DatePicker$1,
@@ -2579,7 +2579,7 @@ function DatePicker({
2579
2579
  className: cn(
2580
2580
  // Box chrome on the wrapper; the DateInput inside carries padding +
2581
2581
  // font so the calendar trigger sits inside the same border + ring.
2582
- styles.box(),
2582
+ styles2.box(),
2583
2583
  "flex items-center gap-1 pr-1",
2584
2584
  focusWithin
2585
2585
  ),
@@ -2589,7 +2589,7 @@ function DatePicker({
2589
2589
  {
2590
2590
  className: cn(
2591
2591
  "flex-1 flex items-center min-w-0 text-ink",
2592
- styles.content(),
2592
+ styles2.content(),
2593
2593
  // Tabular alignment for date segments — overrides content's
2594
2594
  // font-sans on purpose.
2595
2595
  "font-mono"
@@ -2711,7 +2711,7 @@ function DateTimePicker({
2711
2711
  shouldCloseOnSelect = false,
2712
2712
  ...props
2713
2713
  }) {
2714
- const styles = fieldControlStyles({ size, appearance });
2714
+ const styles2 = fieldControlStyles({ size, appearance });
2715
2715
  const focusWithin = appearance === "cell" ? "focus-within:border-accent focus-within:shadow-none" : "focus-within:border-accent focus-within:shadow-[var(--shadow-focus)]";
2716
2716
  const timeGranularity = granularity === "day" ? "minute" : granularity;
2717
2717
  const { hourCycle } = props;
@@ -2734,7 +2734,7 @@ function DateTimePicker({
2734
2734
  className: cn(
2735
2735
  // Box chrome on the wrapper; the DateInput inside carries padding +
2736
2736
  // font so the calendar trigger sits inside the same border + ring.
2737
- styles.box(),
2737
+ styles2.box(),
2738
2738
  "flex items-center gap-1 pr-1",
2739
2739
  focusWithin
2740
2740
  ),
@@ -2744,7 +2744,7 @@ function DateTimePicker({
2744
2744
  {
2745
2745
  className: cn(
2746
2746
  "flex-1 flex items-center min-w-0 text-ink",
2747
- styles.content(),
2747
+ styles2.content(),
2748
2748
  // Tabular alignment for date/time segments — overrides content's
2749
2749
  // font-sans on purpose.
2750
2750
  "font-mono"
@@ -2804,6 +2804,1117 @@ function DateTimePicker({
2804
2804
  }
2805
2805
  );
2806
2806
  }
2807
+
2808
+ // src/components/draft-message-composer/create-draft-message-client.ts
2809
+ function createDraftMessageClient(conversation) {
2810
+ return {
2811
+ getMessage: (messageId) => conversation.messages.get(messageId),
2812
+ getConversation: (conversationId) => conversation.conversations.get(conversationId),
2813
+ updateDraft: (messageId, patch) => conversation.messages.updateDraft(messageId, patch),
2814
+ sendDraft: (messageId) => conversation.messages.sendDraft(messageId),
2815
+ rejectDraft: (messageId) => conversation.messages.rejectDraft(messageId),
2816
+ searchRecipients: async (connectionId, q) => (await conversation.connections.listContactAddresses(connectionId, {
2817
+ page_size: 100,
2818
+ ...q ? { q } : {}
2819
+ })).data
2820
+ };
2821
+ }
2822
+ var actionsByMessageId = /* @__PURE__ */ new Map();
2823
+ var listenersByMessageId = /* @__PURE__ */ new Map();
2824
+ function notify(messageId) {
2825
+ for (const listener of listenersByMessageId.get(messageId) ?? []) listener();
2826
+ }
2827
+ function registerDraftMessageActions(messageId, actions) {
2828
+ actionsByMessageId.set(messageId, actions);
2829
+ notify(messageId);
2830
+ return () => {
2831
+ if (actionsByMessageId.get(messageId) === actions) {
2832
+ actionsByMessageId.delete(messageId);
2833
+ notify(messageId);
2834
+ }
2835
+ };
2836
+ }
2837
+ function useDraftMessageActions(messageId) {
2838
+ return useSyncExternalStore(
2839
+ (listener) => {
2840
+ let listeners3 = listenersByMessageId.get(messageId);
2841
+ if (!listeners3) {
2842
+ listeners3 = /* @__PURE__ */ new Set();
2843
+ listenersByMessageId.set(messageId, listeners3);
2844
+ }
2845
+ listeners3.add(listener);
2846
+ return () => {
2847
+ listeners3.delete(listener);
2848
+ if (listeners3.size === 0) listenersByMessageId.delete(messageId);
2849
+ };
2850
+ },
2851
+ () => actionsByMessageId.get(messageId) ?? null,
2852
+ () => null
2853
+ );
2854
+ }
2855
+ function __resetDraftMessageActions() {
2856
+ actionsByMessageId.clear();
2857
+ listenersByMessageId.clear();
2858
+ }
2859
+ function MultiSelect({
2860
+ label,
2861
+ description,
2862
+ errorMessage,
2863
+ placeholder = "Select\u2026",
2864
+ options,
2865
+ selectedKeys,
2866
+ onSelectionChange,
2867
+ isDisabled = false,
2868
+ isInvalid = false,
2869
+ hideSelectedOptions = false,
2870
+ isSearchable = false,
2871
+ onSearchChange,
2872
+ size = "md",
2873
+ appearance,
2874
+ className
2875
+ }) {
2876
+ const styles2 = fieldControlStyles({ size, invalid: isInvalid, appearance });
2877
+ const focusWithin = appearance === "cell" ? "focus-within:border-accent focus-within:shadow-none" : "focus-within:border-accent focus-within:shadow-[var(--shadow-focus)]";
2878
+ const minHeight = appearance === "cell" ? "min-h-[29px]" : fieldMinHeight[size];
2879
+ const [isOpen, setIsOpen] = useState(false);
2880
+ const [search, setSearch] = useState("");
2881
+ const triggerRef = useRef(null);
2882
+ const selectedSet = new Set(selectedKeys);
2883
+ const selectedOptions = options.filter((o) => selectedSet.has(o.id));
2884
+ const needle = search.trim().toLowerCase();
2885
+ const listboxOptions = (hideSelectedOptions ? options.filter((o) => !selectedSet.has(o.id)) : options).filter((o) => needle === "" || o.label.toLowerCase().includes(needle));
2886
+ const updateSearch = (next) => {
2887
+ setSearch(next);
2888
+ onSearchChange?.(next);
2889
+ };
2890
+ const openPopover = (open) => {
2891
+ setIsOpen(open);
2892
+ if (!open && search !== "") updateSearch("");
2893
+ };
2894
+ const remove = (id) => {
2895
+ const next = new Set(selectedSet);
2896
+ next.delete(id);
2897
+ onSelectionChange(next);
2898
+ };
2899
+ const onTriggerKeyDown = (e) => {
2900
+ if (isDisabled) return;
2901
+ if (e.key === "Enter" || e.key === " " || e.key === "ArrowDown") {
2902
+ e.preventDefault();
2903
+ openPopover(true);
2904
+ }
2905
+ };
2906
+ return /* @__PURE__ */ jsxs("div", { className: cn("grid grid-cols-1 gap-1.5", className), children: [
2907
+ label ? /* @__PURE__ */ jsx(Label, { className: fieldLabelStyles, children: label }) : null,
2908
+ /* @__PURE__ */ jsxs(
2909
+ "div",
2910
+ {
2911
+ ref: triggerRef,
2912
+ role: "combobox",
2913
+ "aria-expanded": isOpen,
2914
+ "aria-haspopup": "listbox",
2915
+ "aria-disabled": isDisabled || void 0,
2916
+ "aria-invalid": isInvalid || void 0,
2917
+ tabIndex: isDisabled ? -1 : 0,
2918
+ onClick: () => !isDisabled && openPopover(!isOpen),
2919
+ onKeyDown: onTriggerKeyDown,
2920
+ "data-invalid": isInvalid || void 0,
2921
+ "data-disabled": isDisabled || void 0,
2922
+ className: cn(
2923
+ styles2.box(),
2924
+ "flex flex-wrap items-center gap-1.5 cursor-pointer",
2925
+ minHeight,
2926
+ fieldChipPadding[size],
2927
+ "pr-2",
2928
+ // Let focus-within light up the ring on both the trigger and any
2929
+ // chip's remove button without any extra JS.
2930
+ focusWithin
2931
+ ),
2932
+ children: [
2933
+ selectedOptions.length === 0 ? (
2934
+ // No vertical padding — wrapper's p-1.5 + min-h-[size] govern the
2935
+ // overall height so it matches the Input/Select trigger at the same
2936
+ // size.
2937
+ /* @__PURE__ */ jsx("span", { className: cn("text-ink-4 font-sans px-1", fieldTextStyles[size]), children: placeholder })
2938
+ ) : selectedOptions.map((o) => /* @__PURE__ */ jsx(Chip, { onRemove: () => remove(o.id), isDisabled, children: o.content ?? o.label }, o.id)),
2939
+ /* @__PURE__ */ jsx("span", { className: "ml-auto pr-1", children: /* @__PURE__ */ jsx(FieldCaret, {}) })
2940
+ ]
2941
+ }
2942
+ ),
2943
+ description ? /* @__PURE__ */ jsx("p", { className: fieldHintStyles, children: description }) : null,
2944
+ errorMessage && appearance !== "cell" ? /* @__PURE__ */ jsx("p", { className: fieldErrorStyles, children: errorMessage }) : null,
2945
+ /* @__PURE__ */ jsxs(
2946
+ Popover$1,
2947
+ {
2948
+ isOpen,
2949
+ onOpenChange: openPopover,
2950
+ triggerRef,
2951
+ offset: 6,
2952
+ placement: "bottom start",
2953
+ className: cn(
2954
+ "w-[var(--trigger-width)]",
2955
+ "rounded-md border border-rule bg-surface p-1",
2956
+ "shadow-[var(--shadow-raised)]",
2957
+ "entering:animate-in entering:fade-in-0 entering:slide-in-from-top-1",
2958
+ "exiting:animate-out exiting:fade-out-0 exiting:slide-out-to-top-1"
2959
+ ),
2960
+ children: [
2961
+ isSearchable ? /* @__PURE__ */ jsx("div", { className: "border-b border-rule-2 px-1 pb-1 mb-1", children: /* @__PURE__ */ jsx(
2962
+ "input",
2963
+ {
2964
+ autoFocus: true,
2965
+ value: search,
2966
+ onChange: (e) => updateSearch(e.target.value),
2967
+ placeholder: "Search\u2026",
2968
+ "aria-label": "Search options",
2969
+ className: cn(
2970
+ "w-full bg-transparent outline-none rounded-[6px] px-2 py-1.5",
2971
+ "font-sans text-[13px] text-ink placeholder:text-ink-4"
2972
+ )
2973
+ }
2974
+ ) }) : null,
2975
+ /* @__PURE__ */ jsx(
2976
+ ListBox,
2977
+ {
2978
+ "aria-label": typeof label === "string" ? label : "Options",
2979
+ selectionMode: "multiple",
2980
+ selectedKeys: selectedSet,
2981
+ onSelectionChange: (keys) => {
2982
+ if (keys === "all") {
2983
+ onSelectionChange(new Set(options.map((o) => o.id)));
2984
+ return;
2985
+ }
2986
+ onSelectionChange(keys);
2987
+ },
2988
+ className: "outline-none max-h-72 overflow-auto",
2989
+ renderEmptyState: () => /* @__PURE__ */ jsx("div", { className: cn("px-3 py-2", fieldHintStyles), children: needle !== "" ? "No matches" : "All options selected" }),
2990
+ children: listboxOptions.map((option) => /* @__PURE__ */ jsxs(
2991
+ ListBoxItem,
2992
+ {
2993
+ id: option.id,
2994
+ textValue: option.label,
2995
+ ...option.isDisabled ? { isDisabled: true } : {},
2996
+ className: cn(
2997
+ "group flex items-center gap-2.5",
2998
+ "rounded-[6px] px-3 py-2 text-[14px] text-ink-2",
2999
+ "cursor-pointer outline-none select-none",
3000
+ "data-[hovered]:bg-surface-2 data-[hovered]:text-ink",
3001
+ "data-[focused]:bg-surface-2 data-[focused]:text-ink",
3002
+ "data-[selected]:bg-surface-3 data-[selected]:text-ink",
3003
+ "data-[disabled]:opacity-45 data-[disabled]:cursor-not-allowed"
3004
+ ),
3005
+ children: [
3006
+ /* @__PURE__ */ jsx(Checkmark, {}),
3007
+ /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: option.content ?? option.label })
3008
+ ]
3009
+ },
3010
+ option.id
3011
+ ))
3012
+ }
3013
+ )
3014
+ ]
3015
+ }
3016
+ )
3017
+ ] });
3018
+ }
3019
+ function Chip({
3020
+ children,
3021
+ onRemove,
3022
+ isDisabled
3023
+ }) {
3024
+ return /* @__PURE__ */ jsxs(
3025
+ "span",
3026
+ {
3027
+ className: cn(
3028
+ "inline-flex items-center gap-1.5",
3029
+ "pl-2 pr-1 py-0.5 rounded-[6px]",
3030
+ "bg-surface-2 border border-rule",
3031
+ "font-sans text-[13px] text-ink"
3032
+ ),
3033
+ children: [
3034
+ /* @__PURE__ */ jsx("span", { className: "inline-flex items-center", children }),
3035
+ /* @__PURE__ */ jsx(
3036
+ "button",
3037
+ {
3038
+ type: "button",
3039
+ "aria-label": "Remove",
3040
+ disabled: isDisabled,
3041
+ onClick: (e) => {
3042
+ e.stopPropagation();
3043
+ onRemove();
3044
+ },
3045
+ className: cn(
3046
+ "inline-flex items-center justify-center shrink-0",
3047
+ "size-4 rounded-[3px]",
3048
+ "text-ink-3 hover:text-ink hover:bg-surface-3 cursor-pointer",
3049
+ "outline-none focus-visible:shadow-[var(--shadow-focus)]",
3050
+ "disabled:opacity-45 disabled:cursor-not-allowed"
3051
+ ),
3052
+ children: /* @__PURE__ */ jsx("svg", { "aria-hidden": true, viewBox: "0 0 10 10", className: "size-2.5", fill: "none", children: /* @__PURE__ */ jsx(
3053
+ "path",
3054
+ {
3055
+ d: "M2 2l6 6M8 2l-6 6",
3056
+ stroke: "currentColor",
3057
+ strokeWidth: "1.4",
3058
+ strokeLinecap: "round"
3059
+ }
3060
+ ) })
3061
+ }
3062
+ )
3063
+ ]
3064
+ }
3065
+ );
3066
+ }
3067
+ function Checkmark() {
3068
+ return /* @__PURE__ */ jsx(
3069
+ "span",
3070
+ {
3071
+ "aria-hidden": true,
3072
+ className: cn(
3073
+ "relative inline-flex items-center justify-center shrink-0",
3074
+ "size-[16px] rounded-[3px] border border-ink-4 bg-surface",
3075
+ "transition-[background-color,border-color] duration-150",
3076
+ "group-data-[selected]:bg-ink group-data-[selected]:border-ink"
3077
+ ),
3078
+ children: /* @__PURE__ */ jsx(
3079
+ "svg",
3080
+ {
3081
+ viewBox: "0 0 12 12",
3082
+ className: cn(
3083
+ "size-3 text-surface transition-opacity duration-150",
3084
+ "opacity-0 group-data-[selected]:opacity-100"
3085
+ ),
3086
+ "aria-hidden": true,
3087
+ children: /* @__PURE__ */ jsx(
3088
+ "path",
3089
+ {
3090
+ d: "M2.5 6.25 5 8.75 9.5 3.75",
3091
+ stroke: "currentColor",
3092
+ strokeWidth: "1.6",
3093
+ strokeLinecap: "round",
3094
+ strokeLinejoin: "round",
3095
+ fill: "none"
3096
+ }
3097
+ )
3098
+ }
3099
+ )
3100
+ }
3101
+ );
3102
+ }
3103
+ var SEARCH_DEBOUNCE_MS = 250;
3104
+ function RecipientSelect({
3105
+ connectionId,
3106
+ searchRecipients,
3107
+ value,
3108
+ onChange,
3109
+ placeholder
3110
+ }) {
3111
+ const [search, setSearch] = useState("");
3112
+ const [debouncedSearch, setDebouncedSearch] = useState("");
3113
+ useEffect(() => {
3114
+ const handle = window.setTimeout(() => setDebouncedSearch(search.trim()), SEARCH_DEBOUNCE_MS);
3115
+ return () => window.clearTimeout(handle);
3116
+ }, [search]);
3117
+ const [addresses, setAddresses] = useState([]);
3118
+ const requestIdRef = useRef(0);
3119
+ useEffect(() => {
3120
+ if (connectionId === "") return;
3121
+ const requestId = ++requestIdRef.current;
3122
+ searchRecipients(connectionId, debouncedSearch).then((results) => {
3123
+ if (requestId === requestIdRef.current) setAddresses(results);
3124
+ }).catch(() => {
3125
+ });
3126
+ }, [connectionId, debouncedSearch, searchRecipients]);
3127
+ const options = useMemo(() => {
3128
+ const map = /* @__PURE__ */ new Map();
3129
+ for (const recipient of value) {
3130
+ map.set(recipient.external_id, { id: recipient.external_id, label: recipient.external_id });
3131
+ }
3132
+ for (const address of addresses) {
3133
+ const identifier = address.raw_value || address.value;
3134
+ map.set(identifier, {
3135
+ id: identifier,
3136
+ label: address.name || identifier
3137
+ });
3138
+ }
3139
+ return Array.from(map.values());
3140
+ }, [addresses, value]);
3141
+ return /* @__PURE__ */ jsx(
3142
+ MultiSelect,
3143
+ {
3144
+ size: "sm",
3145
+ isSearchable: true,
3146
+ onSearchChange: setSearch,
3147
+ ...placeholder !== void 0 ? { placeholder } : {},
3148
+ options,
3149
+ selectedKeys: value.map((recipient) => recipient.external_id),
3150
+ onSelectionChange: (keys) => onChange(
3151
+ Array.from(keys).map((id) => ({
3152
+ kind: "contact-address",
3153
+ external_id: String(id)
3154
+ }))
3155
+ )
3156
+ }
3157
+ );
3158
+ }
3159
+ function Select({
3160
+ label,
3161
+ description,
3162
+ errorMessage,
3163
+ placeholder = "Select\u2026",
3164
+ options,
3165
+ renderItem,
3166
+ size,
3167
+ appearance,
3168
+ triggerClassName,
3169
+ popoverClassName,
3170
+ className,
3171
+ ...props
3172
+ }) {
3173
+ const styles2 = fieldControlStyles({ size, appearance });
3174
+ return /* @__PURE__ */ jsxs(
3175
+ Select$1,
3176
+ {
3177
+ ...props,
3178
+ className: (renderProps) => cn(
3179
+ "grid grid-cols-1 gap-1.5",
3180
+ typeof className === "function" ? className(renderProps) : className
3181
+ ),
3182
+ children: [
3183
+ label ? /* @__PURE__ */ jsx(Label, { className: fieldLabelStyles, children: label }) : null,
3184
+ /* @__PURE__ */ jsxs(
3185
+ Button$1,
3186
+ {
3187
+ className: cn(
3188
+ styles2.box(),
3189
+ styles2.content(),
3190
+ "flex items-center justify-between gap-3 text-left cursor-pointer",
3191
+ triggerClassName
3192
+ ),
3193
+ children: [
3194
+ /* @__PURE__ */ jsx(SelectValue, { className: "truncate", children: ({ defaultChildren, isPlaceholder }) => isPlaceholder ? /* @__PURE__ */ jsx("span", { className: "text-ink-4", children: placeholder }) : defaultChildren }),
3195
+ /* @__PURE__ */ jsx(FieldCaret, {})
3196
+ ]
3197
+ }
3198
+ ),
3199
+ description ? /* @__PURE__ */ jsx(Text, { slot: "description", className: fieldHintStyles, children: description }) : null,
3200
+ errorMessage && appearance !== "cell" ? /* @__PURE__ */ jsx(FieldError, { className: fieldErrorStyles, children: errorMessage }) : null,
3201
+ /* @__PURE__ */ jsxs(
3202
+ Popover$1,
3203
+ {
3204
+ offset: 6,
3205
+ className: cn(
3206
+ // Match the trigger width exactly (RAC exposes --trigger-width on
3207
+ // the Popover when anchored to a Select button).
3208
+ "w-[var(--trigger-width)]",
3209
+ // Float above modals + full-screen editors (e.g. agent-studio's
3210
+ // .as-editor z-1000 / a raised modal overlay), matching Menu's z-[1200].
3211
+ "z-[1300]",
3212
+ "rounded-md border border-rule bg-surface p-1",
3213
+ "shadow-[var(--shadow-raised)]",
3214
+ "entering:animate-in entering:fade-in-0 entering:slide-in-from-top-1",
3215
+ "exiting:animate-out exiting:fade-out-0 exiting:slide-out-to-top-1",
3216
+ popoverClassName
3217
+ ),
3218
+ children: [
3219
+ /* @__PURE__ */ jsx(ListBox, { className: "outline-none max-h-72 overflow-auto", children: options.map((option) => /* @__PURE__ */ jsx(
3220
+ SelectItem,
3221
+ {
3222
+ id: option.id,
3223
+ ...option.textValue ? { textValue: option.textValue } : {},
3224
+ ...option.isDisabled ? { isDisabled: true } : {},
3225
+ children: renderItem ? renderItem(option) : option.label
3226
+ },
3227
+ option.id
3228
+ )) }),
3229
+ options.length === 0 ? /* @__PURE__ */ jsx("div", { className: cn("px-3 py-2", fieldHintStyles), children: placeholder }) : null
3230
+ ]
3231
+ }
3232
+ )
3233
+ ]
3234
+ }
3235
+ );
3236
+ }
3237
+ function SelectItem(props) {
3238
+ return /* @__PURE__ */ jsx(
3239
+ ListBoxItem,
3240
+ {
3241
+ ...props,
3242
+ className: cn(
3243
+ "flex items-center justify-between gap-3",
3244
+ "rounded-[6px] px-3 py-2 text-[14px] text-ink-2",
3245
+ "cursor-pointer outline-none select-none",
3246
+ "data-[hovered]:bg-surface-2 data-[hovered]:text-ink",
3247
+ "data-[focused]:bg-surface-2 data-[focused]:text-ink",
3248
+ "data-[selected]:bg-surface-3 data-[selected]:text-ink",
3249
+ "data-[disabled]:opacity-45 data-[disabled]:cursor-not-allowed"
3250
+ )
3251
+ }
3252
+ );
3253
+ }
3254
+ var MAX_COMPOSER_ATTACHMENT_BYTES = 25 * 1024 * 1024;
3255
+ var messageComposerStyles = tv({
3256
+ base: [
3257
+ "rounded-[10px] border border-rule bg-surface shadow-[var(--shadow-inset)]",
3258
+ "transition-[border-color,box-shadow] duration-150",
3259
+ "focus-within:border-accent focus-within:shadow-[var(--shadow-focus)]"
3260
+ ]
3261
+ });
3262
+ var ROW_LABEL = "w-7 shrink-0 font-mono text-[10px] uppercase tracking-[0.08em] text-ink-4";
3263
+ function MessageComposer({
3264
+ mode,
3265
+ value,
3266
+ onValueChange,
3267
+ placeholder,
3268
+ subjectPrefill,
3269
+ isSubjectReadOnly,
3270
+ disabledReason,
3271
+ onSend,
3272
+ connectionId,
3273
+ connectionPicker,
3274
+ showToField,
3275
+ showRecipientFields,
3276
+ searchRecipients,
3277
+ to: toProp,
3278
+ cc: ccProp,
3279
+ bcc: bccProp,
3280
+ onToChange,
3281
+ onCcChange,
3282
+ onBccChange,
3283
+ attachments,
3284
+ onAddFiles,
3285
+ onRemoveAttachment,
3286
+ hasUploadingAttachments,
3287
+ showSendButton,
3288
+ className
3289
+ }) {
3290
+ const [isSending, setIsSending] = useState(false);
3291
+ const [sendError, setSendError] = useState(null);
3292
+ const [internalTo, setInternalTo] = useState([]);
3293
+ const [internalCc, setInternalCc] = useState([]);
3294
+ const [internalBcc, setInternalBcc] = useState([]);
3295
+ const [showCcBcc, setShowCcBcc] = useState(false);
3296
+ const textareaRef = useRef(null);
3297
+ const to = toProp ?? internalTo;
3298
+ const cc = ccProp ?? internalCc;
3299
+ const bcc = bccProp ?? internalBcc;
3300
+ const setTo = (next) => {
3301
+ if (toProp === void 0) setInternalTo(next);
3302
+ onToChange?.(next);
3303
+ };
3304
+ const setCc = (next) => {
3305
+ if (ccProp === void 0) setInternalCc(next);
3306
+ onCcChange?.(next);
3307
+ };
3308
+ const setBcc = (next) => {
3309
+ if (bccProp === void 0) setInternalBcc(next);
3310
+ onBccChange?.(next);
3311
+ };
3312
+ const directory = searchRecipients !== void 0 && connectionId ? { searchRecipients, connectionId } : void 0;
3313
+ const canPickRecipients = mode === "email" && (showRecipientFields ?? false) && directory !== void 0;
3314
+ const needsTo = (showToField ?? false) && directory !== void 0;
3315
+ const isSendButtonShown = showSendButton ?? true;
3316
+ useEffect(() => {
3317
+ setInternalTo([]);
3318
+ setInternalCc([]);
3319
+ setInternalBcc([]);
3320
+ }, [connectionId]);
3321
+ const subject = value.subject ?? subjectPrefill ?? "";
3322
+ const readyCount = (attachments ?? []).filter(
3323
+ (attachment) => attachment.status === "ready"
3324
+ ).length;
3325
+ const canSend = (value.text.trim() !== "" || readyCount > 0) && (mode !== "email" || subject.trim() !== "") && (!needsTo || to.length > 0) && !(hasUploadingAttachments ?? false);
3326
+ const submit = useCallback(async () => {
3327
+ if (!canSend || isSending) return;
3328
+ setIsSending(true);
3329
+ setSendError(null);
3330
+ try {
3331
+ await onSend({
3332
+ text: value.text.trim(),
3333
+ ...mode === "email" ? { subject: subject.trim() } : {},
3334
+ ...needsTo ? { to } : {},
3335
+ ...cc.length > 0 ? { cc } : {},
3336
+ ...bcc.length > 0 ? { bcc } : {}
3337
+ });
3338
+ onValueChange({ text: "" });
3339
+ setInternalTo([]);
3340
+ setInternalCc([]);
3341
+ setInternalBcc([]);
3342
+ } catch (error) {
3343
+ setSendError(error instanceof Error ? error.message : "Send failed");
3344
+ } finally {
3345
+ setIsSending(false);
3346
+ }
3347
+ }, [canSend, isSending, onSend, value.text, mode, subject, needsTo, to, cc, bcc, onValueChange]);
3348
+ if (disabledReason) {
3349
+ return /* @__PURE__ */ jsx("div", { className: "px-[18px] py-3", children: /* @__PURE__ */ jsx(
3350
+ "p",
3351
+ {
3352
+ className: cn(
3353
+ "rounded-[10px] border border-rule bg-surface-2 px-3.5 py-2.5",
3354
+ "text-[12.5px] text-ink-3 text-center"
3355
+ ),
3356
+ children: disabledReason
3357
+ }
3358
+ ) });
3359
+ }
3360
+ const onKeyDown2 = (event) => {
3361
+ if (!isSendButtonShown) return;
3362
+ const isSendCombo = mode === "email" ? event.key === "Enter" && (event.metaKey || event.ctrlKey) : event.key === "Enter" && !event.shiftKey;
3363
+ if (isSendCombo) {
3364
+ event.preventDefault();
3365
+ void submit();
3366
+ }
3367
+ };
3368
+ const ccBccReveal = canPickRecipients && !showCcBcc ? /* @__PURE__ */ jsx(
3369
+ Button$1,
3370
+ {
3371
+ onPress: () => setShowCcBcc(true),
3372
+ className: "shrink-0 font-mono text-[10px] uppercase tracking-[0.08em] text-ink-4 cursor-pointer outline-none hover:text-ink-3 focus-visible:shadow-[var(--shadow-focus)] rounded",
3373
+ children: "Cc/Bcc"
3374
+ }
3375
+ ) : null;
3376
+ return /* @__PURE__ */ jsxs("div", { className: "px-[18px] pb-3.5 pt-1", children: [
3377
+ /* @__PURE__ */ jsxs("div", { className: cn(messageComposerStyles(), className), children: [
3378
+ connectionPicker ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b border-rule-2 px-3.5 py-1.5", children: [
3379
+ /* @__PURE__ */ jsx("span", { className: ROW_LABEL, children: "Via" }),
3380
+ /* @__PURE__ */ jsx(
3381
+ Select,
3382
+ {
3383
+ "aria-label": "Connection",
3384
+ size: "sm",
3385
+ options: connectionPicker.options.map((option) => ({
3386
+ id: option.id,
3387
+ label: option.label,
3388
+ textValue: option.textValue
3389
+ })),
3390
+ selectedKey: connectionPicker.selectedId,
3391
+ onSelectionChange: (key) => connectionPicker.onSelect(String(key)),
3392
+ triggerClassName: "font-mono text-[11px]",
3393
+ popoverClassName: "min-w-[var(--trigger-width)] w-max max-w-80"
3394
+ }
3395
+ )
3396
+ ] }) : null,
3397
+ mode === "email" ? isSubjectReadOnly ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b border-rule-2 px-3.5 py-1.5", children: [
3398
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] uppercase tracking-[0.08em] text-ink-4", children: "Subject" }),
3399
+ /* @__PURE__ */ jsx("span", { className: "flex-1 min-w-0 truncate text-[13px] text-ink", children: subject }),
3400
+ ccBccReveal
3401
+ ] }) : /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 border-b border-rule-2 px-3.5 py-1.5", children: [
3402
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] uppercase tracking-[0.08em] text-ink-4", children: "Subject" }),
3403
+ /* @__PURE__ */ jsx(
3404
+ "input",
3405
+ {
3406
+ value: subject,
3407
+ onChange: (event) => onValueChange({ ...value, subject: event.target.value }),
3408
+ placeholder: "Subject",
3409
+ className: "flex-1 bg-transparent outline-none text-[13px] text-ink placeholder:text-ink-4"
3410
+ }
3411
+ ),
3412
+ ccBccReveal
3413
+ ] }) : null,
3414
+ needsTo && directory ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b border-rule-2 px-3.5 py-1.5", children: [
3415
+ /* @__PURE__ */ jsx("span", { className: ROW_LABEL, children: "To" }),
3416
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsx(
3417
+ RecipientSelect,
3418
+ {
3419
+ connectionId: directory.connectionId,
3420
+ searchRecipients: directory.searchRecipients,
3421
+ placeholder: "Pick recipients\u2026",
3422
+ value: to,
3423
+ onChange: setTo
3424
+ }
3425
+ ) })
3426
+ ] }) : null,
3427
+ canPickRecipients && directory && showCcBcc ? /* @__PURE__ */ jsxs(Fragment, { children: [
3428
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b border-rule-2 px-3.5 py-1.5", children: [
3429
+ /* @__PURE__ */ jsx("span", { className: ROW_LABEL, children: "Cc" }),
3430
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsx(
3431
+ RecipientSelect,
3432
+ {
3433
+ connectionId: directory.connectionId,
3434
+ searchRecipients: directory.searchRecipients,
3435
+ placeholder: "Add cc\u2026",
3436
+ value: cc,
3437
+ onChange: setCc
3438
+ }
3439
+ ) })
3440
+ ] }),
3441
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b border-rule-2 px-3.5 py-1.5", children: [
3442
+ /* @__PURE__ */ jsx("span", { className: ROW_LABEL, children: "Bcc" }),
3443
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsx(
3444
+ RecipientSelect,
3445
+ {
3446
+ connectionId: directory.connectionId,
3447
+ searchRecipients: directory.searchRecipients,
3448
+ placeholder: "Add bcc\u2026",
3449
+ value: bcc,
3450
+ onChange: setBcc
3451
+ }
3452
+ ) })
3453
+ ] })
3454
+ ] }) : null,
3455
+ attachments && attachments.length > 0 ? (
3456
+ // Single horizontally-scrollable row so the chips never grow the composer tall.
3457
+ /* @__PURE__ */ jsx("div", { className: "flex gap-1.5 overflow-x-auto px-3.5 pt-2 pb-0.5", children: attachments.map((attachment) => /* @__PURE__ */ jsx(
3458
+ AttachmentChip,
3459
+ {
3460
+ name: attachment.name,
3461
+ status: attachment.status,
3462
+ ...attachment.sizeInBytes !== void 0 ? { sizeInBytes: attachment.sizeInBytes } : {},
3463
+ ...attachment.contentType ? { contentType: attachment.contentType } : {},
3464
+ ...attachment.errorMessage ? { errorMessage: attachment.errorMessage } : {},
3465
+ ...onRemoveAttachment ? { onRemove: () => onRemoveAttachment(attachment.localId) } : {},
3466
+ className: "shrink-0"
3467
+ },
3468
+ attachment.localId
3469
+ )) })
3470
+ ) : null,
3471
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 px-3.5 py-2", children: [
3472
+ /* @__PURE__ */ jsx(
3473
+ "textarea",
3474
+ {
3475
+ ref: textareaRef,
3476
+ value: value.text,
3477
+ placeholder,
3478
+ rows: 1,
3479
+ onChange: (event) => {
3480
+ onValueChange({ ...value, text: event.target.value });
3481
+ autoGrow(event.currentTarget);
3482
+ },
3483
+ onKeyDown: onKeyDown2,
3484
+ className: cn(
3485
+ "w-full resize-none bg-transparent outline-none",
3486
+ "min-h-[40px] max-h-48 text-[14px] leading-[1.5] text-ink placeholder:text-ink-4"
3487
+ ),
3488
+ "data-conversations-composer": true
3489
+ }
3490
+ ),
3491
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
3492
+ onAddFiles ? /* @__PURE__ */ jsx(
3493
+ FileTrigger,
3494
+ {
3495
+ allowsMultiple: true,
3496
+ onSelect: (files) => {
3497
+ if (files) onAddFiles(Array.from(files));
3498
+ },
3499
+ children: /* @__PURE__ */ jsx(
3500
+ Button,
3501
+ {
3502
+ variant: "subtle",
3503
+ size: "icon-md",
3504
+ "aria-label": "Attach files",
3505
+ className: "shrink-0",
3506
+ children: /* @__PURE__ */ jsx(Paperclip, { className: "size-4", "aria-hidden": true })
3507
+ }
3508
+ )
3509
+ }
3510
+ ) : /* @__PURE__ */ jsx("span", {}),
3511
+ isSendButtonShown ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
3512
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] text-ink-4 select-none", "aria-hidden": true, children: mode === "email" ? "\u2318\u21B5" : "\u21B5" }),
3513
+ /* @__PURE__ */ jsx(
3514
+ Button$1,
3515
+ {
3516
+ "aria-label": "Send",
3517
+ isDisabled: !canSend || isSending,
3518
+ onPress: () => void submit(),
3519
+ className: cn(
3520
+ "grid place-items-center size-8 rounded-[8px] cursor-pointer",
3521
+ "bg-accent text-tile-fg transition-opacity duration-150",
3522
+ "disabled:opacity-40 disabled:cursor-default",
3523
+ "outline-none focus-visible:shadow-[var(--shadow-focus)]"
3524
+ ),
3525
+ children: /* @__PURE__ */ jsx(SendHorizontal, { className: "size-3.5" })
3526
+ }
3527
+ )
3528
+ ] }) : null
3529
+ ] })
3530
+ ] })
3531
+ ] }),
3532
+ sendError ? /* @__PURE__ */ jsxs("p", { className: "mt-1.5 font-mono text-[11px] text-danger", children: [
3533
+ sendError,
3534
+ " \u2014 your message is kept above.",
3535
+ " ",
3536
+ /* @__PURE__ */ jsx(
3537
+ Button$1,
3538
+ {
3539
+ onPress: () => void submit(),
3540
+ className: "inline underline cursor-pointer outline-none focus-visible:shadow-[var(--shadow-focus)]",
3541
+ children: "retry"
3542
+ }
3543
+ )
3544
+ ] }) : null
3545
+ ] });
3546
+ }
3547
+ function autoGrow(node) {
3548
+ node.style.height = "auto";
3549
+ node.style.height = `${Math.min(node.scrollHeight, 192)}px`;
3550
+ }
3551
+ var listenersByMessageId2 = /* @__PURE__ */ new Map();
3552
+ var lastOutcomeByMessageId = /* @__PURE__ */ new Map();
3553
+ function publishDraftMessageOutcome(messageId, outcome) {
3554
+ lastOutcomeByMessageId.set(messageId, outcome);
3555
+ for (const listener of listenersByMessageId2.get(messageId) ?? []) listener();
3556
+ }
3557
+ function subscribeDraftMessageOutcome(messageId, listener) {
3558
+ let listeners3 = listenersByMessageId2.get(messageId);
3559
+ if (!listeners3) {
3560
+ listeners3 = /* @__PURE__ */ new Set();
3561
+ listenersByMessageId2.set(messageId, listeners3);
3562
+ }
3563
+ listeners3.add(listener);
3564
+ return () => {
3565
+ listeners3.delete(listener);
3566
+ if (listeners3.size === 0) listenersByMessageId2.delete(messageId);
3567
+ };
3568
+ }
3569
+ function useDraftMessageOutcome(messageId) {
3570
+ return useSyncExternalStore(
3571
+ (listener) => subscribeDraftMessageOutcome(messageId, listener),
3572
+ () => lastOutcomeByMessageId.get(messageId) ?? null,
3573
+ () => null
3574
+ );
3575
+ }
3576
+ function __resetDraftMessageOutcomes() {
3577
+ listenersByMessageId2.clear();
3578
+ lastOutcomeByMessageId.clear();
3579
+ }
3580
+ var draftMessageComposerStyles = tv({
3581
+ slots: {
3582
+ root: "flex flex-col gap-1.5",
3583
+ header: "flex items-center gap-2 px-[18px] pt-2",
3584
+ badge: [
3585
+ "inline-flex items-center gap-1 rounded-pill border border-rule px-2 py-0.5",
3586
+ "font-mono text-[10px] uppercase tracking-[0.08em] text-ink-3 bg-surface-2"
3587
+ ],
3588
+ sender: "text-[12.5px] text-ink-3 truncate",
3589
+ actions: "flex items-center justify-between gap-2 px-[18px] pb-3",
3590
+ terminal: [
3591
+ "mx-[18px] my-3 rounded-[10px] border border-rule bg-surface-2 px-3.5 py-2.5",
3592
+ "text-[12.5px] text-ink-3"
3593
+ ],
3594
+ error: "px-[18px] font-mono text-[11px] text-danger"
3595
+ }
3596
+ });
3597
+ var styles = draftMessageComposerStyles();
3598
+ function sendRecipientsFromMessage(message, role) {
3599
+ return (message.recipients ?? []).filter((recipient) => recipient.role === role && !!recipient.external_id).map((recipient) => ({
3600
+ kind: "contact-address",
3601
+ external_id: recipient.external_id
3602
+ }));
3603
+ }
3604
+ function terminalKindFor(status) {
3605
+ switch (status) {
3606
+ case "draft":
3607
+ return null;
3608
+ case "pending":
3609
+ case "sent":
3610
+ return "sent";
3611
+ case "rejected":
3612
+ return "rejected";
3613
+ case "failed":
3614
+ return "failed";
3615
+ default:
3616
+ return "not-draft";
3617
+ }
3618
+ }
3619
+ function DraftMessageComposer({
3620
+ messageId,
3621
+ client,
3622
+ onSent,
3623
+ onRejected,
3624
+ showActions = true,
3625
+ className
3626
+ }) {
3627
+ const [phase, setPhase] = useState("loading");
3628
+ const [busy, setBusy] = useState("idle");
3629
+ const [terminalKind, setTerminalKind] = useState("not-draft");
3630
+ const [message, setMessage] = useState(null);
3631
+ const [conversation, setConversation] = useState(null);
3632
+ const [loadError, setLoadError] = useState(null);
3633
+ const [actionError, setActionError] = useState(null);
3634
+ const [isConfirmingReject, setIsConfirmingReject] = useState(false);
3635
+ const [value, setValue] = useState({ text: "" });
3636
+ const [to, setTo] = useState([]);
3637
+ const [cc, setCc] = useState([]);
3638
+ const [bcc, setBcc] = useState([]);
3639
+ const [snapshot, setSnapshot] = useState({
3640
+ text: "",
3641
+ subject: "",
3642
+ to: [],
3643
+ cc: [],
3644
+ bcc: []
3645
+ });
3646
+ const [loadNonce, setLoadNonce] = useState(0);
3647
+ const isEmail = message?.channel === "email";
3648
+ const isSubjectEditable = isEmail && conversation?.status === "draft";
3649
+ const areRecipientsEditable = isEmail && !conversation?.room_id;
3650
+ const seed = useCallback((draft, thread) => {
3651
+ const text = draft.content.find((block) => block.type === "text")?.text ?? "";
3652
+ const subject = thread?.subject ?? "";
3653
+ const seededTo = sendRecipientsFromMessage(draft, "to");
3654
+ const seededCc = sendRecipientsFromMessage(draft, "cc");
3655
+ const seededBcc = sendRecipientsFromMessage(draft, "bcc");
3656
+ setSnapshot({ text, subject, to: seededTo, cc: seededCc, bcc: seededBcc });
3657
+ setValue({ text, ...draft.channel === "email" ? { subject } : {} });
3658
+ setTo(seededTo);
3659
+ setCc(seededCc);
3660
+ setBcc(seededBcc);
3661
+ }, []);
3662
+ useEffect(() => {
3663
+ let isStale = false;
3664
+ setPhase("loading");
3665
+ setLoadError(null);
3666
+ setActionError(null);
3667
+ client.getMessage(messageId).then(async (draft) => {
3668
+ const thread = draft.conversation_id ? await client.getConversation(draft.conversation_id) : null;
3669
+ if (isStale) return;
3670
+ setMessage(draft);
3671
+ setConversation(thread);
3672
+ const terminal = terminalKindFor(draft.status);
3673
+ if (terminal) {
3674
+ setTerminalKind(terminal);
3675
+ setPhase("terminal");
3676
+ return;
3677
+ }
3678
+ seed(draft, thread);
3679
+ setPhase("ready");
3680
+ }).catch((error) => {
3681
+ if (isStale) return;
3682
+ setLoadError(error instanceof Error ? error.message : "Loading the draft failed");
3683
+ setPhase("load-error");
3684
+ });
3685
+ return () => {
3686
+ isStale = true;
3687
+ };
3688
+ }, [client, messageId, seed, loadNonce]);
3689
+ const isDirty = useMemo(() => {
3690
+ return value.text !== snapshot.text || isSubjectEditable && (value.subject ?? "") !== snapshot.subject || JSON.stringify(to) !== JSON.stringify(snapshot.to) || JSON.stringify(cc) !== JSON.stringify(snapshot.cc) || JSON.stringify(bcc) !== JSON.stringify(snapshot.bcc);
3691
+ }, [value, to, cc, bcc, snapshot, isSubjectEditable]);
3692
+ const buildPatch = useCallback(() => {
3693
+ const current = message;
3694
+ const isBodyEdited = value.text !== snapshot.text;
3695
+ const hasRecipientEdit = areRecipientsEditable && (JSON.stringify(to) !== JSON.stringify(snapshot.to) || JSON.stringify(cc) !== JSON.stringify(snapshot.cc) || JSON.stringify(bcc) !== JSON.stringify(snapshot.bcc));
3696
+ const attachmentRefs = (current.attachments ?? []).map((attachment) => attachment.file);
3697
+ return {
3698
+ // Body edits collapse to one text block; an untouched body echoes the
3699
+ // stored blocks so an agent-provided html part survives the edit.
3700
+ content: isBodyEdited ? [{ type: "text", text: value.text.trim() }] : current.content,
3701
+ ...isSubjectEditable ? { subject: value.subject ?? "" } : {},
3702
+ // Presence-based replacement: any recipient edit ships ALL three groups
3703
+ // verbatim — [] is an explicit clear, an omitted group keeps the stored
3704
+ // one. Shipping only non-empty groups would silently resurrect removed
3705
+ // recipients at send time.
3706
+ ...hasRecipientEdit ? { to, cc, bcc } : {},
3707
+ ...attachmentRefs.length > 0 ? { attachments: attachmentRefs } : {}
3708
+ };
3709
+ }, [message, value, to, cc, bcc, snapshot, areRecipientsEditable, isSubjectEditable]);
3710
+ const persistDirtyEdits = useCallback(async () => {
3711
+ const current = message;
3712
+ if (!isDirty) return current;
3713
+ const updated = await client.updateDraft(messageId, buildPatch());
3714
+ setMessage(updated);
3715
+ setConversation(
3716
+ (thread) => thread && isSubjectEditable ? { ...thread, subject: value.subject ?? "" } : thread
3717
+ );
3718
+ setSnapshot({ text: value.text, subject: value.subject ?? "", to, cc, bcc });
3719
+ return updated;
3720
+ }, [message, isDirty, client, messageId, buildPatch, isSubjectEditable, value, to, cc, bcc]);
3721
+ const saveDraft = useCallback(async () => {
3722
+ if (phase !== "ready" || busy !== "idle") return;
3723
+ setBusy("saving");
3724
+ setActionError(null);
3725
+ try {
3726
+ await persistDirtyEdits();
3727
+ } catch (error) {
3728
+ setActionError(error instanceof Error ? error.message : "Saving the draft failed");
3729
+ } finally {
3730
+ setBusy("idle");
3731
+ }
3732
+ }, [phase, busy, persistDirtyEdits]);
3733
+ const send = useCallback(async () => {
3734
+ if (phase !== "ready" || busy !== "idle") return;
3735
+ if (areRecipientsEditable && conversation?.status === "draft" && to.length === 0) {
3736
+ setActionError("Add at least one recipient before sending");
3737
+ return;
3738
+ }
3739
+ setBusy("sending");
3740
+ setActionError(null);
3741
+ try {
3742
+ await persistDirtyEdits();
3743
+ const result = await client.sendDraft(messageId);
3744
+ setMessage(result);
3745
+ if (result.status === "failed") {
3746
+ setTerminalKind("failed");
3747
+ setPhase("terminal");
3748
+ return;
3749
+ }
3750
+ setTerminalKind("sent");
3751
+ setPhase("terminal");
3752
+ publishDraftMessageOutcome(messageId, { type: "sent", message: result });
3753
+ onSent?.(result);
3754
+ } catch (error) {
3755
+ setActionError(error instanceof Error ? error.message : "Sending the draft failed");
3756
+ throw error;
3757
+ } finally {
3758
+ setBusy("idle");
3759
+ }
3760
+ }, [
3761
+ phase,
3762
+ busy,
3763
+ persistDirtyEdits,
3764
+ client,
3765
+ messageId,
3766
+ onSent,
3767
+ areRecipientsEditable,
3768
+ conversation,
3769
+ to
3770
+ ]);
3771
+ const reject = useCallback(
3772
+ async (isConfirmed) => {
3773
+ if (phase !== "ready" || busy !== "idle") return;
3774
+ if (!isConfirmed) {
3775
+ setIsConfirmingReject(true);
3776
+ return;
3777
+ }
3778
+ setBusy("rejecting");
3779
+ setActionError(null);
3780
+ try {
3781
+ const result = await client.rejectDraft(messageId);
3782
+ setMessage(result);
3783
+ setTerminalKind("rejected");
3784
+ setPhase("terminal");
3785
+ publishDraftMessageOutcome(messageId, { type: "rejected", message: result });
3786
+ onRejected?.(result);
3787
+ } catch (error) {
3788
+ setActionError(error instanceof Error ? error.message : "Rejecting the draft failed");
3789
+ } finally {
3790
+ setBusy("idle");
3791
+ setIsConfirmingReject(false);
3792
+ }
3793
+ },
3794
+ [phase, busy, client, messageId, onRejected]
3795
+ );
3796
+ const sendRef = useRef(send);
3797
+ const rejectRef = useRef(reject);
3798
+ const saveRef = useRef(saveDraft);
3799
+ sendRef.current = send;
3800
+ rejectRef.current = reject;
3801
+ saveRef.current = saveDraft;
3802
+ useEffect(() => {
3803
+ if (phase !== "ready") return;
3804
+ return registerDraftMessageActions(messageId, {
3805
+ // Failures surface via actionError/outcomes — external drivers get a
3806
+ // settled promise, not a rejection to remember to catch.
3807
+ send: () => sendRef.current().catch(() => void 0),
3808
+ // External UIs own their confirmation — bypass the inline confirm.
3809
+ reject: () => rejectRef.current(true),
3810
+ saveDraft: () => saveRef.current(),
3811
+ isBusy: busy !== "idle"
3812
+ });
3813
+ }, [messageId, phase, busy]);
3814
+ const attachments = useMemo(
3815
+ () => (message?.attachments ?? []).map((attachment) => ({
3816
+ localId: attachment.id,
3817
+ name: attachment.file.name || attachment.file.id,
3818
+ status: "ready",
3819
+ ...attachment.file.content_type ? { contentType: attachment.file.content_type } : {}
3820
+ })),
3821
+ [message?.attachments]
3822
+ );
3823
+ if (phase === "loading") {
3824
+ return /* @__PURE__ */ jsx("div", { className: cn(styles.root(), className), children: /* @__PURE__ */ jsx("p", { className: styles.terminal(), children: "Loading draft\u2026" }) });
3825
+ }
3826
+ if (phase === "load-error") {
3827
+ return /* @__PURE__ */ jsx("div", { className: cn(styles.root(), className), children: /* @__PURE__ */ jsxs("p", { className: styles.terminal(), children: [
3828
+ loadError ?? "Loading the draft failed.",
3829
+ " ",
3830
+ /* @__PURE__ */ jsx(
3831
+ Button$1,
3832
+ {
3833
+ onPress: () => setLoadNonce((nonce) => nonce + 1),
3834
+ className: "inline underline cursor-pointer outline-none focus-visible:shadow-[var(--shadow-focus)]",
3835
+ children: "retry"
3836
+ }
3837
+ )
3838
+ ] }) });
3839
+ }
3840
+ if (phase === "terminal" || !message) {
3841
+ const line = terminalKind === "sent" ? "This draft was sent." : terminalKind === "rejected" ? "This draft was rejected." : terminalKind === "failed" ? `Sending failed${message?.error ? ` \u2014 ${message.error}` : ""}.` : "This message is not a reviewable draft.";
3842
+ return /* @__PURE__ */ jsx("div", { className: cn(styles.root(), className), "data-draft-terminal": terminalKind, children: /* @__PURE__ */ jsx("p", { className: styles.terminal(), children: line }) });
3843
+ }
3844
+ return /* @__PURE__ */ jsxs("div", { className: cn(styles.root(), className), children: [
3845
+ /* @__PURE__ */ jsxs("div", { className: styles.header(), children: [
3846
+ /* @__PURE__ */ jsx("span", { className: styles.badge(), children: "Draft \xB7 awaiting review" }),
3847
+ /* @__PURE__ */ jsxs("span", { className: styles.sender(), children: [
3848
+ "from ",
3849
+ message.sender.name
3850
+ ] })
3851
+ ] }),
3852
+ /* @__PURE__ */ jsx(
3853
+ MessageComposer,
3854
+ {
3855
+ mode: isEmail ? "email" : "chat",
3856
+ value,
3857
+ onValueChange: setValue,
3858
+ placeholder: "Draft body\u2026",
3859
+ ...isEmail && !isSubjectEditable ? { isSubjectReadOnly: true } : {},
3860
+ onSend: async () => send(),
3861
+ ...areRecipientsEditable && message.connection_id ? {
3862
+ connectionId: message.connection_id,
3863
+ searchRecipients: client.searchRecipients,
3864
+ showToField: true,
3865
+ showRecipientFields: true,
3866
+ to,
3867
+ cc,
3868
+ bcc,
3869
+ onToChange: setTo,
3870
+ onCcChange: setCc,
3871
+ onBccChange: setBcc
3872
+ } : {},
3873
+ ...attachments.length > 0 ? { attachments } : {},
3874
+ showSendButton: showActions
3875
+ }
3876
+ ),
3877
+ actionError ? /* @__PURE__ */ jsxs("p", { className: styles.error(), children: [
3878
+ actionError,
3879
+ " \u2014 your edits are kept."
3880
+ ] }) : null,
3881
+ showActions ? /* @__PURE__ */ jsxs("div", { className: styles.actions(), children: [
3882
+ isConfirmingReject ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
3883
+ /* @__PURE__ */ jsx("span", { className: "text-[12.5px] text-ink-3", children: "Reject draft?" }),
3884
+ /* @__PURE__ */ jsx(
3885
+ Button,
3886
+ {
3887
+ size: "sm",
3888
+ variant: "ghost",
3889
+ onPress: () => void reject(true),
3890
+ isDisabled: busy !== "idle",
3891
+ children: "Confirm reject"
3892
+ }
3893
+ ),
3894
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "subtle", onPress: () => setIsConfirmingReject(false), children: "Cancel" })
3895
+ ] }) : /* @__PURE__ */ jsx(
3896
+ Button,
3897
+ {
3898
+ size: "sm",
3899
+ variant: "ghost",
3900
+ onPress: () => void reject(false),
3901
+ isDisabled: busy !== "idle",
3902
+ children: "Reject"
3903
+ }
3904
+ ),
3905
+ /* @__PURE__ */ jsx(
3906
+ Button,
3907
+ {
3908
+ size: "sm",
3909
+ variant: "subtle",
3910
+ onPress: () => void saveDraft(),
3911
+ isDisabled: busy !== "idle" || !isDirty,
3912
+ children: busy === "saving" ? "Saving\u2026" : "Save draft"
3913
+ }
3914
+ )
3915
+ ] }) : null
3916
+ ] });
3917
+ }
2807
3918
  var emptyStateStyles = tv({
2808
3919
  slots: {
2809
3920
  root: "flex flex-col items-center text-center px-6",
@@ -2834,12 +3945,12 @@ function EmptyState({
2834
3945
  size,
2835
3946
  className
2836
3947
  }) {
2837
- const styles = emptyStateStyles({ size });
2838
- return /* @__PURE__ */ jsxs("div", { className: cn(styles.root(), className), children: [
2839
- icon ? /* @__PURE__ */ jsx("div", { className: styles.art(), children: icon }) : null,
2840
- /* @__PURE__ */ jsx("div", { className: styles.title(), children: title }),
2841
- description ? /* @__PURE__ */ jsx("div", { className: styles.description(), children: description }) : null,
2842
- action ? /* @__PURE__ */ jsx("div", { className: styles.actions(), children: action }) : null
3948
+ const styles2 = emptyStateStyles({ size });
3949
+ return /* @__PURE__ */ jsxs("div", { className: cn(styles2.root(), className), children: [
3950
+ icon ? /* @__PURE__ */ jsx("div", { className: styles2.art(), children: icon }) : null,
3951
+ /* @__PURE__ */ jsx("div", { className: styles2.title(), children: title }),
3952
+ description ? /* @__PURE__ */ jsx("div", { className: styles2.description(), children: description }) : null,
3953
+ action ? /* @__PURE__ */ jsx("div", { className: styles2.actions(), children: action }) : null
2843
3954
  ] });
2844
3955
  }
2845
3956
  function ErrorAnimation({ className }) {
@@ -3067,427 +4178,183 @@ function isPreviewableImageName(name) {
3067
4178
  if (dot <= 0 || dot === name.length - 1) return false;
3068
4179
  return PREVIEWABLE_EXTENSIONS.has(name.slice(dot + 1).toLowerCase());
3069
4180
  }
3070
- function Input({
3071
- label,
3072
- description,
3073
- errorMessage,
3074
- placeholder,
3075
- size,
3076
- appearance,
3077
- inputProps,
3078
- inputClassName,
3079
- className,
3080
- ...props
3081
- }) {
3082
- const styles = fieldControlStyles({ size, appearance });
3083
- return /* @__PURE__ */ jsxs(
3084
- TextField,
3085
- {
3086
- ...props,
3087
- className: (renderProps) => cn(
3088
- "grid grid-cols-1 gap-1.5",
3089
- typeof className === "function" ? className(renderProps) : className
3090
- ),
3091
- children: [
3092
- label ? /* @__PURE__ */ jsx(Label, { className: fieldLabelStyles, children: label }) : null,
3093
- /* @__PURE__ */ jsx(
3094
- Input$1,
3095
- {
3096
- ...inputProps,
3097
- placeholder: placeholder ?? "",
3098
- className: cn(styles.box(), styles.content(), inputClassName)
3099
- }
3100
- ),
3101
- description ? /* @__PURE__ */ jsx(Text, { slot: "description", className: fieldHintStyles, children: description }) : null,
3102
- errorMessage && appearance !== "cell" ? /* @__PURE__ */ jsx(FieldError, { className: fieldErrorStyles, children: errorMessage }) : null
3103
- ]
3104
- }
3105
- );
3106
- }
3107
- var MenuTrigger = MenuTrigger$1;
3108
- var SubmenuTrigger = SubmenuTrigger$1;
3109
- function MenuPopover({ className, offset = 4, placement = "bottom start", ...props }) {
3110
- return /* @__PURE__ */ jsx(
3111
- Popover$1,
3112
- {
3113
- offset,
3114
- placement,
3115
- ...props,
3116
- className: (renderProps) => cn(
3117
- "min-w-[200px] p-1 bg-surface border border-rule rounded-md",
3118
- "shadow-[var(--shadow-float)] outline-none z-[1200]",
3119
- "entering:animate-in entering:fade-in-0 entering:zoom-in-95",
3120
- "exiting:animate-out exiting:fade-out-0 exiting:zoom-out-95",
3121
- typeof className === "function" ? className(renderProps) : className
3122
- )
3123
- }
3124
- );
3125
- }
3126
- function Menu({ className, ...props }) {
3127
- return /* @__PURE__ */ jsx(
3128
- Menu$1,
3129
- {
3130
- ...props,
3131
- className: (renderProps) => cn(
3132
- "flex flex-col gap-px outline-none",
3133
- typeof className === "function" ? className(renderProps) : className
3134
- )
3135
- }
3136
- );
3137
- }
3138
- var menuItemStyles = cn(
3139
- "flex items-center gap-2.5 px-2.5 py-1.5 rounded-md cursor-pointer outline-none",
3140
- "font-sans text-[13px] text-ink-2 select-none",
3141
- "hover:bg-surface-2 hover:text-ink focus:bg-surface-2 focus:text-ink",
3142
- "data-[open]:bg-surface-2 data-[open]:text-ink",
3143
- // submenu-parent stays lit while its flyout is open
3144
- "aria-disabled:opacity-50 aria-disabled:cursor-default aria-disabled:hover:bg-transparent aria-disabled:hover:text-ink-2",
3145
- "[&_svg]:size-3.5 [&_svg]:text-ink-3 hover:[&_svg]:text-ink focus:[&_svg]:text-ink"
3146
- );
3147
- function MenuItem({ icon, hasSubmenu, children, className, ...props }) {
3148
- return /* @__PURE__ */ jsxs(
3149
- MenuItem$1,
3150
- {
3151
- ...props,
3152
- className: (renderProps) => cn(menuItemStyles, typeof className === "function" ? className(renderProps) : className),
3153
- children: [
3154
- icon ? /* @__PURE__ */ jsx("span", { className: "flex shrink-0 items-center", children: icon }) : null,
3155
- /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children }),
3156
- hasSubmenu ? /* @__PURE__ */ jsx(ChevronRight$1, { className: "ml-2 size-3 shrink-0 text-ink-4" }) : null
3157
- ]
3158
- }
3159
- );
3160
- }
3161
- function Modal(props) {
3162
- return /* @__PURE__ */ jsx(DialogTrigger, { ...props });
3163
- }
3164
- function ModalContent({
3165
- children,
4181
+ function Input({
4182
+ label,
4183
+ description,
4184
+ errorMessage,
4185
+ placeholder,
4186
+ size,
4187
+ appearance,
4188
+ inputProps,
4189
+ inputClassName,
3166
4190
  className,
3167
- modalClassName,
3168
- isDismissable = true,
3169
4191
  ...props
3170
4192
  }) {
3171
- return /* @__PURE__ */ jsx(
3172
- ModalOverlay,
4193
+ const styles2 = fieldControlStyles({ size, appearance });
4194
+ return /* @__PURE__ */ jsxs(
4195
+ TextField,
3173
4196
  {
3174
- isDismissable,
3175
4197
  ...props,
3176
4198
  className: (renderProps) => cn(
3177
- "fixed inset-0 z-50 flex items-center justify-center p-4",
3178
- // Warm ink scrim — stays in the Proteos palette (no pure black).
3179
- "bg-[color-mix(in_oklab,var(--color-ink)_40%,transparent)]",
3180
- "entering:animate-in entering:fade-in-0 entering:duration-150",
3181
- "exiting:animate-out exiting:fade-out-0 exiting:duration-100",
4199
+ "grid grid-cols-1 gap-1.5",
3182
4200
  typeof className === "function" ? className(renderProps) : className
3183
4201
  ),
3184
- children: /* @__PURE__ */ jsx(
3185
- Modal$1,
3186
- {
3187
- className: cn(
3188
- "w-full max-w-lg",
3189
- // Cap the panel at the viewport (minus the overlay's p-4) and lay it
3190
- // out as a column so a tall body scrolls instead of overflowing the
3191
- // screen. Short modals are unaffected — height still tracks content.
3192
- "flex flex-col max-h-[calc(100dvh-2rem)]",
3193
- "bg-surface border border-rule rounded-lg",
3194
- "shadow-[var(--shadow-float)]",
3195
- "entering:animate-in entering:zoom-in-95 entering:slide-in-from-top-2 entering:duration-150",
3196
- "exiting:animate-out exiting:zoom-out-95 exiting:duration-100",
3197
- modalClassName
3198
- ),
3199
- children: /* @__PURE__ */ jsx(Dialog, { className: "outline-none flex flex-col min-h-0 overflow-hidden", children })
3200
- }
3201
- )
3202
- }
3203
- );
3204
- }
3205
- function ModalHeader({ className, ...props }) {
3206
- return /* @__PURE__ */ jsx("div", { ...props, className: cn("shrink-0 px-5 pt-5 pb-3 flex flex-col gap-1.5", className) });
3207
- }
3208
- function ModalTitle({ className, ...props }) {
3209
- return /* @__PURE__ */ jsx(
3210
- Heading,
3211
- {
3212
- slot: "title",
3213
- ...props,
3214
- className: cn(
3215
- "font-sans font-medium text-[20px] leading-[1.25] tracking-[-0.015em] text-ink",
3216
- className
3217
- )
4202
+ children: [
4203
+ label ? /* @__PURE__ */ jsx(Label, { className: fieldLabelStyles, children: label }) : null,
4204
+ /* @__PURE__ */ jsx(
4205
+ Input$1,
4206
+ {
4207
+ ...inputProps,
4208
+ placeholder: placeholder ?? "",
4209
+ className: cn(styles2.box(), styles2.content(), inputClassName)
4210
+ }
4211
+ ),
4212
+ description ? /* @__PURE__ */ jsx(Text, { slot: "description", className: fieldHintStyles, children: description }) : null,
4213
+ errorMessage && appearance !== "cell" ? /* @__PURE__ */ jsx(FieldError, { className: fieldErrorStyles, children: errorMessage }) : null
4214
+ ]
3218
4215
  }
3219
4216
  );
3220
4217
  }
3221
- function ModalDescription({ className, ...props }) {
3222
- return /* @__PURE__ */ jsx("p", { ...props, className: cn("text-[14px] leading-[1.55] text-ink-3", className) });
3223
- }
3224
- function ModalBody({ className, ...props }) {
4218
+ var MenuTrigger = MenuTrigger$1;
4219
+ var SubmenuTrigger = SubmenuTrigger$1;
4220
+ function MenuPopover({ className, offset = 4, placement = "bottom start", ...props }) {
3225
4221
  return /* @__PURE__ */ jsx(
3226
- "div",
4222
+ Popover$1,
3227
4223
  {
4224
+ offset,
4225
+ placement,
3228
4226
  ...props,
3229
- className: cn(
3230
- // The scroll region: caps at the panel's spare height and scrolls when
3231
- // the content is taller, leaving header + footer pinned.
3232
- "min-h-0 overflow-y-auto px-5 py-3 flex flex-col gap-3 text-[14px] text-ink-2",
3233
- className
4227
+ className: (renderProps) => cn(
4228
+ "min-w-[200px] p-1 bg-surface border border-rule rounded-md",
4229
+ "shadow-[var(--shadow-float)] outline-none z-[1200]",
4230
+ "entering:animate-in entering:fade-in-0 entering:zoom-in-95",
4231
+ "exiting:animate-out exiting:fade-out-0 exiting:zoom-out-95",
4232
+ typeof className === "function" ? className(renderProps) : className
3234
4233
  )
3235
4234
  }
3236
4235
  );
3237
4236
  }
3238
- function ModalFooter({ className, ...props }) {
4237
+ function Menu({ className, ...props }) {
3239
4238
  return /* @__PURE__ */ jsx(
3240
- "div",
3241
- {
3242
- ...props,
3243
- className: cn("shrink-0 flex justify-end items-center gap-2", "px-5 pt-3 pb-5", className)
3244
- }
3245
- );
3246
- }
3247
- function MultiSelect({
3248
- label,
3249
- description,
3250
- errorMessage,
3251
- placeholder = "Select\u2026",
3252
- options,
3253
- selectedKeys,
3254
- onSelectionChange,
3255
- isDisabled = false,
3256
- isInvalid = false,
3257
- hideSelectedOptions = false,
3258
- isSearchable = false,
3259
- onSearchChange,
3260
- size = "md",
3261
- appearance,
3262
- className
3263
- }) {
3264
- const styles = fieldControlStyles({ size, invalid: isInvalid, appearance });
3265
- const focusWithin = appearance === "cell" ? "focus-within:border-accent focus-within:shadow-none" : "focus-within:border-accent focus-within:shadow-[var(--shadow-focus)]";
3266
- const minHeight = appearance === "cell" ? "min-h-[29px]" : fieldMinHeight[size];
3267
- const [isOpen, setIsOpen] = useState(false);
3268
- const [search, setSearch] = useState("");
3269
- const triggerRef = useRef(null);
3270
- const selectedSet = new Set(selectedKeys);
3271
- const selectedOptions = options.filter((o) => selectedSet.has(o.id));
3272
- const needle = search.trim().toLowerCase();
3273
- const listboxOptions = (hideSelectedOptions ? options.filter((o) => !selectedSet.has(o.id)) : options).filter((o) => needle === "" || o.label.toLowerCase().includes(needle));
3274
- const updateSearch = (next) => {
3275
- setSearch(next);
3276
- onSearchChange?.(next);
3277
- };
3278
- const openPopover = (open) => {
3279
- setIsOpen(open);
3280
- if (!open && search !== "") updateSearch("");
3281
- };
3282
- const remove = (id) => {
3283
- const next = new Set(selectedSet);
3284
- next.delete(id);
3285
- onSelectionChange(next);
3286
- };
3287
- const onTriggerKeyDown = (e) => {
3288
- if (isDisabled) return;
3289
- if (e.key === "Enter" || e.key === " " || e.key === "ArrowDown") {
3290
- e.preventDefault();
3291
- openPopover(true);
3292
- }
3293
- };
3294
- return /* @__PURE__ */ jsxs("div", { className: cn("grid grid-cols-1 gap-1.5", className), children: [
3295
- label ? /* @__PURE__ */ jsx(Label, { className: fieldLabelStyles, children: label }) : null,
3296
- /* @__PURE__ */ jsxs(
3297
- "div",
3298
- {
3299
- ref: triggerRef,
3300
- role: "combobox",
3301
- "aria-expanded": isOpen,
3302
- "aria-haspopup": "listbox",
3303
- "aria-disabled": isDisabled || void 0,
3304
- "aria-invalid": isInvalid || void 0,
3305
- tabIndex: isDisabled ? -1 : 0,
3306
- onClick: () => !isDisabled && openPopover(!isOpen),
3307
- onKeyDown: onTriggerKeyDown,
3308
- "data-invalid": isInvalid || void 0,
3309
- "data-disabled": isDisabled || void 0,
3310
- className: cn(
3311
- styles.box(),
3312
- "flex flex-wrap items-center gap-1.5 cursor-pointer",
3313
- minHeight,
3314
- fieldChipPadding[size],
3315
- "pr-2",
3316
- // Let focus-within light up the ring on both the trigger and any
3317
- // chip's remove button without any extra JS.
3318
- focusWithin
3319
- ),
3320
- children: [
3321
- selectedOptions.length === 0 ? (
3322
- // No vertical padding — wrapper's p-1.5 + min-h-[size] govern the
3323
- // overall height so it matches the Input/Select trigger at the same
3324
- // size.
3325
- /* @__PURE__ */ jsx("span", { className: cn("text-ink-4 font-sans px-1", fieldTextStyles[size]), children: placeholder })
3326
- ) : selectedOptions.map((o) => /* @__PURE__ */ jsx(Chip, { onRemove: () => remove(o.id), isDisabled, children: o.content ?? o.label }, o.id)),
3327
- /* @__PURE__ */ jsx("span", { className: "ml-auto pr-1", children: /* @__PURE__ */ jsx(FieldCaret, {}) })
3328
- ]
3329
- }
3330
- ),
3331
- description ? /* @__PURE__ */ jsx("p", { className: fieldHintStyles, children: description }) : null,
3332
- errorMessage && appearance !== "cell" ? /* @__PURE__ */ jsx("p", { className: fieldErrorStyles, children: errorMessage }) : null,
3333
- /* @__PURE__ */ jsxs(
3334
- Popover$1,
3335
- {
3336
- isOpen,
3337
- onOpenChange: openPopover,
3338
- triggerRef,
3339
- offset: 6,
3340
- placement: "bottom start",
3341
- className: cn(
3342
- "w-[var(--trigger-width)]",
3343
- "rounded-md border border-rule bg-surface p-1",
3344
- "shadow-[var(--shadow-raised)]",
3345
- "entering:animate-in entering:fade-in-0 entering:slide-in-from-top-1",
3346
- "exiting:animate-out exiting:fade-out-0 exiting:slide-out-to-top-1"
3347
- ),
3348
- children: [
3349
- isSearchable ? /* @__PURE__ */ jsx("div", { className: "border-b border-rule-2 px-1 pb-1 mb-1", children: /* @__PURE__ */ jsx(
3350
- "input",
3351
- {
3352
- autoFocus: true,
3353
- value: search,
3354
- onChange: (e) => updateSearch(e.target.value),
3355
- placeholder: "Search\u2026",
3356
- "aria-label": "Search options",
3357
- className: cn(
3358
- "w-full bg-transparent outline-none rounded-[6px] px-2 py-1.5",
3359
- "font-sans text-[13px] text-ink placeholder:text-ink-4"
3360
- )
3361
- }
3362
- ) }) : null,
3363
- /* @__PURE__ */ jsx(
3364
- ListBox,
3365
- {
3366
- "aria-label": typeof label === "string" ? label : "Options",
3367
- selectionMode: "multiple",
3368
- selectedKeys: selectedSet,
3369
- onSelectionChange: (keys) => {
3370
- if (keys === "all") {
3371
- onSelectionChange(new Set(options.map((o) => o.id)));
3372
- return;
3373
- }
3374
- onSelectionChange(keys);
3375
- },
3376
- className: "outline-none max-h-72 overflow-auto",
3377
- renderEmptyState: () => /* @__PURE__ */ jsx("div", { className: cn("px-3 py-2", fieldHintStyles), children: needle !== "" ? "No matches" : "All options selected" }),
3378
- children: listboxOptions.map((option) => /* @__PURE__ */ jsxs(
3379
- ListBoxItem,
3380
- {
3381
- id: option.id,
3382
- textValue: option.label,
3383
- ...option.isDisabled ? { isDisabled: true } : {},
3384
- className: cn(
3385
- "group flex items-center gap-2.5",
3386
- "rounded-[6px] px-3 py-2 text-[14px] text-ink-2",
3387
- "cursor-pointer outline-none select-none",
3388
- "data-[hovered]:bg-surface-2 data-[hovered]:text-ink",
3389
- "data-[focused]:bg-surface-2 data-[focused]:text-ink",
3390
- "data-[selected]:bg-surface-3 data-[selected]:text-ink",
3391
- "data-[disabled]:opacity-45 data-[disabled]:cursor-not-allowed"
3392
- ),
3393
- children: [
3394
- /* @__PURE__ */ jsx(Checkmark, {}),
3395
- /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: option.content ?? option.label })
3396
- ]
3397
- },
3398
- option.id
3399
- ))
3400
- }
3401
- )
3402
- ]
3403
- }
3404
- )
3405
- ] });
4239
+ Menu$1,
4240
+ {
4241
+ ...props,
4242
+ className: (renderProps) => cn(
4243
+ "flex flex-col gap-px outline-none",
4244
+ typeof className === "function" ? className(renderProps) : className
4245
+ )
4246
+ }
4247
+ );
3406
4248
  }
3407
- function Chip({
3408
- children,
3409
- onRemove,
3410
- isDisabled
3411
- }) {
4249
+ var menuItemStyles = cn(
4250
+ "flex items-center gap-2.5 px-2.5 py-1.5 rounded-md cursor-pointer outline-none",
4251
+ "font-sans text-[13px] text-ink-2 select-none",
4252
+ "hover:bg-surface-2 hover:text-ink focus:bg-surface-2 focus:text-ink",
4253
+ "data-[open]:bg-surface-2 data-[open]:text-ink",
4254
+ // submenu-parent stays lit while its flyout is open
4255
+ "aria-disabled:opacity-50 aria-disabled:cursor-default aria-disabled:hover:bg-transparent aria-disabled:hover:text-ink-2",
4256
+ "[&_svg]:size-3.5 [&_svg]:text-ink-3 hover:[&_svg]:text-ink focus:[&_svg]:text-ink"
4257
+ );
4258
+ function MenuItem({ icon, hasSubmenu, children, className, ...props }) {
3412
4259
  return /* @__PURE__ */ jsxs(
3413
- "span",
4260
+ MenuItem$1,
3414
4261
  {
3415
- className: cn(
3416
- "inline-flex items-center gap-1.5",
3417
- "pl-2 pr-1 py-0.5 rounded-[6px]",
3418
- "bg-surface-2 border border-rule",
3419
- "font-sans text-[13px] text-ink"
3420
- ),
4262
+ ...props,
4263
+ className: (renderProps) => cn(menuItemStyles, typeof className === "function" ? className(renderProps) : className),
3421
4264
  children: [
3422
- /* @__PURE__ */ jsx("span", { className: "inline-flex items-center", children }),
3423
- /* @__PURE__ */ jsx(
3424
- "button",
3425
- {
3426
- type: "button",
3427
- "aria-label": "Remove",
3428
- disabled: isDisabled,
3429
- onClick: (e) => {
3430
- e.stopPropagation();
3431
- onRemove();
3432
- },
3433
- className: cn(
3434
- "inline-flex items-center justify-center shrink-0",
3435
- "size-4 rounded-[3px]",
3436
- "text-ink-3 hover:text-ink hover:bg-surface-3 cursor-pointer",
3437
- "outline-none focus-visible:shadow-[var(--shadow-focus)]",
3438
- "disabled:opacity-45 disabled:cursor-not-allowed"
3439
- ),
3440
- children: /* @__PURE__ */ jsx("svg", { "aria-hidden": true, viewBox: "0 0 10 10", className: "size-2.5", fill: "none", children: /* @__PURE__ */ jsx(
3441
- "path",
3442
- {
3443
- d: "M2 2l6 6M8 2l-6 6",
3444
- stroke: "currentColor",
3445
- strokeWidth: "1.4",
3446
- strokeLinecap: "round"
3447
- }
3448
- ) })
3449
- }
3450
- )
4265
+ icon ? /* @__PURE__ */ jsx("span", { className: "flex shrink-0 items-center", children: icon }) : null,
4266
+ /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children }),
4267
+ hasSubmenu ? /* @__PURE__ */ jsx(ChevronRight$1, { className: "ml-2 size-3 shrink-0 text-ink-4" }) : null
3451
4268
  ]
3452
4269
  }
3453
4270
  );
3454
4271
  }
3455
- function Checkmark() {
4272
+ function Modal(props) {
4273
+ return /* @__PURE__ */ jsx(DialogTrigger, { ...props });
4274
+ }
4275
+ function ModalContent({
4276
+ children,
4277
+ className,
4278
+ modalClassName,
4279
+ isDismissable = true,
4280
+ ...props
4281
+ }) {
3456
4282
  return /* @__PURE__ */ jsx(
3457
- "span",
4283
+ ModalOverlay,
3458
4284
  {
3459
- "aria-hidden": true,
3460
- className: cn(
3461
- "relative inline-flex items-center justify-center shrink-0",
3462
- "size-[16px] rounded-[3px] border border-ink-4 bg-surface",
3463
- "transition-[background-color,border-color] duration-150",
3464
- "group-data-[selected]:bg-ink group-data-[selected]:border-ink"
4285
+ isDismissable,
4286
+ ...props,
4287
+ className: (renderProps) => cn(
4288
+ "fixed inset-0 z-50 flex items-center justify-center p-4",
4289
+ // Warm ink scrim — stays in the Proteos palette (no pure black).
4290
+ "bg-[color-mix(in_oklab,var(--color-ink)_40%,transparent)]",
4291
+ "entering:animate-in entering:fade-in-0 entering:duration-150",
4292
+ "exiting:animate-out exiting:fade-out-0 exiting:duration-100",
4293
+ typeof className === "function" ? className(renderProps) : className
3465
4294
  ),
3466
4295
  children: /* @__PURE__ */ jsx(
3467
- "svg",
4296
+ Modal$1,
3468
4297
  {
3469
- viewBox: "0 0 12 12",
3470
4298
  className: cn(
3471
- "size-3 text-surface transition-opacity duration-150",
3472
- "opacity-0 group-data-[selected]:opacity-100"
4299
+ "w-full max-w-lg",
4300
+ // Cap the panel at the viewport (minus the overlay's p-4) and lay it
4301
+ // out as a column so a tall body scrolls instead of overflowing the
4302
+ // screen. Short modals are unaffected — height still tracks content.
4303
+ "flex flex-col max-h-[calc(100dvh-2rem)]",
4304
+ "bg-surface border border-rule rounded-lg",
4305
+ "shadow-[var(--shadow-float)]",
4306
+ "entering:animate-in entering:zoom-in-95 entering:slide-in-from-top-2 entering:duration-150",
4307
+ "exiting:animate-out exiting:zoom-out-95 exiting:duration-100",
4308
+ modalClassName
3473
4309
  ),
3474
- "aria-hidden": true,
3475
- children: /* @__PURE__ */ jsx(
3476
- "path",
3477
- {
3478
- d: "M2.5 6.25 5 8.75 9.5 3.75",
3479
- stroke: "currentColor",
3480
- strokeWidth: "1.6",
3481
- strokeLinecap: "round",
3482
- strokeLinejoin: "round",
3483
- fill: "none"
3484
- }
3485
- )
4310
+ children: /* @__PURE__ */ jsx(Dialog, { className: "outline-none flex flex-col min-h-0 overflow-hidden", children })
3486
4311
  }
3487
4312
  )
3488
4313
  }
3489
4314
  );
3490
4315
  }
4316
+ function ModalHeader({ className, ...props }) {
4317
+ return /* @__PURE__ */ jsx("div", { ...props, className: cn("shrink-0 px-5 pt-5 pb-3 flex flex-col gap-1.5", className) });
4318
+ }
4319
+ function ModalTitle({ className, ...props }) {
4320
+ return /* @__PURE__ */ jsx(
4321
+ Heading,
4322
+ {
4323
+ slot: "title",
4324
+ ...props,
4325
+ className: cn(
4326
+ "font-sans font-medium text-[20px] leading-[1.25] tracking-[-0.015em] text-ink",
4327
+ className
4328
+ )
4329
+ }
4330
+ );
4331
+ }
4332
+ function ModalDescription({ className, ...props }) {
4333
+ return /* @__PURE__ */ jsx("p", { ...props, className: cn("text-[14px] leading-[1.55] text-ink-3", className) });
4334
+ }
4335
+ function ModalBody({ className, ...props }) {
4336
+ return /* @__PURE__ */ jsx(
4337
+ "div",
4338
+ {
4339
+ ...props,
4340
+ className: cn(
4341
+ // The scroll region: caps at the panel's spare height and scrolls when
4342
+ // the content is taller, leaving header + footer pinned.
4343
+ "min-h-0 overflow-y-auto px-5 py-3 flex flex-col gap-3 text-[14px] text-ink-2",
4344
+ className
4345
+ )
4346
+ }
4347
+ );
4348
+ }
4349
+ function ModalFooter({ className, ...props }) {
4350
+ return /* @__PURE__ */ jsx(
4351
+ "div",
4352
+ {
4353
+ ...props,
4354
+ className: cn("shrink-0 flex justify-end items-center gap-2", "px-5 pt-3 pb-5", className)
4355
+ }
4356
+ );
4357
+ }
3491
4358
  var NavNestedContext = createContext(false);
3492
4359
  function useIsNested() {
3493
4360
  return useContext(NavNestedContext);
@@ -3974,7 +4841,7 @@ function SegmentedControl({
3974
4841
  orientation = "horizontal",
3975
4842
  ...props
3976
4843
  }) {
3977
- const styles = segmentedControlStyles({ size });
4844
+ const styles2 = segmentedControlStyles({ size });
3978
4845
  return /* @__PURE__ */ jsx(
3979
4846
  RadioGroup$1,
3980
4847
  {
@@ -3982,14 +4849,14 @@ function SegmentedControl({
3982
4849
  orientation,
3983
4850
  value: String(value),
3984
4851
  onChange: (next) => onChange(next),
3985
- className: cn(styles.group(), className),
4852
+ className: cn(styles2.group(), className),
3986
4853
  children: options.map((option) => /* @__PURE__ */ jsx(
3987
4854
  Radio$1,
3988
4855
  {
3989
4856
  value: String(option.id),
3990
4857
  ...option.isDisabled ? { isDisabled: true } : {},
3991
4858
  ...autoFocus && option.id === value ? { autoFocus: true } : {},
3992
- className: styles.segment(),
4859
+ className: styles2.segment(),
3993
4860
  children: option.label
3994
4861
  },
3995
4862
  String(option.id)
@@ -3997,101 +4864,6 @@ function SegmentedControl({
3997
4864
  }
3998
4865
  );
3999
4866
  }
4000
- function Select({
4001
- label,
4002
- description,
4003
- errorMessage,
4004
- placeholder = "Select\u2026",
4005
- options,
4006
- renderItem,
4007
- size,
4008
- appearance,
4009
- triggerClassName,
4010
- popoverClassName,
4011
- className,
4012
- ...props
4013
- }) {
4014
- const styles = fieldControlStyles({ size, appearance });
4015
- return /* @__PURE__ */ jsxs(
4016
- Select$1,
4017
- {
4018
- ...props,
4019
- className: (renderProps) => cn(
4020
- "grid grid-cols-1 gap-1.5",
4021
- typeof className === "function" ? className(renderProps) : className
4022
- ),
4023
- children: [
4024
- label ? /* @__PURE__ */ jsx(Label, { className: fieldLabelStyles, children: label }) : null,
4025
- /* @__PURE__ */ jsxs(
4026
- Button$1,
4027
- {
4028
- className: cn(
4029
- styles.box(),
4030
- styles.content(),
4031
- "flex items-center justify-between gap-3 text-left cursor-pointer",
4032
- triggerClassName
4033
- ),
4034
- children: [
4035
- /* @__PURE__ */ jsx(SelectValue, { className: "truncate", children: ({ defaultChildren, isPlaceholder }) => isPlaceholder ? /* @__PURE__ */ jsx("span", { className: "text-ink-4", children: placeholder }) : defaultChildren }),
4036
- /* @__PURE__ */ jsx(FieldCaret, {})
4037
- ]
4038
- }
4039
- ),
4040
- description ? /* @__PURE__ */ jsx(Text, { slot: "description", className: fieldHintStyles, children: description }) : null,
4041
- errorMessage && appearance !== "cell" ? /* @__PURE__ */ jsx(FieldError, { className: fieldErrorStyles, children: errorMessage }) : null,
4042
- /* @__PURE__ */ jsxs(
4043
- Popover$1,
4044
- {
4045
- offset: 6,
4046
- className: cn(
4047
- // Match the trigger width exactly (RAC exposes --trigger-width on
4048
- // the Popover when anchored to a Select button).
4049
- "w-[var(--trigger-width)]",
4050
- // Float above modals + full-screen editors (e.g. agent-studio's
4051
- // .as-editor z-1000 / a raised modal overlay), matching Menu's z-[1200].
4052
- "z-[1300]",
4053
- "rounded-md border border-rule bg-surface p-1",
4054
- "shadow-[var(--shadow-raised)]",
4055
- "entering:animate-in entering:fade-in-0 entering:slide-in-from-top-1",
4056
- "exiting:animate-out exiting:fade-out-0 exiting:slide-out-to-top-1",
4057
- popoverClassName
4058
- ),
4059
- children: [
4060
- /* @__PURE__ */ jsx(ListBox, { className: "outline-none max-h-72 overflow-auto", children: options.map((option) => /* @__PURE__ */ jsx(
4061
- SelectItem,
4062
- {
4063
- id: option.id,
4064
- ...option.textValue ? { textValue: option.textValue } : {},
4065
- ...option.isDisabled ? { isDisabled: true } : {},
4066
- children: renderItem ? renderItem(option) : option.label
4067
- },
4068
- option.id
4069
- )) }),
4070
- options.length === 0 ? /* @__PURE__ */ jsx("div", { className: cn("px-3 py-2", fieldHintStyles), children: placeholder }) : null
4071
- ]
4072
- }
4073
- )
4074
- ]
4075
- }
4076
- );
4077
- }
4078
- function SelectItem(props) {
4079
- return /* @__PURE__ */ jsx(
4080
- ListBoxItem,
4081
- {
4082
- ...props,
4083
- className: cn(
4084
- "flex items-center justify-between gap-3",
4085
- "rounded-[6px] px-3 py-2 text-[14px] text-ink-2",
4086
- "cursor-pointer outline-none select-none",
4087
- "data-[hovered]:bg-surface-2 data-[hovered]:text-ink",
4088
- "data-[focused]:bg-surface-2 data-[focused]:text-ink",
4089
- "data-[selected]:bg-surface-3 data-[selected]:text-ink",
4090
- "data-[disabled]:opacity-45 data-[disabled]:cursor-not-allowed"
4091
- )
4092
- }
4093
- );
4094
- }
4095
4867
  function TagInput({
4096
4868
  label,
4097
4869
  description,
@@ -4107,7 +4879,7 @@ function TagInput({
4107
4879
  autoFocus = false,
4108
4880
  className
4109
4881
  }) {
4110
- const styles = fieldControlStyles({ size, invalid: isInvalid, appearance });
4882
+ const styles2 = fieldControlStyles({ size, invalid: isInvalid, appearance });
4111
4883
  const focusWithin = appearance === "cell" ? "focus-within:border-accent focus-within:shadow-none" : "focus-within:border-accent focus-within:shadow-[var(--shadow-focus)]";
4112
4884
  const minHeight = appearance === "cell" ? "min-h-[29px]" : fieldMinHeight[size];
4113
4885
  const inputRef = useRef(null);
@@ -4148,7 +4920,7 @@ function TagInput({
4148
4920
  "data-invalid": isInvalid || void 0,
4149
4921
  "data-disabled": isDisabled || void 0,
4150
4922
  className: cn(
4151
- styles.box(),
4923
+ styles2.box(),
4152
4924
  "flex flex-wrap items-center gap-1",
4153
4925
  minHeight,
4154
4926
  fieldChipPadding[size],
@@ -5888,45 +6660,45 @@ var tabsStyles = tv({
5888
6660
  defaultVariants: { size: "md" }
5889
6661
  });
5890
6662
  function Root({ size, className, children, ...props }) {
5891
- const styles = tabsStyles({ size });
6663
+ const styles2 = tabsStyles({ size });
5892
6664
  return /* @__PURE__ */ jsx(
5893
6665
  Tabs$1,
5894
6666
  {
5895
6667
  ...props,
5896
- className: (rp) => cn(styles.root(), typeof className === "function" ? className(rp) : className),
6668
+ className: (rp) => cn(styles2.root(), typeof className === "function" ? className(rp) : className),
5897
6669
  children
5898
6670
  }
5899
6671
  );
5900
6672
  }
5901
6673
  function List({ size, className, children, ...props }) {
5902
- const styles = tabsStyles({ size });
6674
+ const styles2 = tabsStyles({ size });
5903
6675
  return /* @__PURE__ */ jsx(
5904
6676
  TabList,
5905
6677
  {
5906
6678
  ...props,
5907
- className: (rp) => cn(styles.list(), typeof className === "function" ? className(rp) : className),
6679
+ className: (rp) => cn(styles2.list(), typeof className === "function" ? className(rp) : className),
5908
6680
  children
5909
6681
  }
5910
6682
  );
5911
6683
  }
5912
6684
  function Trigger({ size, className, children, ...props }) {
5913
- const styles = tabsStyles({ size });
6685
+ const styles2 = tabsStyles({ size });
5914
6686
  return /* @__PURE__ */ jsx(
5915
6687
  Tab,
5916
6688
  {
5917
6689
  ...props,
5918
- className: (rp) => cn(styles.trigger(), typeof className === "function" ? className(rp) : className),
6690
+ className: (rp) => cn(styles2.trigger(), typeof className === "function" ? className(rp) : className),
5919
6691
  children
5920
6692
  }
5921
6693
  );
5922
6694
  }
5923
6695
  function Panel({ className, children, ...props }) {
5924
- const styles = tabsStyles({});
6696
+ const styles2 = tabsStyles({});
5925
6697
  return /* @__PURE__ */ jsx(
5926
6698
  TabPanel,
5927
6699
  {
5928
6700
  ...props,
5929
- className: (rp) => cn(styles.panel(), typeof className === "function" ? className(rp) : className),
6701
+ className: (rp) => cn(styles2.panel(), typeof className === "function" ? className(rp) : className),
5930
6702
  children
5931
6703
  }
5932
6704
  );
@@ -5950,7 +6722,7 @@ function Textarea({
5950
6722
  className,
5951
6723
  ...props
5952
6724
  }) {
5953
- const styles = fieldControlStyles({ size, appearance });
6725
+ const styles2 = fieldControlStyles({ size, appearance });
5954
6726
  return /* @__PURE__ */ jsxs(
5955
6727
  TextField,
5956
6728
  {
@@ -5970,8 +6742,8 @@ function Textarea({
5970
6742
  className: cn(
5971
6743
  // Same hairline chrome as Input, but allow vertical resize and set a
5972
6744
  // sensible minimum height so short content doesn't collapse.
5973
- styles.box(),
5974
- styles.content(),
6745
+ styles2.box(),
6746
+ styles2.content(),
5975
6747
  "min-h-[100px] resize-y",
5976
6748
  textareaClassName
5977
6749
  )
@@ -5992,7 +6764,7 @@ function TimeField({
5992
6764
  className,
5993
6765
  ...props
5994
6766
  }) {
5995
- const styles = fieldControlStyles({ size, appearance });
6767
+ const styles2 = fieldControlStyles({ size, appearance });
5996
6768
  const focusWithin = appearance === "cell" ? "focus-within:border-accent focus-within:shadow-none" : "focus-within:border-accent focus-within:shadow-[var(--shadow-focus)]";
5997
6769
  return /* @__PURE__ */ jsxs(
5998
6770
  TimeField$1,
@@ -6008,8 +6780,8 @@ function TimeField({
6008
6780
  DateInput,
6009
6781
  {
6010
6782
  className: cn(
6011
- styles.box(),
6012
- styles.content(),
6783
+ styles2.box(),
6784
+ styles2.content(),
6013
6785
  "flex items-center min-w-0 text-ink",
6014
6786
  // Tabular alignment for time segments — overrides content's
6015
6787
  // font-sans on purpose.
@@ -6064,6 +6836,6 @@ function Tooltip({ className, offset = 6, ...props }) {
6064
6836
  );
6065
6837
  }
6066
6838
 
6067
- export { ATTRIBUTE_TYPES, ATTRIBUTE_TYPE_ICONS, AttachmentChip, AttributePicker, AttributeType, AttributeTypeIcon, Avatar, AvatarGroup, Badge, BadgeMultiSelect, BooleanCell, Breadcrumbs, COLUMN_DRAG_KIND, Card, CardBody, CardContent, CardEyebrow, CardFeatureTitle, CardFooter, CardHeader, CardTitle, Checkbox, CheckboxGroup, ChipValue, Combobox, Command, CommandHint, CurrencyInput, DataTable, DataTableEmpty, DataTableError, DataTableSkeleton, DateCell, DatePicker, DateTimeCell, DateTimePicker, EmptyState, ErrorAnimation, FieldCaret, FileField, FileViewer, FileViewerFallbackCard, FilterRow, FilterValueInput, ImageAttachment, ImageLightbox, Input, Kbd, LinkCell, Menu, MenuItem, MenuPopover, MenuTrigger, Modal, ModalBody, ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalTitle, MultiSelect, NavGroup, NavItem, NavSection, OPERATORS, OperatorPicker, PROTECTED_COMBOS, Popover, PopoverContent, PopoverPanelBody, PopoverPanelFooter, PopoverPanelHeader, PressableCard, ProgressBar, Radio, RadioGroup, RecordFilterBuilderPanel, RecordFilterButton, RecordFilterChips, RecordFilterEditor, RenderChipItemProvider, RenderValueInputProvider, SegmentedControl, Select, Separator, SubmenuTrigger, SuccessAnimation, Switch, Table, TableBody, TableCell, TableHeader, TableHeaderCell, TableHeaderSpacer, TableRow, TableScroller, Tabs, TagInput, Textarea, TimeField, Tooltip, TooltipTrigger, TruncateCell, addLeaf as addFilterLeaf, addPendingLeaf, addSubgroup, arityOf, attachmentChipStyles, avatarGroupStyles, avatarStyles, badgeStyles, cardStyles2 as cardStyles, countLeaves, defaultValueFor, emptyStateStyles, fieldControlStyles, fieldErrorStyles, fieldHintStyles, fieldLabelStyles, fileViewerFallbackCardStyles, fileViewerStyles, formatFileSize, formatValue, iconForContentType, imageAttachmentStyles, imageLightboxStyles, isColumnDragData, isEditableTarget, isPreviewableImageName, makeActionsColumn, navItemStyles, operatorById, operatorsForType, parseCombo, progressBarStyles, rebaseRowOnFieldChange, rebaseRowOnOperatorChange, removeLeaf, removeSubgroup, replaceLeaf, segmentedControlStyles, setJoiner, tableCellStyles, tableHeaderCellStyles, tabsStyles, updateLeaf, updateSubgroup, useAttributeTypeIcon, useCommand, useDiscoveryActive, useEscapeKey, useInfiniteScrollSentinel, useRenderChipItem, useRenderValueInput, useShowDetails, viewerKindForContentType, wrapLeafInGroup };
6839
+ export { ATTRIBUTE_TYPES, ATTRIBUTE_TYPE_ICONS, AttachmentChip, AttributePicker, AttributeType, AttributeTypeIcon, Avatar, AvatarGroup, Badge, BadgeMultiSelect, BooleanCell, Breadcrumbs, COLUMN_DRAG_KIND, Card, CardBody, CardContent, CardEyebrow, CardFeatureTitle, CardFooter, CardHeader, CardTitle, Checkbox, CheckboxGroup, ChipValue, Combobox, Command, CommandHint, CurrencyInput, DataTable, DataTableEmpty, DataTableError, DataTableSkeleton, DateCell, DatePicker, DateTimeCell, DateTimePicker, DraftMessageComposer, EmptyState, ErrorAnimation, FieldCaret, FileField, FileViewer, FileViewerFallbackCard, FilterRow, FilterValueInput, ImageAttachment, ImageLightbox, Input, Kbd, LinkCell, MAX_COMPOSER_ATTACHMENT_BYTES, Menu, MenuItem, MenuPopover, MenuTrigger, MessageComposer, Modal, ModalBody, ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalTitle, MultiSelect, NavGroup, NavItem, NavSection, OPERATORS, OperatorPicker, PROTECTED_COMBOS, Popover, PopoverContent, PopoverPanelBody, PopoverPanelFooter, PopoverPanelHeader, PressableCard, ProgressBar, Radio, RadioGroup, RecipientSelect, RecordFilterBuilderPanel, RecordFilterButton, RecordFilterChips, RecordFilterEditor, RenderChipItemProvider, RenderValueInputProvider, SegmentedControl, Select, Separator, SubmenuTrigger, SuccessAnimation, Switch, Table, TableBody, TableCell, TableHeader, TableHeaderCell, TableHeaderSpacer, TableRow, TableScroller, Tabs, TagInput, Textarea, TimeField, Tooltip, TooltipTrigger, TruncateCell, __resetDraftMessageActions, __resetDraftMessageOutcomes, addLeaf as addFilterLeaf, addPendingLeaf, addSubgroup, arityOf, attachmentChipStyles, avatarGroupStyles, avatarStyles, badgeStyles, cardStyles2 as cardStyles, countLeaves, createDraftMessageClient, defaultValueFor, draftMessageComposerStyles, emptyStateStyles, fieldControlStyles, fieldErrorStyles, fieldHintStyles, fieldLabelStyles, fileViewerFallbackCardStyles, fileViewerStyles, formatFileSize, formatValue, iconForContentType, imageAttachmentStyles, imageLightboxStyles, isColumnDragData, isEditableTarget, isPreviewableImageName, makeActionsColumn, messageComposerStyles, navItemStyles, operatorById, operatorsForType, parseCombo, progressBarStyles, publishDraftMessageOutcome, rebaseRowOnFieldChange, rebaseRowOnOperatorChange, registerDraftMessageActions, removeLeaf, removeSubgroup, replaceLeaf, segmentedControlStyles, setJoiner, subscribeDraftMessageOutcome, tableCellStyles, tableHeaderCellStyles, tabsStyles, updateLeaf, updateSubgroup, useAttributeTypeIcon, useCommand, useDiscoveryActive, useDraftMessageActions, useDraftMessageOutcome, useEscapeKey, useInfiniteScrollSentinel, useRenderChipItem, useRenderValueInput, useShowDetails, viewerKindForContentType, wrapLeafInGroup };
6068
6840
  //# sourceMappingURL=index.js.map
6069
6841
  //# sourceMappingURL=index.js.map