@geekapps/silo-elements-nextjs 0.2.24 → 0.2.27

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.
@@ -1,6 +1,6 @@
1
1
  import React, { useMemo, useRef, useState, useCallback, useEffect } from 'react';
2
2
  import gsap from 'gsap';
3
- import { Play, Rewind, Pause, FastForward, VolumeX, Volume2, Captions, Settings, Minimize, Maximize } from 'lucide-react';
3
+ import { Play, Settings, Captions, Volume2, Pause, VolumeX, Minimize, Maximize } from 'lucide-react';
4
4
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
5
5
 
6
6
  var AUTO_QUALITY = {
@@ -60,7 +60,7 @@ function Video({
60
60
  const [audioTracks, setAudioTracks] = useState([]);
61
61
  const [selectedAudio, setSelectedAudio] = useState(0);
62
62
  const [settingsOpen, setSettingsOpen] = useState(false);
63
- const [settingsTab, setSettingsTab] = useState("quality");
63
+ const [settingsTab, setSettingsTab] = useState("root");
64
64
  const [playbackRate, setPlaybackRate] = useState(1);
65
65
  const [subtitleStyle, setSubtitleStyle] = useState({
66
66
  track: initialSubtitleMode,
@@ -86,6 +86,7 @@ function Video({
86
86
  const [volume, setVolume] = useState(defaultVolume);
87
87
  const [isMuted, setIsMuted] = useState(false);
88
88
  const [isFullscreen, setIsFullscreen] = useState(false);
89
+ const [playerHeight, setPlayerHeight] = useState(0);
89
90
  const [error, setError] = useState(null);
90
91
  const activeSource = parsed.sources[sourceIndex] ?? parsed.sources[0] ?? null;
91
92
  const progressPercent = duration ? currentTime / duration * 100 : 0;
@@ -266,6 +267,16 @@ function Video({
266
267
  video?.removeEventListener("webkitendfullscreen", onFullscreenChange);
267
268
  };
268
269
  }, []);
270
+ useEffect(() => {
271
+ const player = playerRef.current;
272
+ if (!player) return;
273
+ const ro = new ResizeObserver((entries) => {
274
+ const entry = entries[0];
275
+ if (entry) setPlayerHeight(entry.contentRect.height);
276
+ });
277
+ ro.observe(player);
278
+ return () => ro.disconnect();
279
+ }, []);
269
280
  useEffect(() => {
270
281
  let cancelled = false;
271
282
  async function loadStoryboard() {
@@ -733,132 +744,108 @@ function Video({
733
744
  onClick: togglePlay,
734
745
  className: `absolute inset-0 z-30 flex flex-col justify-between transition-opacity duration-200 ${controlsVisible ? "opacity-100" : "opacity-0 pointer-events-none"}`,
735
746
  children: [
736
- /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-x-0 top-0 h-32 bg-linear-to-b from-black/70 to-transparent" }),
747
+ isFullscreen && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-x-0 top-0 h-32 bg-linear-to-b from-black/70 to-transparent" }),
737
748
  /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-x-0 bottom-0 h-40 bg-linear-to-t from-black/80 to-transparent" }),
738
- /* @__PURE__ */ jsxs("header", { onClick: (e) => e.stopPropagation(), className: "relative z-10 flex items-start justify-between gap-4 px-4 pt-4 text-white @sm:px-7 @sm:pt-7 @lg:px-9 @lg:pt-8", children: [
739
- /* @__PURE__ */ jsxs("div", { children: [
740
- title && /* @__PURE__ */ jsx("h1", { className: "text-sm font-bold tracking-wide @sm:text-base @md:text-lg @lg:text-xl", style: { textShadow: "0 1px 6px rgba(0,0,0,0.6)" }, children: title }),
741
- description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs font-medium text-white/85 @sm:text-sm", children: description })
742
- ] }),
743
- parsed.sources.length > 1 && /* @__PURE__ */ jsx(
744
- "select",
745
- {
746
- value: String(sourceIndex),
747
- onChange: (e) => setSourceIndex(Number(e.target.value)),
748
- "aria-label": "Video source",
749
- className: "h-8 rounded-full border border-white/15 bg-white/10 px-3 text-xs font-semibold text-white outline-none backdrop-blur-md transition hover:bg-white/15",
750
- children: parsed.sources.map((source, index) => /* @__PURE__ */ jsx("option", { value: String(index), className: "text-black", children: source.label ?? source.type ?? `Source ${index + 1}` }, `${source.src}-${index}`))
751
- }
752
- )
753
- ] }),
754
- /* @__PURE__ */ jsxs("footer", { onClick: (e) => e.stopPropagation(), className: "relative z-10 px-3 pb-3 text-white @sm:px-5 @sm:pb-5 @lg:px-9 @lg:pb-8", children: [
749
+ isFullscreen && /* @__PURE__ */ jsx("header", { onClick: (e) => e.stopPropagation(), className: "relative z-10 flex items-start px-4 pt-4 text-white @sm:px-7 @sm:pt-7 @lg:px-9 @lg:pt-8", children: /* @__PURE__ */ jsxs("div", { children: [
750
+ title && /* @__PURE__ */ jsx("h1", { className: "text-sm font-bold tracking-wide @sm:text-base @md:text-lg @lg:text-xl", style: { textShadow: "0 1px 6px rgba(0,0,0,0.6)" }, children: title }),
751
+ description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs font-medium text-white/85 @sm:text-sm", children: description })
752
+ ] }) }),
753
+ /* @__PURE__ */ jsxs("footer", { onClick: (e) => e.stopPropagation(), className: "relative z-10 px-4 pb-4 text-white", children: [
755
754
  settingsOpen && /* @__PURE__ */ jsxs(Fragment, { children: [
756
755
  /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-40", onClick: () => setSettingsOpen(false) }),
757
- /* @__PURE__ */ jsxs("div", { className: "absolute bottom-full right-3 z-50 mb-2 w-52 overflow-hidden rounded-xl border border-white/10 bg-black/90 shadow-2xl backdrop-blur-xl @sm:right-5 @lg:right-9", children: [
758
- /* @__PURE__ */ jsx("div", { className: "flex border-b border-white/10", children: ["quality", ...parsed.subtitles.length > 0 ? ["subtitles"] : [], ...audioTracks.length > 1 ? ["audio"] : [], "playback"].map((tab) => /* @__PURE__ */ jsx(
759
- "button",
760
- {
761
- type: "button",
762
- onClick: () => setSettingsTab(tab),
763
- className: `flex-1 px-3 py-2.5 text-xs font-semibold capitalize transition ${settingsTab === tab ? "text-white border-b-2 border-white -mb-px" : "text-white/50 hover:text-white/80"}`,
764
- children: tab
765
- },
766
- tab
767
- )) }),
768
- /* @__PURE__ */ jsxs("div", { className: "max-h-48 overflow-y-auto py-1", children: [
769
- settingsTab === "quality" && /* @__PURE__ */ jsx(Fragment, { children: [...qualities].reverse().map((quality) => /* @__PURE__ */ jsxs(
770
- SettingsItem,
756
+ /* @__PURE__ */ jsxs("div", { className: "absolute bottom-full right-0 z-50 mb-3 flex flex-col gap-2.5", style: { animation: "spFadeIn 0.15s ease" }, children: [
757
+ /* @__PURE__ */ jsxs("div", { className: "w-52 overflow-hidden rounded-2xl bg-[#1a1a1a]/95 shadow-2xl backdrop-blur-xl ring-1 ring-white/8", children: [
758
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 px-4 py-3 border-b border-white/8", children: [
759
+ /* @__PURE__ */ jsx(Settings, { className: "size-4 text-white/60" }),
760
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-white", children: "Video Quality" })
761
+ ] }),
762
+ /* @__PURE__ */ jsx("div", { className: "py-1.5", children: [...qualities].reverse().map((quality) => /* @__PURE__ */ jsxs(
763
+ "button",
771
764
  {
772
- active: selectedQuality === quality.id,
765
+ type: "button",
773
766
  onClick: () => changeQuality(quality.id),
767
+ className: "flex w-full items-center gap-3 px-4 py-2 text-sm transition hover:bg-white/8",
774
768
  children: [
775
- quality.label,
776
- quality.id === "auto" && /* @__PURE__ */ jsx("span", { className: "ml-1 text-[10px] text-white/40", children: "ABR" })
769
+ /* @__PURE__ */ jsx("span", { className: `size-4 shrink-0 ${selectedQuality === quality.id ? "text-white" : "text-transparent"}`, children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M3 8l3.5 3.5L13 4.5", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }) }) }),
770
+ /* @__PURE__ */ jsxs("span", { className: selectedQuality === quality.id ? "font-semibold text-white" : "text-white/55", children: [
771
+ quality.label,
772
+ quality.id === "auto" ? " (ABR)" : ""
773
+ ] })
777
774
  ]
778
775
  },
779
776
  quality.id
780
- )) }),
781
- settingsTab === "subtitles" && /* @__PURE__ */ jsxs("div", { className: "px-3 py-2 space-y-3", children: [
782
- /* @__PURE__ */ jsxs("div", { children: [
783
- /* @__PURE__ */ jsx("p", { className: "mb-1 text-[10px] font-semibold uppercase tracking-wider text-white/40", children: "Track" }),
784
- /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: [{ srclang: "off", label: "Off" }, ...parsed.subtitles].map((s) => /* @__PURE__ */ jsx(
785
- "button",
786
- {
787
- type: "button",
788
- onClick: () => {
789
- const next = s.srclang === "off" ? "off" : s.srclang;
790
- setSubtitleMode(next);
791
- setSubtitleStyle((st) => ({ ...st, track: next }));
792
- },
793
- className: `rounded-md px-2 py-1 text-xs font-medium transition ${subtitleStyle.track === s.srclang ? "bg-white text-black" : "bg-white/10 text-white hover:bg-white/20"}`,
794
- children: s.label
795
- },
796
- s.srclang
797
- )) })
777
+ )) })
778
+ ] }),
779
+ (parsed.subtitles.length > 0 || audioTracks.length > 1) && /* @__PURE__ */ jsx("div", { className: `overflow-hidden rounded-2xl bg-[#1a1a1a]/95 shadow-2xl backdrop-blur-xl ring-1 ring-white/8 ${parsed.subtitles.length > 0 && audioTracks.length > 1 ? "w-80" : "w-52"}`, children: /* @__PURE__ */ jsxs("div", { className: `grid ${parsed.subtitles.length > 0 && audioTracks.length > 1 ? "grid-cols-2 divide-x divide-white/8" : "grid-cols-1"}`, children: [
780
+ parsed.subtitles.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
781
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 px-4 py-3 border-b border-white/8", children: [
782
+ /* @__PURE__ */ jsx(Captions, { className: "size-4 text-white/60" }),
783
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-white", children: "Subtitles" })
798
784
  ] }),
799
- /* @__PURE__ */ jsxs("div", { children: [
800
- /* @__PURE__ */ jsx("p", { className: "mb-1 text-[10px] font-semibold uppercase tracking-wider text-white/40", children: "Size" }),
801
- /* @__PURE__ */ jsx("div", { className: "flex gap-1", children: ["small", "medium", "large"].map((s) => /* @__PURE__ */ jsx(
802
- "button",
803
- {
804
- type: "button",
805
- onClick: () => setSubtitleStyle((st) => ({ ...st, size: s })),
806
- className: `rounded-md px-2 py-1 text-xs font-medium capitalize transition ${subtitleStyle.size === s ? "bg-white text-black" : "bg-white/10 text-white hover:bg-white/20"}`,
807
- children: s
785
+ /* @__PURE__ */ jsx("div", { className: "py-1.5", children: [{ srclang: "off", label: "Off" }, ...parsed.subtitles].map((s) => /* @__PURE__ */ jsxs(
786
+ "button",
787
+ {
788
+ type: "button",
789
+ onClick: () => {
790
+ const next = s.srclang === "off" ? "off" : s.srclang;
791
+ setSubtitleMode(next);
792
+ setSubtitleStyle((st) => ({ ...st, track: next }));
808
793
  },
809
- s
810
- )) })
794
+ className: "flex w-full items-center gap-3 px-4 py-2 text-sm transition hover:bg-white/8",
795
+ children: [
796
+ /* @__PURE__ */ jsx("span", { className: `size-4 shrink-0 ${subtitleStyle.track === s.srclang ? "text-white" : "text-transparent"}`, children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M3 8l3.5 3.5L13 4.5", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }) }) }),
797
+ /* @__PURE__ */ jsx("span", { className: subtitleStyle.track === s.srclang ? "font-semibold text-white" : "text-white/55", children: s.label })
798
+ ]
799
+ },
800
+ s.srclang
801
+ )) })
802
+ ] }),
803
+ audioTracks.length > 1 && /* @__PURE__ */ jsxs("div", { children: [
804
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 px-4 py-3 border-b border-white/8", children: [
805
+ /* @__PURE__ */ jsx(Volume2, { className: "size-4 text-white/60" }),
806
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-white", children: "Audio" })
811
807
  ] }),
812
- /* @__PURE__ */ jsxs("div", { children: [
813
- /* @__PURE__ */ jsx("p", { className: "mb-1 text-[10px] font-semibold uppercase tracking-wider text-white/40", children: "Color" }),
814
- /* @__PURE__ */ jsx("div", { className: "flex gap-2", children: [["white", "#fff"], ["yellow", "#facc15"], ["cyan", "#22d3ee"]].map(([c, hex]) => /* @__PURE__ */ jsx(
815
- "button",
816
- {
817
- type: "button",
818
- onClick: () => setSubtitleStyle((st) => ({ ...st, color: c })),
819
- className: `size-5 rounded-full ring-2 transition ${subtitleStyle.color === c ? "ring-white" : "ring-transparent"}`,
820
- style: { backgroundColor: hex }
821
- },
822
- c
823
- )) })
808
+ /* @__PURE__ */ jsx("div", { className: "py-1.5", children: audioTracks.map((track) => /* @__PURE__ */ jsxs(
809
+ "button",
810
+ {
811
+ type: "button",
812
+ onClick: () => changeAudio(track.id),
813
+ className: "flex w-full items-center gap-3 px-4 py-2 text-sm transition hover:bg-white/8",
814
+ children: [
815
+ /* @__PURE__ */ jsx("span", { className: `size-4 shrink-0 ${selectedAudio === track.id ? "text-white" : "text-transparent"}`, children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M3 8l3.5 3.5L13 4.5", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }) }) }),
816
+ /* @__PURE__ */ jsx("span", { className: selectedAudio === track.id ? "font-semibold text-white" : "text-white/55", children: track.label })
817
+ ]
818
+ },
819
+ track.id
820
+ )) })
821
+ ] })
822
+ ] }) }),
823
+ /* @__PURE__ */ jsxs("div", { className: "w-64 overflow-hidden rounded-2xl bg-[#1a1a1a]/95 shadow-2xl backdrop-blur-xl ring-1 ring-white/8", children: [
824
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 px-4 py-3 border-b border-white/8", children: [
825
+ /* @__PURE__ */ jsxs("svg", { className: "size-4 text-white/60", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
826
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "9" }),
827
+ /* @__PURE__ */ jsx("path", { d: "M12 12l-2-4M12 12l3.5-1.5", strokeLinecap: "round" })
824
828
  ] }),
825
- /* @__PURE__ */ jsxs("div", { children: [
826
- /* @__PURE__ */ jsx("p", { className: "mb-1 text-[10px] font-semibold uppercase tracking-wider text-white/40", children: "Background" }),
827
- /* @__PURE__ */ jsx("div", { className: "flex gap-1", children: [["none", "None"], ["semi", "Semi"], ["solid", "Solid"]].map(([v, label]) => /* @__PURE__ */ jsx(
828
- "button",
829
- {
830
- type: "button",
831
- onClick: () => setSubtitleStyle((st) => ({ ...st, bg: v })),
832
- className: `rounded-md px-2 py-1 text-xs font-medium transition ${subtitleStyle.bg === v ? "bg-white text-black" : "bg-white/10 text-white hover:bg-white/20"}`,
833
- children: label
834
- },
835
- v
836
- )) })
837
- ] })
829
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-white", children: "Playback Speed" })
838
830
  ] }),
839
- settingsTab === "audio" && audioTracks.length > 1 && /* @__PURE__ */ jsx(Fragment, { children: audioTracks.map((track) => /* @__PURE__ */ jsx(
840
- SettingsItem,
841
- {
842
- active: selectedAudio === track.id,
843
- onClick: () => changeAudio(track.id),
844
- children: track.label
845
- },
846
- track.id
847
- )) }),
848
- settingsTab === "playback" && /* @__PURE__ */ jsx(Fragment, { children: PLAYBACK_SPEEDS.map((speed) => /* @__PURE__ */ jsx(
849
- SettingsItem,
850
- {
851
- active: playbackRate === speed,
852
- onClick: () => {
853
- setPlaybackRate(speed);
854
- setSettingsOpen(false);
855
- },
856
- children: speed === 1 ? "Normal" : `${speed}x`
857
- },
858
- speed
859
- )) })
831
+ /* @__PURE__ */ jsxs("div", { className: "px-4 pb-4 pt-3", children: [
832
+ /* @__PURE__ */ jsx(
833
+ "input",
834
+ {
835
+ type: "range",
836
+ min: 0,
837
+ max: PLAYBACK_SPEEDS.length - 1,
838
+ step: 1,
839
+ value: PLAYBACK_SPEEDS.indexOf(playbackRate) === -1 ? PLAYBACK_SPEEDS.indexOf(1) : PLAYBACK_SPEEDS.indexOf(playbackRate),
840
+ onChange: (e) => setPlaybackRate(PLAYBACK_SPEEDS[Number(e.target.value)] ?? 1),
841
+ className: "w-full accent-white"
842
+ }
843
+ ),
844
+ /* @__PURE__ */ jsx("div", { className: "mt-1.5 flex justify-between", children: PLAYBACK_SPEEDS.map((s) => /* @__PURE__ */ jsx("span", { className: `text-[10px] font-medium ${playbackRate === s ? "text-white font-bold" : "text-white/40"}`, children: s === 1 ? "1x" : `${s}x` }, s)) })
845
+ ] })
860
846
  ] })
861
- ] })
847
+ ] }),
848
+ /* @__PURE__ */ jsx("style", { children: `@keyframes spFadeIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}` })
862
849
  ] }),
863
850
  /* @__PURE__ */ jsxs(
864
851
  "div",
@@ -867,12 +854,12 @@ function Video({
867
854
  onPointerMove: handleProgressPointerMove,
868
855
  onPointerLeave: handleProgressPointerLeave,
869
856
  onPointerDown: handleProgressPointerDown,
870
- className: "relative mb-3 h-5 cursor-pointer @sm:mb-4 @lg:mb-6",
857
+ className: "group relative mb-2 h-5 cursor-pointer",
871
858
  children: [
872
859
  preview && /* @__PURE__ */ jsxs(
873
860
  "div",
874
861
  {
875
- className: "pointer-events-none absolute bottom-8 z-20 -translate-x-1/2 rounded-lg bg-black/80 p-1 shadow-2xl ring-1 ring-white/15 backdrop-blur",
862
+ className: "pointer-events-none absolute bottom-8 z-50 -translate-x-1/2 rounded-lg bg-black/80 p-1 shadow-2xl ring-1 ring-white/15 backdrop-blur",
876
863
  style: { left: preview.left },
877
864
  children: [
878
865
  /* @__PURE__ */ jsx(
@@ -893,7 +880,7 @@ function Video({
893
880
  ]
894
881
  }
895
882
  ),
896
- /* @__PURE__ */ jsxs("div", { className: "absolute left-0 right-0 top-1/2 h-1 -translate-y-1/2 overflow-hidden rounded-full bg-white/22 @sm:h-1.25", children: [
883
+ /* @__PURE__ */ jsxs("div", { className: "absolute left-0 right-0 top-1/2 -translate-y-1/2 overflow-hidden rounded-full bg-white/22 transition-[height] duration-150 group-hover:h-1", style: { height: "2px" }, children: [
897
884
  /* @__PURE__ */ jsx(
898
885
  "div",
899
886
  {
@@ -912,42 +899,27 @@ function Video({
912
899
  ]
913
900
  }
914
901
  ),
915
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 @sm:gap-4 @lg:gap-5", children: [
916
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 @sm:gap-4 @lg:gap-5", children: [
917
- /* @__PURE__ */ jsx(
918
- "button",
919
- {
920
- type: "button",
921
- onClick: () => seekRelative(-10),
922
- className: "grid size-6 place-items-center text-white transition hover:scale-105 hover:text-white/80 @sm:size-8",
923
- "aria-label": "Rewind 10 seconds",
924
- children: /* @__PURE__ */ jsx(Rewind, { className: "size-4 @sm:size-6" })
925
- }
926
- ),
902
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
903
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 @sm:gap-2", children: [
904
+ /* @__PURE__ */ jsx(SeekButton, { seconds: 15, direction: "back", onClick: () => seekRelative(-15) }),
927
905
  /* @__PURE__ */ jsx(
928
906
  "button",
929
907
  {
930
908
  type: "button",
931
909
  onClick: togglePlay,
932
- className: "grid size-6 place-items-center text-white transition hover:scale-105 hover:text-white/80 @sm:size-8",
910
+ className: "grid size-8 place-items-center text-white transition hover:scale-110 hover:text-white/80 @sm:size-10",
933
911
  "aria-label": isPlaying ? "Pause" : "Play",
934
- children: isPlaying ? /* @__PURE__ */ jsx(Pause, { className: "size-4 @sm:size-6" }) : /* @__PURE__ */ jsx(Play, { className: "size-4 @sm:size-6" })
912
+ children: isPlaying ? /* @__PURE__ */ jsx(Pause, { className: "size-5 @sm:size-6" }) : /* @__PURE__ */ jsx(Play, { className: "ml-0.5 size-5 @sm:size-6" })
935
913
  }
936
914
  ),
937
- /* @__PURE__ */ jsx(
938
- "button",
939
- {
940
- type: "button",
941
- onClick: () => seekRelative(10),
942
- className: "grid size-6 place-items-center text-white transition hover:scale-105 hover:text-white/80 @sm:size-8",
943
- "aria-label": "Forward 10 seconds",
944
- children: /* @__PURE__ */ jsx(FastForward, { className: "size-4 @sm:size-6" })
945
- }
946
- ),
947
- /* @__PURE__ */ jsxs("div", { className: "hidden items-center gap-1 text-xs font-semibold text-white/75 @sm:flex @sm:gap-2 @sm:text-sm", children: [
915
+ /* @__PURE__ */ jsx(SeekButton, { seconds: 15, direction: "forward", onClick: () => seekRelative(15) }),
916
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 text-xs font-semibold text-white/75 @sm:text-sm", children: [
948
917
  /* @__PURE__ */ jsx("span", { children: formatTime(currentTime) }),
949
918
  /* @__PURE__ */ jsx("span", { className: "text-white/35", children: "/" }),
950
- /* @__PURE__ */ jsx("span", { children: formatTime(duration) })
919
+ /* @__PURE__ */ jsxs("span", { className: "text-white/50", children: [
920
+ "-",
921
+ formatTime(Math.max(0, duration - currentTime))
922
+ ] })
951
923
  ] })
952
924
  ] }),
953
925
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5 @sm:gap-1", children: [
@@ -956,9 +928,9 @@ function Video({
956
928
  {
957
929
  type: "button",
958
930
  onClick: () => setIsMuted((value) => !value),
959
- className: "grid size-6 place-items-center text-white transition hover:scale-105 hover:text-white/80 @sm:size-8",
931
+ className: "grid size-8 place-items-center text-white transition hover:scale-110 hover:text-white/80 @sm:size-10",
960
932
  "aria-label": isMuted ? "Unmute" : "Mute",
961
- children: isMuted || volume === 0 ? /* @__PURE__ */ jsx(VolumeX, { className: "size-4 @sm:size-6" }) : /* @__PURE__ */ jsx(Volume2, { className: "size-4 @sm:size-6" })
933
+ children: isMuted || volume === 0 ? /* @__PURE__ */ jsx(VolumeX, { className: "size-4 @sm:size-5" }) : /* @__PURE__ */ jsx(Volume2, { className: "size-4 @sm:size-5" })
962
934
  }
963
935
  ),
964
936
  /* @__PURE__ */ jsx(
@@ -984,22 +956,22 @@ function Video({
984
956
  {
985
957
  type: "button",
986
958
  onClick: () => setSubtitleMode((m) => m === "off" ? parsed.subtitles[0]?.srclang ?? "off" : "off"),
987
- className: `grid size-6 place-items-center rounded transition hover:text-white/80 @sm:size-8 ${subtitleMode !== "off" ? "text-white" : "text-white/60"}`,
959
+ className: `grid size-8 place-items-center rounded transition hover:text-white/80 @sm:size-10 ${subtitleMode !== "off" ? "text-white" : "text-white/60"}`,
988
960
  "aria-label": "Captions",
989
961
  children: /* @__PURE__ */ jsx(Captions, { className: "size-4 @sm:size-5" })
990
962
  }
991
963
  ),
992
- /* @__PURE__ */ jsx("div", { className: "relative", children: /* @__PURE__ */ jsxs(
964
+ /* @__PURE__ */ jsx("div", { className: "relative", children: /* @__PURE__ */ jsx(
993
965
  "button",
994
966
  {
995
967
  type: "button",
996
- onClick: () => setSettingsOpen((v) => !v),
997
- className: `flex h-6 items-center gap-1 rounded px-1.5 text-xs font-semibold transition hover:text-white/80 @sm:h-8 @sm:px-2 ${settingsOpen ? "text-white" : "text-white/60"}`,
968
+ onClick: () => {
969
+ setSettingsOpen((v) => !v);
970
+ setSettingsTab("root");
971
+ },
972
+ className: `grid size-8 place-items-center rounded transition hover:text-white/80 @sm:size-10 ${settingsOpen ? "text-white" : "text-white/60"}`,
998
973
  "aria-label": "Settings",
999
- children: [
1000
- /* @__PURE__ */ jsx(Settings, { className: "size-3.5 @sm:size-4" }),
1001
- /* @__PURE__ */ jsx("span", { className: "hidden @sm:inline", children: qualities.find((q) => q.id === selectedQuality)?.label ?? "Auto" })
1002
- ]
974
+ children: /* @__PURE__ */ jsx(Settings, { className: "size-4 @sm:size-5" })
1003
975
  }
1004
976
  ) }),
1005
977
  /* @__PURE__ */ jsx(
@@ -1007,9 +979,9 @@ function Video({
1007
979
  {
1008
980
  type: "button",
1009
981
  onClick: toggleFullscreen,
1010
- className: "grid size-6 place-items-center text-white/60 transition hover:scale-105 hover:text-white/80 @sm:size-8",
982
+ className: "grid size-8 place-items-center text-white/60 transition hover:scale-110 hover:text-white/80 @sm:size-10",
1011
983
  "aria-label": isFullscreen ? "Exit fullscreen" : "Fullscreen",
1012
- children: isFullscreen ? /* @__PURE__ */ jsx(Minimize, { className: "size-5 @sm:size-7" }) : /* @__PURE__ */ jsx(Maximize, { className: "size-5 @sm:size-7" })
984
+ children: isFullscreen ? /* @__PURE__ */ jsx(Minimize, { className: "size-4 @sm:size-5" }) : /* @__PURE__ */ jsx(Maximize, { className: "size-4 @sm:size-5" })
1013
985
  }
1014
986
  )
1015
987
  ] })
@@ -1029,17 +1001,23 @@ function Video({
1029
1001
  activeCue && /* @__PURE__ */ jsx(
1030
1002
  "div",
1031
1003
  {
1032
- className: `pointer-events-none absolute inset-x-0 z-40 flex justify-center px-4 transition-all duration-200 ${controlsVisible ? "bottom-20 @sm:bottom-24 @lg:bottom-28" : "bottom-4 @sm:bottom-6"}`,
1004
+ className: `pointer-events-none absolute inset-x-0 z-35 flex justify-center px-4 transition-all duration-200 ${controlsVisible ? "bottom-20 @sm:bottom-24 @lg:bottom-28" : "bottom-4 @sm:bottom-6"}`,
1033
1005
  children: /* @__PURE__ */ jsx(
1034
1006
  "div",
1035
1007
  {
1036
- className: "max-w-3xl rounded-lg px-4 py-2 text-center font-medium leading-snug",
1008
+ className: "max-w-[80%] rounded-lg px-4 py-1.5 text-center font-medium leading-snug",
1037
1009
  style: {
1038
- fontSize: subtitleStyle.size === "small" ? "0.8rem" : subtitleStyle.size === "large" ? "1.25rem" : "1rem",
1010
+ fontSize: (() => {
1011
+ const base = Math.max(12, Math.min(playerHeight * 0.028, 32));
1012
+ if (subtitleStyle.size === "small") return `${base * 0.75}px`;
1013
+ if (subtitleStyle.size === "large") return `${base * 1.35}px`;
1014
+ if (subtitleStyle.size === "xlarge") return `${base * 1.8}px`;
1015
+ return `${base}px`;
1016
+ })(),
1039
1017
  color: subtitleStyle.color === "yellow" ? "#facc15" : subtitleStyle.color === "cyan" ? "#22d3ee" : "#ffffff",
1040
- backgroundColor: subtitleStyle.bg === "none" ? "transparent" : subtitleStyle.bg === "solid" ? "rgba(0,0,0,0.9)" : "rgba(0,0,0,0.7)",
1041
- backdropFilter: subtitleStyle.bg === "semi" ? "blur(4px)" : void 0,
1042
- textShadow: subtitleStyle.bg === "none" ? "0 1px 6px rgba(0,0,0,1), 0 0 12px rgba(0,0,0,0.8)" : "0 1px 3px rgba(0,0,0,0.6)"
1018
+ backgroundColor: subtitleStyle.bg === "none" ? "transparent" : subtitleStyle.bg === "solid" ? "rgba(0,0,0,0.9)" : "rgba(0,0,0,0.55)",
1019
+ backdropFilter: subtitleStyle.bg === "semi" ? "blur(6px)" : void 0,
1020
+ textShadow: subtitleStyle.bg === "none" ? "0 1px 8px rgba(0,0,0,1), 0 0 16px rgba(0,0,0,0.9)" : "0 1px 3px rgba(0,0,0,0.5)"
1043
1021
  },
1044
1022
  children: activeCue
1045
1023
  }
@@ -1053,21 +1031,36 @@ function Video({
1053
1031
  );
1054
1032
  }
1055
1033
  var VideoPlayer = Video;
1056
- function SettingsItem({
1057
- children,
1058
- active,
1059
- onClick
1060
- }) {
1061
- return /* @__PURE__ */ jsxs(
1034
+ function SeekButton({ seconds, direction, onClick }) {
1035
+ return /* @__PURE__ */ jsx(
1062
1036
  "button",
1063
1037
  {
1064
1038
  type: "button",
1065
1039
  onClick,
1066
- className: `flex w-full items-center gap-2 px-4 py-2.5 text-left text-base font-medium transition hover:bg-white/10 ${active ? "text-white" : "text-white/55"}`,
1067
- children: [
1068
- /* @__PURE__ */ jsx("span", { className: `size-1.5 rounded-full ${active ? "bg-white" : "bg-transparent"}` }),
1069
- children
1070
- ]
1040
+ className: "grid size-8 place-items-center text-white transition hover:scale-110 hover:text-white/80 @sm:size-10",
1041
+ "aria-label": `${direction === "back" ? "Rewind" : "Forward"} ${seconds} seconds`,
1042
+ children: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 36 36", fill: "none", className: "size-7 @sm:size-8", children: [
1043
+ /* @__PURE__ */ jsx(
1044
+ "path",
1045
+ {
1046
+ d: direction === "back" ? "M18 6C11.373 6 6 11.373 6 18s5.373 12 12 12 12-5.373 12-12" : "M18 6C24.627 6 30 11.373 30 18S24.627 30 18 30 6 24.627 6 18",
1047
+ stroke: "currentColor",
1048
+ strokeWidth: "2",
1049
+ strokeLinecap: "round"
1050
+ }
1051
+ ),
1052
+ /* @__PURE__ */ jsx(
1053
+ "path",
1054
+ {
1055
+ d: direction === "back" ? "M18 6l-4 4 4 4" : "M18 6l4 4-4 4",
1056
+ stroke: "currentColor",
1057
+ strokeWidth: "2",
1058
+ strokeLinecap: "round",
1059
+ strokeLinejoin: "round"
1060
+ }
1061
+ ),
1062
+ /* @__PURE__ */ jsx("text", { x: "18", y: "22", textAnchor: "middle", fill: "currentColor", fontSize: "9", fontWeight: "600", fontFamily: "system-ui", children: seconds })
1063
+ ] })
1071
1064
  }
1072
1065
  );
1073
1066
  }