@flamingo-stack/openframe-frontend-core 0.0.177 → 0.0.178

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/{chunk-6LDN3CIY.js → chunk-AAX27BCR.js} +189 -348
  2. package/dist/chunk-AAX27BCR.js.map +1 -0
  3. package/dist/{chunk-WX7PT5C7.cjs → chunk-ALW3D72O.cjs} +61 -2
  4. package/dist/chunk-ALW3D72O.cjs.map +1 -0
  5. package/dist/{chunk-KB2N44BY.js → chunk-FMWHOUFE.js} +61 -2
  6. package/dist/chunk-FMWHOUFE.js.map +1 -0
  7. package/dist/{chunk-C6ZMI4UB.cjs → chunk-L4T24AN4.cjs} +113 -272
  8. package/dist/chunk-L4T24AN4.cjs.map +1 -0
  9. package/dist/components/features/index.cjs +3 -5
  10. package/dist/components/features/index.cjs.map +1 -1
  11. package/dist/components/features/index.js +2 -4
  12. package/dist/components/features/video-player.d.ts +17 -20
  13. package/dist/components/features/video-player.d.ts.map +1 -1
  14. package/dist/components/features/youtube-embed.d.ts +18 -4
  15. package/dist/components/features/youtube-embed.d.ts.map +1 -1
  16. package/dist/components/index.cjs +3 -5
  17. package/dist/components/index.cjs.map +1 -1
  18. package/dist/components/index.js +2 -4
  19. package/dist/components/navigation/index.cjs +3 -3
  20. package/dist/components/navigation/index.js +2 -2
  21. package/dist/components/ui/index.cjs +3 -3
  22. package/dist/components/ui/index.js +2 -2
  23. package/dist/hooks/index.cjs +4 -2
  24. package/dist/hooks/index.cjs.map +1 -1
  25. package/dist/hooks/index.d.ts +1 -0
  26. package/dist/hooks/index.d.ts.map +1 -1
  27. package/dist/hooks/index.js +3 -1
  28. package/dist/hooks/use-near-viewport.d.ts +42 -0
  29. package/dist/hooks/use-near-viewport.d.ts.map +1 -0
  30. package/dist/index.cjs +3 -3
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.js +4 -4
  33. package/package.json +1 -1
  34. package/src/components/features/video-player.tsx +39 -176
  35. package/src/components/features/youtube-embed.tsx +107 -224
  36. package/src/hooks/index.ts +3 -0
  37. package/src/hooks/use-near-viewport.ts +118 -0
  38. package/dist/chunk-6LDN3CIY.js.map +0 -1
  39. package/dist/chunk-C6ZMI4UB.cjs.map +0 -1
  40. package/dist/chunk-KB2N44BY.js.map +0 -1
  41. package/dist/chunk-WX7PT5C7.cjs.map +0 -1
  42. package/src/components/features/__tests__/video-player.test.tsx +0 -142
@@ -16,7 +16,7 @@ import {
16
16
  useMdUp,
17
17
  useOnboardingState,
18
18
  useToast
19
- } from "./chunk-KB2N44BY.js";
19
+ } from "./chunk-FMWHOUFE.js";
20
20
  import {
21
21
  Button,
22
22
  Checkbox,
@@ -413,7 +413,7 @@ function useDynamicTheme() {
413
413
  }
414
414
 
415
415
  // src/components/features/array-entry-manager.tsx
416
- import { useState as useState58, useEffect as useEffect43 } from "react";
416
+ import { useState as useState58, useEffect as useEffect42 } from "react";
417
417
 
418
418
  // src/components/ui/allowed-domains-input.tsx
419
419
  init_cn();
@@ -16397,7 +16397,7 @@ function ProductReleaseCardSkeleton({ className, size = "default" }) {
16397
16397
  }
16398
16398
 
16399
16399
  // src/components/shared/product-release/release-detail-page.tsx
16400
- import { useState as useState37, useEffect as useEffect27 } from "react";
16400
+ import { useState as useState37, useEffect as useEffect26 } from "react";
16401
16401
  import Link4 from "next/link";
16402
16402
 
16403
16403
  // src/components/layout/article-detail-layout.tsx
@@ -16616,58 +16616,20 @@ function ImageGalleryModal({
16616
16616
  import { AlertTriangle, ExternalLink, BookMarked } from "lucide-react";
16617
16617
 
16618
16618
  // src/components/features/youtube-embed.tsx
16619
- import { useState as useState34, useEffect as useEffect25 } from "react";
16620
- import ReactPlayer from "react-player/youtube";
16619
+ import { useRef as useRef21, useState as useState34 } from "react";
16621
16620
  import { jsx as jsx138, jsxs as jsxs111 } from "react/jsx-runtime";
16622
- var Play = ({ size = 16, className }) => /* @__PURE__ */ jsx138("svg", { width: size, height: size, fill: "currentColor", viewBox: "0 0 24 24", className, children: /* @__PURE__ */ jsx138("polygon", { points: "5,3 19,12 5,21" }) });
16623
- var Loader = ({ size = 16, className }) => /* @__PURE__ */ jsxs111("svg", { width: size, height: size, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", className, children: [
16624
- /* @__PURE__ */ jsx138("line", { x1: "12", y1: "2", x2: "12", y2: "6" }),
16625
- /* @__PURE__ */ jsx138("line", { x1: "12", y1: "18", x2: "12", y2: "22" }),
16626
- /* @__PURE__ */ jsx138("line", { x1: "4.93", y1: "4.93", x2: "7.76", y2: "7.76" }),
16627
- /* @__PURE__ */ jsx138("line", { x1: "16.24", y1: "16.24", x2: "19.07", y2: "19.07" }),
16628
- /* @__PURE__ */ jsx138("line", { x1: "2", y1: "12", x2: "6", y2: "12" }),
16629
- /* @__PURE__ */ jsx138("line", { x1: "18", y1: "12", x2: "22", y2: "12" }),
16630
- /* @__PURE__ */ jsx138("line", { x1: "4.93", y1: "19.07", x2: "7.76", y2: "16.24" }),
16631
- /* @__PURE__ */ jsx138("line", { x1: "16.24", y1: "7.76", x2: "19.07", y2: "4.93" })
16632
- ] });
16633
16621
  var YouTubeEmbed = ({
16634
16622
  videoId,
16635
16623
  title = "YouTube Video",
16636
16624
  className = "",
16637
16625
  showTitle = true,
16638
16626
  showMeta = true,
16639
- minimalControls = false
16627
+ minimalControls = false,
16628
+ aboveTheFold = false
16640
16629
  }) => {
16641
- const [isLoading, setIsLoading] = useState34(true);
16642
- const [hasError, setHasError] = useState34(false);
16643
- const [isPlaying, setIsPlaying] = useState34(false);
16644
- const [useIframe, setUseIframe] = useState34(false);
16645
- const [mounted, setMounted] = useState34(false);
16646
- useEffect25(() => {
16647
- setMounted(true);
16648
- setUseIframe(true);
16649
- setIsLoading(false);
16650
- }, []);
16651
- const handleReady = () => {
16652
- setIsLoading(false);
16653
- };
16654
- const handleError = () => {
16655
- setIsLoading(false);
16656
- setHasError(true);
16657
- setUseIframe(true);
16658
- };
16659
- const handlePlay = () => {
16660
- setIsPlaying(true);
16661
- };
16662
- const handlePause = () => {
16663
- setIsPlaying(false);
16664
- };
16665
- const videoUrl = `https://www.youtube.com/watch?v=${videoId}`;
16666
- const embedParams = new URLSearchParams({
16667
- rel: "0",
16668
- modestbranding: "1",
16669
- playsinline: "1"
16670
- });
16630
+ const [activated, setActivated] = useState34(false);
16631
+ const iframeSlotRef = useRef21(null);
16632
+ const embedParams = new URLSearchParams({ autoplay: "1", rel: "0", modestbranding: "1", playsinline: "1" });
16671
16633
  if (minimalControls) {
16672
16634
  embedParams.set("controls", "0");
16673
16635
  embedParams.set("showinfo", "0");
@@ -16675,96 +16637,60 @@ var YouTubeEmbed = ({
16675
16637
  embedParams.set("iv_load_policy", "3");
16676
16638
  embedParams.set("cc_load_policy", "0");
16677
16639
  embedParams.set("disablekb", "1");
16678
- embedParams.set("rel", "0");
16679
- }
16680
- const embedUrl = `https://www.youtube.com/embed/${videoId}?${embedParams.toString()}`;
16681
- if (!mounted) {
16682
- return /* @__PURE__ */ jsx138("div", { className: `youtube-embed-container my-6 ${className}`, children: /* @__PURE__ */ jsx138("div", { className: "video-wrapper relative w-full", style: { paddingBottom: "56.25%" }, children: /* @__PURE__ */ jsx138("div", { className: "loading-overlay absolute inset-0 bg-ods-card border border-ods-border rounded-lg flex items-center justify-center", children: /* @__PURE__ */ jsxs111("div", { className: "loading-content flex flex-col items-center gap-3", children: [
16683
- /* @__PURE__ */ jsx138(Loader, { className: "animate-spin text-ods-accent", size: 32 }),
16684
- /* @__PURE__ */ jsx138("span", { className: "font-sans text-sm text-ods-text-secondary", children: "Loading video..." })
16685
- ] }) }) }) });
16686
- }
16687
- if (hasError) {
16688
- return /* @__PURE__ */ jsx138("div", { className: `youtube-embed-error ${className}`, children: /* @__PURE__ */ jsxs111("div", { className: "error-state bg-ods-card border border-ods-error rounded-lg p-6 text-center my-6", children: [
16689
- /* @__PURE__ */ jsx138("div", { className: "error-icon flex justify-center mb-4", children: /* @__PURE__ */ jsx138("svg", { width: "48", height: "48", fill: "currentColor", viewBox: "0 0 24 24", className: "text-ods-error", children: /* @__PURE__ */ jsx138("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" }) }) }),
16690
- /* @__PURE__ */ jsx138("div", { className: "error-title font-sans font-semibold text-lg text-ods-error mb-2", children: "Video Unavailable" }),
16691
- /* @__PURE__ */ jsx138("div", { className: "error-description font-sans text-sm text-ods-text-secondary mb-4", children: "Unable to load YouTube video. The video may be private or deleted." }),
16692
- /* @__PURE__ */ jsx138(
16693
- "a",
16694
- {
16695
- href: videoUrl,
16696
- target: "_blank",
16697
- rel: "noopener noreferrer",
16698
- className: "error-retry-button bg-ods-error hover:bg-ods-error-hover text-ods-text-on-error border-none rounded px-4 py-2 font-sans font-medium text-sm cursor-pointer transition-colors duration-200",
16699
- children: "Watch on YouTube"
16700
- }
16701
- )
16702
- ] }) });
16703
16640
  }
16641
+ const embedUrl = `https://www.youtube-nocookie.com/embed/${videoId}?${embedParams.toString()}`;
16642
+ const watchUrl = `https://www.youtube.com/watch?v=${videoId}`;
16643
+ const posterJpg = `https://i.ytimg.com/vi/${videoId}/mqdefault.jpg`;
16644
+ const posterWebp = `https://i.ytimg.com/vi_webp/${videoId}/mqdefault.webp`;
16645
+ const handleActivate = () => {
16646
+ const slot = iframeSlotRef.current;
16647
+ if (!slot || activated) return;
16648
+ const iframe = document.createElement("iframe");
16649
+ iframe.setAttribute("allow", "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share");
16650
+ iframe.setAttribute("allowfullscreen", "");
16651
+ iframe.setAttribute("title", title);
16652
+ iframe.className = "absolute inset-0 w-full h-full border-0";
16653
+ iframe.src = embedUrl;
16654
+ slot.appendChild(iframe);
16655
+ setActivated(true);
16656
+ };
16704
16657
  return /* @__PURE__ */ jsxs111("div", { className: `youtube-embed-container my-6 ${className}`, children: [
16705
16658
  title && showTitle && /* @__PURE__ */ jsx138("div", { className: "video-title font-sans text-lg font-medium text-ods-text-primary mb-3", children: title }),
16706
- /* @__PURE__ */ jsxs111("div", { className: "video-wrapper relative w-full", style: { paddingBottom: "56.25%" }, children: [
16707
- isLoading && /* @__PURE__ */ jsx138("div", { className: "loading-overlay absolute inset-0 bg-ods-card border border-ods-border rounded-lg flex items-center justify-center", children: /* @__PURE__ */ jsxs111("div", { className: "loading-content flex flex-col items-center gap-3", children: [
16708
- /* @__PURE__ */ jsx138(Loader, { className: "animate-spin text-ods-accent", size: 32 }),
16709
- /* @__PURE__ */ jsx138("span", { className: "font-sans text-sm text-ods-text-secondary", children: "Loading video..." })
16710
- ] }) }),
16711
- /* @__PURE__ */ jsx138("div", { className: "video-player absolute inset-0 rounded-lg overflow-hidden border border-ods-border bg-ods-bg-inverse", children: useIframe ? (
16712
- // Iframe fallback for mobile and error cases
16713
- /* @__PURE__ */ jsx138(
16714
- "iframe",
16715
- {
16716
- src: embedUrl,
16717
- title,
16718
- className: "w-full h-full border-0",
16719
- allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share",
16720
- allowFullScreen: true,
16721
- style: { border: "none" }
16722
- }
16723
- )
16724
- ) : /* @__PURE__ */ jsx138(
16725
- ReactPlayer,
16726
- {
16727
- url: videoUrl,
16728
- width: "100%",
16729
- height: "100%",
16730
- onReady: handleReady,
16731
- onError: handleError,
16732
- onPlay: handlePlay,
16733
- onPause: handlePause,
16734
- config: {
16735
- youtube: {
16736
- playerVars: {
16737
- autoplay: 0,
16738
- controls: 1,
16739
- rel: 0,
16740
- showinfo: 0,
16741
- modestbranding: 1,
16742
- iv_load_policy: 3,
16743
- cc_load_policy: 0,
16744
- playsinline: 1
16745
- }
16746
- }
16747
- },
16748
- light: false,
16749
- playing: false
16750
- }
16751
- ) }),
16752
- !useIframe && !isPlaying && !isLoading && /* @__PURE__ */ jsx138("div", { className: "play-overlay absolute inset-0 flex items-center justify-center bg-ods-bg-inverse bg-opacity-20 rounded-lg transition-opacity duration-300 hover:bg-opacity-30", children: /* @__PURE__ */ jsx138(
16659
+ /* @__PURE__ */ jsx138("div", { className: "video-wrapper relative w-full", style: { paddingBottom: "56.25%" }, children: /* @__PURE__ */ jsxs111("div", { className: "absolute inset-0 rounded-lg overflow-hidden border border-ods-border bg-ods-card", children: [
16660
+ /* @__PURE__ */ jsx138("div", { ref: iframeSlotRef, className: "absolute inset-0", "aria-hidden": !activated }),
16661
+ !activated && /* @__PURE__ */ jsxs111(
16753
16662
  "button",
16754
16663
  {
16755
- onClick: () => setIsPlaying(true),
16756
- className: "play-button bg-ods-accent hover:bg-ods-accent-hover text-ods-text-on-accent w-16 h-16 rounded-full flex items-center justify-center transition-all duration-200 transform hover:scale-110 shadow-lg",
16757
- "aria-label": "Play video",
16758
- children: /* @__PURE__ */ jsx138(Play, { size: 24, className: "ml-1" })
16664
+ type: "button",
16665
+ "aria-label": `Play: ${title}`,
16666
+ onClick: handleActivate,
16667
+ className: "group absolute inset-0 p-0 m-0 border-0 cursor-pointer bg-transparent",
16668
+ children: [
16669
+ /* @__PURE__ */ jsxs111("picture", { children: [
16670
+ /* @__PURE__ */ jsx138("source", { type: "image/webp", srcSet: posterWebp }),
16671
+ /* @__PURE__ */ jsx138(
16672
+ "img",
16673
+ {
16674
+ src: posterJpg,
16675
+ alt: title,
16676
+ loading: "lazy",
16677
+ fetchPriority: aboveTheFold ? "high" : "low",
16678
+ decoding: aboveTheFold ? "sync" : "async",
16679
+ className: "absolute inset-0 w-full h-full object-cover"
16680
+ }
16681
+ )
16682
+ ] }),
16683
+ /* @__PURE__ */ jsx138("div", { className: "absolute inset-0 flex items-center justify-center bg-ods-bg-inverse bg-opacity-20 transition-opacity duration-200 group-hover:bg-opacity-30", children: /* @__PURE__ */ jsx138("span", { className: "flex items-center justify-center w-16 h-16 rounded-full bg-ods-accent text-ods-text-on-accent shadow-lg transition-transform duration-200 group-hover:scale-110", children: /* @__PURE__ */ jsx138("svg", { width: 24, height: 24, fill: "currentColor", viewBox: "0 0 24 24", className: "ml-1", children: /* @__PURE__ */ jsx138("polygon", { points: "5,3 19,12 5,21" }) }) }) })
16684
+ ]
16759
16685
  }
16760
- ) })
16761
- ] }),
16686
+ )
16687
+ ] }) }),
16762
16688
  showMeta && /* @__PURE__ */ jsxs111("div", { className: "video-meta flex items-center justify-between mt-3 text-sm text-ods-text-secondary", children: [
16763
16689
  /* @__PURE__ */ jsx138("div", { className: "video-platform font-sans", children: "YouTube" }),
16764
16690
  /* @__PURE__ */ jsx138(
16765
16691
  "a",
16766
16692
  {
16767
- href: videoUrl,
16693
+ href: watchUrl,
16768
16694
  target: "_blank",
16769
16695
  rel: "noopener noreferrer",
16770
16696
  className: "video-link font-sans text-ods-accent hover:text-ods-accent-hover transition-colors duration-200",
@@ -16774,111 +16700,37 @@ var YouTubeEmbed = ({
16774
16700
  ] })
16775
16701
  ] });
16776
16702
  };
16703
+ var YT_HOSTS = /* @__PURE__ */ new Set([
16704
+ "youtube.com",
16705
+ "www.youtube.com",
16706
+ "m.youtube.com",
16707
+ "youtu.be",
16708
+ "youtube-nocookie.com",
16709
+ "www.youtube-nocookie.com"
16710
+ ]);
16711
+ var YT_PATH_RE = /^\/(?:embed|v|shorts)\/([^/]+)\/?$/;
16777
16712
  var extractYouTubeId = (url) => {
16778
- const patterns = [
16779
- /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\n?#]+)/,
16780
- /youtube\.com\/v\/([^&\n?#]+)/,
16781
- /youtube\.com\/watch\?.*v=([^&\n?#]+)/
16782
- ];
16783
- for (const pattern of patterns) {
16784
- const match = url.match(pattern);
16785
- if (match) {
16786
- return match[1];
16787
- }
16713
+ let u;
16714
+ try {
16715
+ u = new URL(url);
16716
+ } catch {
16717
+ return null;
16788
16718
  }
16789
- return null;
16790
- };
16791
- var YouTubeLinkParser = ({ href, children }) => {
16792
- const videoId = extractYouTubeId(href);
16793
- if (videoId) {
16794
- return /* @__PURE__ */ jsx138(YouTubeEmbed, { videoId, title: typeof children === "string" ? children : void 0 });
16719
+ if (!YT_HOSTS.has(u.hostname.toLowerCase())) return null;
16720
+ if (u.hostname.toLowerCase().endsWith("youtu.be")) {
16721
+ return u.pathname.split("/").filter(Boolean)[0] ?? null;
16795
16722
  }
16796
- return /* @__PURE__ */ jsx138(
16797
- "a",
16798
- {
16799
- href,
16800
- target: "_blank",
16801
- rel: "noopener noreferrer",
16802
- className: "text-ods-accent hover:text-ods-accent-hover transition-colors duration-200",
16803
- children
16804
- }
16805
- );
16723
+ const v = u.searchParams.get("v");
16724
+ if (v) return v;
16725
+ const m = u.pathname.match(YT_PATH_RE);
16726
+ return m ? m[1] : null;
16806
16727
  };
16807
16728
 
16808
16729
  // src/components/features/video-player.tsx
16809
- import { useState as useState35, useEffect as useEffect26, useRef as useRef21, useMemo as useMemo11, useCallback as useCallback19 } from "react";
16810
- import ReactPlayer2 from "react-player";
16730
+ import { useState as useState35, useEffect as useEffect25, useRef as useRef22, useMemo as useMemo11, useCallback as useCallback19 } from "react";
16731
+ import ReactPlayer from "react-player";
16811
16732
  init_button2();
16812
16733
  import { jsx as jsx139, jsxs as jsxs112 } from "react/jsx-runtime";
16813
- function useVideoFirstFramePoster(url, enabled) {
16814
- const [poster, setPoster] = useState35(null);
16815
- useEffect26(() => {
16816
- if (!enabled || !url) {
16817
- setPoster(null);
16818
- return;
16819
- }
16820
- const isDirectFile = /\.(mp4|webm|mov|m4v)(\?|#|$)/i.test(url);
16821
- if (!isDirectFile) {
16822
- setPoster(null);
16823
- return;
16824
- }
16825
- let cancelled = false;
16826
- const video = document.createElement("video");
16827
- video.crossOrigin = "anonymous";
16828
- video.preload = "metadata";
16829
- video.muted = true;
16830
- video.playsInline = true;
16831
- video.setAttribute("data-poster-extractor", "true");
16832
- const cleanup = () => {
16833
- video.removeAttribute("src");
16834
- try {
16835
- video.load();
16836
- } catch {
16837
- }
16838
- };
16839
- const onLoadedMetadata = () => {
16840
- if (cancelled) return;
16841
- const dur = video.duration || 10;
16842
- const target = Math.max(2, Math.min(dur * 0.1, 30));
16843
- video.currentTime = target;
16844
- };
16845
- const onSeeked = () => {
16846
- if (cancelled) return;
16847
- try {
16848
- const canvas = document.createElement("canvas");
16849
- canvas.width = video.videoWidth;
16850
- canvas.height = video.videoHeight;
16851
- const ctx = canvas.getContext("2d");
16852
- if (!ctx || !canvas.width || !canvas.height) {
16853
- cleanup();
16854
- return;
16855
- }
16856
- ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
16857
- const dataUrl = canvas.toDataURL("image/jpeg", 0.82);
16858
- if (!cancelled) setPoster(dataUrl);
16859
- } catch (err) {
16860
- console.warn("[VideoPlayer] first-frame extraction failed", err);
16861
- } finally {
16862
- cleanup();
16863
- }
16864
- };
16865
- const onError = () => {
16866
- cleanup();
16867
- };
16868
- video.addEventListener("loadedmetadata", onLoadedMetadata);
16869
- video.addEventListener("seeked", onSeeked);
16870
- video.addEventListener("error", onError);
16871
- video.src = url;
16872
- return () => {
16873
- cancelled = true;
16874
- video.removeEventListener("loadedmetadata", onLoadedMetadata);
16875
- video.removeEventListener("seeked", onSeeked);
16876
- video.removeEventListener("error", onError);
16877
- cleanup();
16878
- };
16879
- }, [url, enabled]);
16880
- return poster;
16881
- }
16882
16734
  var webkitCaptionCSSInjected = false;
16883
16735
  function ensureWebkitCaptionCSS() {
16884
16736
  if (webkitCaptionCSSInjected || typeof document === "undefined") return;
@@ -16929,7 +16781,6 @@ function useSubtitleOverlay(srtContent) {
16929
16781
  return { activeText, updateTime, hasCues: cues.length > 0 };
16930
16782
  }
16931
16783
  var SPEED_OPTIONS = [0.5, 0.75, 1, 1.25, 1.5, 2];
16932
- var LAZY_MOUNT_PLAY_FAILURE_GRACE_MS = 2e3;
16933
16784
  function formatTime(secs) {
16934
16785
  if (!secs || !isFinite(secs)) return "0:00";
16935
16786
  const h = Math.floor(secs / 3600);
@@ -16951,14 +16802,14 @@ var VideoPlayer = ({
16951
16802
  srtContent,
16952
16803
  captionsUrl,
16953
16804
  subtitleLabel,
16954
- lazyMount = false
16805
+ preloadStrategy = "metadata"
16955
16806
  }) => {
16956
16807
  const [hasError, setHasError] = useState35(false);
16957
16808
  const [isPlaying, setIsPlaying] = useState35(autoPlay);
16958
16809
  const [mounted, setMounted] = useState35(false);
16959
16810
  const [hasStarted, setHasStarted] = useState35(autoPlay);
16960
- const playerRef = useRef21(null);
16961
- const containerRef = useRef21(null);
16811
+ const playerRef = useRef22(null);
16812
+ const containerRef = useRef22(null);
16962
16813
  const [played, setPlayed] = useState35(0);
16963
16814
  const [loaded, setLoaded] = useState35(0);
16964
16815
  const [duration, setDuration] = useState35(0);
@@ -16967,14 +16818,13 @@ var VideoPlayer = ({
16967
16818
  const [isMuted, setIsMuted] = useState35(muted);
16968
16819
  const [isBuffering, setIsBuffering] = useState35(false);
16969
16820
  const [showControls, setShowControls] = useState35(true);
16970
- const hideTimeoutRef = useRef21(void 0);
16971
- const clickTimerRef = useRef21(void 0);
16972
- const iosFullscreenTimerRef = useRef21(void 0);
16973
- const lazyMountFailureTimerRef = useRef21(void 0);
16821
+ const hideTimeoutRef = useRef22(void 0);
16822
+ const clickTimerRef = useRef22(void 0);
16823
+ const iosFullscreenTimerRef = useRef22(void 0);
16974
16824
  const [captionsEnabled, setCaptionsEnabled] = useState35(true);
16975
16825
  const [isFullscreen, setIsFullscreen] = useState35(false);
16976
16826
  const { activeText, updateTime, hasCues } = useSubtitleOverlay(srtContent);
16977
- useEffect26(() => {
16827
+ useEffect25(() => {
16978
16828
  const onChange = () => {
16979
16829
  const fsEl = document.fullscreenElement || document.webkitFullscreenElement;
16980
16830
  setIsFullscreen(!!fsEl);
@@ -17088,7 +16938,7 @@ var VideoPlayer = ({
17088
16938
  return next;
17089
16939
  });
17090
16940
  }, [hasStarted, isPlaying]);
17091
- useEffect26(() => {
16941
+ useEffect25(() => {
17092
16942
  if (!hasStarted) return;
17093
16943
  const el = containerRef.current;
17094
16944
  if (!el) return;
@@ -17152,9 +17002,9 @@ var VideoPlayer = ({
17152
17002
  return SPEED_OPTIONS[(idx + 1) % SPEED_OPTIONS.length];
17153
17003
  });
17154
17004
  }, []);
17155
- const progressBarRef = useRef21(null);
17156
- const isDraggingRef = useRef21(false);
17157
- const dragListenersRef = useRef21(null);
17005
+ const progressBarRef = useRef22(null);
17006
+ const isDraggingRef = useRef22(false);
17007
+ const dragListenersRef = useRef22(null);
17158
17008
  const seekToClientX = useCallback19((clientX) => {
17159
17009
  const rect = progressBarRef.current?.getBoundingClientRect();
17160
17010
  if (!rect) return;
@@ -17224,7 +17074,7 @@ var VideoPlayer = ({
17224
17074
  const x = Math.max(30, Math.min(rect.width - 30, e.clientX - rect.left));
17225
17075
  setSeekPreview({ fraction, x });
17226
17076
  }, []);
17227
- const isTouchRef = useRef21(false);
17077
+ const isTouchRef = useRef22(false);
17228
17078
  const handleContainerClick = useCallback19((e) => {
17229
17079
  if (isTouchRef.current) {
17230
17080
  isTouchRef.current = false;
@@ -17249,16 +17099,14 @@ var VideoPlayer = ({
17249
17099
  if (!hasStarted) return;
17250
17100
  handleTouchToggle();
17251
17101
  }, [hasStarted, handleTouchToggle]);
17252
- const extractedPoster = useVideoFirstFramePoster(url, !lazyMount && !hasStarted && !poster);
17253
- const effectivePoster = poster || extractedPoster || void 0;
17102
+ const effectivePoster = poster || void 0;
17254
17103
  const posterBgColor = useImageEdgeColor(effectivePoster);
17255
- useEffect26(() => {
17104
+ useEffect25(() => {
17256
17105
  setMounted(true);
17257
17106
  return () => {
17258
17107
  clearTimeout(clickTimerRef.current);
17259
17108
  clearTimeout(hideTimeoutRef.current);
17260
17109
  clearTimeout(iosFullscreenTimerRef.current);
17261
- clearTimeout(lazyMountFailureTimerRef.current);
17262
17110
  isDraggingRef.current = false;
17263
17111
  if (dragListenersRef.current) {
17264
17112
  document.removeEventListener("mousemove", dragListenersRef.current.move);
@@ -17267,7 +17115,7 @@ var VideoPlayer = ({
17267
17115
  }
17268
17116
  };
17269
17117
  }, []);
17270
- useEffect26(() => {
17118
+ useEffect25(() => {
17271
17119
  if (!hasStarted) return;
17272
17120
  const video = playerRef.current?.getInternalPlayer();
17273
17121
  if (!video) return;
@@ -17283,22 +17131,16 @@ var VideoPlayer = ({
17283
17131
  const handlePause = useCallback19(() => setIsPlaying(false), []);
17284
17132
  const handleEnded = useCallback19(() => setIsPlaying(false), []);
17285
17133
  const handlePlayClick = useCallback19(() => {
17286
- if (lazyMount) {
17287
- const native = playerRef.current?.getInternalPlayer();
17288
- if (native instanceof HTMLVideoElement) {
17289
- native.play().catch(() => {
17290
- clearTimeout(lazyMountFailureTimerRef.current);
17291
- lazyMountFailureTimerRef.current = setTimeout(() => {
17292
- if (native.paused && native.error) setHasError(true);
17293
- }, LAZY_MOUNT_PLAY_FAILURE_GRACE_MS);
17294
- });
17295
- } else if (process.env.NODE_ENV !== "production") {
17296
- console.warn("[VideoPlayer] lazyMount sync play(): no native HTMLVideoElement yet");
17297
- }
17134
+ const native = playerRef.current?.getInternalPlayer();
17135
+ if (native instanceof HTMLVideoElement) {
17136
+ native.play().catch(() => {
17137
+ });
17138
+ } else if (process.env.NODE_ENV !== "production") {
17139
+ console.warn("[VideoPlayer] sync play(): no native HTMLVideoElement yet");
17298
17140
  }
17299
17141
  setHasStarted(true);
17300
17142
  setIsPlaying(true);
17301
- }, [lazyMount]);
17143
+ }, []);
17302
17144
  const handleProgress = useCallback19(({ played: p, loaded: l, playedSeconds }) => {
17303
17145
  setPlayed(p);
17304
17146
  setLoaded(l);
@@ -17352,7 +17194,7 @@ var VideoPlayer = ({
17352
17194
  /* @__PURE__ */ jsx139("div", { className: `absolute inset-0 ${effectivePoster ? "bg-black/40" : "bg-black/20"} group-hover:bg-black/50 transition-all flex items-center justify-center rounded-md`, children: /* @__PURE__ */ jsx139("div", { className: "w-16 h-16 rounded-full bg-ods-accent hover:bg-ods-accent/90 transition-all flex items-center justify-center shadow-lg", children: /* @__PURE__ */ jsx139(PlayIcon, { size: 24, className: "ml-1 text-ods-text-on-accent" }) }) })
17353
17195
  ] }),
17354
17196
  /* @__PURE__ */ jsx139("div", { className: isFullscreen ? "video-player absolute inset-0" : useNativeAspectRatio ? "video-player rounded-md overflow-hidden border border-ods-border bg-ods-background" : "video-player absolute inset-0 rounded-md overflow-hidden border border-ods-border bg-ods-background", children: /* @__PURE__ */ jsx139(
17355
- ReactPlayer2,
17197
+ ReactPlayer,
17356
17198
  {
17357
17199
  ref: playerRef,
17358
17200
  url,
@@ -17373,7 +17215,7 @@ var VideoPlayer = ({
17373
17215
  onBufferEnd: handleBufferEnd,
17374
17216
  onProgress: handleProgress,
17375
17217
  progressInterval: 200,
17376
- config: { file: { attributes: { controlsList: "nodownload", playsInline: true, preload: lazyMount && !hasStarted ? "none" : hasStarted ? "auto" : "metadata" } } },
17218
+ config: { file: { attributes: { controlsList: "nodownload", playsInline: true, preload: hasStarted ? "auto" : preloadStrategy } } },
17377
17219
  light: false,
17378
17220
  playsinline: true
17379
17221
  }
@@ -18358,7 +18200,7 @@ function ReleaseDetailPage({
18358
18200
  const [deliveryData, setDeliveryData] = useState37(null);
18359
18201
  const [roadmapLoading, setRoadmapLoading] = useState37(false);
18360
18202
  const [deliveryLoading, setDeliveryLoading] = useState37(false);
18361
- useEffect27(() => {
18203
+ useEffect26(() => {
18362
18204
  async function fetchLinkedTasks() {
18363
18205
  if (!release) return;
18364
18206
  try {
@@ -18913,7 +18755,7 @@ function CompactPageLoader({
18913
18755
  }
18914
18756
 
18915
18757
  // src/components/ui/progress-bar.tsx
18916
- import { useEffect as useEffect29, useRef as useRef22, useState as useState39 } from "react";
18758
+ import { useEffect as useEffect28, useRef as useRef23, useState as useState39 } from "react";
18917
18759
  import { jsx as jsx152 } from "react/jsx-runtime";
18918
18760
  var ProgressBar = ({
18919
18761
  progress,
@@ -18929,9 +18771,9 @@ var ProgressBar = ({
18929
18771
  const isMdUp = useMdUp() ?? true;
18930
18772
  const effectiveSegmentWidth = isMdUp ? segmentWidth : mobileSegmentWidth;
18931
18773
  const effectiveHeight = isMdUp ? height : mobileHeight;
18932
- const containerRef = useRef22(null);
18774
+ const containerRef = useRef23(null);
18933
18775
  const [segmentCount, setSegmentCount] = useState39(0);
18934
- useEffect29(() => {
18776
+ useEffect28(() => {
18935
18777
  if (!containerRef.current) return;
18936
18778
  const resizeObserver = new ResizeObserver(() => {
18937
18779
  if (containerRef.current) {
@@ -19755,11 +19597,11 @@ DialogDescription.displayName = DialogPrimitive3.Description.displayName;
19755
19597
  // src/components/ui/modal.tsx
19756
19598
  init_cn();
19757
19599
  import * as React56 from "react";
19758
- import { useEffect as useEffect30 } from "react";
19600
+ import { useEffect as useEffect29 } from "react";
19759
19601
  import { jsx as jsx158, jsxs as jsxs129 } from "react/jsx-runtime";
19760
19602
  var Modal = React56.forwardRef(
19761
19603
  ({ isOpen, onClose, children, className }, ref) => {
19762
- useEffect30(() => {
19604
+ useEffect29(() => {
19763
19605
  const handleKeyDown = (event) => {
19764
19606
  if (event.key === "Escape") {
19765
19607
  onClose();
@@ -19841,13 +19683,13 @@ ModalFooter.displayName = "ModalFooter";
19841
19683
 
19842
19684
  // src/components/ui/modal-v2.tsx
19843
19685
  import * as React57 from "react";
19844
- import { useEffect as useEffect31 } from "react";
19686
+ import { useEffect as useEffect30 } from "react";
19845
19687
  init_cn();
19846
19688
  import { jsx as jsx159, jsxs as jsxs130 } from "react/jsx-runtime";
19847
19689
  var ModalContext = React57.createContext({});
19848
19690
  var Modal2 = React57.forwardRef(
19849
19691
  ({ isOpen, onClose, children, className }, ref) => {
19850
- useEffect31(() => {
19692
+ useEffect30(() => {
19851
19693
  const handleKeyDown = (event) => {
19852
19694
  if (event.key === "Escape") {
19853
19695
  onClose();
@@ -20521,7 +20363,7 @@ function TabContent({
20521
20363
 
20522
20364
  // src/components/ui/tab-navigation.tsx
20523
20365
  init_cn();
20524
- import { useState as useState42, useEffect as useEffect32, useMemo as useMemo12, useRef as useRef24, useCallback as useCallback22 } from "react";
20366
+ import { useState as useState42, useEffect as useEffect31, useMemo as useMemo12, useRef as useRef25, useCallback as useCallback22 } from "react";
20525
20367
  import { useSearchParams as useSearchParams3, useRouter as useRouter5, usePathname as usePathname3 } from "next/navigation";
20526
20368
  import { Fragment as Fragment22, jsx as jsx168, jsxs as jsxs137 } from "react/jsx-runtime";
20527
20369
  function TabNavigation({
@@ -20554,7 +20396,7 @@ function TabNavigation({
20554
20396
  };
20555
20397
  const [internalActiveTab, setInternalActiveTab] = useState42(getInitialTab);
20556
20398
  const activeTab = isUrlSyncEnabled ? internalActiveTab : controlledActiveTab || "";
20557
- useEffect32(() => {
20399
+ useEffect31(() => {
20558
20400
  if (!isUrlSyncEnabled) return;
20559
20401
  const fromUrl = searchParams?.get(paramName) || "";
20560
20402
  const nextTab = validTabIds.has(fromUrl) ? fromUrl : defaultTab || tabs[0]?.id || "";
@@ -20574,7 +20416,7 @@ function TabNavigation({
20574
20416
  controlledOnTabChange?.(tabId);
20575
20417
  }
20576
20418
  };
20577
- const scrollRef = useRef24(null);
20419
+ const scrollRef = useRef25(null);
20578
20420
  const [canScrollLeft, setCanScrollLeft] = useState42(false);
20579
20421
  const [canScrollRight, setCanScrollRight] = useState42(false);
20580
20422
  const updateScrollShadows = useCallback22(() => {
@@ -20583,7 +20425,7 @@ function TabNavigation({
20583
20425
  setCanScrollLeft(el.scrollLeft > 0);
20584
20426
  setCanScrollRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 1);
20585
20427
  }, []);
20586
- useEffect32(() => {
20428
+ useEffect31(() => {
20587
20429
  const el = scrollRef.current;
20588
20430
  if (!el) return;
20589
20431
  updateScrollShadows();
@@ -20772,11 +20614,11 @@ function StatusIndicator({ status, label, href }) {
20772
20614
 
20773
20615
  // src/components/layout/list-page-layout.tsx
20774
20616
  init_cn();
20775
- import { useEffect as useEffect34, useState as useState44 } from "react";
20617
+ import { useEffect as useEffect33, useState as useState44 } from "react";
20776
20618
 
20777
20619
  // src/components/ui/filter-modal.tsx
20778
20620
  init_cn();
20779
- import { useEffect as useEffect33, useState as useState43 } from "react";
20621
+ import { useEffect as useEffect32, useState as useState43 } from "react";
20780
20622
  init_button2();
20781
20623
 
20782
20624
  // src/components/ui/filter-checkbox-item.tsx
@@ -20976,7 +20818,7 @@ function FilterModal({
20976
20818
  return { ...currentFilters };
20977
20819
  });
20978
20820
  const [pendingTags, setPendingTags] = useState43(selectedTags ?? []);
20979
- useEffect33(() => {
20821
+ useEffect32(() => {
20980
20822
  if (isOpen) {
20981
20823
  setSelectedFilters({ ...currentFilters });
20982
20824
  setPendingTags(selectedTags ?? []);
@@ -21122,10 +20964,10 @@ function ListPageLayout({
21122
20964
  const [mobileFilterOpen, setMobileFilterOpen] = useState44(false);
21123
20965
  const [localSearchValue, setLocalSearchValue] = useState44(searchValue);
21124
20966
  const debouncedSearchValue = useDebounce(localSearchValue, 500);
21125
- useEffect34(() => {
20967
+ useEffect33(() => {
21126
20968
  setLocalSearchValue(searchValue);
21127
20969
  }, [searchValue]);
21128
- useEffect34(() => {
20970
+ useEffect33(() => {
21129
20971
  if (debouncedSearchValue !== searchValue) {
21130
20972
  onSearch(debouncedSearchValue);
21131
20973
  }
@@ -23302,7 +23144,7 @@ var ListLoader = (props) => /* @__PURE__ */ jsx206(ContentLoader, { ...props, va
23302
23144
 
23303
23145
  // src/components/ui/table/table.tsx
23304
23146
  init_cn();
23305
- import { useEffect as useEffect35, useRef as useRef27 } from "react";
23147
+ import { useEffect as useEffect34, useRef as useRef28 } from "react";
23306
23148
  init_pagination();
23307
23149
  init_button2();
23308
23150
 
@@ -24053,10 +23895,10 @@ function Table({
24053
23895
  };
24054
23896
  const allSelected = selectedRows.length > 0 && selectedRows.length === data.length;
24055
23897
  const someSelected = selectedRows.length > 0 && selectedRows.length < data.length;
24056
- const sentinelRef = useRef27(null);
24057
- const onLoadMoreRef = useRef27(infiniteScroll?.onLoadMore);
23898
+ const sentinelRef = useRef28(null);
23899
+ const onLoadMoreRef = useRef28(infiniteScroll?.onLoadMore);
24058
23900
  onLoadMoreRef.current = infiniteScroll?.onLoadMore;
24059
- useEffect35(() => {
23901
+ useEffect34(() => {
24060
23902
  if (!infiniteScroll?.hasNextPage || infiniteScroll.isFetchingNextPage) return;
24061
23903
  const sentinel = sentinelRef.current;
24062
23904
  if (!sentinel) return;
@@ -24268,7 +24110,7 @@ import { useMemo as useMemo16 } from "react";
24268
24110
 
24269
24111
  // src/components/ui/query-report-table/query-report-table-header.tsx
24270
24112
  init_cn();
24271
- import { useRef as useRef28, useState as useState47, useCallback as useCallback23 } from "react";
24113
+ import { useRef as useRef29, useState as useState47, useCallback as useCallback23 } from "react";
24272
24114
  import { jsx as jsx217, jsxs as jsxs176 } from "react/jsx-runtime";
24273
24115
  function QueryReportTableHeader({
24274
24116
  columns,
@@ -24297,7 +24139,7 @@ function QueryReportTableHeader({
24297
24139
  );
24298
24140
  }
24299
24141
  function TruncatedHeaderCell({ value, width }) {
24300
- const textRef = useRef28(null);
24142
+ const textRef = useRef29(null);
24301
24143
  const [isTruncated, setIsTruncated] = useState47(false);
24302
24144
  const checkTruncation = useCallback23(() => {
24303
24145
  const el = textRef.current;
@@ -24327,7 +24169,7 @@ function TruncatedHeaderCell({ value, width }) {
24327
24169
 
24328
24170
  // src/components/ui/query-report-table/query-report-table-row.tsx
24329
24171
  init_cn();
24330
- import { useRef as useRef29, useState as useState48, useCallback as useCallback24 } from "react";
24172
+ import { useRef as useRef30, useState as useState48, useCallback as useCallback24 } from "react";
24331
24173
  import { jsx as jsx218, jsxs as jsxs177 } from "react/jsx-runtime";
24332
24174
  function QueryReportTableRow({
24333
24175
  row,
@@ -24373,7 +24215,7 @@ function QueryReportTableRow({
24373
24215
  );
24374
24216
  }
24375
24217
  function TruncatedCell({ value, className }) {
24376
- const textRef = useRef29(null);
24218
+ const textRef = useRef30(null);
24377
24219
  const [isTruncated, setIsTruncated] = useState48(false);
24378
24220
  const checkTruncation = useCallback24(() => {
24379
24221
  const el = textRef.current;
@@ -25110,7 +24952,7 @@ function DataTableBody({
25110
24952
  }
25111
24953
 
25112
24954
  // src/components/ui/data-table/data-table-infinite-footer.tsx
25113
- import { useEffect as useEffect36, useRef as useRef30 } from "react";
24955
+ import { useEffect as useEffect35, useRef as useRef31 } from "react";
25114
24956
  import { Fragment as Fragment34, jsx as jsx228, jsxs as jsxs186 } from "react/jsx-runtime";
25115
24957
  function DataTableInfiniteFooter({
25116
24958
  hasNextPage,
@@ -25119,10 +24961,10 @@ function DataTableInfiniteFooter({
25119
24961
  skeletonRows = 3,
25120
24962
  rootMargin = "200px"
25121
24963
  }) {
25122
- const sentinelRef = useRef30(null);
25123
- const onLoadMoreRef = useRef30(onLoadMore);
24964
+ const sentinelRef = useRef31(null);
24965
+ const onLoadMoreRef = useRef31(onLoadMore);
25124
24966
  onLoadMoreRef.current = onLoadMore;
25125
- useEffect36(() => {
24967
+ useEffect35(() => {
25126
24968
  if (!hasNextPage || isFetchingNextPage) return;
25127
24969
  const sentinel = sentinelRef.current;
25128
24970
  if (!sentinel) return;
@@ -25247,7 +25089,7 @@ var DataTable = Object.assign(DataTableRoot, {
25247
25089
  });
25248
25090
 
25249
25091
  // src/components/ui/phone-input.tsx
25250
- import { useCallback as useCallback27, useEffect as useEffect37, useMemo as useMemo18, useRef as useRef31, useState as useState49 } from "react";
25092
+ import { useCallback as useCallback27, useEffect as useEffect36, useMemo as useMemo18, useRef as useRef32, useState as useState49 } from "react";
25251
25093
  import { jsx as jsx230, jsxs as jsxs188 } from "react/jsx-runtime";
25252
25094
  function PhoneInput({
25253
25095
  value,
@@ -25265,7 +25107,7 @@ function PhoneInput({
25265
25107
  [countryCode, priority, others]
25266
25108
  );
25267
25109
  const [isInvalid, setIsInvalid] = useState49(false);
25268
- const debounceRef = useRef31(null);
25110
+ const debounceRef = useRef32(null);
25269
25111
  const digitCount = useCallback27((val) => val.replace(/[^0-9]/g, "").length, []);
25270
25112
  const runValidation = useCallback27((phone) => {
25271
25113
  if (!phone || digitCount(phone) === 0) {
@@ -25281,7 +25123,7 @@ function PhoneInput({
25281
25123
  if (debounceRef.current) clearTimeout(debounceRef.current);
25282
25124
  debounceRef.current = setTimeout(() => runValidation(phone), 300);
25283
25125
  }, [runValidation]);
25284
- useEffect37(() => {
25126
+ useEffect36(() => {
25285
25127
  return () => {
25286
25128
  if (debounceRef.current) clearTimeout(debounceRef.current);
25287
25129
  };
@@ -25843,8 +25685,8 @@ function FilterList({
25843
25685
 
25844
25686
  // src/components/ui/tag-search-input.tsx
25845
25687
  import {
25846
- useEffect as useEffect39,
25847
- useRef as useRef33,
25688
+ useEffect as useEffect38,
25689
+ useRef as useRef34,
25848
25690
  useState as useState51
25849
25691
  } from "react";
25850
25692
  init_cn();
@@ -25879,11 +25721,11 @@ function TagSearchInput({
25879
25721
  limitTags,
25880
25722
  placeholder: currentPlaceholder
25881
25723
  });
25882
- const wrapperRef = useRef33(null);
25883
- const hiddenTagsRef = useRef33(null);
25884
- const hiddenTagsPopupRef = useRef33(null);
25724
+ const wrapperRef = useRef34(null);
25725
+ const hiddenTagsRef = useRef34(null);
25726
+ const hiddenTagsPopupRef = useRef34(null);
25885
25727
  const [showHiddenTags, setShowHiddenTags] = useState51(false);
25886
- useEffect39(() => {
25728
+ useEffect38(() => {
25887
25729
  if (!showHiddenTags) return;
25888
25730
  const handleClick = (e) => {
25889
25731
  const target = e.target;
@@ -26039,7 +25881,7 @@ function TagSearchInput({
26039
25881
 
26040
25882
  // src/components/ui/markdown-editor.tsx
26041
25883
  init_cn();
26042
- import { useRef as useRef34, useCallback as useCallback28, useState as useState52, useEffect as useEffect40 } from "react";
25884
+ import { useRef as useRef35, useCallback as useCallback28, useState as useState52, useEffect as useEffect39 } from "react";
26043
25885
  import dynamic from "next/dynamic";
26044
25886
  import { Loader2 as Loader23, Upload as Upload2 } from "lucide-react";
26045
25887
  import { jsx as jsx234, jsxs as jsxs192 } from "react/jsx-runtime";
@@ -26070,7 +25912,7 @@ body .w-md-editor .w-md-editor-bar::after { content: '' !important; display: blo
26070
25912
  body .w-md-editor .w-md-editor-bar:hover::after { border-color: var(--color-text-secondary) !important; }
26071
25913
  `;
26072
25914
  function MarkdownEditorStyles() {
26073
- useEffect40(() => {
25915
+ useEffect39(() => {
26074
25916
  if (document.getElementById(MARKDOWN_EDITOR_STYLE_ID)) return;
26075
25917
  const style = document.createElement("style");
26076
25918
  style.id = MARKDOWN_EDITOR_STYLE_ID;
@@ -26094,11 +25936,11 @@ function MarkdownEditor({
26094
25936
  onFileUploaded,
26095
25937
  renderPreview
26096
25938
  }) {
26097
- const fileInputRef = useRef34(null);
25939
+ const fileInputRef = useRef35(null);
26098
25940
  const [isUploading, setIsUploading] = useState52(false);
26099
25941
  const [uploadProgress, setUploadProgress] = useState52("");
26100
25942
  const [defaultExtraCommands, setDefaultExtraCommands] = useState52([]);
26101
- useEffect40(() => {
25943
+ useEffect39(() => {
26102
25944
  import("@uiw/react-md-editor").then((mod) => {
26103
25945
  if (mod.commands?.getExtraCommands) {
26104
25946
  setDefaultExtraCommands(mod.commands.getExtraCommands());
@@ -26187,11 +26029,11 @@ function MarkdownEditor({
26187
26029
  }
26188
26030
  } : null;
26189
26031
  const extraCommands = uploadCommand ? [...defaultExtraCommands, uploadCommand] : defaultExtraCommands;
26190
- const wrapperRef = useRef34(null);
26191
- const isDraggingRef = useRef34(false);
26192
- const mouseYRef = useRef34(0);
26193
- const rafRef = useRef34(0);
26194
- const scrollParentRef = useRef34(window);
26032
+ const wrapperRef = useRef35(null);
26033
+ const isDraggingRef = useRef35(false);
26034
+ const mouseYRef = useRef35(0);
26035
+ const rafRef = useRef35(0);
26036
+ const scrollParentRef = useRef35(window);
26195
26037
  const EDGE_ZONE = 60;
26196
26038
  const MAX_SCROLL_SPEED = 15;
26197
26039
  const findScrollParent = useCallback28((el) => {
@@ -26221,7 +26063,7 @@ function MarkdownEditor({
26221
26063
  }
26222
26064
  rafRef.current = requestAnimationFrame(scrollLoop);
26223
26065
  }, []);
26224
- useEffect40(() => {
26066
+ useEffect39(() => {
26225
26067
  const wrapper = wrapperRef.current;
26226
26068
  if (!wrapper) return;
26227
26069
  const onMouseMove = (e) => {
@@ -27503,12 +27345,12 @@ function ArrayEntryManager({
27503
27345
  }) {
27504
27346
  const [draftItems, setDraftItems] = useState58(items);
27505
27347
  const [isDirty, setIsDirty] = useState58(false);
27506
- useEffect43(() => {
27348
+ useEffect42(() => {
27507
27349
  if (!isDirty && !isSaving) {
27508
27350
  setDraftItems(items);
27509
27351
  }
27510
27352
  }, [items, isDirty, isSaving]);
27511
- useEffect43(() => {
27353
+ useEffect42(() => {
27512
27354
  if (onDirtyChange) {
27513
27355
  onDirtyChange(isDirty);
27514
27356
  }
@@ -27723,7 +27565,7 @@ function AuthProvidersList({
27723
27565
 
27724
27566
  // src/components/features/changelog-manager.tsx
27725
27567
  import { Trash2 as Trash23, Plus as Plus3, ChevronDown as ChevronDown7, ChevronUp as ChevronUp3, Eye, EyeOff } from "lucide-react";
27726
- import { useState as useState60, useEffect as useEffect44 } from "react";
27568
+ import { useState as useState60, useEffect as useEffect43 } from "react";
27727
27569
  import { jsx as jsx247, jsxs as jsxs204 } from "react/jsx-runtime";
27728
27570
  function ChangelogManager({
27729
27571
  title,
@@ -27734,7 +27576,7 @@ function ChangelogManager({
27734
27576
  showVisibilityToggle = false
27735
27577
  }) {
27736
27578
  const [expandedIndices, setExpandedIndices] = useState60(/* @__PURE__ */ new Set());
27737
- useEffect44(() => {
27579
+ useEffect43(() => {
27738
27580
  if (expandAll && entries.length > 0) {
27739
27581
  setExpandedIndices(new Set(entries.map((_, i) => i)));
27740
27582
  }
@@ -28046,7 +27888,7 @@ var ErrorBoundary = class extends Component {
28046
27888
 
28047
27889
  // src/components/features/figma-prototype-viewer.tsx
28048
27890
  init_cn();
28049
- import { useState as useState61, useRef as useRef37, useEffect as useEffect45, useCallback as useCallback30, useMemo as useMemo22 } from "react";
27891
+ import { useState as useState61, useRef as useRef38, useEffect as useEffect44, useCallback as useCallback30, useMemo as useMemo22 } from "react";
28050
27892
 
28051
27893
  // src/components/features/section-selector.tsx
28052
27894
  init_cn();
@@ -28415,10 +28257,10 @@ var FigmaPrototypeViewer = ({
28415
28257
  }) => {
28416
28258
  const clientId = process.env.NEXT_PUBLIC_FIGMA_CLIENT_ID || "UTQPwZHR9OZp68TTGPFFi5";
28417
28259
  const showDebugPanel = process.env.NEXT_PUBLIC_FIGMA_DEBUG === "true";
28418
- const iframeRef = useRef37(null);
28419
- const containerRef = useRef37(null);
28420
- const navTimerRef = useRef37(null);
28421
- const touchTimerRef = useRef37(null);
28260
+ const iframeRef = useRef38(null);
28261
+ const containerRef = useRef38(null);
28262
+ const navTimerRef = useRef38(null);
28263
+ const touchTimerRef = useRef38(null);
28422
28264
  const [screenWidth, setScreenWidth] = useState61(
28423
28265
  typeof window !== "undefined" ? window.innerWidth : DESKTOP_BREAKPOINT
28424
28266
  );
@@ -28484,7 +28326,7 @@ var FigmaPrototypeViewer = ({
28484
28326
  embedUrl,
28485
28327
  iframeKey
28486
28328
  ]);
28487
- useEffect45(() => {
28329
+ useEffect44(() => {
28488
28330
  const handleResize = () => {
28489
28331
  const newWidth = window.innerWidth;
28490
28332
  setScreenWidth(newWidth);
@@ -28498,7 +28340,7 @@ var FigmaPrototypeViewer = ({
28498
28340
  return () => window.removeEventListener("resize", handleResize);
28499
28341
  }, []);
28500
28342
  const [lastViewMode, setLastViewMode] = useState61(viewMode);
28501
- useEffect45(() => {
28343
+ useEffect44(() => {
28502
28344
  if (lastViewMode !== viewMode && iframeState === "READY") {
28503
28345
  console.log("[ViewMode Change]", lastViewMode, "\u2192", viewMode);
28504
28346
  setIframeState("RELOADING");
@@ -28506,7 +28348,7 @@ var FigmaPrototypeViewer = ({
28506
28348
  }
28507
28349
  setLastViewMode(viewMode);
28508
28350
  }, [viewMode, lastViewMode, iframeState]);
28509
- useEffect45(() => {
28351
+ useEffect44(() => {
28510
28352
  const handleVisibilityChange = () => {
28511
28353
  if (document.visibilityState === "visible" && iframeState === "READY") {
28512
28354
  console.log("[Sleep Recovery] Reloading iframe after sleep");
@@ -28517,7 +28359,7 @@ var FigmaPrototypeViewer = ({
28517
28359
  document.addEventListener("visibilitychange", handleVisibilityChange);
28518
28360
  return () => document.removeEventListener("visibilitychange", handleVisibilityChange);
28519
28361
  }, [iframeState]);
28520
- useEffect45(() => {
28362
+ useEffect44(() => {
28521
28363
  const handleMessage = (event) => {
28522
28364
  if (event.origin !== "https://www.figma.com") return;
28523
28365
  const validEvents = ["INITIAL_LOAD", "NEW_STATE", "PRESENTED_NODE_CHANGED"];
@@ -28601,12 +28443,12 @@ var FigmaPrototypeViewer = ({
28601
28443
  }
28602
28444
  }, 500);
28603
28445
  }, []);
28604
- useEffect45(() => {
28446
+ useEffect44(() => {
28605
28447
  if (externalActiveSection && externalActiveSection !== activeSection && isInitialized) {
28606
28448
  navigateToSection(externalActiveSection);
28607
28449
  }
28608
28450
  }, [externalActiveSection, activeSection, isInitialized, navigateToSection]);
28609
- useEffect45(() => {
28451
+ useEffect44(() => {
28610
28452
  return () => {
28611
28453
  if (navTimerRef.current) clearTimeout(navTimerRef.current);
28612
28454
  if (touchTimerRef.current) clearTimeout(touchTimerRef.current);
@@ -28709,7 +28551,7 @@ var FigmaPrototypeViewer = ({
28709
28551
 
28710
28552
  // src/components/features/filters-dropdown.tsx
28711
28553
  init_cn();
28712
- import { useEffect as useEffect46, useRef as useRef38, useState as useState62 } from "react";
28554
+ import { useEffect as useEffect45, useRef as useRef39, useState as useState62 } from "react";
28713
28555
  import { jsx as jsx253, jsxs as jsxs209 } from "react/jsx-runtime";
28714
28556
  var FilterCheckbox = ({ checked, disabled = false, className }) => {
28715
28557
  return /* @__PURE__ */ jsx253(
@@ -28746,10 +28588,10 @@ var FiltersDropdown = ({
28746
28588
  const [isVisible, setIsVisible] = useState62(false);
28747
28589
  const [isMobile, setIsMobile] = useState62(false);
28748
28590
  const [actualPlacement, setActualPlacement] = useState62(placement);
28749
- const dropdownRef = useRef38(null);
28750
- const triggerRef = useRef38(null);
28751
- const containerRef = useRef38(null);
28752
- useEffect46(() => {
28591
+ const dropdownRef = useRef39(null);
28592
+ const triggerRef = useRef39(null);
28593
+ const containerRef = useRef39(null);
28594
+ useEffect45(() => {
28753
28595
  if (isOpen) {
28754
28596
  setShouldRender(true);
28755
28597
  let id2 = 0;
@@ -28765,7 +28607,7 @@ var FiltersDropdown = ({
28765
28607
  const t = setTimeout(() => setShouldRender(false), ANIMATION_MS);
28766
28608
  return () => clearTimeout(t);
28767
28609
  }, [isOpen]);
28768
- useEffect46(() => {
28610
+ useEffect45(() => {
28769
28611
  if (!responsive) {
28770
28612
  setIsMobile(false);
28771
28613
  return;
@@ -28777,7 +28619,7 @@ var FiltersDropdown = ({
28777
28619
  window.addEventListener("resize", checkMobile);
28778
28620
  return () => window.removeEventListener("resize", checkMobile);
28779
28621
  }, [responsive]);
28780
- useEffect46(() => {
28622
+ useEffect45(() => {
28781
28623
  if (!isOpen || isMobile || !triggerRef.current) return;
28782
28624
  const calculateOptimalPlacement = () => {
28783
28625
  const trigger = triggerRef.current;
@@ -28814,12 +28656,12 @@ var FiltersDropdown = ({
28814
28656
  return initial;
28815
28657
  });
28816
28658
  const currentFiltersStr = currentFilters ? JSON.stringify(currentFilters) : "";
28817
- useEffect46(() => {
28659
+ useEffect45(() => {
28818
28660
  if (currentFilters) {
28819
28661
  setSelectedFilters({ ...currentFilters });
28820
28662
  }
28821
28663
  }, [currentFiltersStr]);
28822
- useEffect46(() => {
28664
+ useEffect45(() => {
28823
28665
  const handleClickOutside = (event) => {
28824
28666
  if (containerRef.current && !containerRef.current.contains(event.target)) {
28825
28667
  setIsOpen(false);
@@ -28836,7 +28678,7 @@ var FiltersDropdown = ({
28836
28678
  };
28837
28679
  }
28838
28680
  }, [isOpen, placement]);
28839
- useEffect46(() => {
28681
+ useEffect45(() => {
28840
28682
  const handleEscape = (e) => {
28841
28683
  if (e.key === "Escape" && isOpen) {
28842
28684
  setIsOpen(false);
@@ -29102,13 +28944,13 @@ function KnowledgeBaseLinksManager({
29102
28944
  }
29103
28945
 
29104
28946
  // src/components/features/loading-provider.tsx
29105
- import { createContext as createContext7, useContext as useContext7, useState as useState63, useEffect as useEffect47 } from "react";
28947
+ import { createContext as createContext7, useContext as useContext7, useState as useState63, useEffect as useEffect46 } from "react";
29106
28948
  import { jsx as jsx256, jsxs as jsxs210 } from "react/jsx-runtime";
29107
28949
  var LoadingContext = createContext7(void 0);
29108
28950
  function LoadingProvider({ children }) {
29109
28951
  const [isLoading, setIsLoading] = useState63(false);
29110
28952
  const [progress, setProgress] = useState63(0);
29111
- useEffect47(() => {
28953
+ useEffect46(() => {
29112
28954
  let interval;
29113
28955
  if (isLoading) {
29114
28956
  setProgress(10);
@@ -29153,7 +28995,7 @@ function useLoading() {
29153
28995
  }
29154
28996
 
29155
28997
  // src/components/features/media-gallery-manager.tsx
29156
- import { useState as useState64, useRef as useRef39, useCallback as useCallback31 } from "react";
28998
+ import { useState as useState64, useRef as useRef40, useCallback as useCallback31 } from "react";
29157
28999
  import {
29158
29000
  Upload as Upload3,
29159
29001
  Image as ImageIcon2,
@@ -29174,7 +29016,7 @@ function MediaGalleryManager({
29174
29016
  modalTitle = "Media Gallery",
29175
29017
  className = ""
29176
29018
  }) {
29177
- const fileInputRef = useRef39(null);
29019
+ const fileInputRef = useRef40(null);
29178
29020
  const [deletingIndex, setDeletingIndex] = useState64(null);
29179
29021
  const [draggedIndex, setDraggedIndex] = useState64(null);
29180
29022
  const handleFileSelect = useCallback31(async (event) => {
@@ -29386,7 +29228,7 @@ function OSTypeBadgeGroup({
29386
29228
  }
29387
29229
 
29388
29230
  // src/components/features/parallax-image-showcase.tsx
29389
- import { useEffect as useEffect48, useState as useState65, useRef as useRef40 } from "react";
29231
+ import { useEffect as useEffect47, useState as useState65, useRef as useRef41 } from "react";
29390
29232
  import Image12 from "next/image";
29391
29233
  import { motion as motion2, useScroll, useTransform, useMotionValue, useSpring } from "framer-motion";
29392
29234
  import { jsx as jsx260, jsxs as jsxs213 } from "react/jsx-runtime";
@@ -29406,8 +29248,8 @@ var ParallaxImageShowcase = ({
29406
29248
  const mouseXSpring = useSpring(mouseX, springConfig);
29407
29249
  const mouseYSpring = useSpring(mouseY, springConfig);
29408
29250
  const [componentRect, setComponentRect] = useState65(null);
29409
- const componentRef = useRef40(null);
29410
- useEffect48(() => {
29251
+ const componentRef = useRef41(null);
29252
+ useEffect47(() => {
29411
29253
  const updateRect = () => {
29412
29254
  if (componentRef.current) {
29413
29255
  setComponentRect(componentRef.current.getBoundingClientRect());
@@ -29421,7 +29263,7 @@ var ParallaxImageShowcase = ({
29421
29263
  window.removeEventListener("scroll", updateRect);
29422
29264
  };
29423
29265
  }, []);
29424
- useEffect48(() => {
29266
+ useEffect47(() => {
29425
29267
  const handleGlobalMouseMove = (e) => {
29426
29268
  if (!componentRect) return;
29427
29269
  const centerX = componentRect.left + componentRect.width / 2;
@@ -30065,7 +29907,7 @@ function PushButtonSelector({
30065
29907
  }
30066
29908
 
30067
29909
  // src/components/features/release-media-manager.tsx
30068
- import { useState as useState66, useRef as useRef41 } from "react";
29910
+ import { useState as useState66, useRef as useRef42 } from "react";
30069
29911
  import { Trash2 as Trash25, Plus as Plus5, Image as ImageIcon3, Video as Video4, Upload as Upload4, Loader2 as Loader28, GripVertical as GripVertical2 } from "lucide-react";
30070
29912
  import Image13 from "next/image";
30071
29913
  import { jsx as jsx265, jsxs as jsxs218 } from "react/jsx-runtime";
@@ -30075,7 +29917,7 @@ function ReleaseMediaManager({
30075
29917
  onUpload,
30076
29918
  className = ""
30077
29919
  }) {
30078
- const fileInputRef = useRef41(null);
29920
+ const fileInputRef = useRef42(null);
30079
29921
  const [uploadingIndex, setUploadingIndex] = useState66(null);
30080
29922
  const handleFileSelect = async (event) => {
30081
29923
  const file = event.target.files?.[0];
@@ -32511,7 +32353,7 @@ function ViewToggle({
32511
32353
 
32512
32354
  // src/components/features/policy-configuration-panel.tsx
32513
32355
  init_cn();
32514
- import { useRef as useRef42, useEffect as useEffect49, useState as useState71 } from "react";
32356
+ import { useRef as useRef43, useEffect as useEffect48, useState as useState71 } from "react";
32515
32357
  import { ChevronDown as ChevronDown8 } from "lucide-react";
32516
32358
  init_button2();
32517
32359
 
@@ -32693,8 +32535,8 @@ var PolicyRow = ({ policy, categoryId, editMode, onPermissionChange }) => {
32693
32535
  };
32694
32536
  var useAnimatedHeight = (isExpanded) => {
32695
32537
  const [height, setHeight] = useState71(0);
32696
- const contentRef = useRef42(null);
32697
- useEffect49(() => {
32538
+ const contentRef = useRef43(null);
32539
+ useEffect48(() => {
32698
32540
  if (contentRef.current) {
32699
32541
  const contentHeight = contentRef.current.scrollHeight;
32700
32542
  setHeight(isExpanded ? contentHeight : 0);
@@ -32850,7 +32692,7 @@ PolicyConfigurationPanel.displayName = "PolicyConfigurationPanel";
32850
32692
  init_button2();
32851
32693
  init_cn();
32852
32694
  import { getCountries as getCountries2 } from "libphonenumber-js";
32853
- import { useEffect as useEffect50, useState as useState72 } from "react";
32695
+ import { useEffect as useEffect49, useState as useState72 } from "react";
32854
32696
  import { Fragment as Fragment41, jsx as jsx292, jsxs as jsxs240 } from "react/jsx-runtime";
32855
32697
  function WaitlistForm({
32856
32698
  id = "waitlist-form",
@@ -32879,12 +32721,12 @@ function WaitlistForm({
32879
32721
  const [isPhoneInvalid, setIsPhoneInvalid] = useState72(false);
32880
32722
  const [showConsentError, setShowConsentError] = useState72(false);
32881
32723
  const isMailDomainGeneric = hasGenericEmailDomain(email);
32882
- useEffect50(() => {
32724
+ useEffect49(() => {
32883
32725
  if (defaultEmail) {
32884
32726
  setEmail(defaultEmail);
32885
32727
  }
32886
32728
  }, [defaultEmail]);
32887
- useEffect50(() => {
32729
+ useEffect49(() => {
32888
32730
  setIsClient(true);
32889
32731
  if (!geoApiUrl) return;
32890
32732
  const supportedCountries = new Set(getCountries2());
@@ -33950,7 +33792,6 @@ export {
33950
33792
  ViewToggle,
33951
33793
  YouTubeEmbed,
33952
33794
  extractYouTubeId,
33953
- YouTubeLinkParser,
33954
33795
  PolicyConfigurationPanel,
33955
33796
  PhoneInput,
33956
33797
  WaitlistForm,
@@ -34345,4 +34186,4 @@ export {
34345
34186
  TMCG_SOCIAL_PLATFORMS,
34346
34187
  assets
34347
34188
  };
34348
- //# sourceMappingURL=chunk-6LDN3CIY.js.map
34189
+ //# sourceMappingURL=chunk-AAX27BCR.js.map