@sesionario/blocks 1.11.0 → 1.13.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
@@ -2988,8 +2988,8 @@ function EmojiPicker({
2988
2988
  {
2989
2989
  ref: scrollRef,
2990
2990
  onKeyDown: handleGridKeyDown,
2991
- className: "h-64 overflow-x-hidden overflow-y-auto p-2",
2992
- children: normalizedQuery ? searchResults.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground py-8 text-center text-sm", children: strings.noResults }) : /* @__PURE__ */ jsxRuntime.jsx(
2991
+ className: "h-64 overflow-x-hidden overflow-y-auto px-2 pb-2",
2992
+ children: normalizedQuery ? searchResults.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground py-8 text-center text-sm", children: strings.noResults }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pt-2", children: /* @__PURE__ */ jsxRuntime.jsx(
2993
2993
  EmojiGrid,
2994
2994
  {
2995
2995
  emojis: searchResults,
@@ -2998,7 +2998,7 @@ function EmojiPicker({
2998
2998
  skinTone,
2999
2999
  onSelect: handleSelect
3000
3000
  }
3001
- ) : sections.map((section) => {
3001
+ ) }) : sections.map((section) => {
3002
3002
  const startIndex = flatOffset;
3003
3003
  flatOffset += section.emojis.length;
3004
3004
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -3009,7 +3009,7 @@ function EmojiPicker({
3009
3009
  },
3010
3010
  className: "mb-2",
3011
3011
  children: [
3012
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-muted-foreground bg-popover sticky top-0 z-[1] px-1 py-1 text-xs font-medium", children: section.title }),
3012
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-muted-foreground bg-popover sticky top-0 z-[1] -mx-2 px-3 pt-2 pb-1 text-xs font-medium", children: section.title }),
3013
3013
  /* @__PURE__ */ jsxRuntime.jsx(
3014
3014
  "div",
3015
3015
  {
@@ -3130,7 +3130,7 @@ function EmojiPickerPopover({
3130
3130
  align = "end",
3131
3131
  open,
3132
3132
  onOpenChange,
3133
- keepOpenOnSelect = false,
3133
+ keepOpenOnSelect = true,
3134
3134
  locale = "en",
3135
3135
  ...pickerProps
3136
3136
  }) {
@@ -3164,7 +3164,16 @@ function EmojiPickerPopover({
3164
3164
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Smile, { className: "size-5" })
3165
3165
  }
3166
3166
  ) }),
3167
- /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { side, align, className: "w-auto p-0", children: /* @__PURE__ */ jsxRuntime.jsx(EmojiPicker, { onSelect: handleSelect, locale, ...pickerProps }) })
3167
+ /* @__PURE__ */ jsxRuntime.jsx(
3168
+ PopoverContent,
3169
+ {
3170
+ side,
3171
+ align,
3172
+ className: "w-auto p-0",
3173
+ onFocusOutside: (e) => e.preventDefault(),
3174
+ children: /* @__PURE__ */ jsxRuntime.jsx(EmojiPicker, { onSelect: handleSelect, locale, ...pickerProps })
3175
+ }
3176
+ )
3168
3177
  ] });
3169
3178
  }
3170
3179
 
@@ -3453,6 +3462,20 @@ function kindForNative(input) {
3453
3462
  if (code === MEDIA_ERR_NETWORK) return "unavailable";
3454
3463
  return "generic";
3455
3464
  }
3465
+ function describeAvResync(input) {
3466
+ const lag = Number.isFinite(input.lagSeconds) ? input.lagSeconds : 0;
3467
+ return {
3468
+ summary: `av-resync lag=${lag.toFixed(3)}s (non-fatal)`,
3469
+ kind: "resync",
3470
+ detail: {
3471
+ source: input.context.source,
3472
+ kind: "resync",
3473
+ fatal: false,
3474
+ reason: `presented frame lag ${lag.toFixed(3)}s`,
3475
+ context: input.context
3476
+ }
3477
+ };
3478
+ }
3456
3479
  function describePlaybackError(input) {
3457
3480
  if (input.source === "unsupported") {
3458
3481
  return {
@@ -3511,6 +3534,10 @@ function describePlaybackError(input) {
3511
3534
  // src/components/video/useHlsPlayback.ts
3512
3535
  var RETRY_INTERVAL_MS = 3e3;
3513
3536
  var MAX_MEDIA_ERROR_RETRIES = 5;
3537
+ var DRIFT_THRESHOLD_S = 0.4;
3538
+ var DRIFT_CONSECUTIVE_SAMPLES = 3;
3539
+ var RESYNC_COOLDOWN_MS = 1e4;
3540
+ var LIVE_EDGE_RESYNC_THRESHOLD_S = 8;
3514
3541
  function useHlsPlayback({
3515
3542
  src,
3516
3543
  mode,
@@ -3550,6 +3577,7 @@ function useHlsPlayback({
3550
3577
  let retryTimer = null;
3551
3578
  let manifestParsed = false;
3552
3579
  let mediaErrorRetries = 0;
3580
+ let playbackSource = "hls";
3553
3581
  const teardown = [];
3554
3582
  media.muted = muted;
3555
3583
  setState("loading");
@@ -3558,7 +3586,13 @@ function useHlsPlayback({
3558
3586
  setQualities([]);
3559
3587
  setActiveLevel(-1);
3560
3588
  function baseContext() {
3561
- return { mode, src, manifestParsed, retryCount: mediaErrorRetries };
3589
+ return {
3590
+ mode,
3591
+ src,
3592
+ manifestParsed,
3593
+ retryCount: mediaErrorRetries,
3594
+ source: playbackSource
3595
+ };
3562
3596
  }
3563
3597
  function clearRetryTimer() {
3564
3598
  if (retryTimer !== null) {
@@ -3597,6 +3631,53 @@ function useHlsPlayback({
3597
3631
  if (!cancelled) retryFn();
3598
3632
  }, RETRY_INTERVAL_MS);
3599
3633
  }
3634
+ function startDriftWatchdog(el) {
3635
+ if (typeof el.requestVideoFrameCallback !== "function") return;
3636
+ let consecutiveLagSamples = 0;
3637
+ let lastResyncAt = 0;
3638
+ let handle = null;
3639
+ const tick = (_now, metadata) => {
3640
+ if (cancelled) return;
3641
+ handle = el.requestVideoFrameCallback(tick);
3642
+ if (el.paused || el.ended) {
3643
+ consecutiveLagSamples = 0;
3644
+ return;
3645
+ }
3646
+ const lag = el.currentTime - metadata.mediaTime;
3647
+ if (!(lag > DRIFT_THRESHOLD_S)) {
3648
+ consecutiveLagSamples = 0;
3649
+ return;
3650
+ }
3651
+ consecutiveLagSamples += 1;
3652
+ if (consecutiveLagSamples < DRIFT_CONSECUTIVE_SAMPLES) return;
3653
+ const nowMs = performance.now();
3654
+ if (nowMs - lastResyncAt < RESYNC_COOLDOWN_MS) return;
3655
+ lastResyncAt = nowMs;
3656
+ consecutiveLagSamples = 0;
3657
+ if (mode === "live" && el.seekable.length > 0) {
3658
+ const edge = el.seekable.end(el.seekable.length - 1);
3659
+ if (edge - el.currentTime > LIVE_EDGE_RESYNC_THRESHOLD_S) {
3660
+ el.currentTime = edge;
3661
+ } else {
3662
+ el.currentTime = el.currentTime + 1e-3;
3663
+ }
3664
+ } else {
3665
+ el.currentTime = el.currentTime + 1e-3;
3666
+ }
3667
+ onErrorRef.current?.(
3668
+ describeAvResync({
3669
+ lagSeconds: lag,
3670
+ context: baseContext()
3671
+ })
3672
+ );
3673
+ };
3674
+ handle = el.requestVideoFrameCallback(tick);
3675
+ teardown.push(() => {
3676
+ if (handle != null && typeof el.cancelVideoFrameCallback === "function") {
3677
+ el.cancelVideoFrameCallback(handle);
3678
+ }
3679
+ });
3680
+ }
3600
3681
  function onPlaying() {
3601
3682
  manifestParsed = true;
3602
3683
  mediaErrorRetries = 0;
@@ -3615,7 +3696,9 @@ function useHlsPlayback({
3615
3696
  media.removeEventListener("pause", onPaused);
3616
3697
  media.removeEventListener("ended", onPaused);
3617
3698
  });
3699
+ startDriftWatchdog(media);
3618
3700
  function initNative() {
3701
+ playbackSource = "native";
3619
3702
  manifestParsed = false;
3620
3703
  media.removeAttribute("src");
3621
3704
  media.load();
@@ -3631,7 +3714,7 @@ function useHlsPlayback({
3631
3714
  reportFatal({
3632
3715
  source: "native",
3633
3716
  mediaError: media.error,
3634
- context: { ...baseContext(), source: "native" }
3717
+ context: baseContext()
3635
3718
  });
3636
3719
  return;
3637
3720
  }
@@ -3639,7 +3722,7 @@ function useHlsPlayback({
3639
3722
  describePlaybackError({
3640
3723
  source: "native",
3641
3724
  mediaError: media.error,
3642
- context: { ...baseContext(), source: "native" }
3725
+ context: baseContext()
3643
3726
  })
3644
3727
  );
3645
3728
  scheduleRetry(() => {
@@ -3655,6 +3738,7 @@ function useHlsPlayback({
3655
3738
  });
3656
3739
  }
3657
3740
  function initHlsJs(Hls) {
3741
+ playbackSource = "hls";
3658
3742
  destroyHls();
3659
3743
  manifestParsed = false;
3660
3744
  const config = mode === "live" ? createLiveHlsConfig() : createVodHlsConfig();
@@ -3686,7 +3770,7 @@ function useHlsPlayback({
3686
3770
  response: data.response,
3687
3771
  reason: data.reason,
3688
3772
  error: data.error,
3689
- context: { ...baseContext(), source: "hls" }
3773
+ context: baseContext()
3690
3774
  })
3691
3775
  );
3692
3776
  if (!data.fatal) return;
@@ -3721,7 +3805,7 @@ function useHlsPlayback({
3721
3805
  response: data.response,
3722
3806
  reason: data.reason,
3723
3807
  error: data.error,
3724
- context: { ...baseContext(), source: "hls" }
3808
+ context: baseContext()
3725
3809
  });
3726
3810
  });
3727
3811
  }
@@ -3738,9 +3822,10 @@ function useHlsPlayback({
3738
3822
  } else if (media.canPlayType("application/vnd.apple.mpegurl")) {
3739
3823
  initNative();
3740
3824
  } else {
3825
+ playbackSource = "unsupported";
3741
3826
  reportFatal({
3742
3827
  source: "unsupported",
3743
- context: { ...baseContext(), source: "unsupported" }
3828
+ context: baseContext()
3744
3829
  });
3745
3830
  }
3746
3831
  }
@@ -4087,6 +4172,8 @@ function VideoPlayer({
4087
4172
  return t.errorMedia;
4088
4173
  case "unsupported":
4089
4174
  return t.errorUnsupported;
4175
+ case "resync":
4176
+ return t.errorMedia;
4090
4177
  case "generic":
4091
4178
  return t.errorGeneric;
4092
4179
  default: {
@@ -6167,8 +6254,12 @@ var MessageComposer = React7__namespace.forwardRef(function MessageComposer2({
6167
6254
  onKeyDown,
6168
6255
  sendLabel,
6169
6256
  sendDisabled = false,
6257
+ allowEmptySubmit = false,
6170
6258
  sendButtonClassName,
6171
6259
  sendButtonSize = "sm",
6260
+ submitOnEnter = true,
6261
+ tools,
6262
+ attachments,
6172
6263
  triggerLabel,
6173
6264
  keepOpenOnSelect,
6174
6265
  open,
@@ -6187,7 +6278,7 @@ var MessageComposer = React7__namespace.forwardRef(function MessageComposer2({
6187
6278
  const handleEmojiSelect = useEmojiInsert(fieldRef, value, onChange);
6188
6279
  const pickerDisabled = Boolean(disabled || readOnly);
6189
6280
  const draft = value !== void 0 && value !== null ? String(value) : "";
6190
- const canSubmit = Boolean(draft.trim()) && !sendDisabled && !disabled && !readOnly;
6281
+ const canSubmit = (allowEmptySubmit || Boolean(draft.trim())) && !sendDisabled && !disabled && !readOnly;
6191
6282
  const handleSubmit = React7__namespace.useCallback(
6192
6283
  (event) => {
6193
6284
  event.preventDefault();
@@ -6200,13 +6291,14 @@ var MessageComposer = React7__namespace.forwardRef(function MessageComposer2({
6200
6291
  (event) => {
6201
6292
  onKeyDown?.(event);
6202
6293
  if (event.defaultPrevented) return;
6294
+ if (!submitOnEnter) return;
6203
6295
  if (event.key !== "Enter" || event.shiftKey) return;
6204
6296
  if (event.nativeEvent.isComposing || event.keyCode === 229) return;
6205
6297
  event.preventDefault();
6206
6298
  if (!canSubmit) return;
6207
6299
  formRef.current?.requestSubmit();
6208
6300
  },
6209
- [canSubmit, onKeyDown]
6301
+ [canSubmit, onKeyDown, submitOnEnter]
6210
6302
  );
6211
6303
  return /* @__PURE__ */ jsxRuntime.jsxs(
6212
6304
  "form",
@@ -6233,28 +6325,32 @@ var MessageComposer = React7__namespace.forwardRef(function MessageComposer2({
6233
6325
  ...props
6234
6326
  }
6235
6327
  ),
6328
+ attachments ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t px-2 py-2", children: attachments }) : null,
6236
6329
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 px-1.5 pb-1.5", children: [
6237
- /* @__PURE__ */ jsxRuntime.jsx(
6238
- EmojiPickerPopover,
6239
- {
6240
- locale,
6241
- onSelect: handleEmojiSelect,
6242
- disabled: pickerDisabled,
6243
- triggerLabel,
6244
- keepOpenOnSelect,
6245
- open,
6246
- onOpenChange,
6247
- side,
6248
- align,
6249
- labels,
6250
- recentLimit,
6251
- storageKey,
6252
- triggerClassName: cn(
6253
- "size-7 text-muted-foreground",
6254
- emojiTriggerClassName
6255
- )
6256
- }
6257
- ),
6330
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-0.5", children: [
6331
+ /* @__PURE__ */ jsxRuntime.jsx(
6332
+ EmojiPickerPopover,
6333
+ {
6334
+ locale,
6335
+ onSelect: handleEmojiSelect,
6336
+ disabled: pickerDisabled,
6337
+ triggerLabel,
6338
+ keepOpenOnSelect,
6339
+ open,
6340
+ onOpenChange,
6341
+ side,
6342
+ align,
6343
+ labels,
6344
+ recentLimit,
6345
+ storageKey,
6346
+ triggerClassName: cn(
6347
+ "size-7 text-muted-foreground",
6348
+ emojiTriggerClassName
6349
+ )
6350
+ }
6351
+ ),
6352
+ tools
6353
+ ] }),
6258
6354
  /* @__PURE__ */ jsxRuntime.jsx(
6259
6355
  Button,
6260
6356
  {
@@ -6887,6 +6983,105 @@ function createSupportChatClient(basePath = "/api/chat-svc") {
6887
6983
  }
6888
6984
  };
6889
6985
  }
6986
+ var DEFAULT_RECONNECT_MS = 2e3;
6987
+ var MAX_RECONNECT_MS = 3e4;
6988
+ function useRealtimeChannel({
6989
+ getWsUrl,
6990
+ onEvent,
6991
+ enabled = true,
6992
+ reconnectDelayMs = DEFAULT_RECONNECT_MS
6993
+ }) {
6994
+ const onEventRef = React7.useRef(onEvent);
6995
+ onEventRef.current = onEvent;
6996
+ const getWsUrlRef = React7.useRef(getWsUrl);
6997
+ getWsUrlRef.current = getWsUrl;
6998
+ React7.useEffect(() => {
6999
+ if (!enabled) return;
7000
+ let cancelled = false;
7001
+ let socket = null;
7002
+ let reconnectTimer = null;
7003
+ let attempt = 0;
7004
+ const connectGen = { current: 0 };
7005
+ const clearReconnect = () => {
7006
+ if (reconnectTimer) {
7007
+ clearTimeout(reconnectTimer);
7008
+ reconnectTimer = null;
7009
+ }
7010
+ };
7011
+ const scheduleReconnect = () => {
7012
+ if (cancelled) return;
7013
+ clearReconnect();
7014
+ const delay = Math.min(reconnectDelayMs * 2 ** attempt, MAX_RECONNECT_MS);
7015
+ attempt += 1;
7016
+ reconnectTimer = setTimeout(() => {
7017
+ void connect();
7018
+ }, delay);
7019
+ };
7020
+ const connect = async () => {
7021
+ const gen = ++connectGen.current;
7022
+ clearReconnect();
7023
+ const url = await getWsUrlRef.current();
7024
+ if (cancelled || gen !== connectGen.current) return;
7025
+ if (!url) {
7026
+ scheduleReconnect();
7027
+ return;
7028
+ }
7029
+ if (socket) {
7030
+ try {
7031
+ socket.close();
7032
+ } catch {
7033
+ }
7034
+ socket = null;
7035
+ }
7036
+ const ws = new WebSocket(url);
7037
+ socket = ws;
7038
+ ws.onopen = () => {
7039
+ if (cancelled || gen !== connectGen.current) return;
7040
+ attempt = 0;
7041
+ };
7042
+ ws.onmessage = (msg) => {
7043
+ if (cancelled || gen !== connectGen.current) return;
7044
+ let data;
7045
+ try {
7046
+ data = JSON.parse(String(msg.data));
7047
+ } catch {
7048
+ return;
7049
+ }
7050
+ if (typeof data?.type !== "string") return;
7051
+ if (data.type === "pong") return;
7052
+ onEventRef.current(data);
7053
+ };
7054
+ ws.onclose = () => {
7055
+ if (cancelled || gen !== connectGen.current) return;
7056
+ socket = null;
7057
+ scheduleReconnect();
7058
+ };
7059
+ ws.onerror = () => {
7060
+ };
7061
+ };
7062
+ const onVisibility = () => {
7063
+ if (document.visibilityState !== "visible" || cancelled) return;
7064
+ if (!socket || socket.readyState === WebSocket.CLOSED) {
7065
+ void connect();
7066
+ }
7067
+ };
7068
+ void connect();
7069
+ document.addEventListener("visibilitychange", onVisibility);
7070
+ return () => {
7071
+ cancelled = true;
7072
+ connectGen.current += 1;
7073
+ clearReconnect();
7074
+ document.removeEventListener("visibilitychange", onVisibility);
7075
+ if (socket) {
7076
+ try {
7077
+ socket.close();
7078
+ } catch {
7079
+ }
7080
+ socket = null;
7081
+ }
7082
+ };
7083
+ }, [enabled, reconnectDelayMs]);
7084
+ }
6890
7085
 
6891
7086
  Object.defineProperty(exports, "useMessageScroller", {
6892
7087
  enumerable: true,
@@ -6959,6 +7154,7 @@ exports.createLiveHlsConfig = createLiveHlsConfig;
6959
7154
  exports.createSupportChatClient = createSupportChatClient;
6960
7155
  exports.createVodHlsConfig = createVodHlsConfig;
6961
7156
  exports.defaultLanguages = defaultLanguages;
7157
+ exports.describeAvResync = describeAvResync;
6962
7158
  exports.describePlaybackError = describePlaybackError;
6963
7159
  exports.fuzzyMatch = fuzzyMatch;
6964
7160
  exports.getAvatarColors = getAvatarColors;
@@ -6973,6 +7169,7 @@ exports.useHlsPlayback = useHlsPlayback;
6973
7169
  exports.useLanguageContext = useLanguageContext;
6974
7170
  exports.useMediaQuery = useMediaQuery2;
6975
7171
  exports.usePlaybackStats = usePlaybackStats;
7172
+ exports.useRealtimeChannel = useRealtimeChannel;
6976
7173
  exports.useSearchHotkey = useSearchHotkey;
6977
7174
  exports.useSupportConversation = useSupportConversation;
6978
7175
  exports.useSupportMessenger = useSupportMessenger;