@refraction-ui/react 0.16.1 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -19423,8 +19423,8 @@ var init_Sender = __esm({
19423
19423
  /* @min:%2elength */
19424
19424
  ]);
19425
19425
  },
19426
- beaconOnRetry: function(data, onComplete, canSend) {
19427
- return _onBeaconRetry(data, onComplete, canSend);
19426
+ beaconOnRetry: function(data, onComplete, canSend2) {
19427
+ return _onBeaconRetry(data, onComplete, canSend2);
19428
19428
  }
19429
19429
  };
19430
19430
  var config = {
@@ -19884,7 +19884,7 @@ var init_Sender = __esm({
19884
19884
  return _doSend(beaconInst, payload, isAsync);
19885
19885
  }
19886
19886
  }
19887
- function _onBeaconRetry(payload, onComplete, canSend) {
19887
+ function _onBeaconRetry(payload, onComplete, canSend2) {
19888
19888
  var internalPayload = payload;
19889
19889
  var data = internalPayload && internalPayload.oriPayload;
19890
19890
  if (!_disableBeaconSplit) {
@@ -19896,7 +19896,7 @@ var init_Sender = __esm({
19896
19896
  var thePayload = data[lp];
19897
19897
  var arr = [thePayload];
19898
19898
  var item = _getPayload(arr);
19899
- if (!canSend(item, onComplete)) {
19899
+ if (!canSend2(item, onComplete)) {
19900
19900
  droppedPayload[
19901
19901
  _DYN_PUSH2
19902
19902
  /* @min:%2epush */
@@ -31204,14 +31204,14 @@ function createCalendar(props = {}) {
31204
31204
  const gridId = generateId("rfr-calendar");
31205
31205
  const labelId = generateId("rfr-calendar-label");
31206
31206
  function isDateDisabled(date) {
31207
- if (minDate && date < startOfDay2(minDate)) return true;
31208
- if (maxDate && date > endOfDay2(maxDate)) return true;
31207
+ if (minDate && date < startOfDay(minDate)) return true;
31208
+ if (maxDate && date > endOfDay(maxDate)) return true;
31209
31209
  return disabledDates.some((d2) => isSameDay(d2, date));
31210
31210
  }
31211
- function startOfDay2(date) {
31211
+ function startOfDay(date) {
31212
31212
  return new Date(date.getFullYear(), date.getMonth(), date.getDate());
31213
31213
  }
31214
- function endOfDay2(date) {
31214
+ function endOfDay(date) {
31215
31215
  return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59, 999);
31216
31216
  }
31217
31217
  function buildDays() {
@@ -32755,6 +32755,1989 @@ var CommandSeparator = React11__namespace.forwardRef(
32755
32755
  }
32756
32756
  );
32757
32757
  CommandSeparator.displayName = "CommandSeparator";
32758
+ function detectTriggerInText(text, caret, triggerChar) {
32759
+ if (triggerChar.length === 0) return null;
32760
+ const before = text.slice(0, caret);
32761
+ const idx = before.lastIndexOf(triggerChar);
32762
+ if (idx === -1) return null;
32763
+ if (idx > 0 && !/\s/.test(before[idx - 1])) return null;
32764
+ const query = before.slice(idx + triggerChar.length);
32765
+ if (/\s/.test(query)) return null;
32766
+ return { triggered: true, query, start: idx };
32767
+ }
32768
+ var EMOJI_MAP = {
32769
+ ":smile:": "\u{1F604}",
32770
+ ":laugh:": "\u{1F602}",
32771
+ ":joy:": "\u{1F602}",
32772
+ ":grin:": "\u{1F601}",
32773
+ ":wink:": "\u{1F609}",
32774
+ ":blush:": "\u{1F60A}",
32775
+ ":heart_eyes:": "\u{1F60D}",
32776
+ ":kissing:": "\u{1F617}",
32777
+ ":thinking:": "\u{1F914}",
32778
+ ":neutral:": "\u{1F610}",
32779
+ ":expressionless:": "\u{1F611}",
32780
+ ":unamused:": "\u{1F612}",
32781
+ ":sweat:": "\u{1F613}",
32782
+ ":pensive:": "\u{1F614}",
32783
+ ":confused:": "\u{1F615}",
32784
+ ":disappointed:": "\u{1F61E}",
32785
+ ":worried:": "\u{1F61F}",
32786
+ ":angry:": "\u{1F620}",
32787
+ ":rage:": "\u{1F621}",
32788
+ ":cry:": "\u{1F622}",
32789
+ ":sob:": "\u{1F62D}",
32790
+ ":scream:": "\u{1F631}",
32791
+ ":fearful:": "\u{1F628}",
32792
+ ":cold_sweat:": "\u{1F630}",
32793
+ ":relieved:": "\u{1F60C}",
32794
+ ":sleepy:": "\u{1F62A}",
32795
+ ":sleeping:": "\u{1F634}",
32796
+ ":mask:": "\u{1F637}",
32797
+ ":sunglasses:": "\u{1F60E}",
32798
+ ":nerd:": "\u{1F913}",
32799
+ ":heart:": "\u2764\uFE0F",
32800
+ ":broken_heart:": "\u{1F494}",
32801
+ ":sparkling_heart:": "\u{1F496}",
32802
+ ":two_hearts:": "\u{1F495}",
32803
+ ":fire:": "\u{1F525}",
32804
+ ":100:": "\u{1F4AF}",
32805
+ ":star:": "\u2B50",
32806
+ ":star2:": "\u{1F31F}",
32807
+ ":sparkles:": "\u2728",
32808
+ ":thumbsup:": "\u{1F44D}",
32809
+ ":thumbsdown:": "\u{1F44E}",
32810
+ ":wave:": "\u{1F44B}",
32811
+ ":clap:": "\u{1F44F}",
32812
+ ":raised_hands:": "\u{1F64C}",
32813
+ ":pray:": "\u{1F64F}",
32814
+ ":muscle:": "\u{1F4AA}",
32815
+ ":point_up:": "\u261D\uFE0F",
32816
+ ":point_down:": "\u{1F447}",
32817
+ ":point_left:": "\u{1F448}",
32818
+ ":point_right:": "\u{1F449}",
32819
+ ":ok_hand:": "\u{1F44C}",
32820
+ ":v:": "\u270C\uFE0F",
32821
+ ":eyes:": "\u{1F440}",
32822
+ ":tongue:": "\u{1F445}",
32823
+ ":lips:": "\u{1F444}",
32824
+ ":rocket:": "\u{1F680}",
32825
+ ":tada:": "\u{1F389}",
32826
+ ":confetti:": "\u{1F38A}",
32827
+ ":balloon:": "\u{1F388}",
32828
+ ":gift:": "\u{1F381}",
32829
+ ":trophy:": "\u{1F3C6}",
32830
+ ":medal:": "\u{1F3C5}",
32831
+ ":crown:": "\u{1F451}",
32832
+ ":gem:": "\u{1F48E}",
32833
+ ":bell:": "\u{1F514}",
32834
+ ":check:": "\u2705",
32835
+ ":x:": "\u274C",
32836
+ ":warning:": "\u26A0\uFE0F",
32837
+ ":no_entry:": "\u26D4",
32838
+ ":question:": "\u2753",
32839
+ ":exclamation:": "\u2757",
32840
+ ":bulb:": "\u{1F4A1}",
32841
+ ":book:": "\u{1F4D6}",
32842
+ ":memo:": "\u{1F4DD}",
32843
+ ":pencil:": "\u270F\uFE0F",
32844
+ ":calendar:": "\u{1F4C5}",
32845
+ ":clock:": "\u{1F550}",
32846
+ ":hourglass:": "\u231B",
32847
+ ":lock:": "\u{1F512}",
32848
+ ":key:": "\u{1F511}",
32849
+ ":hammer:": "\u{1F528}",
32850
+ ":wrench:": "\u{1F527}",
32851
+ ":link:": "\u{1F517}",
32852
+ ":paperclip:": "\u{1F4CE}",
32853
+ ":scissors:": "\u2702\uFE0F",
32854
+ ":package:": "\u{1F4E6}",
32855
+ ":email:": "\u{1F4E7}",
32856
+ ":phone:": "\u{1F4DE}",
32857
+ ":computer:": "\u{1F4BB}",
32858
+ ":house:": "\u{1F3E0}",
32859
+ ":tree:": "\u{1F333}",
32860
+ ":sun:": "\u2600\uFE0F",
32861
+ ":moon:": "\u{1F319}",
32862
+ ":cloud:": "\u2601\uFE0F",
32863
+ ":rain:": "\u{1F327}\uFE0F",
32864
+ ":snow:": "\u2744\uFE0F",
32865
+ ":zap:": "\u26A1",
32866
+ ":rainbow:": "\u{1F308}",
32867
+ ":pizza:": "\u{1F355}",
32868
+ ":hamburger:": "\u{1F354}",
32869
+ ":coffee:": "\u2615",
32870
+ ":beer:": "\u{1F37A}",
32871
+ ":wine:": "\u{1F377}",
32872
+ ":cake:": "\u{1F370}",
32873
+ ":dog:": "\u{1F436}",
32874
+ ":cat:": "\u{1F431}",
32875
+ ":poop:": "\u{1F4A9}",
32876
+ ":ghost:": "\u{1F47B}",
32877
+ ":skull:": "\u{1F480}",
32878
+ ":alien:": "\u{1F47D}",
32879
+ ":robot:": "\u{1F916}",
32880
+ ":handshake:": "\u{1F91D}",
32881
+ ":pray2:": "\u{1F64F}",
32882
+ ":salute:": "\u{1FAE1}"
32883
+ };
32884
+ function detectEmojiShortcode(text) {
32885
+ const match = text.match(/:([a-z0-9_]+):$/);
32886
+ if (!match) return null;
32887
+ const shortcode = `:${match[1]}:`;
32888
+ const unicode = EMOJI_MAP[shortcode];
32889
+ if (!unicode) return null;
32890
+ return { shortcode, unicode };
32891
+ }
32892
+
32893
+ // ../composer/dist/index.js
32894
+ function toMessageAttachment(attachment) {
32895
+ return {
32896
+ id: attachment.id,
32897
+ name: attachment.name,
32898
+ url: attachment.previewUrl ?? "",
32899
+ type: attachment.mimeType ?? attachment.kind,
32900
+ size: attachment.sizeBytes
32901
+ };
32902
+ }
32903
+ var cachedSegmenter;
32904
+ function getSegmenter() {
32905
+ if (cachedSegmenter === void 0) {
32906
+ const IntlAny = typeof Intl !== "undefined" ? Intl : void 0;
32907
+ const Segmenter = IntlAny?.Segmenter;
32908
+ cachedSegmenter = Segmenter ? new Segmenter(void 0, { granularity: "grapheme" }) : null;
32909
+ }
32910
+ return cachedSegmenter;
32911
+ }
32912
+ function isHighSurrogate(code) {
32913
+ return code >= 55296 && code <= 56319;
32914
+ }
32915
+ function isLowSurrogate(code) {
32916
+ return code >= 56320 && code <= 57343;
32917
+ }
32918
+ function graphemeLength(text) {
32919
+ if (text === "") return 0;
32920
+ const segmenter = getSegmenter();
32921
+ if (segmenter) {
32922
+ let count2 = 0;
32923
+ for (const _2 of segmenter.segment(text)) count2++;
32924
+ return count2;
32925
+ }
32926
+ let count = 0;
32927
+ for (const _2 of text) count++;
32928
+ return count;
32929
+ }
32930
+ function clampGraphemes(text, max) {
32931
+ if (max <= 0) return "";
32932
+ const segmenter = getSegmenter();
32933
+ if (segmenter) {
32934
+ let count2 = 0;
32935
+ let endIndex = text.length;
32936
+ for (const part of segmenter.segment(text)) {
32937
+ count2++;
32938
+ if (count2 > max) {
32939
+ endIndex = part.index;
32940
+ break;
32941
+ }
32942
+ }
32943
+ return text.slice(0, endIndex);
32944
+ }
32945
+ let count = 0;
32946
+ let i2 = 0;
32947
+ while (i2 < text.length && count < max) {
32948
+ const code = text.charCodeAt(i2);
32949
+ i2 += isHighSurrogate(code) && i2 + 1 < text.length && isLowSurrogate(text.charCodeAt(i2 + 1)) ? 2 : 1;
32950
+ count++;
32951
+ }
32952
+ return text.slice(0, i2);
32953
+ }
32954
+ function payloadFor(raw) {
32955
+ return raw.trim();
32956
+ }
32957
+ function canSend({ text, attachments = [], disabled = false, readOnly = false, busy = false }) {
32958
+ if (disabled || readOnly || busy) return false;
32959
+ return payloadFor(text).length > 0 || attachments.length > 0;
32960
+ }
32961
+ function shouldSubmitOnEnter({
32962
+ shiftPressed,
32963
+ isComposing
32964
+ }) {
32965
+ return !shiftPressed && !isComposing;
32966
+ }
32967
+ var COUNTER_VISIBLE_FRACTION = 0.2;
32968
+ function computeCounter(text, maxLength) {
32969
+ if (maxLength === void 0) {
32970
+ return { visible: false, remaining: null, overLimit: false };
32971
+ }
32972
+ const remaining = maxLength - graphemeLength(text);
32973
+ return {
32974
+ visible: remaining <= maxLength * COUNTER_VISIBLE_FRACTION,
32975
+ remaining,
32976
+ overLimit: remaining <= 0
32977
+ };
32978
+ }
32979
+ var DEFAULT_MAX_QUERY_LENGTH = 40;
32980
+ var DEFAULT_MAX_VISIBLE_RESULTS = 6;
32981
+ var SCAN_UNITS_PER_GRAPHEME = 16;
32982
+ function resolveTriggerConfig(trigger) {
32983
+ const symbol = trigger.symbol;
32984
+ return {
32985
+ id: trigger.id,
32986
+ symbol,
32987
+ scope: trigger.scope ?? "anywhere",
32988
+ queryPattern: trigger.queryPattern ?? null,
32989
+ maxQueryLength: trigger.maxQueryLength ?? DEFAULT_MAX_QUERY_LENGTH,
32990
+ closeOnSpace: trigger.closeOnSpace ?? true,
32991
+ allowMidWord: trigger.allowMidWord ?? false,
32992
+ extraBoundaryChars: trigger.extraBoundaryChars ?? [],
32993
+ debounceMs: trigger.debounceMs ?? 0,
32994
+ maxVisibleResults: trigger.maxVisibleResults ?? DEFAULT_MAX_VISIBLE_RESULTS,
32995
+ wrapNavigation: trigger.wrapNavigation ?? true,
32996
+ toDisplay: trigger.toDisplay ?? ((candidate) => `${symbol}${candidate.display}`),
32997
+ resolve: trigger.resolve
32998
+ };
32999
+ }
33000
+ function scanWindowFor(trigger) {
33001
+ return trigger.maxQueryLength * SCAN_UNITS_PER_GRAPHEME + trigger.symbol.length;
33002
+ }
33003
+ function isWhitespace(ch) {
33004
+ return /\s/.test(ch);
33005
+ }
33006
+ function boundaryOk(text, symbolStart, trigger) {
33007
+ if (trigger.allowMidWord) return true;
33008
+ if (symbolStart === 0) return true;
33009
+ const prev = text[symbolStart - 1];
33010
+ return isWhitespace(prev) || trigger.extraBoundaryChars.includes(prev);
33011
+ }
33012
+ function scopeOk(text, symbolStart, scope) {
33013
+ if (scope === "anywhere") return true;
33014
+ if (scope === "startOfMessage") return symbolStart === 0;
33015
+ return symbolStart === 0 || text[symbolStart - 1] === "\n";
33016
+ }
33017
+ function findCandidate(text, caret, trigger) {
33018
+ const window2 = scanWindowFor(trigger);
33019
+ const isSimple = trigger.symbol.length === 1 && trigger.scope === "anywhere" && trigger.closeOnSpace && !trigger.allowMidWord && trigger.extraBoundaryChars.length === 0;
33020
+ if (isSimple) {
33021
+ const sliceStart2 = Math.max(0, caret - window2 - 1);
33022
+ const slice = text.slice(sliceStart2, caret);
33023
+ const hit = detectTriggerInText(slice, slice.length, trigger.symbol);
33024
+ if (!hit) return null;
33025
+ return { symbolStart: sliceStart2 + hit.start, query: hit.query };
33026
+ }
33027
+ const sliceStart = Math.max(0, caret - window2);
33028
+ const rel = text.slice(sliceStart, caret).lastIndexOf(trigger.symbol);
33029
+ if (rel === -1) return null;
33030
+ const symbolStart = sliceStart + rel;
33031
+ if (symbolStart + trigger.symbol.length > caret) return null;
33032
+ return { symbolStart, query: text.slice(symbolStart + trigger.symbol.length, caret) };
33033
+ }
33034
+ function validateCandidate(text, caret, trigger, candidate, dismissed, tokenRanges) {
33035
+ const { symbolStart, query } = candidate;
33036
+ if (!boundaryOk(text, symbolStart, trigger)) return null;
33037
+ if (!scopeOk(text, symbolStart, trigger.scope)) return null;
33038
+ for (const range of tokenRanges) {
33039
+ if (symbolStart >= range.start && symbolStart < range.end) return null;
33040
+ }
33041
+ if (dismissed.some((d2) => d2.triggerId === trigger.id && d2.symbolStart === symbolStart)) return null;
33042
+ if (trigger.closeOnSpace && /\s/.test(query)) return null;
33043
+ if (!trigger.closeOnSpace && query.includes("\n")) return null;
33044
+ if (graphemeLength(query) > trigger.maxQueryLength) return null;
33045
+ if (trigger.queryPattern && !trigger.queryPattern.test(query)) return null;
33046
+ return { trigger, symbolStart, caret, query };
33047
+ }
33048
+ function detectActiveTrigger({
33049
+ text,
33050
+ caret,
33051
+ triggers,
33052
+ isComposing = false,
33053
+ dismissed = [],
33054
+ tokenRanges = []
33055
+ }) {
33056
+ if (isComposing) return null;
33057
+ let best = null;
33058
+ for (const trigger of triggers) {
33059
+ const candidate = findCandidate(text, caret, trigger);
33060
+ if (!candidate) continue;
33061
+ const match = validateCandidate(text, caret, trigger, candidate, dismissed, tokenRanges);
33062
+ if (!match) continue;
33063
+ if (!best || match.symbolStart > best.symbolStart || match.symbolStart === best.symbolStart && match.trigger.symbol.length > best.trigger.symbol.length) {
33064
+ best = match;
33065
+ }
33066
+ }
33067
+ return best;
33068
+ }
33069
+ var LOADING_DELAY_MS = 120;
33070
+ var DISMISS_GRACE_MS = 120;
33071
+ var CIRCUIT_BREAKER_THRESHOLD = 2;
33072
+ var CLOSED_SUGGESTION_STATE = {
33073
+ isOpen: false,
33074
+ items: [],
33075
+ visibleItems: [],
33076
+ activeIndex: 0,
33077
+ loading: false,
33078
+ error: null,
33079
+ requestToken: 0
33080
+ };
33081
+ function createSuggestionController({ emit: emit2 }) {
33082
+ let state = CLOSED_SUGGESTION_STATE;
33083
+ let match = null;
33084
+ let requestCounter = 0;
33085
+ let debounceTimer = null;
33086
+ let loadingTimer = null;
33087
+ let dismissTimer = null;
33088
+ const consecutiveFailures = /* @__PURE__ */ new Map();
33089
+ const tripped = /* @__PURE__ */ new Set();
33090
+ function clearTimer(timer) {
33091
+ if (timer !== null) clearTimeout(timer);
33092
+ return null;
33093
+ }
33094
+ function setState(patch) {
33095
+ state = { ...state, ...patch };
33096
+ }
33097
+ function sameOccurrence(a2, b2) {
33098
+ return a2 !== null && b2 !== null && a2.trigger.id === b2.trigger.id && a2.symbolStart === b2.symbolStart;
33099
+ }
33100
+ function closeInternal() {
33101
+ debounceTimer = clearTimer(debounceTimer);
33102
+ loadingTimer = clearTimer(loadingTimer);
33103
+ requestCounter++;
33104
+ match = null;
33105
+ state = CLOSED_SUGGESTION_STATE;
33106
+ }
33107
+ function applyResults(forMatch, items) {
33108
+ loadingTimer = clearTimer(loadingTimer);
33109
+ consecutiveFailures.set(forMatch.trigger.id, 0);
33110
+ setState({
33111
+ isOpen: true,
33112
+ items,
33113
+ visibleItems: items.slice(0, forMatch.trigger.maxVisibleResults),
33114
+ activeIndex: 0,
33115
+ loading: false,
33116
+ error: null
33117
+ });
33118
+ }
33119
+ function applyFailure(forMatch, error) {
33120
+ loadingTimer = clearTimer(loadingTimer);
33121
+ const triggerId = forMatch.trigger.id;
33122
+ const failures = (consecutiveFailures.get(triggerId) ?? 0) + 1;
33123
+ consecutiveFailures.set(triggerId, failures);
33124
+ if (failures >= CIRCUIT_BREAKER_THRESHOLD) {
33125
+ tripped.add(triggerId);
33126
+ closeInternal();
33127
+ return;
33128
+ }
33129
+ setState({
33130
+ isOpen: true,
33131
+ items: [],
33132
+ visibleItems: [],
33133
+ loading: false,
33134
+ error: error instanceof Error ? error.message : "Failed to load suggestions"
33135
+ });
33136
+ }
33137
+ function dispatch(forMatch, notifySync) {
33138
+ const token = ++requestCounter;
33139
+ setState({ requestToken: token });
33140
+ let result;
33141
+ try {
33142
+ result = forMatch.trigger.resolve(forMatch.query);
33143
+ } catch (error) {
33144
+ applyFailure(forMatch, error);
33145
+ if (notifySync) emit2();
33146
+ return;
33147
+ }
33148
+ if (Array.isArray(result)) {
33149
+ applyResults(forMatch, result);
33150
+ if (notifySync) emit2();
33151
+ return;
33152
+ }
33153
+ loadingTimer = clearTimer(loadingTimer);
33154
+ loadingTimer = setTimeout(() => {
33155
+ if (token !== requestCounter) return;
33156
+ setState({ isOpen: true, loading: true });
33157
+ emit2();
33158
+ }, LOADING_DELAY_MS);
33159
+ result.then(
33160
+ (items) => {
33161
+ if (token !== requestCounter || !sameOccurrence(match, forMatch)) return;
33162
+ applyResults(forMatch, items);
33163
+ emit2();
33164
+ },
33165
+ (error) => {
33166
+ if (token !== requestCounter || !sameOccurrence(match, forMatch)) return;
33167
+ applyFailure(forMatch, error);
33168
+ emit2();
33169
+ }
33170
+ );
33171
+ }
33172
+ function schedule(forMatch) {
33173
+ debounceTimer = clearTimer(debounceTimer);
33174
+ if (forMatch.trigger.debounceMs <= 0) {
33175
+ dispatch(forMatch, false);
33176
+ return;
33177
+ }
33178
+ debounceTimer = setTimeout(() => {
33179
+ debounceTimer = null;
33180
+ if (sameOccurrence(match, forMatch) && match?.query === forMatch.query) {
33181
+ dispatch(forMatch, true);
33182
+ }
33183
+ }, forMatch.trigger.debounceMs);
33184
+ }
33185
+ return {
33186
+ get state() {
33187
+ return state;
33188
+ },
33189
+ sync(next) {
33190
+ if (next === null) {
33191
+ if (match !== null || state.isOpen) closeInternal();
33192
+ return;
33193
+ }
33194
+ if (tripped.has(next.trigger.id)) {
33195
+ if (match !== null || state.isOpen) closeInternal();
33196
+ return;
33197
+ }
33198
+ const isSame = sameOccurrence(match, next);
33199
+ const queryChanged = !isSame || match?.query !== next.query;
33200
+ match = next;
33201
+ if (!isSame) {
33202
+ state = { ...CLOSED_SUGGESTION_STATE, isOpen: true, requestToken: state.requestToken };
33203
+ }
33204
+ if (queryChanged) schedule(next);
33205
+ },
33206
+ moveNext() {
33207
+ const count = state.items.length;
33208
+ if (!state.isOpen || count === 0) return;
33209
+ const wrap = match?.trigger.wrapNavigation ?? true;
33210
+ const next = wrap ? (state.activeIndex + 1) % count : Math.min(state.activeIndex + 1, count - 1);
33211
+ setState({ activeIndex: next });
33212
+ },
33213
+ movePrevious() {
33214
+ const count = state.items.length;
33215
+ if (!state.isOpen || count === 0) return;
33216
+ const wrap = match?.trigger.wrapNavigation ?? true;
33217
+ const previous = wrap ? (state.activeIndex - 1 + count) % count : Math.max(state.activeIndex - 1, 0);
33218
+ setState({ activeIndex: previous });
33219
+ },
33220
+ setActiveIndex(index) {
33221
+ if (!state.isOpen || state.items.length === 0) return;
33222
+ setState({ activeIndex: Math.max(0, Math.min(index, state.items.length - 1)) });
33223
+ },
33224
+ retry() {
33225
+ if (match === null) return;
33226
+ if (tripped.has(match.trigger.id)) return;
33227
+ setState({ error: null });
33228
+ dispatch(match, false);
33229
+ },
33230
+ dismissDeferred(onClosed) {
33231
+ dismissTimer = clearTimer(dismissTimer);
33232
+ dismissTimer = setTimeout(() => {
33233
+ dismissTimer = null;
33234
+ closeInternal();
33235
+ onClosed();
33236
+ }, DISMISS_GRACE_MS);
33237
+ },
33238
+ cancelDeferredDismiss() {
33239
+ dismissTimer = clearTimer(dismissTimer);
33240
+ },
33241
+ close() {
33242
+ dismissTimer = clearTimer(dismissTimer);
33243
+ closeInternal();
33244
+ },
33245
+ isTripped(triggerId) {
33246
+ return tripped.has(triggerId);
33247
+ },
33248
+ destroy() {
33249
+ debounceTimer = clearTimer(debounceTimer);
33250
+ loadingTimer = clearTimer(loadingTimer);
33251
+ dismissTimer = clearTimer(dismissTimer);
33252
+ match = null;
33253
+ state = CLOSED_SUGGESTION_STATE;
33254
+ }
33255
+ };
33256
+ }
33257
+ function diffEdit(oldText, newText) {
33258
+ if (oldText === newText) return null;
33259
+ const minLen = Math.min(oldText.length, newText.length);
33260
+ let prefix = 0;
33261
+ while (prefix < minLen && oldText[prefix] === newText[prefix]) prefix++;
33262
+ let suffix = 0;
33263
+ while (suffix < minLen - prefix && oldText[oldText.length - 1 - suffix] === newText[newText.length - 1 - suffix]) {
33264
+ suffix++;
33265
+ }
33266
+ return { start: prefix, oldEnd: oldText.length - suffix, newEnd: newText.length - suffix };
33267
+ }
33268
+ function nearestBoundary(offset, token) {
33269
+ return offset - token.start <= token.end - offset ? token.start : token.end;
33270
+ }
33271
+ function applyValueEdit({
33272
+ oldValue,
33273
+ newValue,
33274
+ tokens,
33275
+ newSelection,
33276
+ maxLength
33277
+ }) {
33278
+ const noChange = {
33279
+ value: oldValue,
33280
+ tokens: [...tokens],
33281
+ selection: newSelection,
33282
+ rejected: false,
33283
+ trimmed: false,
33284
+ removedTokens: [],
33285
+ edit: null
33286
+ };
33287
+ const diff = diffEdit(oldValue, newValue);
33288
+ if (!diff) return noChange;
33289
+ const insertedRaw = newValue.slice(diff.start, diff.newEnd);
33290
+ if (diff.oldEnd === diff.start) {
33291
+ const host = tokens.find((t2) => t2.start < diff.start && diff.start < t2.end);
33292
+ if (host) {
33293
+ const caret2 = nearestBoundary(diff.start, host);
33294
+ return { ...noChange, rejected: true, selection: { start: caret2, end: caret2 } };
33295
+ }
33296
+ }
33297
+ let expStart = diff.start;
33298
+ let expOldEnd = diff.oldEnd;
33299
+ const removedTokens = [];
33300
+ for (const token of tokens) {
33301
+ const overlaps = token.start < expOldEnd && token.end > expStart;
33302
+ if (!overlaps) continue;
33303
+ removedTokens.push(token);
33304
+ expStart = Math.min(expStart, token.start);
33305
+ expOldEnd = Math.max(expOldEnd, token.end);
33306
+ }
33307
+ let inserted = insertedRaw;
33308
+ let trimmed = false;
33309
+ if (maxLength !== void 0 && inserted.length > 0) {
33310
+ const baseLength = graphemeLength(oldValue.slice(0, expStart)) + graphemeLength(oldValue.slice(expOldEnd));
33311
+ const budget = Math.max(0, maxLength - baseLength);
33312
+ if (graphemeLength(inserted) > budget) {
33313
+ inserted = clampGraphemes(inserted, budget);
33314
+ trimmed = true;
33315
+ }
33316
+ }
33317
+ const value = oldValue.slice(0, expStart) + inserted + oldValue.slice(expOldEnd);
33318
+ const delta = expStart + inserted.length - expOldEnd;
33319
+ const nextTokens = tokens.filter((t2) => !removedTokens.includes(t2)).map((t2) => t2.start >= expOldEnd ? { ...t2, start: t2.start + delta, end: t2.end + delta } : t2);
33320
+ const appliedMatchesRaw = expStart === diff.start && expOldEnd === diff.oldEnd && !trimmed;
33321
+ const caret = expStart + inserted.length;
33322
+ const selection = appliedMatchesRaw ? newSelection : { start: caret, end: caret };
33323
+ return {
33324
+ value,
33325
+ tokens: nextTokens,
33326
+ selection: clampSelectionToValue(selection, value),
33327
+ rejected: false,
33328
+ trimmed,
33329
+ removedTokens,
33330
+ edit: { start: expStart, oldEnd: expOldEnd, newEnd: expStart + inserted.length }
33331
+ };
33332
+ }
33333
+ function clampSelectionToValue(selection, value) {
33334
+ const clamp3 = (n2) => Math.max(0, Math.min(n2, value.length));
33335
+ return { start: clamp3(selection.start), end: clamp3(selection.end) };
33336
+ }
33337
+ function snapSelectionToTokens(tokens, selection, previous) {
33338
+ const inside = (offset) => tokens.find((t2) => t2.start < offset && offset < t2.end);
33339
+ if (selection.start === selection.end) {
33340
+ const host = inside(selection.start);
33341
+ if (!host) return selection;
33342
+ let caret;
33343
+ if (previous && previous.start === previous.end && previous.start !== selection.start) {
33344
+ caret = selection.start > previous.start ? host.end : host.start;
33345
+ } else {
33346
+ caret = nearestBoundary(selection.start, host);
33347
+ }
33348
+ return { start: caret, end: caret };
33349
+ }
33350
+ const startHost = inside(selection.start);
33351
+ const endHost = inside(selection.end);
33352
+ return {
33353
+ start: startHost ? startHost.start : selection.start,
33354
+ end: endHost ? endHost.end : selection.end
33355
+ };
33356
+ }
33357
+ function expandRangeOverTokens(tokens, range) {
33358
+ let { start, end } = range;
33359
+ for (const token of tokens) {
33360
+ if (token.start < end && token.end > start) {
33361
+ start = Math.min(start, token.start);
33362
+ end = Math.max(end, token.end);
33363
+ }
33364
+ }
33365
+ return { start, end };
33366
+ }
33367
+ function commitTokenAt({ value, tokens, start, end, token }) {
33368
+ const display = token.display;
33369
+ const nextValue = value.slice(0, start) + display + value.slice(end);
33370
+ const delta = display.length - (end - start);
33371
+ const placed = { ...token, start, end: start + display.length };
33372
+ const shifted = tokens.map(
33373
+ (t2) => t2.start >= end ? { ...t2, start: t2.start + delta, end: t2.end + delta } : t2
33374
+ );
33375
+ const nextTokens = [...shifted, placed].sort((a2, b2) => a2.start - b2.start);
33376
+ const caret = placed.end;
33377
+ return { value: nextValue, tokens: nextTokens, selection: { start: caret, end: caret } };
33378
+ }
33379
+ function serializeTokens(value, tokens) {
33380
+ const resolved = tokens.map((t2) => ({
33381
+ type: t2.triggerId,
33382
+ id: t2.id,
33383
+ display: t2.display,
33384
+ start: t2.start,
33385
+ end: t2.end
33386
+ }));
33387
+ return { plainText: value, tokens: resolved };
33388
+ }
33389
+ function detectTypedEmoji(value, caret) {
33390
+ const hit = detectEmojiShortcode(value.slice(0, caret));
33391
+ if (!hit) return null;
33392
+ return { shortcode: hit.shortcode, unicode: hit.unicode, start: caret - hit.shortcode.length };
33393
+ }
33394
+ var DEFAULT_DRAFT_DEBOUNCE_MS = 400;
33395
+ var DEFAULT_TYPING_SIGNAL_INTERVAL_MS = 3e3;
33396
+ function validateDraft(raw) {
33397
+ if (typeof raw !== "object" || raw === null) return null;
33398
+ const draft = raw;
33399
+ if (typeof draft.value !== "string") return null;
33400
+ if (!Array.isArray(draft.tokens)) return null;
33401
+ if (!Array.isArray(draft.attachmentIds)) return null;
33402
+ for (const token of draft.tokens) {
33403
+ if (typeof token !== "object" || token === null || typeof token.start !== "number" || typeof token.end !== "number" || typeof token.display !== "string" || // The one invariant that matters: ranges must project the display.
33404
+ draft.value.substring(token.start, token.end) !== token.display) {
33405
+ return null;
33406
+ }
33407
+ }
33408
+ return {
33409
+ value: draft.value,
33410
+ tokens: draft.tokens,
33411
+ attachmentIds: draft.attachmentIds,
33412
+ updatedAt: typeof draft.updatedAt === "number" ? draft.updatedAt : 0
33413
+ };
33414
+ }
33415
+ function createComposer(config = {}) {
33416
+ const now = config.now ?? (() => Date.now());
33417
+ const generateId$1 = config.generateId ?? generateId;
33418
+ const onEvent = config.onEvent ?? (() => void 0);
33419
+ const triggers = (config.triggers ?? []).map(resolveTriggerConfig);
33420
+ const draftDebounceMs = config.draftDebounceMs ?? DEFAULT_DRAFT_DEBOUNCE_MS;
33421
+ const typingSignalIntervalMs = config.typingSignalIntervalMs ?? DEFAULT_TYPING_SIGNAL_INTERVAL_MS;
33422
+ const emojiTrigger = triggers.find((t2) => t2.symbol === ":") ?? null;
33423
+ let value = config.initialValue ?? "";
33424
+ let tokens = [...config.initialTokens ?? []];
33425
+ let selection = { start: value.length, end: value.length };
33426
+ let isComposing = false;
33427
+ let isBusy = false;
33428
+ let disabled = false;
33429
+ let readOnly = false;
33430
+ let error = null;
33431
+ let attachments = [];
33432
+ let mode = "compose";
33433
+ let editingMessageId;
33434
+ let preEditSnapshot = null;
33435
+ let activeMatch = null;
33436
+ let dismissed = [];
33437
+ let clipShadow = null;
33438
+ let lastTypingAt = Number.NEGATIVE_INFINITY;
33439
+ let draftTimer = null;
33440
+ let destroyed = false;
33441
+ const listeners = /* @__PURE__ */ new Set();
33442
+ if (config.draftStore && config.draftKey) {
33443
+ try {
33444
+ const draft = validateDraft(config.draftStore.read(config.draftKey));
33445
+ if (draft) {
33446
+ value = draft.value;
33447
+ tokens = draft.tokens;
33448
+ selection = { start: value.length, end: value.length };
33449
+ }
33450
+ } catch {
33451
+ }
33452
+ }
33453
+ const suggestions = createSuggestionController({ emit: () => emit2() });
33454
+ function toActiveTriggerState(match) {
33455
+ if (!match) return null;
33456
+ return {
33457
+ triggerId: match.trigger.id,
33458
+ symbol: match.trigger.symbol,
33459
+ symbolStart: match.symbolStart,
33460
+ caret: match.caret,
33461
+ query: match.query
33462
+ };
33463
+ }
33464
+ function buildState() {
33465
+ const counter = computeCounter(value, config.maxLength);
33466
+ const overBudget = counter.remaining !== null && counter.remaining < 0;
33467
+ return {
33468
+ value,
33469
+ selection,
33470
+ isComposing,
33471
+ isBusy,
33472
+ disabled,
33473
+ readOnly,
33474
+ isEmpty: payloadFor(value).length === 0 && attachments.length === 0,
33475
+ canSend: canSend({ text: value, attachments, disabled, readOnly, busy: isBusy }) && !overBudget,
33476
+ error,
33477
+ attachments: [...attachments],
33478
+ tokens: [...tokens],
33479
+ activeTrigger: toActiveTriggerState(activeMatch),
33480
+ suggestion: destroyed ? CLOSED_SUGGESTION_STATE : suggestions.state,
33481
+ counter,
33482
+ mode,
33483
+ editingMessageId
33484
+ };
33485
+ }
33486
+ function emit2() {
33487
+ if (destroyed) return;
33488
+ const snapshot = buildState();
33489
+ for (const listener of listeners) listener(snapshot);
33490
+ }
33491
+ function shiftDismissedForEdit(editStart, editOldEnd, delta) {
33492
+ dismissed = dismissed.map((d2) => d2.symbolStart >= editOldEnd ? { ...d2, symbolStart: d2.symbolStart + delta } : d2).filter((d2) => {
33493
+ if (d2.symbolStart >= editStart && d2.symbolStart < editOldEnd) return false;
33494
+ const trigger = triggers.find((t2) => t2.id === d2.triggerId);
33495
+ return trigger !== void 0 && value.startsWith(trigger.symbol, d2.symbolStart);
33496
+ });
33497
+ }
33498
+ function recomputeTriggers() {
33499
+ const collapsed = selection.start === selection.end;
33500
+ activeMatch = collapsed && !isComposing && !disabled && !readOnly ? detectActiveTrigger({
33501
+ text: value,
33502
+ caret: selection.start,
33503
+ triggers,
33504
+ isComposing,
33505
+ dismissed,
33506
+ tokenRanges: tokens
33507
+ }) : null;
33508
+ suggestions.sync(activeMatch);
33509
+ }
33510
+ function fireTypingSignal() {
33511
+ const at2 = now();
33512
+ if (at2 - lastTypingAt >= typingSignalIntervalMs) {
33513
+ lastTypingAt = at2;
33514
+ onEvent({ type: "typing" });
33515
+ }
33516
+ }
33517
+ function scheduleDraftWrite() {
33518
+ if (!config.draftStore || !config.draftKey) return;
33519
+ if (draftTimer !== null) clearTimeout(draftTimer);
33520
+ draftTimer = setTimeout(() => {
33521
+ draftTimer = null;
33522
+ if (isComposing || destroyed) return;
33523
+ config.draftStore?.write(config.draftKey, {
33524
+ value,
33525
+ tokens: [...tokens],
33526
+ attachmentIds: attachments.map((a2) => a2.id),
33527
+ updatedAt: now()
33528
+ });
33529
+ }, draftDebounceMs);
33530
+ }
33531
+ function cancelDraftWrite() {
33532
+ if (draftTimer !== null) {
33533
+ clearTimeout(draftTimer);
33534
+ draftTimer = null;
33535
+ }
33536
+ }
33537
+ function clearPersistedDraft() {
33538
+ cancelDraftWrite();
33539
+ if (config.draftStore && config.draftKey) config.draftStore.clear(config.draftKey);
33540
+ }
33541
+ function maybeCommitTypedEmoji() {
33542
+ if (!emojiTrigger || selection.start !== selection.end) return;
33543
+ const caretBefore = selection.start;
33544
+ const hit = detectTypedEmoji(value, caretBefore);
33545
+ if (!hit) return;
33546
+ if (tokens.some((t2) => t2.start < caretBefore && hit.start < t2.end)) return;
33547
+ const token = {
33548
+ triggerId: emojiTrigger.id,
33549
+ symbol: emojiTrigger.symbol,
33550
+ id: hit.shortcode,
33551
+ label: hit.shortcode.slice(1, -1),
33552
+ display: hit.unicode
33553
+ };
33554
+ const committed = commitTokenAt({
33555
+ value,
33556
+ tokens,
33557
+ start: hit.start,
33558
+ end: caretBefore,
33559
+ token
33560
+ });
33561
+ const delta = committed.value.length - value.length;
33562
+ value = committed.value;
33563
+ tokens = committed.tokens;
33564
+ selection = committed.selection;
33565
+ shiftDismissedForEdit(hit.start, caretBefore, delta);
33566
+ }
33567
+ function mutateValue(newText, newSelection, opts = {}) {
33568
+ const previousSelection = selection;
33569
+ if (isComposing) {
33570
+ const result2 = applyValueEdit({ oldValue: value, newValue: newText, tokens, newSelection });
33571
+ value = result2.value;
33572
+ tokens = result2.tokens;
33573
+ selection = result2.selection;
33574
+ emit2();
33575
+ return { applied: true, editStart: result2.edit?.start ?? newSelection.start };
33576
+ }
33577
+ const result = applyValueEdit({
33578
+ oldValue: value,
33579
+ newValue: newText,
33580
+ tokens,
33581
+ newSelection,
33582
+ maxLength: config.maxLength
33583
+ });
33584
+ if (result.rejected) {
33585
+ onEvent({ type: "edit-rejected", reason: "inside-token" });
33586
+ selection = result.selection;
33587
+ recomputeTriggers();
33588
+ emit2();
33589
+ return { applied: false, editStart: 0 };
33590
+ }
33591
+ if (result.trimmed && opts.trimEventOnClamp !== false) {
33592
+ onEvent({ type: "paste-trimmed" });
33593
+ }
33594
+ const editStart = result.edit?.start ?? newSelection.start;
33595
+ value = result.value;
33596
+ tokens = result.tokens;
33597
+ selection = snapSelectionToTokens(tokens, result.selection, previousSelection);
33598
+ if (result.edit) {
33599
+ shiftDismissedForEdit(
33600
+ result.edit.start,
33601
+ result.edit.oldEnd,
33602
+ result.edit.newEnd - result.edit.oldEnd
33603
+ );
33604
+ }
33605
+ maybeCommitTypedEmoji();
33606
+ recomputeTriggers();
33607
+ if (!opts.programmatic) fireTypingSignal();
33608
+ scheduleDraftWrite();
33609
+ emit2();
33610
+ return { applied: true, editStart };
33611
+ }
33612
+ function buildSubmissionTokens() {
33613
+ const plainText = payloadFor(value);
33614
+ const leading = value.length - value.trimStart().length;
33615
+ const serialized = serializeTokens(value, tokens);
33616
+ return {
33617
+ plainText,
33618
+ tokens: serialized.tokens.map((t2) => ({ ...t2, start: t2.start - leading, end: t2.end - leading }))
33619
+ };
33620
+ }
33621
+ function commitSuggestion(index) {
33622
+ const match = activeMatch;
33623
+ const menu = suggestions.state;
33624
+ if (!match || !menu.isOpen) return false;
33625
+ const item = menu.items[index ?? menu.activeIndex];
33626
+ if (!item) return false;
33627
+ const display = match.trigger.toDisplay(item);
33628
+ if (config.maxLength !== void 0) {
33629
+ const replacedSpan = value.slice(match.symbolStart, match.caret);
33630
+ const nextLength = graphemeLength(value) - graphemeLength(replacedSpan) + graphemeLength(display);
33631
+ if (nextLength > config.maxLength) {
33632
+ onEvent({ type: "insert-rejected", reason: "max-length" });
33633
+ emit2();
33634
+ return false;
33635
+ }
33636
+ }
33637
+ const token = {
33638
+ triggerId: match.trigger.id,
33639
+ symbol: match.trigger.symbol,
33640
+ id: item.id,
33641
+ label: item.display,
33642
+ display,
33643
+ metadata: item.metadata
33644
+ };
33645
+ const committed = commitTokenAt({
33646
+ value,
33647
+ tokens,
33648
+ start: match.symbolStart,
33649
+ end: match.caret,
33650
+ token
33651
+ });
33652
+ const delta = committed.value.length - value.length;
33653
+ value = committed.value;
33654
+ tokens = committed.tokens;
33655
+ selection = committed.selection;
33656
+ shiftDismissedForEdit(match.symbolStart, match.caret, delta);
33657
+ suggestions.cancelDeferredDismiss();
33658
+ activeMatch = null;
33659
+ suggestions.sync(null);
33660
+ scheduleDraftWrite();
33661
+ emit2();
33662
+ return true;
33663
+ }
33664
+ function performSubmit() {
33665
+ const counter = computeCounter(value, config.maxLength);
33666
+ const overBudget = counter.remaining !== null && counter.remaining < 0;
33667
+ if (!canSend({ text: value, attachments, disabled, readOnly, busy: isBusy }) || overBudget) {
33668
+ return null;
33669
+ }
33670
+ const { plainText, tokens: submissionTokens } = buildSubmissionTokens();
33671
+ if (config.validator) {
33672
+ const verdict = config.validator(plainText, submissionTokens);
33673
+ if (!verdict.isValid) {
33674
+ error = verdict.reason ?? "Message failed validation";
33675
+ emit2();
33676
+ return null;
33677
+ }
33678
+ }
33679
+ const submission = {
33680
+ plainText,
33681
+ tokens: submissionTokens,
33682
+ attachments: [...attachments],
33683
+ replyToMessageId: config.replyToMessageId,
33684
+ ...mode === "edit" ? { editingMessageId } : {}
33685
+ };
33686
+ value = "";
33687
+ tokens = [];
33688
+ attachments = [];
33689
+ selection = { start: 0, end: 0 };
33690
+ error = null;
33691
+ activeMatch = null;
33692
+ dismissed = [];
33693
+ clipShadow = null;
33694
+ mode = "compose";
33695
+ editingMessageId = void 0;
33696
+ preEditSnapshot = null;
33697
+ suggestions.close();
33698
+ clearPersistedDraft();
33699
+ emit2();
33700
+ return submission;
33701
+ }
33702
+ const api = {
33703
+ getState() {
33704
+ return buildState();
33705
+ },
33706
+ subscribe(listener) {
33707
+ listeners.add(listener);
33708
+ return () => listeners.delete(listener);
33709
+ },
33710
+ setValue(text, sel, opts) {
33711
+ if (destroyed || disabled) return;
33712
+ if (readOnly) {
33713
+ if (text === value && sel) api.setSelection(sel);
33714
+ return;
33715
+ }
33716
+ const nextSelection = sel ?? { start: text.length, end: text.length };
33717
+ mutateValue(text, nextSelection, { programmatic: opts?.programmatic });
33718
+ },
33719
+ setSelection(sel) {
33720
+ if (destroyed || disabled) return;
33721
+ const previous = selection;
33722
+ selection = snapSelectionToTokens(tokens, {
33723
+ start: Math.max(0, Math.min(sel.start, value.length)),
33724
+ end: Math.max(0, Math.min(sel.end, value.length))
33725
+ }, previous);
33726
+ recomputeTriggers();
33727
+ emit2();
33728
+ },
33729
+ insertTextAtCursor(text) {
33730
+ if (destroyed) return;
33731
+ if (disabled) {
33732
+ onEvent({ type: "insert-rejected", reason: "disabled" });
33733
+ return;
33734
+ }
33735
+ if (readOnly) {
33736
+ onEvent({ type: "insert-rejected", reason: "read-only" });
33737
+ return;
33738
+ }
33739
+ if (isComposing) {
33740
+ onEvent({ type: "insert-rejected", reason: "composing" });
33741
+ return;
33742
+ }
33743
+ if (config.maxLength !== void 0) {
33744
+ const range = expandRangeOverTokens(tokens, selection);
33745
+ const base = graphemeLength(value.slice(0, range.start)) + graphemeLength(value.slice(range.end));
33746
+ if (base + graphemeLength(text) > config.maxLength) {
33747
+ onEvent({ type: "insert-rejected", reason: "max-length" });
33748
+ return;
33749
+ }
33750
+ }
33751
+ const nextText = value.slice(0, selection.start) + text + value.slice(selection.end);
33752
+ const caret = selection.start + text.length;
33753
+ mutateValue(nextText, { start: caret, end: caret });
33754
+ },
33755
+ setComposing(composing) {
33756
+ if (destroyed || isComposing === composing) return;
33757
+ isComposing = composing;
33758
+ if (!composing) {
33759
+ if (config.maxLength !== void 0 && graphemeLength(value) > config.maxLength) {
33760
+ value = clampGraphemes(value, config.maxLength);
33761
+ tokens = tokens.filter((t2) => t2.end <= value.length);
33762
+ selection = {
33763
+ start: Math.min(selection.start, value.length),
33764
+ end: Math.min(selection.end, value.length)
33765
+ };
33766
+ }
33767
+ maybeCommitTypedEmoji();
33768
+ recomputeTriggers();
33769
+ scheduleDraftWrite();
33770
+ } else {
33771
+ activeMatch = null;
33772
+ suggestions.sync(null);
33773
+ }
33774
+ emit2();
33775
+ },
33776
+ applyEnter({ shiftPressed }) {
33777
+ if (destroyed || disabled || readOnly) return "noop";
33778
+ const menu = suggestions.state;
33779
+ if (menu.isOpen && !isComposing) {
33780
+ if (menu.items.length > 0) {
33781
+ return commitSuggestion() ? "committed-suggestion" : "noop";
33782
+ }
33783
+ return "noop";
33784
+ }
33785
+ if (!shouldSubmitOnEnter({ shiftPressed, isComposing })) {
33786
+ return isComposing ? "noop" : "newline";
33787
+ }
33788
+ return performSubmit() ? "submitted" : "noop";
33789
+ },
33790
+ moveSuggestionNext() {
33791
+ suggestions.moveNext();
33792
+ emit2();
33793
+ },
33794
+ moveSuggestionPrevious() {
33795
+ suggestions.movePrevious();
33796
+ emit2();
33797
+ },
33798
+ setSuggestionActiveIndex(index) {
33799
+ suggestions.setActiveIndex(index);
33800
+ emit2();
33801
+ },
33802
+ applySuggestion(index) {
33803
+ commitSuggestion(index);
33804
+ },
33805
+ dismissSuggestion() {
33806
+ if (activeMatch) {
33807
+ dismissed.push({ triggerId: activeMatch.trigger.id, symbolStart: activeMatch.symbolStart });
33808
+ activeMatch = null;
33809
+ }
33810
+ suggestions.close();
33811
+ emit2();
33812
+ },
33813
+ dismissSuggestionDeferred() {
33814
+ suggestions.dismissDeferred(() => {
33815
+ activeMatch = null;
33816
+ emit2();
33817
+ });
33818
+ },
33819
+ retrySuggestions() {
33820
+ suggestions.retry();
33821
+ emit2();
33822
+ },
33823
+ addAttachment(draft) {
33824
+ if (destroyed) return null;
33825
+ if (config.maxAttachments !== void 0 && attachments.length >= config.maxAttachments) {
33826
+ onEvent({ type: "attachment-rejected", reason: "max-attachments", name: draft.name });
33827
+ return null;
33828
+ }
33829
+ if (config.maxAttachmentSizeBytes !== void 0 && draft.sizeBytes !== void 0 && draft.sizeBytes > config.maxAttachmentSizeBytes) {
33830
+ onEvent({ type: "attachment-rejected", reason: "max-size", name: draft.name });
33831
+ return null;
33832
+ }
33833
+ if (config.acceptAttachment) {
33834
+ const verdict = config.acceptAttachment(draft);
33835
+ if (verdict !== true) {
33836
+ onEvent({
33837
+ type: "attachment-rejected",
33838
+ reason: "not-accepted",
33839
+ name: draft.name,
33840
+ ...typeof verdict === "string" ? { detail: verdict } : {}
33841
+ });
33842
+ return null;
33843
+ }
33844
+ }
33845
+ const id = draft.id ?? generateId$1("rfr-attachment");
33846
+ attachments = [...attachments, { ...draft, id, status: draft.status ?? "pending" }];
33847
+ scheduleDraftWrite();
33848
+ emit2();
33849
+ return id;
33850
+ },
33851
+ updateAttachment(id, patch) {
33852
+ if (destroyed) return;
33853
+ attachments = attachments.map((a2) => a2.id === id ? { ...a2, ...patch, id } : a2);
33854
+ emit2();
33855
+ },
33856
+ removeAttachment(id) {
33857
+ if (destroyed) return;
33858
+ attachments = attachments.filter((a2) => a2.id !== id);
33859
+ scheduleDraftWrite();
33860
+ emit2();
33861
+ },
33862
+ setBusy(busy) {
33863
+ if (destroyed || isBusy === busy) return;
33864
+ isBusy = busy;
33865
+ emit2();
33866
+ },
33867
+ setError(nextError) {
33868
+ if (destroyed) return;
33869
+ error = nextError;
33870
+ emit2();
33871
+ },
33872
+ setDisabled(nextDisabled) {
33873
+ if (destroyed || disabled === nextDisabled) return;
33874
+ disabled = nextDisabled;
33875
+ if (disabled) {
33876
+ activeMatch = null;
33877
+ suggestions.close();
33878
+ }
33879
+ emit2();
33880
+ },
33881
+ setReadOnly(nextReadOnly) {
33882
+ if (destroyed || readOnly === nextReadOnly) return;
33883
+ readOnly = nextReadOnly;
33884
+ if (readOnly) {
33885
+ activeMatch = null;
33886
+ suggestions.close();
33887
+ }
33888
+ emit2();
33889
+ },
33890
+ beginEdit({ value: editValue, tokens: editTokens, messageId }) {
33891
+ if (destroyed || disabled) return;
33892
+ preEditSnapshot = { value, tokens: [...tokens], selection, attachments: [...attachments] };
33893
+ mode = "edit";
33894
+ editingMessageId = messageId;
33895
+ value = editValue;
33896
+ tokens = [...editTokens ?? []];
33897
+ selection = { start: value.length, end: value.length };
33898
+ activeMatch = null;
33899
+ dismissed = [];
33900
+ suggestions.close();
33901
+ emit2();
33902
+ },
33903
+ cancelEdit() {
33904
+ if (destroyed || mode !== "edit") return;
33905
+ const snapshot = preEditSnapshot;
33906
+ mode = "compose";
33907
+ editingMessageId = void 0;
33908
+ preEditSnapshot = null;
33909
+ if (snapshot) {
33910
+ value = snapshot.value;
33911
+ tokens = snapshot.tokens;
33912
+ selection = snapshot.selection;
33913
+ attachments = snapshot.attachments;
33914
+ }
33915
+ activeMatch = null;
33916
+ suggestions.close();
33917
+ emit2();
33918
+ },
33919
+ copySelection() {
33920
+ return value.slice(selection.start, selection.end);
33921
+ },
33922
+ cutSelection() {
33923
+ if (destroyed || disabled || readOnly) return "";
33924
+ if (selection.start === selection.end) return "";
33925
+ const range = expandRangeOverTokens(tokens, selection);
33926
+ const text = value.slice(range.start, range.end);
33927
+ clipShadow = {
33928
+ text,
33929
+ tokens: tokens.filter((t2) => t2.start >= range.start && t2.end <= range.end).map((t2) => ({ ...t2, start: t2.start - range.start, end: t2.end - range.start }))
33930
+ };
33931
+ const nextText = value.slice(0, range.start) + value.slice(range.end);
33932
+ mutateValue(nextText, { start: range.start, end: range.start });
33933
+ return text;
33934
+ },
33935
+ pasteText(text) {
33936
+ if (destroyed || disabled || readOnly) return;
33937
+ const shadow = clipShadow;
33938
+ const range = expandRangeOverTokens(tokens, selection);
33939
+ const nextText = value.slice(0, range.start) + text + value.slice(range.end);
33940
+ const caret = range.start + text.length;
33941
+ const before = value;
33942
+ mutateValue(nextText, { start: caret, end: caret }, { trimEventOnClamp: true });
33943
+ if (shadow && text === shadow.text && value === before.slice(0, range.start) + text + before.slice(range.end)) {
33944
+ const restored = shadow.tokens.map((t2) => ({
33945
+ ...t2,
33946
+ start: t2.start + range.start,
33947
+ end: t2.end + range.start
33948
+ }));
33949
+ tokens = [...tokens, ...restored].sort((a2, b2) => a2.start - b2.start);
33950
+ emit2();
33951
+ }
33952
+ },
33953
+ submit() {
33954
+ if (destroyed) return null;
33955
+ return performSubmit();
33956
+ },
33957
+ reset() {
33958
+ if (destroyed) return;
33959
+ cancelDraftWrite();
33960
+ suggestions.close();
33961
+ value = config.initialValue ?? "";
33962
+ tokens = [...config.initialTokens ?? []];
33963
+ selection = { start: value.length, end: value.length };
33964
+ isComposing = false;
33965
+ isBusy = false;
33966
+ error = null;
33967
+ attachments = [];
33968
+ mode = "compose";
33969
+ editingMessageId = void 0;
33970
+ preEditSnapshot = null;
33971
+ activeMatch = null;
33972
+ dismissed = [];
33973
+ clipShadow = null;
33974
+ lastTypingAt = Number.NEGATIVE_INFINITY;
33975
+ recomputeTriggers();
33976
+ emit2();
33977
+ },
33978
+ serialize() {
33979
+ return serializeTokens(value, tokens);
33980
+ },
33981
+ destroy() {
33982
+ if (destroyed) return;
33983
+ cancelDraftWrite();
33984
+ suggestions.destroy();
33985
+ listeners.clear();
33986
+ destroyed = true;
33987
+ }
33988
+ };
33989
+ return api;
33990
+ }
33991
+ var composerSurfaceVariants = cva({
33992
+ base: [
33993
+ "overflow-hidden rounded-2xl border border-border bg-background shadow-sm",
33994
+ "transition-shadow focus-within:border-foreground/25 focus-within:shadow-md"
33995
+ ].join(" "),
33996
+ variants: {
33997
+ disabled: {
33998
+ true: "pointer-events-none opacity-60",
33999
+ false: ""
34000
+ },
34001
+ error: {
34002
+ true: "border-destructive/50",
34003
+ false: ""
34004
+ }
34005
+ },
34006
+ defaultVariants: {
34007
+ disabled: "false",
34008
+ error: "false"
34009
+ }
34010
+ });
34011
+ var composerFieldClass = "block w-full resize-none bg-transparent px-3.5 py-3 text-sm placeholder:text-muted-foreground focus:outline-none";
34012
+ var composerTrayClass = "flex flex-wrap gap-2 px-3 pt-3";
34013
+ var composerAttachmentChipVariants = cva({
34014
+ base: "inline-flex items-center gap-1 rounded-md bg-muted px-2 py-0.5 text-xs",
34015
+ variants: {
34016
+ status: {
34017
+ pending: "opacity-70",
34018
+ uploading: "opacity-70",
34019
+ ready: "",
34020
+ error: "text-destructive"
34021
+ }
34022
+ },
34023
+ defaultVariants: {
34024
+ status: "pending"
34025
+ }
34026
+ });
34027
+ var composerTokenPillClass = "rounded bg-accent/40 px-0.5 text-accent-foreground";
34028
+ var composerMenuClass = "z-20 w-72 overflow-hidden rounded-xl border border-border bg-popover shadow-lg";
34029
+ var composerMenuItemVariants = cva({
34030
+ base: "flex w-full items-center gap-2 px-3 py-2 text-left text-sm",
34031
+ variants: {
34032
+ active: {
34033
+ true: "bg-accent",
34034
+ false: "hover:bg-accent/50"
34035
+ }
34036
+ },
34037
+ defaultVariants: {
34038
+ active: "false"
34039
+ }
34040
+ });
34041
+ var composerCounterVariants = cva({
34042
+ base: "text-xs tabular-nums",
34043
+ variants: {
34044
+ overLimit: {
34045
+ true: "font-medium text-destructive",
34046
+ false: "text-muted-foreground"
34047
+ }
34048
+ },
34049
+ defaultVariants: {
34050
+ overLimit: "false"
34051
+ }
34052
+ });
34053
+ var composerPrimaryActionVariants = cva({
34054
+ base: "flex h-9 w-9 items-center justify-center rounded-full transition",
34055
+ variants: {
34056
+ enabled: {
34057
+ true: "bg-primary text-primary-foreground hover:opacity-90",
34058
+ false: "bg-muted text-muted-foreground"
34059
+ }
34060
+ },
34061
+ defaultVariants: {
34062
+ enabled: "false"
34063
+ }
34064
+ });
34065
+ function useComposer(config = {}, init) {
34066
+ const configRef = React11__namespace.useRef(config);
34067
+ const initRef = React11__namespace.useRef(init);
34068
+ const coreRef = React11__namespace.useRef(null);
34069
+ const getCore = React11__namespace.useCallback(() => {
34070
+ if (coreRef.current === null) {
34071
+ const api = createComposer(configRef.current);
34072
+ initRef.current?.(api);
34073
+ coreRef.current = { api, snapshot: api.getState() };
34074
+ }
34075
+ return coreRef.current;
34076
+ }, []);
34077
+ const subscribe = React11__namespace.useCallback(
34078
+ (onStoreChange) => {
34079
+ const core = getCore();
34080
+ return core.api.subscribe((next) => {
34081
+ core.snapshot = next;
34082
+ onStoreChange();
34083
+ });
34084
+ },
34085
+ [getCore]
34086
+ );
34087
+ const getSnapshot = React11__namespace.useCallback(() => getCore().snapshot, [getCore]);
34088
+ const state = React11__namespace.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
34089
+ React11__namespace.useEffect(
34090
+ () => () => {
34091
+ coreRef.current?.api.destroy();
34092
+ coreRef.current = null;
34093
+ },
34094
+ []
34095
+ );
34096
+ return { state, api: getCore().api };
34097
+ }
34098
+ var FIELD_LINE_HEIGHT = 1.5;
34099
+ var FIELD_VERTICAL_PADDING_REM = 1.5;
34100
+ var TRIMMED_NOTICE_HIDE_MS = 4e3;
34101
+ function fieldHeightFor(lines) {
34102
+ return `calc(${lines * FIELD_LINE_HEIGHT}em + ${FIELD_VERTICAL_PADDING_REM}rem)`;
34103
+ }
34104
+ var DEFAULT_COMPOSER_STRINGS = {
34105
+ composerLabel: "Message composer",
34106
+ messageLabel: "Message",
34107
+ sendLabel: "Send",
34108
+ stopLabel: "Stop",
34109
+ attachLabel: "Attach a file",
34110
+ removeAttachmentLabel: (name) => `Remove attachment ${name}`,
34111
+ suggestionsLabel: "Suggestions",
34112
+ noMatchesLabel: (query) => `No matches for "${query}"`,
34113
+ loadingLabel: "Loading suggestions\u2026",
34114
+ retryLabel: "Retry",
34115
+ trimmedNotice: "Pasted text was trimmed to fit the length limit",
34116
+ counterLabel: (remaining) => `${remaining} characters remaining`,
34117
+ editingLabel: "Editing message",
34118
+ cancelEditLabel: "Cancel edit",
34119
+ resultsAnnouncement: (count) => `${count} suggestions available`
34120
+ };
34121
+ function useLatestRef(value) {
34122
+ const ref = React11__namespace.useRef(value);
34123
+ ref.current = value;
34124
+ return ref;
34125
+ }
34126
+ function kindFromMime(mimeType) {
34127
+ if (mimeType.startsWith("image/")) return "image";
34128
+ if (mimeType.startsWith("video/")) return "video";
34129
+ if (mimeType.startsWith("audio/")) return "audio";
34130
+ return "file";
34131
+ }
34132
+ function segmentValue(value, tokens) {
34133
+ const segments = [];
34134
+ let cursor = 0;
34135
+ for (const token of tokens) {
34136
+ if (token.start > cursor) {
34137
+ segments.push({ key: `p${cursor}`, text: value.slice(cursor, token.start), isToken: false });
34138
+ }
34139
+ segments.push({ key: `t${token.start}`, text: value.slice(token.start, token.end), isToken: true });
34140
+ cursor = token.end;
34141
+ }
34142
+ if (cursor < value.length) {
34143
+ segments.push({ key: `p${cursor}`, text: value.slice(cursor), isToken: false });
34144
+ }
34145
+ return segments;
34146
+ }
34147
+ var iconSvgProps = {
34148
+ xmlns: "http://www.w3.org/2000/svg",
34149
+ viewBox: "0 0 24 24",
34150
+ width: 16,
34151
+ height: 16,
34152
+ fill: "none",
34153
+ stroke: "currentColor",
34154
+ strokeWidth: 2,
34155
+ strokeLinecap: "round",
34156
+ strokeLinejoin: "round",
34157
+ "aria-hidden": true
34158
+ };
34159
+ function IconArrowUp() {
34160
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { ...iconSvgProps, children: [
34161
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 19V5" }),
34162
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m5 12 7-7 7 7" })
34163
+ ] });
34164
+ }
34165
+ function IconStop() {
34166
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { ...iconSvgProps, children: /* @__PURE__ */ jsxRuntime.jsx("rect", { x: 7, y: 7, width: 10, height: 10, rx: 1.5 }) });
34167
+ }
34168
+ var RefractionComposer = React11__namespace.forwardRef(
34169
+ ({
34170
+ value: valueProp,
34171
+ defaultValue,
34172
+ onChange,
34173
+ placeholder,
34174
+ disabledPlaceholder,
34175
+ minLines = 1,
34176
+ maxLines = 6,
34177
+ maxLength,
34178
+ maxAttachments,
34179
+ disabled = false,
34180
+ readOnly = false,
34181
+ busy = false,
34182
+ onStop,
34183
+ autoFocus,
34184
+ dir,
34185
+ triggers,
34186
+ submitOnEnter,
34187
+ strings: stringsProp,
34188
+ leading,
34189
+ trailing,
34190
+ primaryAction,
34191
+ renderSuggestion,
34192
+ initialAttachments,
34193
+ draftStore,
34194
+ draftKey,
34195
+ validator,
34196
+ replyToMessageId,
34197
+ onSubmit,
34198
+ onEditLastRequested,
34199
+ onEditCancel,
34200
+ onTypingActivity,
34201
+ onAttachmentAdd,
34202
+ onAttachmentRejected,
34203
+ onEvent,
34204
+ apiRef,
34205
+ id,
34206
+ className,
34207
+ ...rest
34208
+ }, forwardedRef) => {
34209
+ const strings = React11__namespace.useMemo(
34210
+ () => ({ ...DEFAULT_COMPOSER_STRINGS, ...stringsProp }),
34211
+ [stringsProp]
34212
+ );
34213
+ const onSubmitRef = useLatestRef(onSubmit);
34214
+ const onChangeRef = useLatestRef(onChange);
34215
+ const onStopRef = useLatestRef(onStop);
34216
+ const onEventRef = useLatestRef(onEvent);
34217
+ const onTypingActivityRef = useLatestRef(onTypingActivity);
34218
+ const onAttachmentAddRef = useLatestRef(onAttachmentAdd);
34219
+ const onAttachmentRejectedRef = useLatestRef(onAttachmentRejected);
34220
+ const onEditLastRequestedRef = useLatestRef(onEditLastRequested);
34221
+ const onEditCancelRef = useLatestRef(onEditCancel);
34222
+ const [trimmedNoticeVisible, setTrimmedNoticeVisible] = React11__namespace.useState(false);
34223
+ const trimmedTimerRef = React11__namespace.useRef(null);
34224
+ const configRef = React11__namespace.useRef(null);
34225
+ if (configRef.current === null) {
34226
+ configRef.current = {
34227
+ initialValue: valueProp ?? defaultValue ?? "",
34228
+ minLines,
34229
+ maxLines,
34230
+ triggers,
34231
+ onEvent: (event) => {
34232
+ if (event.type === "typing") onTypingActivityRef.current?.();
34233
+ if (event.type === "attachment-rejected") onAttachmentRejectedRef.current?.(event);
34234
+ if (event.type === "paste-trimmed") {
34235
+ setTrimmedNoticeVisible(true);
34236
+ if (trimmedTimerRef.current !== null) clearTimeout(trimmedTimerRef.current);
34237
+ trimmedTimerRef.current = setTimeout(() => {
34238
+ trimmedTimerRef.current = null;
34239
+ setTrimmedNoticeVisible(false);
34240
+ }, TRIMMED_NOTICE_HIDE_MS);
34241
+ }
34242
+ onEventRef.current?.(event);
34243
+ }
34244
+ };
34245
+ }
34246
+ configRef.current.maxLength = maxLength;
34247
+ configRef.current.maxAttachments = maxAttachments;
34248
+ configRef.current.validator = validator;
34249
+ configRef.current.replyToMessageId = replyToMessageId;
34250
+ configRef.current.draftStore = draftStore;
34251
+ configRef.current.draftKey = draftKey;
34252
+ const { state, api } = useComposer(configRef.current, (core) => {
34253
+ for (const draft of initialAttachments ?? []) core.addAttachment(draft);
34254
+ if (disabled) core.setDisabled(true);
34255
+ if (readOnly) core.setReadOnly(true);
34256
+ if (busy) core.setBusy(true);
34257
+ });
34258
+ React11__namespace.useEffect(() => () => {
34259
+ if (trimmedTimerRef.current !== null) clearTimeout(trimmedTimerRef.current);
34260
+ }, []);
34261
+ const textareaRef = React11__namespace.useRef(null);
34262
+ const highlightRef = React11__namespace.useRef(null);
34263
+ const mergedTextareaRef = React11__namespace.useCallback(
34264
+ (node) => {
34265
+ textareaRef.current = node;
34266
+ if (typeof forwardedRef === "function") forwardedRef(node);
34267
+ else if (forwardedRef) forwardedRef.current = node;
34268
+ },
34269
+ [forwardedRef]
34270
+ );
34271
+ const idBase = React11__namespace.useRef(id ?? generateId("composer")).current;
34272
+ const listId = `${idBase}-listbox`;
34273
+ const optionId = (index) => `${idBase}-option-${index}`;
34274
+ React11__namespace.useEffect(() => {
34275
+ if (!apiRef) return;
34276
+ if (typeof apiRef === "function") {
34277
+ apiRef(api);
34278
+ return () => {
34279
+ apiRef(null);
34280
+ };
34281
+ }
34282
+ const mutableRef = apiRef;
34283
+ mutableRef.current = api;
34284
+ return () => {
34285
+ mutableRef.current = null;
34286
+ };
34287
+ }, [apiRef, api]);
34288
+ React11__namespace.useEffect(() => {
34289
+ api.setDisabled(disabled);
34290
+ }, [api, disabled]);
34291
+ React11__namespace.useEffect(() => {
34292
+ api.setReadOnly(readOnly);
34293
+ }, [api, readOnly]);
34294
+ React11__namespace.useEffect(() => {
34295
+ api.setBusy(busy);
34296
+ }, [api, busy]);
34297
+ React11__namespace.useEffect(() => {
34298
+ if (valueProp === void 0) return;
34299
+ const current = api.getState();
34300
+ if (current.disabled || current.readOnly || current.value === valueProp) return;
34301
+ api.setValue(
34302
+ valueProp,
34303
+ { start: valueProp.length, end: valueProp.length },
34304
+ { programmatic: true }
34305
+ );
34306
+ }, [api, valueProp, state.value]);
34307
+ const withValueChange = React11__namespace.useCallback(
34308
+ (mutate) => {
34309
+ const before = api.getState().value;
34310
+ const result = mutate();
34311
+ const after = api.getState().value;
34312
+ if (after !== before) onChangeRef.current?.(after);
34313
+ return result;
34314
+ },
34315
+ [api, onChangeRef]
34316
+ );
34317
+ const [coarsePointer, setCoarsePointer] = React11__namespace.useState(false);
34318
+ React11__namespace.useEffect(() => {
34319
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
34320
+ const query = window.matchMedia("(pointer: coarse)");
34321
+ const update = () => setCoarsePointer(query.matches);
34322
+ update();
34323
+ query.addEventListener?.("change", update);
34324
+ return () => query.removeEventListener?.("change", update);
34325
+ }, []);
34326
+ const submitEnabled = submitOnEnter ?? !coarsePointer;
34327
+ React11__namespace.useEffect(() => {
34328
+ const el = textareaRef.current;
34329
+ if (!el) return;
34330
+ if (el.value !== state.value) el.value = state.value;
34331
+ if (document.activeElement === el && (el.selectionStart !== state.selection.start || el.selectionEnd !== state.selection.end)) {
34332
+ el.setSelectionRange(state.selection.start, state.selection.end);
34333
+ }
34334
+ }, [state.value, state.selection.start, state.selection.end]);
34335
+ const readSelection = (el) => ({
34336
+ start: el.selectionStart ?? el.value.length,
34337
+ end: el.selectionEnd ?? el.value.length
34338
+ });
34339
+ const handleChange = (event) => {
34340
+ const el = event.currentTarget;
34341
+ withValueChange(() => api.setValue(el.value, readSelection(el)));
34342
+ };
34343
+ const handleSelect = (event) => {
34344
+ const el = event.currentTarget;
34345
+ const next = readSelection(el);
34346
+ const current = api.getState().selection;
34347
+ if (next.start !== current.start || next.end !== current.end) {
34348
+ api.setSelection(next);
34349
+ }
34350
+ };
34351
+ const submitNow = React11__namespace.useCallback(() => {
34352
+ const submission = withValueChange(() => api.submit());
34353
+ if (submission) onSubmitRef.current?.(submission);
34354
+ return submission !== null;
34355
+ }, [api, onSubmitRef, withValueChange]);
34356
+ const handleKeyDown = (event) => {
34357
+ const snapshot = api.getState();
34358
+ const menuOpen2 = snapshot.suggestion.isOpen && !snapshot.isComposing;
34359
+ const hasMenuItems = snapshot.suggestion.items.length > 0;
34360
+ if (menuOpen2) {
34361
+ if (event.key === "ArrowDown" && hasMenuItems) {
34362
+ event.preventDefault();
34363
+ api.moveSuggestionNext();
34364
+ return;
34365
+ }
34366
+ if (event.key === "ArrowUp" && hasMenuItems) {
34367
+ event.preventDefault();
34368
+ api.moveSuggestionPrevious();
34369
+ return;
34370
+ }
34371
+ if (event.key === "Tab" && hasMenuItems) {
34372
+ event.preventDefault();
34373
+ withValueChange(() => api.applySuggestion());
34374
+ return;
34375
+ }
34376
+ if (event.key === "Escape") {
34377
+ event.preventDefault();
34378
+ api.dismissSuggestion();
34379
+ return;
34380
+ }
34381
+ }
34382
+ if (event.key === "Escape" && !menuOpen2 && snapshot.mode === "edit") {
34383
+ event.preventDefault();
34384
+ withValueChange(() => api.cancelEdit());
34385
+ onEditCancelRef.current?.();
34386
+ return;
34387
+ }
34388
+ if (event.key === "ArrowUp" && !menuOpen2 && !snapshot.isComposing && snapshot.value === "" && onEditLastRequestedRef.current) {
34389
+ event.preventDefault();
34390
+ onEditLastRequestedRef.current();
34391
+ return;
34392
+ }
34393
+ if (event.key === "Enter") {
34394
+ if (menuOpen2) {
34395
+ event.preventDefault();
34396
+ withValueChange(() => api.applyEnter({ shiftPressed: event.shiftKey }));
34397
+ return;
34398
+ }
34399
+ if (snapshot.isComposing) return;
34400
+ if (event.shiftKey || !submitEnabled) return;
34401
+ event.preventDefault();
34402
+ if (snapshot.disabled || snapshot.readOnly) return;
34403
+ submitNow();
34404
+ }
34405
+ };
34406
+ const handleCompositionStart = () => api.setComposing(true);
34407
+ const handleCompositionEnd = () => withValueChange(() => api.setComposing(false));
34408
+ const handleCopy = (event) => {
34409
+ const text = api.copySelection();
34410
+ if (text === "") return;
34411
+ event.preventDefault();
34412
+ event.clipboardData.setData("text/plain", text);
34413
+ };
34414
+ const handleCut = (event) => {
34415
+ event.preventDefault();
34416
+ const text = withValueChange(() => api.cutSelection());
34417
+ if (text !== "") event.clipboardData.setData("text/plain", text);
34418
+ };
34419
+ const addFiles = React11__namespace.useCallback(
34420
+ (files) => {
34421
+ for (const file of Array.from(files)) {
34422
+ const previewUrl = typeof URL !== "undefined" && typeof URL.createObjectURL === "function" ? URL.createObjectURL(file) : void 0;
34423
+ const attachmentId = api.addAttachment({
34424
+ kind: kindFromMime(file.type),
34425
+ name: file.name,
34426
+ mimeType: file.type || void 0,
34427
+ sizeBytes: file.size,
34428
+ previewUrl
34429
+ });
34430
+ if (attachmentId === null) {
34431
+ if (previewUrl !== void 0) URL.revokeObjectURL(previewUrl);
34432
+ continue;
34433
+ }
34434
+ const added = api.getState().attachments.find((a2) => a2.id === attachmentId);
34435
+ if (added) onAttachmentAddRef.current?.(added);
34436
+ }
34437
+ },
34438
+ [api, onAttachmentAddRef]
34439
+ );
34440
+ const handlePaste = (event) => {
34441
+ const files = event.clipboardData?.files;
34442
+ if (files && files.length > 0) {
34443
+ event.preventDefault();
34444
+ addFiles(files);
34445
+ return;
34446
+ }
34447
+ event.preventDefault();
34448
+ withValueChange(() => api.pasteText(event.clipboardData?.getData("text/plain") ?? ""));
34449
+ };
34450
+ const [dragActive, setDragActive] = React11__namespace.useState(false);
34451
+ const dragHasPayload = (event) => {
34452
+ const types = event.dataTransfer ? Array.from(event.dataTransfer.types ?? []) : [];
34453
+ return types.includes("Files") || types.includes("text/plain");
34454
+ };
34455
+ const handleDragOver = (event) => {
34456
+ if (!dragHasPayload(event)) return;
34457
+ event.preventDefault();
34458
+ setDragActive(true);
34459
+ };
34460
+ const handleDragLeave = () => setDragActive(false);
34461
+ const handleDrop = (event) => {
34462
+ event.preventDefault();
34463
+ setDragActive(false);
34464
+ const transfer = event.dataTransfer;
34465
+ if (!transfer) return;
34466
+ if (transfer.files && transfer.files.length > 0) {
34467
+ addFiles(transfer.files);
34468
+ return;
34469
+ }
34470
+ const text = transfer.getData("text/plain");
34471
+ if (text) withValueChange(() => api.pasteText(text));
34472
+ };
34473
+ const handleBlur = () => api.dismissSuggestionDeferred();
34474
+ const handleScroll = (event) => {
34475
+ if (highlightRef.current) {
34476
+ highlightRef.current.scrollTop = event.currentTarget.scrollTop;
34477
+ }
34478
+ };
34479
+ const hasTriggers = (triggers?.length ?? 0) > 0;
34480
+ const menuOpen = state.suggestion.isOpen;
34481
+ const visibleItems = state.suggestion.visibleItems;
34482
+ const activeIndex = state.suggestion.activeIndex;
34483
+ const hasText = state.value.trim().length > 0;
34484
+ const effectivePlaceholder = state.disabled && disabledPlaceholder !== void 0 ? disabledPlaceholder : placeholder;
34485
+ const segments = React11__namespace.useMemo(
34486
+ () => segmentValue(state.value, state.tokens),
34487
+ [state.value, state.tokens]
34488
+ );
34489
+ const primaryContext = {
34490
+ hasText,
34491
+ canSend: state.canSend,
34492
+ busy: state.isBusy
34493
+ };
34494
+ const defaultPrimaryAction = state.isBusy ? /* @__PURE__ */ jsxRuntime.jsx(
34495
+ "button",
34496
+ {
34497
+ type: "button",
34498
+ "aria-label": strings.stopLabel,
34499
+ className: composerPrimaryActionVariants({ enabled: "true" }),
34500
+ onMouseDown: (event) => event.preventDefault(),
34501
+ onClick: () => onStopRef.current?.(),
34502
+ children: /* @__PURE__ */ jsxRuntime.jsx(IconStop, {})
34503
+ }
34504
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
34505
+ "button",
34506
+ {
34507
+ type: "button",
34508
+ "aria-label": strings.sendLabel,
34509
+ disabled: !state.canSend,
34510
+ className: composerPrimaryActionVariants({ enabled: state.canSend ? "true" : "false" }),
34511
+ onMouseDown: (event) => event.preventDefault(),
34512
+ onClick: submitNow,
34513
+ children: /* @__PURE__ */ jsxRuntime.jsx(IconArrowUp, {})
34514
+ }
34515
+ );
34516
+ const menuBody = () => {
34517
+ if (state.suggestion.loading) {
34518
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2 text-sm text-muted-foreground", children: strings.loadingLabel });
34519
+ }
34520
+ if (state.suggestion.error !== null) {
34521
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 px-3 py-2 text-sm text-destructive", children: [
34522
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 truncate", children: state.suggestion.error }),
34523
+ /* @__PURE__ */ jsxRuntime.jsx(
34524
+ "button",
34525
+ {
34526
+ type: "button",
34527
+ className: "font-medium underline",
34528
+ onMouseDown: (event) => event.preventDefault(),
34529
+ onClick: () => api.retrySuggestions(),
34530
+ children: strings.retryLabel
34531
+ }
34532
+ )
34533
+ ] });
34534
+ }
34535
+ if (visibleItems.length === 0) {
34536
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2 text-sm text-muted-foreground", children: strings.noMatchesLabel(state.activeTrigger?.query ?? "") });
34537
+ }
34538
+ return visibleItems.map((candidate, index) => {
34539
+ const active = index === activeIndex;
34540
+ return /* @__PURE__ */ jsxRuntime.jsx(
34541
+ "button",
34542
+ {
34543
+ type: "button",
34544
+ role: "option",
34545
+ id: optionId(index),
34546
+ "aria-selected": active,
34547
+ className: composerMenuItemVariants({ active: active ? "true" : "false" }),
34548
+ onMouseEnter: () => api.setSuggestionActiveIndex(index),
34549
+ onMouseDown: (event) => event.preventDefault(),
34550
+ onClick: () => withValueChange(() => api.applySuggestion(index)),
34551
+ children: renderSuggestion ? renderSuggestion(candidate, { active, index }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
34552
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 truncate", children: candidate.display }),
34553
+ candidate.subtitle !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-xs text-muted-foreground", children: candidate.subtitle })
34554
+ ] })
34555
+ },
34556
+ candidate.id
34557
+ );
34558
+ });
34559
+ };
34560
+ return /* @__PURE__ */ jsxRuntime.jsxs(
34561
+ "div",
34562
+ {
34563
+ ...rest,
34564
+ id: idBase,
34565
+ dir,
34566
+ role: "form",
34567
+ "aria-label": strings.composerLabel,
34568
+ className: cn("relative", className),
34569
+ onDragOver: handleDragOver,
34570
+ onDragLeave: handleDragLeave,
34571
+ onDrop: handleDrop,
34572
+ children: [
34573
+ menuOpen && /* @__PURE__ */ jsxRuntime.jsx(
34574
+ "div",
34575
+ {
34576
+ id: listId,
34577
+ role: "listbox",
34578
+ "aria-label": strings.suggestionsLabel,
34579
+ className: cn(composerMenuClass, "absolute bottom-full start-0 mb-2"),
34580
+ children: menuBody()
34581
+ }
34582
+ ),
34583
+ /* @__PURE__ */ jsxRuntime.jsxs(
34584
+ "div",
34585
+ {
34586
+ "data-dragover": dragActive ? "" : void 0,
34587
+ className: cn(
34588
+ composerSurfaceVariants({
34589
+ disabled: state.disabled ? "true" : "false",
34590
+ error: state.error !== null ? "true" : "false"
34591
+ }),
34592
+ dragActive && "border-primary/60 shadow-md"
34593
+ ),
34594
+ children: [
34595
+ state.error !== null && /* @__PURE__ */ jsxRuntime.jsx(
34596
+ "div",
34597
+ {
34598
+ role: "alert",
34599
+ className: "flex items-center gap-2 border-b border-border bg-destructive/5 px-3 py-2 text-xs text-destructive",
34600
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 truncate", children: state.error })
34601
+ }
34602
+ ),
34603
+ state.mode === "edit" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 border-b border-border px-3 py-2 text-xs text-muted-foreground", children: [
34604
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 truncate", children: strings.editingLabel }),
34605
+ /* @__PURE__ */ jsxRuntime.jsx(
34606
+ "button",
34607
+ {
34608
+ type: "button",
34609
+ className: "font-medium underline",
34610
+ onClick: () => {
34611
+ withValueChange(() => api.cancelEdit());
34612
+ onEditCancelRef.current?.();
34613
+ },
34614
+ children: strings.cancelEditLabel
34615
+ }
34616
+ )
34617
+ ] }),
34618
+ trimmedNoticeVisible && /* @__PURE__ */ jsxRuntime.jsx("div", { role: "status", className: "px-3 pt-2 text-xs text-muted-foreground", children: strings.trimmedNotice }),
34619
+ state.attachments.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: composerTrayClass, children: state.attachments.map((attachment) => /* @__PURE__ */ jsxRuntime.jsxs(
34620
+ "span",
34621
+ {
34622
+ className: composerAttachmentChipVariants({ status: attachment.status }),
34623
+ children: [
34624
+ attachment.name,
34625
+ /* @__PURE__ */ jsxRuntime.jsx(
34626
+ "button",
34627
+ {
34628
+ type: "button",
34629
+ "aria-label": strings.removeAttachmentLabel(attachment.name),
34630
+ className: "text-muted-foreground hover:text-destructive",
34631
+ onClick: () => {
34632
+ api.removeAttachment(attachment.id);
34633
+ if (attachment.previewUrl !== void 0 && typeof URL !== "undefined" && typeof URL.revokeObjectURL === "function") {
34634
+ URL.revokeObjectURL(attachment.previewUrl);
34635
+ }
34636
+ },
34637
+ children: "\u2715"
34638
+ }
34639
+ )
34640
+ ]
34641
+ },
34642
+ attachment.id
34643
+ )) }),
34644
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
34645
+ /* @__PURE__ */ jsxRuntime.jsxs(
34646
+ "div",
34647
+ {
34648
+ ref: highlightRef,
34649
+ "aria-hidden": "true",
34650
+ className: cn(
34651
+ composerFieldClass,
34652
+ "pointer-events-none overflow-hidden whitespace-pre-wrap break-words text-transparent"
34653
+ ),
34654
+ style: {
34655
+ lineHeight: FIELD_LINE_HEIGHT,
34656
+ minHeight: fieldHeightFor(minLines),
34657
+ maxHeight: fieldHeightFor(maxLines)
34658
+ },
34659
+ children: [
34660
+ segments.map(
34661
+ (segment) => segment.isToken ? /* @__PURE__ */ jsxRuntime.jsx(
34662
+ "span",
34663
+ {
34664
+ "data-token": "",
34665
+ className: cn(composerTokenPillClass, "px-0 text-transparent"),
34666
+ children: /* @__PURE__ */ jsxRuntime.jsx("bdi", { children: segment.text })
34667
+ },
34668
+ segment.key
34669
+ ) : /* @__PURE__ */ jsxRuntime.jsx("span", { children: segment.text }, segment.key)
34670
+ ),
34671
+ "\u200B"
34672
+ ]
34673
+ }
34674
+ ),
34675
+ /* @__PURE__ */ jsxRuntime.jsx(
34676
+ "textarea",
34677
+ {
34678
+ ref: mergedTextareaRef,
34679
+ id: `${idBase}-input`,
34680
+ rows: minLines,
34681
+ value: state.value,
34682
+ placeholder: effectivePlaceholder,
34683
+ disabled: state.disabled,
34684
+ readOnly: state.readOnly,
34685
+ autoFocus,
34686
+ "aria-multiline": "true",
34687
+ "aria-label": strings.messageLabel,
34688
+ ...hasTriggers ? {
34689
+ "aria-autocomplete": "list",
34690
+ "aria-expanded": menuOpen,
34691
+ "aria-controls": listId,
34692
+ "aria-activedescendant": menuOpen && visibleItems.length > 0 ? optionId(activeIndex) : void 0
34693
+ } : {},
34694
+ className: cn(
34695
+ composerFieldClass,
34696
+ "absolute inset-0 h-full overflow-y-auto rounded-md focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
34697
+ ),
34698
+ style: { lineHeight: FIELD_LINE_HEIGHT, maxHeight: fieldHeightFor(maxLines) },
34699
+ onChange: handleChange,
34700
+ onSelect: handleSelect,
34701
+ onKeyDown: handleKeyDown,
34702
+ onCompositionStart: handleCompositionStart,
34703
+ onCompositionEnd: handleCompositionEnd,
34704
+ onCopy: handleCopy,
34705
+ onCut: handleCut,
34706
+ onPaste: handlePaste,
34707
+ onBlur: handleBlur,
34708
+ onScroll: handleScroll
34709
+ }
34710
+ )
34711
+ ] }),
34712
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5 px-2 pb-2", children: [
34713
+ leading,
34714
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1" }),
34715
+ state.counter.visible && state.counter.remaining !== null && /* @__PURE__ */ jsxRuntime.jsx(
34716
+ "div",
34717
+ {
34718
+ "aria-live": state.counter.overLimit ? "polite" : void 0,
34719
+ className: cn(
34720
+ composerCounterVariants({
34721
+ overLimit: state.counter.overLimit ? "true" : "false"
34722
+ }),
34723
+ "me-2"
34724
+ ),
34725
+ children: strings.counterLabel(state.counter.remaining)
34726
+ }
34727
+ ),
34728
+ trailing,
34729
+ primaryAction ? primaryAction(primaryContext) : defaultPrimaryAction
34730
+ ] })
34731
+ ]
34732
+ }
34733
+ ),
34734
+ /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-live": "polite", className: "sr-only", children: menuOpen && !state.suggestion.loading && state.suggestion.error === null ? visibleItems.length === 0 ? strings.noMatchesLabel(state.activeTrigger?.query ?? "") : strings.resultsAnnouncement(visibleItems.length) : "" })
34735
+ ]
34736
+ }
34737
+ );
34738
+ }
34739
+ );
34740
+ RefractionComposer.displayName = "RefractionComposer";
32758
34741
 
32759
34742
  // ../conversation/dist/index.js
32760
34743
  function byTime(a2, b2) {
@@ -33498,11 +35481,11 @@ var IconListOl = () => svg([
33498
35481
  h("path", { key: "e", d: "M4 10h2" }),
33499
35482
  h("path", { key: "f", d: "M6 18H4c0-1 2-2 2-3s-1-1.5-2-1" })
33500
35483
  ]);
33501
- var IconArrowUp = () => svg([
35484
+ var IconArrowUp2 = () => svg([
33502
35485
  h("path", { key: "a", d: "M12 19V5" }),
33503
35486
  h("path", { key: "b", d: "m5 12 7-7 7 7" })
33504
35487
  ]);
33505
- var IconStop = () => svg(h("rect", { x: 7, y: 7, width: 10, height: 10, rx: 1.5 }));
35488
+ var IconStop2 = () => svg(h("rect", { x: 7, y: 7, width: 10, height: 10, rx: 1.5 }));
33506
35489
  var EMOJI = {
33507
35490
  smile: "\u{1F604}",
33508
35491
  grin: "\u{1F601}",
@@ -33546,13 +35529,18 @@ var EMOJI = {
33546
35529
  moon: "\u{1F319}",
33547
35530
  zap: "\u26A1"
33548
35531
  };
35532
+ var TRIGGER_CHARS = ["/", "@", ":"];
35533
+ var TRIGGER_QUERY_PATTERN = /^[\w+-]*$/;
33549
35534
  function detectTrigger(text, caret) {
33550
- const before = text.slice(0, caret);
33551
- const m2 = before.match(/(?:^|\s)([/@:])([\w+-]*)$/);
33552
- if (!m2) return null;
33553
- const type = m2[1];
33554
- const query = m2[2] ?? "";
33555
- return { type, query, start: caret - query.length - 1, end: caret };
35535
+ let best = null;
35536
+ for (const type of TRIGGER_CHARS) {
35537
+ const hit = detectTriggerInText(text, caret, type);
35538
+ if (!hit || !TRIGGER_QUERY_PATTERN.test(hit.query)) continue;
35539
+ if (!best || hit.start > best.start) {
35540
+ best = { type, query: hit.query, start: hit.start, end: caret };
35541
+ }
35542
+ }
35543
+ return best;
33556
35544
  }
33557
35545
  function Composer({
33558
35546
  placeholder = "Send a message\u2026",
@@ -33846,7 +35834,7 @@ ${sel}
33846
35834
  busy ? h(
33847
35835
  "button",
33848
35836
  { type: "button", "aria-label": "Stop", className: "flex h-9 w-9 items-center justify-center rounded-full bg-primary text-primary-foreground transition hover:opacity-90", onClick: () => onStop?.() },
33849
- h(IconStop)
35837
+ h(IconStop2)
33850
35838
  ) : h(
33851
35839
  "button",
33852
35840
  {
@@ -33856,7 +35844,7 @@ ${sel}
33856
35844
  disabled: !value.trim() && pending.length === 0,
33857
35845
  onClick: submit
33858
35846
  },
33859
- h(IconArrowUp)
35847
+ h(IconArrowUp2)
33860
35848
  )
33861
35849
  )
33862
35850
  )
@@ -42173,427 +44161,39 @@ var Checkbox = React11__namespace.forwardRef(
42173
44161
  Checkbox.displayName = "Checkbox";
42174
44162
 
42175
44163
  // ../date-picker/dist/index.js
42176
- function isSameDay2(a2, b2) {
42177
- return a2.getFullYear() === b2.getFullYear() && a2.getMonth() === b2.getMonth() && a2.getDate() === b2.getDate();
42178
- }
42179
- function startOfMonth2(date) {
42180
- return new Date(date.getFullYear(), date.getMonth(), 1);
42181
- }
42182
- function startOfDay(date) {
42183
- return new Date(date.getFullYear(), date.getMonth(), date.getDate());
42184
- }
42185
- function endOfDay(date) {
42186
- return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59, 999);
42187
- }
42188
- var DAY_NAMES2 = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
42189
- function padZero(n2) {
42190
- return n2 < 10 ? `0${n2}` : `${n2}`;
42191
- }
42192
- function formatDate(date, format, showTime) {
42193
- if (!date) return "";
42194
- const year = date.getFullYear();
42195
- const month = padZero(date.getMonth() + 1);
42196
- const day = padZero(date.getDate());
42197
- const hours = padZero(date.getHours());
42198
- const minutes = padZero(date.getMinutes());
42199
- let result = format.replace("YYYY", String(year)).replace("MM", month).replace("DD", day);
42200
- if (showTime) {
42201
- result = result.replace("HH", hours).replace("mm", minutes);
42202
- }
42203
- return result;
42204
- }
42205
- function createDatePicker(props = {}) {
42206
- const {
42207
- value,
42208
- onChange,
42209
- minDate,
42210
- maxDate,
42211
- showTime = false,
42212
- format = showTime ? "YYYY-MM-DD HH:mm" : "YYYY-MM-DD",
42213
- placeholder = "Select date...",
42214
- open: controlledOpen,
42215
- defaultOpen = false,
42216
- onOpenChange
42217
- } = props;
42218
- const today = /* @__PURE__ */ new Date();
42219
- let internalOpen = controlledOpen ?? defaultOpen;
42220
- let currentMonth = value ? startOfMonth2(value) : startOfMonth2(today);
42221
- let view = "calendar";
42222
- const hours = value ? value.getHours() : 0;
42223
- const minutes = value ? value.getMinutes() : 0;
42224
- const triggerId = generateId("rfr-datepicker-trigger");
42225
- const dropdownId = generateId("rfr-datepicker-dropdown");
42226
- const gridId = generateId("rfr-datepicker-grid");
42227
- const labelId = generateId("rfr-datepicker-label");
42228
- function isOpen() {
42229
- if (controlledOpen !== void 0) return controlledOpen;
42230
- return internalOpen;
42231
- }
42232
- function isDateDisabled(date) {
42233
- if (minDate && date < startOfDay(minDate)) return true;
42234
- if (maxDate && date > endOfDay(maxDate)) return true;
42235
- return false;
42236
- }
42237
- function buildDays() {
42238
- const year = currentMonth.getFullYear();
42239
- const month = currentMonth.getMonth();
42240
- const first = new Date(year, month, 1);
42241
- const startDow = first.getDay();
42242
- const gridStart = new Date(year, month, 1 - startDow);
42243
- const days = [];
42244
- for (let i2 = 0; i2 < 42; i2++) {
42245
- const date = new Date(
42246
- gridStart.getFullYear(),
42247
- gridStart.getMonth(),
42248
- gridStart.getDate() + i2
42249
- );
42250
- days.push({
42251
- date,
42252
- isToday: isSameDay2(date, today),
42253
- isSelected: value ? isSameDay2(date, value) : false,
42254
- isCurrentMonth: date.getMonth() === month,
42255
- isDisabled: isDateDisabled(date)
42256
- });
42257
- }
42258
- return days;
42259
- }
42260
- function openPicker() {
42261
- internalOpen = true;
42262
- onOpenChange?.(true);
42263
- }
42264
- function closePicker() {
42265
- internalOpen = false;
42266
- onOpenChange?.(false);
42267
- }
42268
- function togglePicker() {
42269
- if (isOpen()) {
42270
- closePicker();
42271
- } else {
42272
- openPicker();
42273
- }
42274
- }
42275
- function selectDate(date) {
42276
- if (isDateDisabled(date)) return;
42277
- const newDate = new Date(date);
42278
- if (value) {
42279
- newDate.setHours(value.getHours());
42280
- newDate.setMinutes(value.getMinutes());
42281
- }
42282
- onChange?.(newDate);
42283
- if (!showTime) {
42284
- closePicker();
42285
- }
42286
- }
42287
- function setHours(h5) {
42288
- if (h5 < 0 || h5 > 23) return;
42289
- const newDate = value ? new Date(value) : /* @__PURE__ */ new Date();
42290
- newDate.setHours(h5);
42291
- onChange?.(newDate);
42292
- }
42293
- function setMinutes(m2) {
42294
- if (m2 < 0 || m2 > 59) return;
42295
- const newDate = value ? new Date(value) : /* @__PURE__ */ new Date();
42296
- newDate.setMinutes(m2);
42297
- onChange?.(newDate);
42298
- }
42299
- function prevMonth() {
42300
- currentMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth() - 1, 1);
42301
- }
42302
- function nextMonth() {
42303
- currentMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 1);
42304
- }
42305
- function setView(v2) {
42306
- view = v2;
42307
- }
42308
- function formatDisplay() {
42309
- return value ? formatDate(value, format, showTime) : placeholder;
42310
- }
42311
- const keyboardHandlers = {
42312
- [Keys.Escape]: (e2) => {
42313
- e2.preventDefault();
42314
- closePicker();
42315
- }
42316
- };
42317
- const triggerProps = {
42318
- "aria-expanded": isOpen(),
42319
- "aria-controls": dropdownId,
42320
- "aria-haspopup": "dialog",
42321
- id: triggerId,
42322
- role: "combobox"
42323
- };
42324
- const dropdownProps = {
42325
- role: "dialog",
42326
- "aria-modal": true,
42327
- "aria-labelledby": labelId,
42328
- id: dropdownId
42329
- };
42330
- const gridProps = {
42331
- role: "grid",
42332
- "aria-labelledby": labelId,
42333
- id: gridId
42334
- };
42335
- function getDayAriaProps(day) {
42336
- return {
42337
- role: "gridcell",
42338
- "aria-selected": day.isSelected,
42339
- "aria-disabled": day.isDisabled,
42340
- "aria-current": day.isToday ? "date" : void 0,
42341
- "aria-label": `${DAY_NAMES2[day.date.getDay()]}, ${day.date.toLocaleDateString("en-US", {
42342
- month: "long",
42343
- day: "numeric",
42344
- year: "numeric"
42345
- })}`
42346
- };
42347
- }
42348
- return {
42349
- state: {
42350
- open: isOpen(),
42351
- selectedDate: value,
42352
- view,
42353
- hours,
42354
- minutes,
42355
- currentMonth
42356
- },
42357
- days: buildDays(),
42358
- openPicker,
42359
- closePicker,
42360
- togglePicker,
42361
- selectDate,
42362
- setHours,
42363
- setMinutes,
42364
- prevMonth,
42365
- nextMonth,
42366
- setView,
42367
- formatDisplay,
42368
- triggerProps,
42369
- dropdownProps,
42370
- gridProps,
42371
- getDayAriaProps,
42372
- keyboardHandlers,
42373
- ids: {
42374
- trigger: triggerId,
42375
- dropdown: dropdownId,
42376
- grid: gridId,
42377
- label: labelId
42378
- }
42379
- };
42380
- }
42381
44164
  var datePickerTriggerStyles = "inline-flex items-center gap-2 rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring cursor-pointer";
42382
- var datePickerDropdownStyles = "absolute z-50 mt-1 rounded-md border bg-popover p-4 text-popover-foreground shadow-md";
42383
- var datePickerDayVariants = cva({
42384
- base: "inline-flex h-8 w-8 items-center justify-center rounded-md text-sm cursor-pointer hover:bg-accent hover:text-accent-foreground",
42385
- variants: {
42386
- state: {
42387
- default: "",
42388
- selected: "bg-primary text-primary-foreground hover:bg-primary/90",
42389
- today: "border border-accent-foreground",
42390
- disabled: "text-muted-foreground opacity-50 pointer-events-none",
42391
- outside: "text-muted-foreground opacity-30"
42392
- }
42393
- },
42394
- defaultVariants: {
42395
- state: "default"
42396
- }
42397
- });
42398
- var datePickerTimeStyles = "flex items-center gap-2 mt-3 pt-3 border-t";
42399
- var datePickerTimeInputStyles = "w-14 rounded-md border border-input bg-background px-2 py-1 text-sm text-center";
42400
-
42401
- // ../react-date-picker/dist/index.js
42402
- function DatePicker({
42403
- value,
42404
- onChange,
42405
- minDate,
42406
- maxDate,
42407
- showTime = false,
42408
- format,
42409
- placeholder,
42410
- className,
42411
- disabled = false
42412
- }) {
42413
- const [open, setOpen] = React11__namespace.useState(false);
42414
- const [currentMonth, setCurrentMonth] = React11__namespace.useState(
42415
- () => value ? new Date(value.getFullYear(), value.getMonth(), 1) : new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth(), 1)
42416
- );
42417
- const [view, setView] = React11__namespace.useState("calendar");
42418
- const containerRef = React11__namespace.useRef(null);
42419
- const api = React11__namespace.useMemo(
42420
- () => createDatePicker({
42421
- value,
42422
- onChange,
42423
- minDate,
42424
- maxDate,
42425
- showTime,
42426
- format,
42427
- placeholder,
42428
- open,
42429
- onOpenChange: setOpen
42430
- }),
42431
- [value, onChange, minDate, maxDate, showTime, format, placeholder, open]
42432
- );
42433
- React11__namespace.useMemo(() => {
42434
- createDatePicker({
42435
- value,
42436
- minDate,
42437
- maxDate,
42438
- open: true
42439
- });
42440
- const tempApi = createDatePicker({
42441
- value,
42442
- minDate,
42443
- maxDate,
42444
- open: true
42445
- });
42446
- return tempApi.days;
42447
- }, [value, minDate, maxDate, currentMonth]);
42448
- React11__namespace.useEffect(() => {
42449
- if (!open) return;
42450
- function handleClick(e2) {
42451
- if (containerRef.current && !containerRef.current.contains(e2.target)) {
42452
- setOpen(false);
42453
- }
42454
- }
42455
- document.addEventListener("mousedown", handleClick);
42456
- return () => document.removeEventListener("mousedown", handleClick);
42457
- }, [open]);
42458
- React11__namespace.useEffect(() => {
42459
- if (!open) return;
42460
- function handleKeyDown(e2) {
42461
- if (e2.key === "Escape") {
42462
- e2.preventDefault();
42463
- setOpen(false);
44165
+ var DatePicker = React11__namespace.forwardRef(
44166
+ ({ value, onChange, minDate, maxDate, showTime = false, format, placeholder, className, disabled = false, ...props }, ref) => {
44167
+ const dateToString = (date) => {
44168
+ if (!date) return "";
44169
+ const offset = date.getTimezoneOffset() * 6e4;
44170
+ const localDate = new Date(date.getTime() - offset);
44171
+ const iso = localDate.toISOString();
44172
+ return showTime ? iso.slice(0, 16) : iso.slice(0, 10);
44173
+ };
44174
+ const handleChange = (e2) => {
44175
+ if (!onChange) return;
44176
+ if (e2.target.value) {
44177
+ onChange(new Date(e2.target.value));
42464
44178
  }
42465
- }
42466
- document.addEventListener("keydown", handleKeyDown);
42467
- return () => document.removeEventListener("keydown", handleKeyDown);
42468
- }, [open]);
42469
- const displayValue = api.formatDisplay();
42470
- const handleSelectDate = (date) => {
42471
- const newDate = new Date(date);
42472
- if (value) {
42473
- newDate.setHours(value.getHours());
42474
- newDate.setMinutes(value.getMinutes());
42475
- }
42476
- onChange?.(newDate);
42477
- if (!showTime) {
42478
- setOpen(false);
42479
- }
42480
- };
42481
- const handleHoursChange = (e2) => {
42482
- const h5 = parseInt(e2.target.value, 10);
42483
- if (isNaN(h5) || h5 < 0 || h5 > 23) return;
42484
- const newDate = value ? new Date(value) : /* @__PURE__ */ new Date();
42485
- newDate.setHours(h5);
42486
- onChange?.(newDate);
42487
- };
42488
- const handleMinutesChange = (e2) => {
42489
- const m2 = parseInt(e2.target.value, 10);
42490
- if (isNaN(m2) || m2 < 0 || m2 > 59) return;
42491
- const newDate = value ? new Date(value) : /* @__PURE__ */ new Date();
42492
- newDate.setMinutes(m2);
42493
- onChange?.(newDate);
42494
- };
42495
- const monthLabel = currentMonth.toLocaleDateString("en-US", { month: "long", year: "numeric" });
42496
- return React11__namespace.createElement(
42497
- "div",
42498
- { ref: containerRef, className: cn("relative inline-block", className) },
42499
- React11__namespace.createElement(
42500
- "button",
42501
- {
42502
- type: "button",
42503
- className: cn(datePickerTriggerStyles, disabled && "opacity-50 cursor-not-allowed"),
42504
- onClick: () => !disabled && setOpen(!open),
42505
- "aria-expanded": open,
42506
- "aria-haspopup": "dialog",
42507
- disabled
42508
- },
42509
- displayValue
42510
- ),
42511
- open && React11__namespace.createElement(
42512
- "div",
44179
+ };
44180
+ return /* @__PURE__ */ jsxRuntime.jsx(
44181
+ "input",
42513
44182
  {
42514
- className: datePickerDropdownStyles,
42515
- role: "dialog",
42516
- "aria-modal": true
42517
- },
42518
- React11__namespace.createElement(
42519
- "div",
42520
- { className: "flex items-center justify-between mb-3" },
42521
- React11__namespace.createElement(
42522
- "button",
42523
- {
42524
- type: "button",
42525
- className: "p-1 hover:bg-accent rounded cursor-pointer",
42526
- onClick: () => setCurrentMonth(new Date(currentMonth.getFullYear(), currentMonth.getMonth() - 1, 1)),
42527
- "aria-label": "Previous month"
42528
- },
42529
- "\u2039"
42530
- ),
42531
- React11__namespace.createElement("span", { className: "text-sm font-medium" }, monthLabel),
42532
- React11__namespace.createElement(
42533
- "button",
42534
- {
42535
- type: "button",
42536
- className: "p-1 hover:bg-accent rounded cursor-pointer",
42537
- onClick: () => setCurrentMonth(new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 1)),
42538
- "aria-label": "Next month"
42539
- },
42540
- "\u203A"
42541
- )
42542
- ),
42543
- React11__namespace.createElement(
42544
- "div",
42545
- { className: "grid grid-cols-7 gap-0 text-center", role: "grid" },
42546
- ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"].map(
42547
- (d2) => React11__namespace.createElement("div", { key: d2, className: "text-xs text-muted-foreground py-1 font-medium" }, d2)
42548
- ),
42549
- api.days.map((day, i2) => {
42550
- let state = "default";
42551
- if (day.isDisabled) state = "disabled";
42552
- else if (day.isSelected) state = "selected";
42553
- else if (day.isToday) state = "today";
42554
- else if (!day.isCurrentMonth) state = "outside";
42555
- return React11__namespace.createElement(
42556
- "button",
42557
- {
42558
- key: i2,
42559
- type: "button",
42560
- className: datePickerDayVariants({ state }),
42561
- onClick: () => !day.isDisabled && handleSelectDate(day.date),
42562
- disabled: day.isDisabled,
42563
- "aria-selected": day.isSelected,
42564
- "aria-disabled": day.isDisabled
42565
- },
42566
- day.date.getDate()
42567
- );
42568
- })
42569
- ),
42570
- showTime && React11__namespace.createElement(
42571
- "div",
42572
- { className: datePickerTimeStyles },
42573
- React11__namespace.createElement("span", { className: "text-sm text-muted-foreground" }, "Time:"),
42574
- React11__namespace.createElement("input", {
42575
- type: "number",
42576
- min: 0,
42577
- max: 23,
42578
- value: value ? value.getHours() : 0,
42579
- onChange: handleHoursChange,
42580
- className: datePickerTimeInputStyles,
42581
- "aria-label": "Hours"
42582
- }),
42583
- React11__namespace.createElement("span", { className: "text-muted-foreground" }, ":"),
42584
- React11__namespace.createElement("input", {
42585
- type: "number",
42586
- min: 0,
42587
- max: 59,
42588
- value: value ? value.getMinutes() : 0,
42589
- onChange: handleMinutesChange,
42590
- className: datePickerTimeInputStyles,
42591
- "aria-label": "Minutes"
42592
- })
42593
- )
42594
- )
42595
- );
42596
- }
44183
+ ref,
44184
+ type: showTime ? "datetime-local" : "date",
44185
+ className: cn(datePickerTriggerStyles, "justify-start text-left font-normal w-[280px]", className),
44186
+ value: dateToString(value),
44187
+ min: dateToString(minDate),
44188
+ max: dateToString(maxDate),
44189
+ onChange: handleChange,
44190
+ disabled,
44191
+ placeholder,
44192
+ ...props
44193
+ }
44194
+ );
44195
+ }
44196
+ );
42597
44197
  DatePicker.displayName = "DatePicker";
42598
44198
 
42599
44199
  // ../emoji-picker/dist/index.js
@@ -52940,6 +54540,7 @@ exports.ContentProtection = ContentProtection;
52940
54540
  exports.CookieConsent = CookieConsent;
52941
54541
  exports.Cursor = Cursor;
52942
54542
  exports.DEFAULT_CATEGORIES = DEFAULT_CATEGORIES;
54543
+ exports.DEFAULT_COMPOSER_STRINGS = DEFAULT_COMPOSER_STRINGS;
52943
54544
  exports.DEFAULT_VOICE_PILL_POSITION = DEFAULT_VOICE_PILL_POSITION;
52944
54545
  exports.DEFAULT_VOICE_PILL_SPEAKER = DEFAULT_VOICE_PILL_SPEAKER;
52945
54546
  exports.DEFAULT_WAVEFORM_BAR_COUNT = DEFAULT_WAVEFORM_BAR_COUNT;
@@ -53028,6 +54629,7 @@ exports.RadioItem = RadioItem;
53028
54629
  exports.RatingScale = RatingScale;
53029
54630
  exports.ReactionBar = ReactionBar;
53030
54631
  exports.ReactionBurst = ReactionBurst;
54632
+ exports.RefractionComposer = RefractionComposer;
53031
54633
  exports.ResizableDivider = ResizableDivider;
53032
54634
  exports.ResizableLayout = ResizableLayout;
53033
54635
  exports.ResizablePane = ResizablePane;
@@ -53154,10 +54756,20 @@ exports.commandGroupVariants = commandGroupVariants;
53154
54756
  exports.commandInputVariants = commandInputVariants;
53155
54757
  exports.commandItemVariants = commandItemVariants;
53156
54758
  exports.commandVariants = commandVariants;
54759
+ exports.composerAttachmentChipVariants = composerAttachmentChipVariants;
54760
+ exports.composerCounterVariants = composerCounterVariants;
54761
+ exports.composerFieldClass = composerFieldClass;
54762
+ exports.composerMenuClass = composerMenuClass;
54763
+ exports.composerMenuItemVariants = composerMenuItemVariants;
54764
+ exports.composerPrimaryActionVariants = composerPrimaryActionVariants;
54765
+ exports.composerSurfaceVariants = composerSurfaceVariants;
54766
+ exports.composerTokenPillClass = composerTokenPillClass;
54767
+ exports.composerTrayClass = composerTrayClass;
53157
54768
  exports.contentProtectionVariants = contentProtectionVariants;
53158
54769
  exports.controlsVariants = controlsVariants;
53159
54770
  exports.createAnalytics = createAnalytics;
53160
54771
  exports.createAppInsightsSink = createAppInsightsSink;
54772
+ exports.createComposer = createComposer;
53161
54773
  exports.createConsoleSink = createConsoleSink;
53162
54774
  exports.createConversation = createConversation;
53163
54775
  exports.createCookieConsent = createCookieConsent;
@@ -53315,12 +54927,14 @@ exports.threadMessageStyles = threadMessageStyles;
53315
54927
  exports.threadReactionsStyles = threadReactionsStyles;
53316
54928
  exports.threadTimestampStyles = threadTimestampStyles;
53317
54929
  exports.toCssDimension = toCssDimension;
54930
+ exports.toMessageAttachment = toMessageAttachment;
53318
54931
  exports.toastVariants = toastVariants;
53319
54932
  exports.toolbarVariants = toolbarVariants;
53320
54933
  exports.tooltipContentVariants = tooltipContentVariants;
53321
54934
  exports.typewriterVariants = typewriterVariants;
53322
54935
  exports.useAnalytics = useAnalytics;
53323
54936
  exports.useAuth = useAuth;
54937
+ exports.useComposer = useComposer;
53324
54938
  exports.useConversation = useConversation;
53325
54939
  exports.useCookieConsent = useCookieConsent;
53326
54940
  exports.useFloatingReactions = useFloatingReactions;