@sesionario/blocks 1.12.0 → 1.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -3462,6 +3462,20 @@ function kindForNative(input) {
3462
3462
  if (code === MEDIA_ERR_NETWORK) return "unavailable";
3463
3463
  return "generic";
3464
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
+ }
3465
3479
  function describePlaybackError(input) {
3466
3480
  if (input.source === "unsupported") {
3467
3481
  return {
@@ -3520,6 +3534,10 @@ function describePlaybackError(input) {
3520
3534
  // src/components/video/useHlsPlayback.ts
3521
3535
  var RETRY_INTERVAL_MS = 3e3;
3522
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;
3523
3541
  function useHlsPlayback({
3524
3542
  src,
3525
3543
  mode,
@@ -3559,6 +3577,7 @@ function useHlsPlayback({
3559
3577
  let retryTimer = null;
3560
3578
  let manifestParsed = false;
3561
3579
  let mediaErrorRetries = 0;
3580
+ let playbackSource = "hls";
3562
3581
  const teardown = [];
3563
3582
  media.muted = muted;
3564
3583
  setState("loading");
@@ -3567,7 +3586,13 @@ function useHlsPlayback({
3567
3586
  setQualities([]);
3568
3587
  setActiveLevel(-1);
3569
3588
  function baseContext() {
3570
- return { mode, src, manifestParsed, retryCount: mediaErrorRetries };
3589
+ return {
3590
+ mode,
3591
+ src,
3592
+ manifestParsed,
3593
+ retryCount: mediaErrorRetries,
3594
+ source: playbackSource
3595
+ };
3571
3596
  }
3572
3597
  function clearRetryTimer() {
3573
3598
  if (retryTimer !== null) {
@@ -3606,6 +3631,53 @@ function useHlsPlayback({
3606
3631
  if (!cancelled) retryFn();
3607
3632
  }, RETRY_INTERVAL_MS);
3608
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
+ }
3609
3681
  function onPlaying() {
3610
3682
  manifestParsed = true;
3611
3683
  mediaErrorRetries = 0;
@@ -3624,7 +3696,9 @@ function useHlsPlayback({
3624
3696
  media.removeEventListener("pause", onPaused);
3625
3697
  media.removeEventListener("ended", onPaused);
3626
3698
  });
3699
+ startDriftWatchdog(media);
3627
3700
  function initNative() {
3701
+ playbackSource = "native";
3628
3702
  manifestParsed = false;
3629
3703
  media.removeAttribute("src");
3630
3704
  media.load();
@@ -3640,7 +3714,7 @@ function useHlsPlayback({
3640
3714
  reportFatal({
3641
3715
  source: "native",
3642
3716
  mediaError: media.error,
3643
- context: { ...baseContext(), source: "native" }
3717
+ context: baseContext()
3644
3718
  });
3645
3719
  return;
3646
3720
  }
@@ -3648,7 +3722,7 @@ function useHlsPlayback({
3648
3722
  describePlaybackError({
3649
3723
  source: "native",
3650
3724
  mediaError: media.error,
3651
- context: { ...baseContext(), source: "native" }
3725
+ context: baseContext()
3652
3726
  })
3653
3727
  );
3654
3728
  scheduleRetry(() => {
@@ -3664,6 +3738,7 @@ function useHlsPlayback({
3664
3738
  });
3665
3739
  }
3666
3740
  function initHlsJs(Hls) {
3741
+ playbackSource = "hls";
3667
3742
  destroyHls();
3668
3743
  manifestParsed = false;
3669
3744
  const config = mode === "live" ? createLiveHlsConfig() : createVodHlsConfig();
@@ -3695,7 +3770,7 @@ function useHlsPlayback({
3695
3770
  response: data.response,
3696
3771
  reason: data.reason,
3697
3772
  error: data.error,
3698
- context: { ...baseContext(), source: "hls" }
3773
+ context: baseContext()
3699
3774
  })
3700
3775
  );
3701
3776
  if (!data.fatal) return;
@@ -3730,7 +3805,7 @@ function useHlsPlayback({
3730
3805
  response: data.response,
3731
3806
  reason: data.reason,
3732
3807
  error: data.error,
3733
- context: { ...baseContext(), source: "hls" }
3808
+ context: baseContext()
3734
3809
  });
3735
3810
  });
3736
3811
  }
@@ -3747,9 +3822,10 @@ function useHlsPlayback({
3747
3822
  } else if (media.canPlayType("application/vnd.apple.mpegurl")) {
3748
3823
  initNative();
3749
3824
  } else {
3825
+ playbackSource = "unsupported";
3750
3826
  reportFatal({
3751
3827
  source: "unsupported",
3752
- context: { ...baseContext(), source: "unsupported" }
3828
+ context: baseContext()
3753
3829
  });
3754
3830
  }
3755
3831
  }
@@ -4096,6 +4172,8 @@ function VideoPlayer({
4096
4172
  return t.errorMedia;
4097
4173
  case "unsupported":
4098
4174
  return t.errorUnsupported;
4175
+ case "resync":
4176
+ return t.errorMedia;
4099
4177
  case "generic":
4100
4178
  return t.errorGeneric;
4101
4179
  default: {
@@ -6107,7 +6185,7 @@ function MessageScrollerButton({
6107
6185
  "data-size": size,
6108
6186
  direction,
6109
6187
  className: cn(
6110
- "absolute inset-s-1/2 -translate-x-1/2 border-border bg-background text-foreground transition-[translate,scale,opacity] duration-200 hover:bg-muted hover:text-foreground data-[active=false]:pointer-events-none data-[active=false]:scale-95 data-[active=false]:opacity-0 data-[active=false]:duration-400 data-[active=false]:ease-[cubic-bezier(0.7,0,0.84,0)] data-[active=true]:translate-y-0 data-[active=true]:scale-100 data-[active=true]:opacity-100 data-[active=true]:ease-[cubic-bezier(0.23,1,0.32,1)] data-[direction=end]:bottom-4 data-[direction=end]:data-[active=false]:translate-y-full data-[direction=start]:top-4 data-[direction=start]:data-[active=false]:-translate-y-full rtl:translate-x-1/2 data-[direction=start]:[&_svg]:rotate-180",
6188
+ "absolute left-1/2 -translate-x-1/2 border-border bg-background text-foreground transition-[translate,scale,opacity] duration-200 hover:bg-muted hover:text-foreground data-[active=false]:pointer-events-none data-[active=false]:scale-95 data-[active=false]:opacity-0 data-[active=false]:duration-400 data-[active=false]:ease-[cubic-bezier(0.7,0,0.84,0)] data-[active=true]:translate-y-0 data-[active=true]:scale-100 data-[active=true]:opacity-100 data-[active=true]:ease-[cubic-bezier(0.23,1,0.32,1)] data-[direction=end]:bottom-4 data-[direction=end]:data-[active=false]:translate-y-full data-[direction=start]:top-4 data-[direction=start]:data-[active=false]:-translate-y-full data-[direction=start]:[&_svg]:rotate-180",
6111
6189
  className
6112
6190
  ),
6113
6191
  render: render ?? /* @__PURE__ */ jsxRuntime.jsx(Button, { variant, size }),
@@ -6905,6 +6983,105 @@ function createSupportChatClient(basePath = "/api/chat-svc") {
6905
6983
  }
6906
6984
  };
6907
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
+ }
6908
7085
 
6909
7086
  Object.defineProperty(exports, "useMessageScroller", {
6910
7087
  enumerable: true,
@@ -6977,6 +7154,7 @@ exports.createLiveHlsConfig = createLiveHlsConfig;
6977
7154
  exports.createSupportChatClient = createSupportChatClient;
6978
7155
  exports.createVodHlsConfig = createVodHlsConfig;
6979
7156
  exports.defaultLanguages = defaultLanguages;
7157
+ exports.describeAvResync = describeAvResync;
6980
7158
  exports.describePlaybackError = describePlaybackError;
6981
7159
  exports.fuzzyMatch = fuzzyMatch;
6982
7160
  exports.getAvatarColors = getAvatarColors;
@@ -6991,6 +7169,7 @@ exports.useHlsPlayback = useHlsPlayback;
6991
7169
  exports.useLanguageContext = useLanguageContext;
6992
7170
  exports.useMediaQuery = useMediaQuery2;
6993
7171
  exports.usePlaybackStats = usePlaybackStats;
7172
+ exports.useRealtimeChannel = useRealtimeChannel;
6994
7173
  exports.useSearchHotkey = useSearchHotkey;
6995
7174
  exports.useSupportConversation = useSupportConversation;
6996
7175
  exports.useSupportMessenger = useSupportMessenger;