@proteos/ui 0.31.0 → 0.32.0

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