@gentomiyano/optimized-web-audio-player 0.2.0 → 0.2.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/README.md CHANGED
@@ -39,6 +39,18 @@ The public npm registry, scoped package name, and release workflow are fixed.
39
39
  Initial npm publication still requires the scope owner's authentication.
40
40
  Release and consumer pinning rules are documented in `docs/RELEASING.md`.
41
41
 
42
+ ## WP-04.3 React package boundary
43
+
44
+ Version 0.2.1 keeps React Context consumers in the `/react` entrypoint and
45
+ exports props-only views from `/ui`. Import `PlayerProvider`, `usePlayer`,
46
+ `useBoundPlayer`, and `BoundCompactPlayer` from `/react`; import
47
+ `BoundCompactPlayerView`, `MiniPlayer`, and the other pure views from `/ui`.
48
+
49
+ The release check packs the built package, installs that tarball into an empty
50
+ consumer, renders a bound inline Player with a shared Mini Player, and verifies
51
+ one Store, one active item, and one audio element. See
52
+ `docs/WP-04.3-REACT-BOUNDARY.md`.
53
+
42
54
  ## WP-04.2 playback contexts and bound inline players
43
55
 
44
56
  Version 0.2.0 adds consumer-neutral `manual-history` and `ordered-queue`
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
- import { j as PlayerCommands, x as PlayerMediaSyncReason, Q as QueueItem, p as PlayerExperienceConfig, z as PlayerStore, r as PlayerExperienceConfigStore, y as PlayerState, c as PlaybackContextInput, a as BoundPlayerViewState, B as BoundPlayerCommands } from '../player-BxNFzRq4.js';
3
+ import { j as PlayerCommands, x as PlayerMediaSyncReason, Q as QueueItem, p as PlayerExperienceConfig, z as PlayerStore, r as PlayerExperienceConfigStore, y as PlayerState, c as PlaybackContextInput, a as BoundPlayerViewState, B as BoundPlayerCommands, h as PlayerAppearance } from '../player-BxNFzRq4.js';
4
4
  import { c as PlayerMediaEventType, a as MediaElementStateSnapshot } from '../media-element-DyMO-PhK.js';
5
5
 
6
6
  type PlayerAudioMode = "auto" | "web-audio" | "html-media";
@@ -94,4 +94,12 @@ interface UseBoundPlayerResult {
94
94
  declare function deriveBoundPlayerViewState(playerState: Readonly<PlayerState>, contextId: string, queueItem: QueueItem): Readonly<BoundPlayerViewState>;
95
95
  declare function useBoundPlayer({ context, queueItem, }: UseBoundPlayerOptions): UseBoundPlayerResult;
96
96
 
97
- export { MiniPlayerHost, type MiniPlayerHostProps, PlayerProvider, type PlayerProviderProps, type PlayerRuntimeContextValue, type PlayerRuntimeDiagnosticEvent, type PlayerRuntimeDiagnosticListener, type PlayerRuntimeDiagnosticPayload, type UseBoundPlayerOptions, type UseBoundPlayerResult, type UsePlayerResult, deriveBoundPlayerViewState, useBoundPlayer, usePlayer };
97
+ interface BoundCompactPlayerProps {
98
+ readonly appearance: PlayerAppearance;
99
+ readonly context: PlaybackContextInput;
100
+ readonly queueItem: QueueItem;
101
+ readonly className?: string;
102
+ }
103
+ declare function BoundCompactPlayer({ appearance, className, context, queueItem, }: BoundCompactPlayerProps): react.JSX.Element;
104
+
105
+ export { BoundCompactPlayer, type BoundCompactPlayerProps, MiniPlayerHost, type MiniPlayerHostProps, PlayerProvider, type PlayerProviderProps, type PlayerRuntimeContextValue, type PlayerRuntimeDiagnosticEvent, type PlayerRuntimeDiagnosticListener, type PlayerRuntimeDiagnosticPayload, type UseBoundPlayerOptions, type UseBoundPlayerResult, type UsePlayerResult, deriveBoundPlayerViewState, useBoundPlayer, usePlayer };
@@ -1,4 +1,4 @@
1
- import { createContext, useRef, useState, useEffect, useSyncExternalStore, useMemo, useContext } from 'react';
1
+ import { createContext, useRef, useState, useEffect, useSyncExternalStore, useMemo, useContext, useId } from 'react';
2
2
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
3
3
 
4
4
  // src/react/player-provider.tsx
@@ -2568,7 +2568,535 @@ function useBoundPlayer({
2568
2568
  }, [context, player.commands, player.state, queueItem]);
2569
2569
  return { state, commands };
2570
2570
  }
2571
+ function IconFrame({
2572
+ children,
2573
+ size = 20,
2574
+ ...props
2575
+ }) {
2576
+ return /* @__PURE__ */ jsx(
2577
+ "svg",
2578
+ {
2579
+ "aria-hidden": "true",
2580
+ focusable: "false",
2581
+ viewBox: "0 0 24 24",
2582
+ width: size,
2583
+ height: size,
2584
+ fill: "currentColor",
2585
+ ...props,
2586
+ children
2587
+ }
2588
+ );
2589
+ }
2590
+ function PlayIcon(props) {
2591
+ return /* @__PURE__ */ jsx(IconFrame, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M7 4.8c0-1.05 1.16-1.68 2.04-1.1l11 7.2a1.3 1.3 0 0 1 0 2.2l-11 7.2A1.31 1.31 0 0 1 7 19.2V4.8Z" }) });
2592
+ }
2593
+ function PauseIcon(props) {
2594
+ return /* @__PURE__ */ jsxs(IconFrame, { ...props, children: [
2595
+ /* @__PURE__ */ jsx("rect", { x: "5", y: "4", width: "5", height: "16", rx: "0.8" }),
2596
+ /* @__PURE__ */ jsx("rect", { x: "14", y: "4", width: "5", height: "16", rx: "0.8" })
2597
+ ] });
2598
+ }
2599
+
2600
+ // src/ui/player-utils.ts
2601
+ function clamp(value, min, max) {
2602
+ return Math.min(Math.max(value, min), max);
2603
+ }
2604
+ function formatTime(seconds) {
2605
+ if (seconds === null || !Number.isFinite(seconds) || seconds < 0) {
2606
+ return "--:--";
2607
+ }
2608
+ const wholeSeconds = Math.floor(seconds);
2609
+ const hours = Math.floor(wholeSeconds / 3600);
2610
+ const minutes = Math.floor(wholeSeconds % 3600 / 60);
2611
+ const remainder = wholeSeconds % 60;
2612
+ if (hours > 0) {
2613
+ return `${String(hours)}:${String(minutes).padStart(2, "0")}:${String(remainder).padStart(2, "0")}`;
2614
+ }
2615
+ return `${String(minutes).padStart(2, "0")}:${String(remainder).padStart(2, "0")}`;
2616
+ }
2617
+ function formatTimeAria(seconds) {
2618
+ const safeSeconds = Math.max(0, Math.floor(seconds));
2619
+ const hours = Math.floor(safeSeconds / 3600);
2620
+ const minutes = Math.floor(safeSeconds % 3600 / 60);
2621
+ const remainder = safeSeconds % 60;
2622
+ const parts = [
2623
+ hours > 0 ? `${String(hours)} hour${hours === 1 ? "" : "s"}` : "",
2624
+ minutes > 0 ? `${String(minutes)} minute${minutes === 1 ? "" : "s"}` : "",
2625
+ `${String(remainder)} second${remainder === 1 ? "" : "s"}`
2626
+ ].filter(Boolean);
2627
+ return parts.join(" ");
2628
+ }
2629
+ function PlayerSurface({
2630
+ appearance,
2631
+ children,
2632
+ className,
2633
+ label
2634
+ }) {
2635
+ return /* @__PURE__ */ jsx(
2636
+ "section",
2637
+ {
2638
+ "aria-label": label,
2639
+ className: ["player-surface", className].filter(Boolean).join(" "),
2640
+ "data-appearance": appearance,
2641
+ children
2642
+ }
2643
+ );
2644
+ }
2645
+ function Artwork({ className, item }) {
2646
+ const classes = ["player-artwork", className].filter(Boolean).join(" ");
2647
+ if (item?.artwork !== void 0) {
2648
+ return /* @__PURE__ */ jsx("span", { className: `${classes} player-artwork--image`, children: /* @__PURE__ */ jsx(
2649
+ "img",
2650
+ {
2651
+ alt: item.artwork.alt,
2652
+ className: "player-artwork__image",
2653
+ decoding: "async",
2654
+ src: item.artwork.url
2655
+ }
2656
+ ) });
2657
+ }
2658
+ return /* @__PURE__ */ jsx(
2659
+ "div",
2660
+ {
2661
+ "aria-label": "Artwork unavailable",
2662
+ className: `${classes} player-artwork--fallback`,
2663
+ role: "img",
2664
+ children: /* @__PURE__ */ jsx("span", { "aria-hidden": "true" })
2665
+ }
2666
+ );
2667
+ }
2668
+ function ScrollingTitle({
2669
+ enabled,
2670
+ title
2671
+ }) {
2672
+ const containsJapanese = /[\u3040-\u30ff\u3400-\u9fff]/u.test(title);
2673
+ const viewportRef = useRef(null);
2674
+ const contentRef = useRef(null);
2675
+ const [marquee, setMarquee] = useState({
2676
+ distance: 0,
2677
+ duration: 10,
2678
+ overflow: false
2679
+ });
2680
+ useEffect(() => {
2681
+ const viewport = viewportRef.current;
2682
+ const content = contentRef.current;
2683
+ if (viewport === null || content === null) {
2684
+ return;
2685
+ }
2686
+ const viewportElement = viewport;
2687
+ const contentElement = content;
2688
+ function measure() {
2689
+ const viewportWidth = Math.max(
2690
+ viewportElement.getBoundingClientRect().width,
2691
+ viewportElement.clientWidth
2692
+ );
2693
+ const contentWidth = Math.max(
2694
+ contentElement.getBoundingClientRect().width,
2695
+ contentElement.scrollWidth
2696
+ );
2697
+ const overflow = contentWidth - viewportWidth > 1;
2698
+ const distance = enabled ? contentWidth + 12 : 0;
2699
+ setMarquee({
2700
+ distance,
2701
+ duration: Math.max(10, distance / 22 + 4),
2702
+ overflow
2703
+ });
2704
+ }
2705
+ measure();
2706
+ if (typeof ResizeObserver === "undefined") {
2707
+ window.addEventListener("resize", measure);
2708
+ return () => {
2709
+ window.removeEventListener("resize", measure);
2710
+ };
2711
+ }
2712
+ const observer = new ResizeObserver(measure);
2713
+ observer.observe(viewportElement);
2714
+ observer.observe(contentElement);
2715
+ return () => {
2716
+ observer.disconnect();
2717
+ };
2718
+ }, [enabled, title]);
2719
+ const style = {
2720
+ "--player-title-scroll-offset": `${String(-marquee.distance)}px`,
2721
+ "--player-title-scroll-duration": `${String(marquee.duration)}s`
2722
+ };
2723
+ return /* @__PURE__ */ jsx(
2724
+ "strong",
2725
+ {
2726
+ className: "player-title",
2727
+ "data-overflow": marquee.overflow ? "true" : "false",
2728
+ "data-scroll": enabled ? "true" : "false",
2729
+ "data-script": containsJapanese ? "japanese" : "latin",
2730
+ ref: viewportRef,
2731
+ style,
2732
+ children: /* @__PURE__ */ jsx("span", { className: "player-title__text", ref: contentRef, children: title })
2733
+ }
2734
+ );
2735
+ }
2736
+ function TrackMetadata({
2737
+ className,
2738
+ item,
2739
+ scrollTitle = false,
2740
+ status
2741
+ }) {
2742
+ return /* @__PURE__ */ jsxs(
2743
+ "div",
2744
+ {
2745
+ className: ["player-metadata", className].filter(Boolean).join(" "),
2746
+ children: [
2747
+ status === void 0 ? null : /* @__PURE__ */ jsx("span", { className: "player-kicker", children: status }),
2748
+ /* @__PURE__ */ jsx(
2749
+ ScrollingTitle,
2750
+ {
2751
+ enabled: item !== null && scrollTitle,
2752
+ title: item?.title ?? "No media selected"
2753
+ },
2754
+ item?.title ?? "No media selected"
2755
+ ),
2756
+ /* @__PURE__ */ jsx("span", { className: "player-creator", children: item === null ? "Select media to begin" : item.creatorName ?? "Artist unavailable" }),
2757
+ item?.collectionTitle === void 0 ? null : /* @__PURE__ */ jsx("span", { className: "player-collection", children: item.collectionTitle })
2758
+ ]
2759
+ }
2760
+ );
2761
+ }
2762
+ function TimeReadout({
2763
+ className,
2764
+ currentTimeSec,
2765
+ durationSec
2766
+ }) {
2767
+ return /* @__PURE__ */ jsxs(
2768
+ "div",
2769
+ {
2770
+ "aria-label": `${formatTime(currentTimeSec)} of ${formatTime(durationSec)}`,
2771
+ className: ["player-time", className].filter(Boolean).join(" "),
2772
+ children: [
2773
+ /* @__PURE__ */ jsx("time", { children: formatTime(currentTimeSec) }),
2774
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "/" }),
2775
+ /* @__PURE__ */ jsx("time", { children: formatTime(durationSec) })
2776
+ ]
2777
+ }
2778
+ );
2779
+ }
2780
+ function IconButton({
2781
+ children,
2782
+ className,
2783
+ disabled = false,
2784
+ label,
2785
+ onClick,
2786
+ pressed
2787
+ }) {
2788
+ return /* @__PURE__ */ jsx(
2789
+ "button",
2790
+ {
2791
+ "aria-label": label,
2792
+ ...pressed === void 0 ? {} : { "aria-pressed": pressed },
2793
+ className: ["player-icon-button", className].filter(Boolean).join(" "),
2794
+ disabled,
2795
+ onClick,
2796
+ title: label,
2797
+ type: "button",
2798
+ children
2799
+ }
2800
+ );
2801
+ }
2802
+ function SeekBar({
2803
+ canSeek,
2804
+ className,
2805
+ commitMode,
2806
+ currentTimeSec,
2807
+ durationSec,
2808
+ label = "Seek",
2809
+ onSeek,
2810
+ status
2811
+ }) {
2812
+ const duration = durationSec !== null && Number.isFinite(durationSec) && durationSec > 0 ? durationSec : 0;
2813
+ const disabled = !canSeek || duration === 0 || status === "loading";
2814
+ const safeCurrentTime = clamp(currentTimeSec, 0, duration);
2815
+ const [previewTime, setPreviewTime] = useState(safeCurrentTime);
2816
+ const previewProgress = duration === 0 ? 0 : clamp(previewTime / duration, 0, 1) * 100;
2817
+ const isAdjusting = useRef(false);
2818
+ const lastCommitted = useRef(null);
2819
+ useEffect(() => {
2820
+ if (!isAdjusting.current) {
2821
+ setPreviewTime(safeCurrentTime);
2822
+ }
2823
+ }, [safeCurrentTime]);
2824
+ function updatePreview(value) {
2825
+ const nextValue = clamp(value, 0, duration);
2826
+ isAdjusting.current = true;
2827
+ setPreviewTime(nextValue);
2828
+ if (commitMode === "live") {
2829
+ onSeek(nextValue);
2830
+ lastCommitted.current = nextValue;
2831
+ }
2832
+ }
2833
+ function commitPreview(value = previewTime) {
2834
+ const nextValue = clamp(value, 0, duration);
2835
+ if (disabled || !isAdjusting.current || commitMode === "live" && lastCommitted.current === nextValue) {
2836
+ isAdjusting.current = false;
2837
+ return;
2838
+ }
2839
+ onSeek(nextValue);
2840
+ lastCommitted.current = nextValue;
2841
+ isAdjusting.current = false;
2842
+ }
2843
+ function cancelPreview() {
2844
+ isAdjusting.current = false;
2845
+ lastCommitted.current = null;
2846
+ setPreviewTime(safeCurrentTime);
2847
+ }
2848
+ return /* @__PURE__ */ jsxs(
2849
+ "label",
2850
+ {
2851
+ className: ["player-seek", className].filter(Boolean).join(" "),
2852
+ "data-disabled": disabled ? "true" : "false",
2853
+ children: [
2854
+ /* @__PURE__ */ jsx("span", { className: "player-visually-hidden", children: label }),
2855
+ /* @__PURE__ */ jsx(
2856
+ "span",
2857
+ {
2858
+ "aria-hidden": "true",
2859
+ className: "player-seek__progress",
2860
+ style: { width: `${String(disabled ? 0 : previewProgress)}%` }
2861
+ }
2862
+ ),
2863
+ /* @__PURE__ */ jsx(
2864
+ "input",
2865
+ {
2866
+ "aria-label": label,
2867
+ "aria-valuetext": formatTimeAria(previewTime),
2868
+ disabled,
2869
+ max: duration || 1,
2870
+ min: 0,
2871
+ onBlur: (event) => {
2872
+ commitPreview(Number(event.currentTarget.value));
2873
+ },
2874
+ onChange: (event) => {
2875
+ updatePreview(Number(event.currentTarget.value));
2876
+ },
2877
+ onKeyUp: (event) => {
2878
+ commitPreview(Number(event.currentTarget.value));
2879
+ },
2880
+ onPointerDown: () => {
2881
+ isAdjusting.current = true;
2882
+ },
2883
+ onPointerCancel: cancelPreview,
2884
+ onPointerUp: (event) => {
2885
+ commitPreview(Number(event.currentTarget.value));
2886
+ },
2887
+ step: 0.1,
2888
+ type: "range",
2889
+ value: disabled ? 0 : previewTime
2890
+ }
2891
+ )
2892
+ ]
2893
+ }
2894
+ );
2895
+ }
2896
+ var FALLBACK_PEAKS = [
2897
+ 0.16,
2898
+ 0.24,
2899
+ 0.42,
2900
+ 0.3,
2901
+ 0.58,
2902
+ 0.76,
2903
+ 0.48,
2904
+ 0.34,
2905
+ 0.66,
2906
+ 0.88,
2907
+ 0.62,
2908
+ 0.4,
2909
+ 0.52,
2910
+ 0.72,
2911
+ 0.44,
2912
+ 0.28,
2913
+ 0.56,
2914
+ 0.82,
2915
+ 0.68,
2916
+ 0.38,
2917
+ 0.24,
2918
+ 0.46,
2919
+ 0.32,
2920
+ 0.18
2921
+ ];
2922
+ var BAR_COUNT = 512;
2923
+ var VIEWBOX_WIDTH = BAR_COUNT;
2924
+ function createWaveformBars(peaks) {
2925
+ const source = peaks.length > 1 ? peaks : FALLBACK_PEAKS;
2926
+ return Array.from({ length: BAR_COUNT }, (_, index) => {
2927
+ const sourcePosition = index / Math.max(BAR_COUNT - 1, 1) * (source.length - 1);
2928
+ const leftIndex = Math.floor(sourcePosition);
2929
+ const rightIndex = Math.min(leftIndex + 1, source.length - 1);
2930
+ const mix = sourcePosition - leftIndex;
2931
+ const leftPeak = Math.abs(source[leftIndex] ?? 0);
2932
+ const rightPeak = Math.abs(source[rightIndex] ?? leftPeak);
2933
+ const interpolated = leftPeak + (rightPeak - leftPeak) * mix;
2934
+ if (source.length >= BAR_COUNT) {
2935
+ return clamp(Math.pow(interpolated, 1.18), 0.04, 1);
2936
+ }
2937
+ const noiseSeed = Math.sin((index + 1) * 12.9898 + source.length * 78.233) * 43758.5453;
2938
+ const noise = noiseSeed - Math.floor(noiseSeed);
2939
+ const fineDetail = 0.76 + noise * 0.34;
2940
+ const transient = noise > 0.975 ? 3.2 : noise > 0.91 ? 1.75 : 1;
2941
+ return clamp(
2942
+ Math.pow(interpolated, 1.22) * fineDetail * transient,
2943
+ 0.04,
2944
+ 1
2945
+ );
2946
+ });
2947
+ }
2948
+ function Waveform({
2949
+ className,
2950
+ opacity = 1,
2951
+ playedStateEnabled = true,
2952
+ progress = 0,
2953
+ waveform
2954
+ }) {
2955
+ const clipId = useId().replaceAll(":", "");
2956
+ const bars = createWaveformBars(waveform?.peaks ?? FALLBACK_PEAKS);
2957
+ const progressWidth = clamp(progress, 0, 1) * VIEWBOX_WIDTH;
2958
+ const classes = ["player-waveform", className].filter(Boolean).join(" ");
2959
+ const barPath = bars.map((peak, index) => {
2960
+ const halfHeight = peak * 47;
2961
+ const x = index + 0.5;
2962
+ return `M ${String(x)} ${String(50 - halfHeight)} V ${String(
2963
+ 50 + halfHeight
2964
+ )}`;
2965
+ }).join(" ");
2966
+ return /* @__PURE__ */ jsxs(
2967
+ "svg",
2968
+ {
2969
+ "aria-hidden": "true",
2970
+ className: classes,
2971
+ focusable: "false",
2972
+ preserveAspectRatio: "none",
2973
+ style: { opacity: clamp(opacity, 0, 1), pointerEvents: "none" },
2974
+ viewBox: `0 0 ${String(VIEWBOX_WIDTH)} 100`,
2975
+ children: [
2976
+ /* @__PURE__ */ jsx("path", { className: "player-waveform__base", d: barPath }),
2977
+ playedStateEnabled ? /* @__PURE__ */ jsxs(Fragment, { children: [
2978
+ /* @__PURE__ */ jsx("clipPath", { id: clipId, children: /* @__PURE__ */ jsx("rect", { width: progressWidth, height: "100" }) }),
2979
+ /* @__PURE__ */ jsx(
2980
+ "path",
2981
+ {
2982
+ className: "player-waveform__played",
2983
+ clipPath: `url(#${clipId})`,
2984
+ d: barPath
2985
+ }
2986
+ )
2987
+ ] }) : null
2988
+ ]
2989
+ }
2990
+ );
2991
+ }
2992
+ function BoundCompactPlayerView({
2993
+ appearance,
2994
+ className,
2995
+ commands,
2996
+ contextId,
2997
+ contextMode,
2998
+ options,
2999
+ state
3000
+ }) {
3001
+ const progress = state.durationSec === null || state.durationSec <= 0 ? 0 : state.currentTimeSec / state.durationSec;
3002
+ return /* @__PURE__ */ jsx(
3003
+ PlayerSurface,
3004
+ {
3005
+ appearance,
3006
+ className: ["player-bound-compact", className].filter(Boolean).join(" "),
3007
+ label: `Inline player: ${state.item.title}`,
3008
+ children: /* @__PURE__ */ jsxs(
3009
+ "div",
3010
+ {
3011
+ className: "player-bound-compact__main",
3012
+ "data-active": state.isActive ? "true" : "false",
3013
+ "data-playback-context-id": contextId,
3014
+ "data-playback-context-mode": contextMode,
3015
+ "data-queue-item-id": state.queueItemId,
3016
+ children: [
3017
+ /* @__PURE__ */ jsx(
3018
+ IconButton,
3019
+ {
3020
+ className: "player-play-button",
3021
+ label: state.isPlaying ? "Pause" : "Play",
3022
+ onClick: () => {
3023
+ void commands.togglePlayback();
3024
+ },
3025
+ children: state.isPlaying ? /* @__PURE__ */ jsx(PauseIcon, { size: 22 }) : /* @__PURE__ */ jsx(PlayIcon, { size: 22 })
3026
+ }
3027
+ ),
3028
+ /* @__PURE__ */ jsx(
3029
+ Artwork,
3030
+ {
3031
+ className: "player-bound-compact__artwork",
3032
+ item: state.item
3033
+ }
3034
+ ),
3035
+ /* @__PURE__ */ jsx(TrackMetadata, { item: state.item, scrollTitle: state.isPlaying }),
3036
+ /* @__PURE__ */ jsxs("div", { className: "player-bound-compact__signal", children: [
3037
+ /* @__PURE__ */ jsx(
3038
+ Waveform,
3039
+ {
3040
+ opacity: options.waveformOpacity,
3041
+ playedStateEnabled: options.waveformPlayedStateEnabled,
3042
+ progress,
3043
+ waveform: state.item.waveform
3044
+ }
3045
+ ),
3046
+ /* @__PURE__ */ jsx(
3047
+ TimeReadout,
3048
+ {
3049
+ currentTimeSec: state.currentTimeSec,
3050
+ durationSec: state.durationSec
3051
+ }
3052
+ )
3053
+ ] }),
3054
+ /* @__PURE__ */ jsx(
3055
+ SeekBar,
3056
+ {
3057
+ canSeek: state.canSeek,
3058
+ commitMode: options.seekCommitMode,
3059
+ currentTimeSec: state.currentTimeSec,
3060
+ durationSec: state.durationSec,
3061
+ label: `Seek in ${state.item.title}`,
3062
+ onSeek: (seconds) => {
3063
+ commands.seekTo(seconds);
3064
+ },
3065
+ status: state.status
3066
+ }
3067
+ )
3068
+ ]
3069
+ }
3070
+ )
3071
+ }
3072
+ );
3073
+ }
3074
+ function BoundCompactPlayer({
3075
+ appearance,
3076
+ className,
3077
+ context,
3078
+ queueItem
3079
+ }) {
3080
+ const bound = useBoundPlayer({ context, queueItem });
3081
+ const player = usePlayer();
3082
+ return /* @__PURE__ */ jsx(
3083
+ BoundCompactPlayerView,
3084
+ {
3085
+ appearance,
3086
+ ...className === void 0 ? {} : { className },
3087
+ commands: bound.commands,
3088
+ contextId: context.id,
3089
+ contextMode: context.mode,
3090
+ options: {
3091
+ seekCommitMode: player.state.experienceConfig.seekCommitMode,
3092
+ waveformOpacity: player.state.experienceConfig.waveformOpacity,
3093
+ waveformPlayedStateEnabled: player.state.experienceConfig.waveformPlayedStateEnabled
3094
+ },
3095
+ state: bound.state
3096
+ }
3097
+ );
3098
+ }
2571
3099
 
2572
- export { MiniPlayerHost, PlayerProvider, deriveBoundPlayerViewState, useBoundPlayer, usePlayer };
3100
+ export { BoundCompactPlayer, MiniPlayerHost, PlayerProvider, deriveBoundPlayerViewState, useBoundPlayer, usePlayer };
2573
3101
  //# sourceMappingURL=index.js.map
2574
3102
  //# sourceMappingURL=index.js.map