@sesionario/blocks 1.41.0-rc.1 → 1.41.0-rc.10

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
@@ -311,7 +311,7 @@ function AlertDialogOverlay({
311
311
  {
312
312
  "data-slot": "alert-dialog-overlay",
313
313
  className: cn(
314
- "fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
314
+ "fixed inset-0 z-50 bg-black/50 dark:bg-black/80 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
315
315
  className
316
316
  ),
317
317
  ...props
@@ -1174,31 +1174,54 @@ function useZoomPanGestures(options = {}) {
1174
1174
  clearSnapBack
1175
1175
  };
1176
1176
  }
1177
+ var STATIC_BLUR_FILTER = "blur(20px)";
1177
1178
  function isBrokenImage(img) {
1178
1179
  return img.complete && img.naturalWidth === 0;
1179
1180
  }
1181
+ function resolveBlurDataUrl(value) {
1182
+ return typeof value === "string" && value.length > 0 ? value : null;
1183
+ }
1180
1184
  function SafeImage({
1181
1185
  src,
1182
1186
  fallback,
1183
1187
  alt = "",
1184
1188
  onError,
1189
+ onLoad,
1190
+ blurDataUrl,
1191
+ style,
1185
1192
  ...imgProps
1186
1193
  }) {
1187
1194
  const [failed, setFailed] = React2.useState(false);
1195
+ const [loaded, setLoaded] = React2.useState(false);
1188
1196
  const imgRef = React2.useRef(null);
1189
1197
  const resolvedSrc = typeof src === "string" && src.length > 0 ? src : null;
1198
+ const resolvedBlur = resolveBlurDataUrl(blurDataUrl);
1190
1199
  React2.useLayoutEffect(() => {
1191
1200
  setFailed(false);
1201
+ setLoaded(false);
1192
1202
  }, [resolvedSrc]);
1193
1203
  React2.useLayoutEffect(() => {
1194
1204
  const img = imgRef.current;
1195
- if (img && isBrokenImage(img)) {
1205
+ if (!img) return;
1206
+ if (isBrokenImage(img)) {
1196
1207
  setFailed(true);
1208
+ return;
1209
+ }
1210
+ if (resolvedBlur && img.complete && img.naturalWidth > 0) {
1211
+ setLoaded(true);
1197
1212
  }
1198
- }, [resolvedSrc, failed]);
1213
+ }, [resolvedSrc, failed, resolvedBlur]);
1199
1214
  if (!resolvedSrc || failed) {
1200
1215
  return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback });
1201
1216
  }
1217
+ const paintedStyle = resolvedBlur === null ? style : {
1218
+ ...style,
1219
+ backgroundImage: `url(${JSON.stringify(resolvedBlur)})`,
1220
+ backgroundSize: "cover",
1221
+ backgroundPosition: "center",
1222
+ backgroundRepeat: "no-repeat",
1223
+ ...loaded || prefersReducedMotion() ? {} : { filter: STATIC_BLUR_FILTER }
1224
+ };
1202
1225
  return /* @__PURE__ */ jsxRuntime.jsx(
1203
1226
  "img",
1204
1227
  {
@@ -1206,6 +1229,11 @@ function SafeImage({
1206
1229
  ref: imgRef,
1207
1230
  src: resolvedSrc,
1208
1231
  alt,
1232
+ style: paintedStyle,
1233
+ onLoad: (event) => {
1234
+ if (resolvedBlur) setLoaded(true);
1235
+ onLoad?.(event);
1236
+ },
1209
1237
  onError: (event) => {
1210
1238
  setFailed(true);
1211
1239
  onError?.(event);
@@ -1215,6 +1243,7 @@ function SafeImage({
1215
1243
  }
1216
1244
  function ImageLightbox({
1217
1245
  images,
1246
+ blurDataUrls,
1218
1247
  index,
1219
1248
  onIndexChange,
1220
1249
  onClose,
@@ -1340,6 +1369,7 @@ function ImageLightbox({
1340
1369
  {
1341
1370
  src: images[index],
1342
1371
  alt: "",
1372
+ blurDataUrl: blurDataUrls?.[index] ?? null,
1343
1373
  draggable: false,
1344
1374
  className: "max-h-[calc(100dvh-2rem)] max-w-full select-none rounded-lg object-contain",
1345
1375
  style: {
@@ -1472,6 +1502,7 @@ function MediaCarousel({
1472
1502
  {
1473
1503
  src: item.src,
1474
1504
  alt: item.alt ?? "",
1505
+ blurDataUrl: item.blurDataUrl,
1475
1506
  draggable: false,
1476
1507
  className: "max-h-full max-w-full select-none object-contain",
1477
1508
  style: {
@@ -1575,7 +1606,7 @@ function DialogOverlay({
1575
1606
  {
1576
1607
  "data-slot": "dialog-overlay",
1577
1608
  className: cn(
1578
- "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
1609
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50 dark:bg-black/80",
1579
1610
  className
1580
1611
  ),
1581
1612
  ...props
@@ -1707,6 +1738,79 @@ function MediaViewerDialog({
1707
1738
  }
1708
1739
  ) });
1709
1740
  }
1741
+
1742
+ // src/components/media/blurDataUrl.ts
1743
+ var BLUR_LONG_EDGE_PX = 16;
1744
+ var BLUR_DATA_URL_MAX_CHARS = 1024;
1745
+ var JPEG_DATA_URL_PREFIX = "data:image/jpeg;base64,";
1746
+ var JPEG_QUALITIES = [0.7, 0.5, 0.35, 0.2];
1747
+ function longEdgeSteps(start) {
1748
+ const steps = [];
1749
+ let edge = start;
1750
+ while (edge >= 2) {
1751
+ steps.push(edge);
1752
+ edge = Math.floor(edge / 2);
1753
+ }
1754
+ return steps;
1755
+ }
1756
+ function sizeForLongEdge(width, height, longEdge) {
1757
+ const longest = Math.max(width, height);
1758
+ const scale = longEdge / longest;
1759
+ return {
1760
+ width: Math.max(1, Math.round(width * scale)),
1761
+ height: Math.max(1, Math.round(height * scale))
1762
+ };
1763
+ }
1764
+ function acceptableBlurDataUrl(value) {
1765
+ return value.startsWith(JPEG_DATA_URL_PREFIX) && value.length > JPEG_DATA_URL_PREFIX.length && value.length <= BLUR_DATA_URL_MAX_CHARS;
1766
+ }
1767
+ async function decodeBlob(blob) {
1768
+ if (typeof createImageBitmap !== "function") {
1769
+ throw new Error("createImageBitmap unavailable");
1770
+ }
1771
+ const bitmap = await createImageBitmap(blob);
1772
+ return {
1773
+ source: bitmap,
1774
+ width: bitmap.width,
1775
+ height: bitmap.height,
1776
+ release() {
1777
+ bitmap.close();
1778
+ }
1779
+ };
1780
+ }
1781
+ async function blurDataUrlFromBlob(blob) {
1782
+ if (typeof document === "undefined") return null;
1783
+ let decoded;
1784
+ try {
1785
+ decoded = await decodeBlob(blob);
1786
+ } catch {
1787
+ return null;
1788
+ }
1789
+ try {
1790
+ if (Math.min(decoded.width, decoded.height) <= 0) return null;
1791
+ const canvas = document.createElement("canvas");
1792
+ for (const edge of longEdgeSteps(BLUR_LONG_EDGE_PX)) {
1793
+ const size = sizeForLongEdge(decoded.width, decoded.height, edge);
1794
+ canvas.width = size.width;
1795
+ canvas.height = size.height;
1796
+ const context = canvas.getContext("2d");
1797
+ if (!context) return null;
1798
+ context.drawImage(decoded.source, 0, 0, size.width, size.height);
1799
+ for (const quality of JPEG_QUALITIES) {
1800
+ let url;
1801
+ try {
1802
+ url = canvas.toDataURL("image/jpeg", quality);
1803
+ } catch {
1804
+ return null;
1805
+ }
1806
+ if (acceptableBlurDataUrl(url)) return url;
1807
+ }
1808
+ }
1809
+ return null;
1810
+ } finally {
1811
+ decoded.release();
1812
+ }
1813
+ }
1710
1814
  var miniMenuContentClassName = "max-h-none overflow-x-visible overflow-y-visible";
1711
1815
  var miniMenuContentDefaults = {
1712
1816
  sideOffset: 8,
@@ -3626,9 +3730,17 @@ function AvatarEditor({
3626
3730
  );
3627
3731
  }
3628
3732
  function Drawer({
3733
+ repositionInputs = false,
3629
3734
  ...props
3630
3735
  }) {
3631
- return /* @__PURE__ */ jsxRuntime.jsx(vaul.Drawer.Root, { "data-slot": "drawer", ...props });
3736
+ return /* @__PURE__ */ jsxRuntime.jsx(
3737
+ vaul.Drawer.Root,
3738
+ {
3739
+ "data-slot": "drawer",
3740
+ repositionInputs,
3741
+ ...props
3742
+ }
3743
+ );
3632
3744
  }
3633
3745
  function DrawerPortal({
3634
3746
  ...props
@@ -3649,7 +3761,7 @@ function DrawerOverlay({
3649
3761
  {
3650
3762
  "data-slot": "drawer-overlay",
3651
3763
  className: cn(
3652
- "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
3764
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50 dark:bg-black/80",
3653
3765
  className
3654
3766
  ),
3655
3767
  ...props
@@ -4225,7 +4337,7 @@ function EmojiPicker({
4225
4337
  onKeyDown: handleSearchKeyDown,
4226
4338
  placeholder: strings.searchPlaceholder,
4227
4339
  "aria-label": strings.searchAriaLabel,
4228
- className: "border-input bg-background focus-visible:border-ring focus-visible:ring-ring/50 h-9 w-full rounded-md border py-2 pr-3 pl-8 text-sm outline-none focus-visible:ring-[3px]"
4340
+ className: "border-input bg-background focus-visible:border-ring focus-visible:ring-ring/50 h-9 w-full rounded-md border py-2 pr-3 pl-8 text-base outline-none focus-visible:ring-[3px] md:text-sm"
4229
4341
  }
4230
4342
  )
4231
4343
  ] }),
@@ -4713,6 +4825,41 @@ function useMediaQuery2(query) {
4713
4825
  }, [query]);
4714
4826
  return matches;
4715
4827
  }
4828
+ function readMetrics() {
4829
+ if (typeof window === "undefined") {
4830
+ return { height: 0, offsetTop: 0, keyboardInset: 0 };
4831
+ }
4832
+ const vv = window.visualViewport;
4833
+ if (!vv) {
4834
+ return {
4835
+ height: window.innerHeight,
4836
+ offsetTop: 0,
4837
+ keyboardInset: 0
4838
+ };
4839
+ }
4840
+ return {
4841
+ height: vv.height,
4842
+ offsetTop: vv.offsetTop,
4843
+ keyboardInset: Math.max(0, window.innerHeight - vv.height - vv.offsetTop)
4844
+ };
4845
+ }
4846
+ function useVisualViewportMetrics() {
4847
+ const [metrics, setMetrics] = React2.useState(readMetrics);
4848
+ React2.useEffect(() => {
4849
+ const vv = window.visualViewport;
4850
+ const update = () => setMetrics(readMetrics());
4851
+ update();
4852
+ vv?.addEventListener("resize", update);
4853
+ vv?.addEventListener("scroll", update);
4854
+ window.addEventListener("resize", update);
4855
+ return () => {
4856
+ vv?.removeEventListener("resize", update);
4857
+ vv?.removeEventListener("scroll", update);
4858
+ window.removeEventListener("resize", update);
4859
+ };
4860
+ }, []);
4861
+ return metrics;
4862
+ }
4716
4863
  function useSearchHotkey(options = {}) {
4717
4864
  const { defaultOpen = false, disabled = false } = options;
4718
4865
  const [open, setOpen] = React2.useState(defaultOpen);
@@ -5204,6 +5351,24 @@ function Command({
5204
5351
  }
5205
5352
  );
5206
5353
  }
5354
+ function useCommandDialogScrollLock(open) {
5355
+ React2__namespace.useEffect(() => {
5356
+ if (!open) return;
5357
+ const onTouchMove = (event) => {
5358
+ const target = event.target;
5359
+ if (!(target instanceof Element)) {
5360
+ event.preventDefault();
5361
+ return;
5362
+ }
5363
+ if (target.closest('[data-slot="command-list"]')) return;
5364
+ event.preventDefault();
5365
+ };
5366
+ document.addEventListener("touchmove", onTouchMove, { passive: false });
5367
+ return () => {
5368
+ document.removeEventListener("touchmove", onTouchMove);
5369
+ };
5370
+ }, [open]);
5371
+ }
5207
5372
  function CommandDialog({
5208
5373
  title = "Command Palette",
5209
5374
  description = "Search for a command to run...",
@@ -5213,6 +5378,9 @@ function CommandDialog({
5213
5378
  commandProps,
5214
5379
  ...props
5215
5380
  }) {
5381
+ const { height: viewportHeight } = useVisualViewportMetrics();
5382
+ useCommandDialogScrollLock(props.open);
5383
+ const mobileMaxHeight = viewportHeight > 0 ? Math.max(280, viewportHeight - 16) : void 0;
5216
5384
  return /* @__PURE__ */ jsxRuntime.jsxs(Dialog, { ...props, children: [
5217
5385
  /* @__PURE__ */ jsxRuntime.jsxs(DialogHeader, { className: "sr-only", children: [
5218
5386
  /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { children: title }),
@@ -5221,12 +5389,22 @@ function CommandDialog({
5221
5389
  /* @__PURE__ */ jsxRuntime.jsx(
5222
5390
  DialogContent,
5223
5391
  {
5224
- className: cn("overflow-hidden p-0", className),
5392
+ className: cn(
5393
+ "flex flex-col gap-0 overflow-hidden p-0",
5394
+ // iOS: top-pin in the visual viewport, near full-bleed width,
5395
+ // fill available height so the list (not the page) scrolls.
5396
+ "max-sm:top-[max(0.5rem,env(safe-area-inset-top))] max-sm:right-2 max-sm:left-2 max-sm:w-auto max-sm:max-w-none max-sm:translate-x-0 max-sm:translate-y-0",
5397
+ "max-sm:h-[var(--command-dialog-max-height,calc(100dvh-1rem))] max-sm:max-h-[var(--command-dialog-max-height,calc(100dvh-1rem))]",
5398
+ className
5399
+ ),
5400
+ style: mobileMaxHeight ? {
5401
+ ["--command-dialog-max-height"]: `${mobileMaxHeight}px`
5402
+ } : void 0,
5225
5403
  showCloseButton,
5226
5404
  children: /* @__PURE__ */ jsxRuntime.jsx(
5227
5405
  Command,
5228
5406
  {
5229
- className: "**:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5",
5407
+ className: "flex h-full min-h-0 flex-col **:data-[slot=command-input-wrapper]:h-12 **:data-[slot=command-list]:max-sm:max-h-none **:data-[slot=command-list]:max-sm:min-h-0 **:data-[slot=command-list]:max-sm:flex-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5",
5230
5408
  ...commandProps,
5231
5409
  children
5232
5410
  }
@@ -5243,7 +5421,7 @@ function CommandInput({
5243
5421
  "div",
5244
5422
  {
5245
5423
  "data-slot": "command-input-wrapper",
5246
- className: "flex h-9 items-center gap-2 border-b px-3",
5424
+ className: "flex h-9 shrink-0 items-center gap-2 border-b px-3",
5247
5425
  children: [
5248
5426
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SearchIcon, { className: "size-4 shrink-0 opacity-50" }),
5249
5427
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -5251,7 +5429,8 @@ function CommandInput({
5251
5429
  {
5252
5430
  "data-slot": "command-input",
5253
5431
  className: cn(
5254
- "flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
5432
+ // text-base (≥16px) on mobile prevents iOS Safari focus zoom
5433
+ "flex h-10 w-full rounded-md bg-transparent py-3 text-base outline-hidden placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
5255
5434
  className
5256
5435
  ),
5257
5436
  ...props
@@ -5270,7 +5449,7 @@ function CommandList({
5270
5449
  {
5271
5450
  "data-slot": "command-list",
5272
5451
  className: cn(
5273
- "max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto",
5452
+ "max-h-[min(300px,calc(100dvh-12rem))] scroll-py-1 overflow-x-hidden overflow-y-auto overscroll-contain touch-pan-y",
5274
5453
  className
5275
5454
  ),
5276
5455
  ...props
@@ -5543,6 +5722,7 @@ function matchesQuery(entry, query, getOptionKeywords) {
5543
5722
  function SearchablePicker(props) {
5544
5723
  const {
5545
5724
  options,
5725
+ selectedItems: selectedItemsProp,
5546
5726
  fetchOptions,
5547
5727
  getOptionValue,
5548
5728
  getOptionLabel,
@@ -5601,11 +5781,16 @@ function SearchablePicker(props) {
5601
5781
  },
5602
5782
  [getOptionValue]
5603
5783
  );
5784
+ if (options?.length) rememberItems(options);
5785
+ if (selectedItemsProp?.length) rememberItems(selectedItemsProp);
5786
+ if (pinned?.recommended?.length) rememberItems(pinned.recommended);
5787
+ if (pinned?.recent?.length) rememberItems(pinned.recent);
5604
5788
  React2.useEffect(() => {
5605
5789
  if (options) rememberItems(options);
5790
+ if (selectedItemsProp) rememberItems(selectedItemsProp);
5606
5791
  if (pinned?.recommended) rememberItems(pinned.recommended);
5607
5792
  if (pinned?.recent) rememberItems(pinned.recent);
5608
- }, [options, pinned, rememberItems]);
5793
+ }, [options, selectedItemsProp, pinned, rememberItems]);
5609
5794
  React2.useEffect(() => {
5610
5795
  if (!open) {
5611
5796
  setQuery("");
@@ -5653,7 +5838,7 @@ function SearchablePicker(props) {
5653
5838
  const selectedItems = React2.useMemo(() => {
5654
5839
  const values = mode === "multiple" ? selectedValueKey ? selectedValueKey.split("\0") : [] : selectedValueKey ? [selectedValueKey] : [];
5655
5840
  return values.map((value) => knownItemsRef.current.get(value)).filter((item) => item !== void 0);
5656
- }, [mode, selectedValueKey, options, pinned, remoteItems]);
5841
+ }, [mode, selectedValueKey, options, selectedItemsProp, pinned, remoteItems]);
5657
5842
  const queryForFilter = query.trim();
5658
5843
  const recommended = React2.useMemo(() => {
5659
5844
  if (queryForFilter) return [];
@@ -5757,13 +5942,27 @@ function SearchablePicker(props) {
5757
5942
  );
5758
5943
  const handleLoadMore = React2.useCallback(async () => {
5759
5944
  if (!onLoadMore || isLoadingMore) return;
5945
+ const requestId = requestIdRef.current;
5760
5946
  setIsLoadingMore(true);
5761
5947
  try {
5762
- await onLoadMore();
5948
+ const more = await onLoadMore();
5949
+ if (requestIdRef.current !== requestId || !Array.isArray(more)) return;
5950
+ setRemoteItems((prev) => {
5951
+ const seen = new Set(prev.map((item) => getOptionValue(item)));
5952
+ const next = [...prev];
5953
+ for (const item of more) {
5954
+ const value = getOptionValue(item);
5955
+ if (seen.has(value)) continue;
5956
+ seen.add(value);
5957
+ next.push(item);
5958
+ }
5959
+ return next;
5960
+ });
5961
+ rememberItems(more);
5763
5962
  } finally {
5764
- setIsLoadingMore(false);
5963
+ if (requestIdRef.current === requestId) setIsLoadingMore(false);
5765
5964
  }
5766
- }, [onLoadMore, isLoadingMore]);
5965
+ }, [onLoadMore, isLoadingMore, getOptionValue, rememberItems]);
5767
5966
  const triggerLabel = React2.useMemo(() => {
5768
5967
  if (renderValue) {
5769
5968
  if (mode === "multiple") {
@@ -5897,7 +6096,7 @@ function SearchablePicker(props) {
5897
6096
  ] });
5898
6097
  }
5899
6098
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex w-full items-center gap-1", className), children: [
5900
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open, onOpenChange: setOpen, modal: true, children: [
6099
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open, onOpenChange: setOpen, modal: false, children: [
5901
6100
  /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: triggerButton }),
5902
6101
  /* @__PURE__ */ jsxRuntime.jsx(
5903
6102
  PopoverContent,
@@ -7323,6 +7522,9 @@ var MessageComposer = React2__namespace.forwardRef(function MessageComposer2({
7323
7522
  event.preventDefault();
7324
7523
  if (!canSubmit) return;
7325
7524
  onSubmit(event);
7525
+ requestAnimationFrame(() => {
7526
+ fieldRef.current?.focus({ preventScroll: true });
7527
+ });
7326
7528
  },
7327
7529
  [canSubmit, onSubmit]
7328
7530
  );
@@ -7397,6 +7599,10 @@ var MessageComposer = React2__namespace.forwardRef(function MessageComposer2({
7397
7599
  size: sendButtonSize,
7398
7600
  disabled: !canSubmit,
7399
7601
  className: sendButtonClassName,
7602
+ onPointerDown: (event) => {
7603
+ if (event.button !== 0) return;
7604
+ event.preventDefault();
7605
+ },
7400
7606
  children: sendLabel
7401
7607
  }
7402
7608
  )
@@ -7654,7 +7860,7 @@ function ThreadPanel({
7654
7860
  sendLabel: labels.send,
7655
7861
  placeholder: labels.placeholder,
7656
7862
  "aria-label": labels.placeholder,
7657
- disabled: loading || sending,
7863
+ disabled: loading,
7658
7864
  sendDisabled: loading || sending,
7659
7865
  maxLength: 2e3,
7660
7866
  rows: 2
@@ -7725,7 +7931,7 @@ function ListPanel({
7725
7931
  value: query,
7726
7932
  onChange: (e) => setQuery(e.target.value),
7727
7933
  placeholder: labels.searchPlaceholder ?? "Buscar\u2026",
7728
- className: "h-8 w-full rounded-md border bg-transparent pr-2 pl-8 text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring"
7934
+ className: "h-9 w-full rounded-md border bg-transparent pr-2 pl-8 text-base outline-none focus-visible:ring-2 focus-visible:ring-ring md:h-8 md:text-sm"
7729
7935
  }
7730
7936
  )
7731
7937
  ] }) }) : null,
@@ -7783,10 +7989,13 @@ function SupportMessengerProvider({
7783
7989
  className
7784
7990
  }) {
7785
7991
  const isMobile = useMediaQuery2("(max-width: 767px)");
7992
+ const { height: viewportHeight, keyboardInset } = useVisualViewportMetrics();
7786
7993
  const dockRef = React2.useRef(null);
7787
7994
  const [listOpen, setListOpen] = React2.useState(false);
7788
7995
  const [threads, setThreads] = React2.useState([]);
7789
7996
  const [mobileThreadKey, setMobileThreadKey] = React2.useState(null);
7997
+ const mobileSheetOpen = listOpen || Boolean(mobileThreadKey);
7998
+ const mobileSheetHeight = viewportHeight > 0 ? Math.max(240, Math.min(viewportHeight * 0.85, viewportHeight)) : void 0;
7790
7999
  const totalUnread = React2.useMemo(
7791
8000
  () => items.reduce((sum, item) => sum + (item.unreadCount ?? 0), 0),
7792
8001
  [items]
@@ -7930,48 +8139,61 @@ function SupportMessengerProvider({
7930
8139
  isMobile ? /* @__PURE__ */ jsxRuntime.jsx(
7931
8140
  Drawer,
7932
8141
  {
7933
- open: listOpen || Boolean(activeMobileThread),
8142
+ open: mobileSheetOpen,
7934
8143
  onOpenChange: (open) => {
7935
8144
  if (!open) closeAll();
7936
8145
  },
7937
- children: /* @__PURE__ */ jsxRuntime.jsx(DrawerContent, { className: "pointer-events-auto mx-auto flex h-[85vh] max-w-lg flex-col", children: activeMobileThread ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
7938
- /* @__PURE__ */ jsxRuntime.jsx(DrawerHeader, { className: "sr-only", children: /* @__PURE__ */ jsxRuntime.jsx(DrawerTitle, { children: activeMobileThread.title }) }),
7939
- /* @__PURE__ */ jsxRuntime.jsx(
7940
- ThreadPanel,
7941
- {
7942
- thread: activeMobileThread,
7943
- client,
7944
- getWsUrl,
7945
- currentUserId,
7946
- labels,
7947
- emojiLocale,
7948
- showBack: true,
7949
- onBack: () => {
7950
- closeThread(activeMobileThread.key);
7951
- setListOpen(true);
7952
- },
7953
- onClose: closeAll,
7954
- onConversationReady: () => void onRefreshItems?.(),
7955
- className: "h-full rounded-none border-0 shadow-none"
7956
- }
7957
- )
7958
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
7959
- /* @__PURE__ */ jsxRuntime.jsx(DrawerHeader, { className: "sr-only", children: /* @__PURE__ */ jsxRuntime.jsx(DrawerTitle, { children: labels.listTitle }) }),
7960
- /* @__PURE__ */ jsxRuntime.jsx(
7961
- ListPanel,
7962
- {
7963
- items,
7964
- labels,
7965
- tabs,
7966
- activeTab,
7967
- onTabChange,
7968
- onSelect: handleSelectItem,
7969
- onClose: closeAll,
7970
- searchThreshold,
7971
- className: "h-full rounded-none border-0 shadow-none"
7972
- }
7973
- )
7974
- ] }) })
8146
+ children: /* @__PURE__ */ jsxRuntime.jsx(
8147
+ DrawerContent,
8148
+ {
8149
+ className: "pointer-events-auto mx-auto flex max-h-[85dvh] max-w-lg flex-col",
8150
+ style: {
8151
+ // Lift above the software keyboard without Vaul's
8152
+ // repositionInputs focus hack (see drawer.tsx).
8153
+ bottom: keyboardInset,
8154
+ height: mobileSheetHeight ?? "85dvh",
8155
+ maxHeight: viewportHeight > 0 ? viewportHeight : "85dvh"
8156
+ },
8157
+ children: activeMobileThread ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
8158
+ /* @__PURE__ */ jsxRuntime.jsx(DrawerHeader, { className: "sr-only", children: /* @__PURE__ */ jsxRuntime.jsx(DrawerTitle, { children: activeMobileThread.title }) }),
8159
+ /* @__PURE__ */ jsxRuntime.jsx(
8160
+ ThreadPanel,
8161
+ {
8162
+ thread: activeMobileThread,
8163
+ client,
8164
+ getWsUrl,
8165
+ currentUserId,
8166
+ labels,
8167
+ emojiLocale,
8168
+ showBack: true,
8169
+ onBack: () => {
8170
+ closeThread(activeMobileThread.key);
8171
+ setListOpen(true);
8172
+ },
8173
+ onClose: closeAll,
8174
+ onConversationReady: () => void onRefreshItems?.(),
8175
+ className: "h-full rounded-none border-0 shadow-none"
8176
+ }
8177
+ )
8178
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
8179
+ /* @__PURE__ */ jsxRuntime.jsx(DrawerHeader, { className: "sr-only", children: /* @__PURE__ */ jsxRuntime.jsx(DrawerTitle, { children: labels.listTitle }) }),
8180
+ /* @__PURE__ */ jsxRuntime.jsx(
8181
+ ListPanel,
8182
+ {
8183
+ items,
8184
+ labels,
8185
+ tabs,
8186
+ activeTab,
8187
+ onTabChange,
8188
+ onSelect: handleSelectItem,
8189
+ onClose: closeAll,
8190
+ searchThreshold,
8191
+ className: "h-full rounded-none border-0 shadow-none"
8192
+ }
8193
+ )
8194
+ ] })
8195
+ }
8196
+ )
7975
8197
  }
7976
8198
  ) : null,
7977
8199
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -8986,6 +9208,7 @@ exports.ZOOM_PAN_MIN_SCALE = ZOOM_PAN_MIN_SCALE;
8986
9208
  exports.ZOOM_PAN_SWIPE_COMMIT_PX = ZOOM_PAN_SWIPE_COMMIT_PX;
8987
9209
  exports.ZOOM_PAN_SWIPE_SNAP_MS = ZOOM_PAN_SWIPE_SNAP_MS;
8988
9210
  exports.avatarFallbackStyle = avatarFallbackStyle;
9211
+ exports.blurDataUrlFromBlob = blurDataUrlFromBlob;
8989
9212
  exports.bubbleVariants = bubbleVariants;
8990
9213
  exports.cn = cn;
8991
9214
  exports.createSupportChatClient = createSupportChatClient;
@@ -9016,6 +9239,7 @@ exports.useSearchHotkey = useSearchHotkey;
9016
9239
  exports.useSupportConversation = useSupportConversation;
9017
9240
  exports.useSupportMessenger = useSupportMessenger;
9018
9241
  exports.useSupportMessengerOptional = useSupportMessengerOptional;
9242
+ exports.useVisualViewportMetrics = useVisualViewportMetrics;
9019
9243
  exports.useZoomPanGestures = useZoomPanGestures;
9020
9244
  exports.zoomToward = zoomToward;
9021
9245
  //# sourceMappingURL=index.cjs.map