@scarlett-player/ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1597 -0
- package/dist/index.d.cts +150 -0
- package/dist/index.d.ts +150 -0
- package/dist/index.js +1566 -0
- package/package.json +59 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { Plugin } from '@scarlett-player/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* UI Controls Plugin Types
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Available control slot identifiers.
|
|
9
|
+
*/
|
|
10
|
+
type ControlSlot = 'play' | 'volume' | 'progress' | 'time' | 'live-indicator' | 'quality' | 'airplay' | 'chromecast' | 'pip' | 'fullscreen' | 'spacer';
|
|
11
|
+
/**
|
|
12
|
+
* Layout configuration for the control bar.
|
|
13
|
+
*/
|
|
14
|
+
interface LayoutConfig {
|
|
15
|
+
/** Order of controls in the control bar */
|
|
16
|
+
controls?: ControlSlot[];
|
|
17
|
+
/** Delay in ms before hiding controls (default: 3000) */
|
|
18
|
+
hideDelay?: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Theme configuration for styling the controls.
|
|
22
|
+
*/
|
|
23
|
+
interface ThemeConfig {
|
|
24
|
+
/** Primary text/icon color (default: '#fff') */
|
|
25
|
+
primaryColor?: string;
|
|
26
|
+
/** Accent color for active states (default: '#e50914') */
|
|
27
|
+
accentColor?: string;
|
|
28
|
+
/** Control bar background (default: 'rgba(0,0,0,0.7)') */
|
|
29
|
+
backgroundColor?: string;
|
|
30
|
+
/** Control bar height in px (default: 48) */
|
|
31
|
+
controlBarHeight?: number;
|
|
32
|
+
/** Icon size in px (default: 24) */
|
|
33
|
+
iconSize?: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* UI plugin configuration.
|
|
37
|
+
*/
|
|
38
|
+
interface UIPluginConfig extends LayoutConfig {
|
|
39
|
+
/** Theme configuration */
|
|
40
|
+
theme?: ThemeConfig;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Base interface for all control components.
|
|
44
|
+
*/
|
|
45
|
+
interface Control {
|
|
46
|
+
/** Render the control element */
|
|
47
|
+
render(): HTMLElement;
|
|
48
|
+
/** Update control state */
|
|
49
|
+
update(): void;
|
|
50
|
+
/** Cleanup when control is destroyed */
|
|
51
|
+
destroy(): void;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* UI Controls Plugin interface.
|
|
55
|
+
*/
|
|
56
|
+
interface IUIPlugin extends Plugin {
|
|
57
|
+
readonly id: 'ui-controls';
|
|
58
|
+
/** Show the controls */
|
|
59
|
+
show(): void;
|
|
60
|
+
/** Hide the controls */
|
|
61
|
+
hide(): void;
|
|
62
|
+
/** Apply a theme configuration */
|
|
63
|
+
setTheme(theme: ThemeConfig): void;
|
|
64
|
+
/** Get the control bar element */
|
|
65
|
+
getControlBar(): HTMLElement | null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* SVG Icons for UI Controls
|
|
70
|
+
*
|
|
71
|
+
* Clean, minimal icons - 24x24 viewBox with currentColor fill.
|
|
72
|
+
* Optimized for clarity at small sizes.
|
|
73
|
+
*/
|
|
74
|
+
declare const icons: {
|
|
75
|
+
readonly play: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M8 5v14l11-7z\"/></svg>";
|
|
76
|
+
readonly pause: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M6 4h4v16H6V4zm8 0h4v16h-4V4z\"/></svg>";
|
|
77
|
+
readonly replay: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z\"/></svg>";
|
|
78
|
+
readonly volumeHigh: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z\"/></svg>";
|
|
79
|
+
readonly volumeLow: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z\"/></svg>";
|
|
80
|
+
readonly volumeMute: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z\"/></svg>";
|
|
81
|
+
readonly fullscreen: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z\"/></svg>";
|
|
82
|
+
readonly exitFullscreen: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z\"/></svg>";
|
|
83
|
+
readonly pip: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M19 7h-8v6h8V7zm2-4H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3V5h18v14z\"/></svg>";
|
|
84
|
+
readonly exitPip: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3V5h18v14zM9 9h6v2H9z\"/></svg>";
|
|
85
|
+
readonly settings: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M19.14 12.94c.04-.31.06-.63.06-.94 0-.31-.02-.63-.06-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.04.31-.06.63-.06.94s.02.63.06.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z\"/></svg>";
|
|
86
|
+
readonly chromecast: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M1 18v3h3c0-1.66-1.34-3-3-3zm0-4v2c2.76 0 5 2.24 5 5h2c0-3.87-3.13-7-7-7zm0-4v2c4.97 0 9 4.03 9 9h2c0-6.08-4.93-11-11-11zm20-7H3c-1.1 0-2 .9-2 2v3h2V5h18v14h-7v2h7c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z\"/></svg>";
|
|
87
|
+
readonly chromecastConnected: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M1 18v3h3c0-1.66-1.34-3-3-3zm0-4v2c2.76 0 5 2.24 5 5h2c0-3.87-3.13-7-7-7zm18-7H5v1.63c3.96 1.28 7.09 4.41 8.37 8.37H19V7zM1 10v2c4.97 0 9 4.03 9 9h2c0-6.08-4.93-11-11-11zm20-7H3c-1.1 0-2 .9-2 2v3h2V5h18v14h-7v2h7c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z\"/></svg>";
|
|
88
|
+
readonly airplay: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M6 22h12l-6-6-6 6zM21 3H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h4v-2H3V5h18v12h-4v2h4c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z\"/></svg>";
|
|
89
|
+
readonly captions: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 7H9.5v-.5h-2v3h2V13H11v1c0 .55-.45 1-1 1H7c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1zm7 0h-1.5v-.5h-2v3h2V13H18v1c0 .55-.45 1-1 1h-3c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1z\"/></svg>";
|
|
90
|
+
readonly captionsOff: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M19.5 5.5v13h-15v-13h15zM19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2z\"/></svg>";
|
|
91
|
+
readonly checkmark: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\"/></svg>";
|
|
92
|
+
readonly chevronUp: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 8l-6 6 1.41 1.41L12 10.83l4.59 4.58L18 14z\"/></svg>";
|
|
93
|
+
readonly chevronDown: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z\"/></svg>";
|
|
94
|
+
readonly spinner: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" class=\"sp-spin\"><path d=\"M12 4V2A10 10 0 0 0 2 12h2a8 8 0 0 1 8-8z\"/></svg>";
|
|
95
|
+
readonly skipForward: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M4 18l8.5-6L4 6v12zm9-12v12l8.5-6L13 6z\"/></svg>";
|
|
96
|
+
readonly skipBack: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M11 18V6l-8.5 6 8.5 6zm.5-6l8.5 6V6l-8.5 6z\"/></svg>";
|
|
97
|
+
readonly forward10: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M18 13c0 3.31-2.69 6-6 6s-6-2.69-6-6 2.69-6 6-6v4l5-5-5-5v4c-4.42 0-8 3.58-8 8s3.58 8 8 8 8-3.58 8-8h-2z\"/><text x=\"12\" y=\"15\" text-anchor=\"middle\" font-size=\"7\" font-weight=\"600\">10</text></svg>";
|
|
98
|
+
readonly replay10: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z\"/><text x=\"12\" y=\"15\" text-anchor=\"middle\" font-size=\"7\" font-weight=\"600\">10</text></svg>";
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* CSS Styles for UI Controls
|
|
103
|
+
*
|
|
104
|
+
* Modern, minimal design inspired by Mux Player and Vidstack.
|
|
105
|
+
* Uses CSS custom properties for theming.
|
|
106
|
+
*/
|
|
107
|
+
declare const styles = "\n/* ============================================\n Container & Base\n ============================================ */\n.sp-container {\n position: relative;\n width: 100%;\n height: 100%;\n background: #000;\n overflow: hidden;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n}\n\n.sp-container video {\n width: 100%;\n height: 100%;\n display: block;\n object-fit: contain;\n}\n\n.sp-container:focus {\n outline: none;\n}\n\n/* ============================================\n Gradient Overlay\n ============================================ */\n.sp-gradient {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 160px;\n background: linear-gradient(\n to top,\n rgba(0, 0, 0, 0.8) 0%,\n rgba(0, 0, 0, 0.4) 50%,\n transparent 100%\n );\n pointer-events: none;\n opacity: 0;\n transition: opacity 0.25s ease;\n z-index: 5;\n}\n\n.sp-gradient--visible {\n opacity: 1;\n}\n\n/* ============================================\n Controls Container\n ============================================ */\n.sp-controls {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n display: flex;\n align-items: center;\n padding: 0 12px 12px;\n gap: 4px;\n opacity: 0;\n transform: translateY(4px);\n transition: opacity 0.25s ease, transform 0.25s ease;\n z-index: 10;\n}\n\n.sp-controls--visible {\n opacity: 1;\n transform: translateY(0);\n}\n\n.sp-controls--hidden {\n opacity: 0;\n transform: translateY(4px);\n pointer-events: none;\n}\n\n/* ============================================\n Progress Bar (Above Controls)\n ============================================ */\n.sp-progress-wrapper {\n position: absolute;\n bottom: 48px;\n left: 12px;\n right: 12px;\n height: 20px;\n display: flex;\n align-items: center;\n cursor: pointer;\n z-index: 10;\n opacity: 0;\n transition: opacity 0.25s ease;\n}\n\n.sp-progress-wrapper--visible {\n opacity: 1;\n}\n\n.sp-progress {\n position: relative;\n width: 100%;\n height: 3px;\n background: rgba(255, 255, 255, 0.3);\n border-radius: 1.5px;\n transition: height 0.15s ease;\n}\n\n.sp-progress-wrapper:hover .sp-progress,\n.sp-progress--dragging {\n height: 5px;\n}\n\n.sp-progress__track {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n border-radius: inherit;\n overflow: hidden;\n}\n\n.sp-progress__buffered {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: rgba(255, 255, 255, 0.4);\n border-radius: inherit;\n transition: width 0.1s linear;\n}\n\n.sp-progress__filled {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: var(--sp-accent, #e50914);\n border-radius: inherit;\n}\n\n.sp-progress__handle {\n position: absolute;\n top: 50%;\n width: 14px;\n height: 14px;\n background: var(--sp-accent, #e50914);\n border-radius: 50%;\n transform: translate(-50%, -50%) scale(0);\n transition: transform 0.15s ease;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);\n}\n\n.sp-progress-wrapper:hover .sp-progress__handle,\n.sp-progress--dragging .sp-progress__handle {\n transform: translate(-50%, -50%) scale(1);\n}\n\n/* Progress Tooltip */\n.sp-progress__tooltip {\n position: absolute;\n bottom: calc(100% + 8px);\n padding: 6px 10px;\n background: rgba(20, 20, 20, 0.95);\n color: #fff;\n font-size: 12px;\n font-weight: 500;\n font-variant-numeric: tabular-nums;\n border-radius: 4px;\n white-space: nowrap;\n transform: translateX(-50%);\n pointer-events: none;\n opacity: 0;\n transition: opacity 0.15s ease;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);\n}\n\n.sp-progress-wrapper:hover .sp-progress__tooltip {\n opacity: 1;\n}\n\n/* ============================================\n Control Buttons\n ============================================ */\n.sp-control {\n background: none;\n border: none;\n color: rgba(255, 255, 255, 0.9);\n cursor: pointer;\n padding: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n transition: color 0.15s ease, transform 0.15s ease, background 0.15s ease;\n flex-shrink: 0;\n}\n\n.sp-control:hover {\n color: #fff;\n background: rgba(255, 255, 255, 0.1);\n}\n\n.sp-control:active {\n transform: scale(0.92);\n}\n\n.sp-control:focus-visible {\n outline: 2px solid var(--sp-accent, #e50914);\n outline-offset: 2px;\n}\n\n.sp-control:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n transform: none;\n}\n\n.sp-control:disabled:hover {\n background: none;\n}\n\n.sp-control svg {\n width: 24px;\n height: 24px;\n fill: currentColor;\n display: block;\n}\n\n.sp-control--small svg {\n width: 20px;\n height: 20px;\n}\n\n/* ============================================\n Spacer\n ============================================ */\n.sp-spacer {\n flex: 1;\n min-width: 0;\n}\n\n/* ============================================\n Time Display\n ============================================ */\n.sp-time {\n font-size: 13px;\n font-variant-numeric: tabular-nums;\n color: rgba(255, 255, 255, 0.9);\n white-space: nowrap;\n padding: 0 4px;\n letter-spacing: 0.02em;\n}\n\n/* ============================================\n Volume Control\n ============================================ */\n.sp-volume {\n display: flex;\n align-items: center;\n position: relative;\n}\n\n.sp-volume__slider-wrap {\n width: 0;\n overflow: hidden;\n transition: width 0.2s ease;\n}\n\n.sp-volume:hover .sp-volume__slider-wrap,\n.sp-volume:focus-within .sp-volume__slider-wrap {\n width: 64px;\n}\n\n.sp-volume__slider {\n width: 64px;\n height: 3px;\n background: rgba(255, 255, 255, 0.3);\n border-radius: 1.5px;\n cursor: pointer;\n position: relative;\n margin: 0 8px 0 4px;\n}\n\n.sp-volume__level {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: #fff;\n border-radius: inherit;\n transition: width 0.1s ease;\n}\n\n/* ============================================\n Live Indicator\n ============================================ */\n.sp-live {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--sp-accent, #e50914);\n cursor: pointer;\n padding: 6px 10px;\n border-radius: 4px;\n transition: background 0.15s ease, opacity 0.15s ease;\n}\n\n.sp-live:hover {\n background: rgba(255, 255, 255, 0.1);\n}\n\n.sp-live__dot {\n width: 8px;\n height: 8px;\n background: currentColor;\n border-radius: 50%;\n animation: sp-pulse 2s ease-in-out infinite;\n}\n\n.sp-live--behind {\n opacity: 0.6;\n}\n\n.sp-live--behind .sp-live__dot {\n animation: none;\n}\n\n@keyframes sp-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.4; }\n}\n\n/* ============================================\n Quality / Settings Menu\n ============================================ */\n.sp-quality {\n position: relative;\n}\n\n.sp-quality__btn {\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.sp-quality__label {\n font-size: 12px;\n font-weight: 500;\n opacity: 0.9;\n}\n\n.sp-quality-menu {\n position: absolute;\n bottom: calc(100% + 8px);\n right: 0;\n background: rgba(20, 20, 20, 0.95);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n border-radius: 8px;\n padding: 8px 0;\n min-width: 150px;\n box-shadow: 0 4px 24px rgba(0, 0, 0, 0.4);\n opacity: 0;\n visibility: hidden;\n transform: translateY(8px);\n transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s;\n z-index: 20;\n}\n\n.sp-quality-menu--open {\n opacity: 1;\n visibility: visible;\n transform: translateY(0);\n}\n\n.sp-quality-menu__item {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 16px;\n font-size: 13px;\n color: rgba(255, 255, 255, 0.8);\n cursor: pointer;\n transition: background 0.1s ease, color 0.1s ease;\n}\n\n.sp-quality-menu__item:hover {\n background: rgba(255, 255, 255, 0.1);\n color: #fff;\n}\n\n.sp-quality-menu__item--active {\n color: var(--sp-accent, #e50914);\n}\n\n.sp-quality-menu__check {\n width: 16px;\n height: 16px;\n fill: currentColor;\n margin-left: 8px;\n opacity: 0;\n}\n\n.sp-quality-menu__item--active .sp-quality-menu__check {\n opacity: 1;\n}\n\n/* ============================================\n Cast Button States\n ============================================ */\n.sp-cast--active {\n color: var(--sp-accent, #e50914);\n}\n\n.sp-cast--unavailable {\n opacity: 0.4;\n}\n\n/* ============================================\n Buffering Indicator\n ============================================ */\n.sp-buffering {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n z-index: 15;\n pointer-events: none;\n opacity: 0;\n transition: opacity 0.2s ease;\n}\n\n.sp-buffering--visible {\n opacity: 1;\n}\n\n.sp-buffering svg {\n width: 48px;\n height: 48px;\n fill: rgba(255, 255, 255, 0.9);\n filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3));\n}\n\n@keyframes sp-spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n}\n\n.sp-spin {\n animation: sp-spin 0.8s linear infinite;\n}\n\n/* ============================================\n Reduced Motion\n ============================================ */\n@media (prefers-reduced-motion: reduce) {\n .sp-gradient,\n .sp-controls,\n .sp-progress-wrapper,\n .sp-progress,\n .sp-progress__handle,\n .sp-progress__tooltip,\n .sp-control,\n .sp-volume__slider-wrap,\n .sp-quality-menu,\n .sp-buffering {\n transition: none;\n }\n\n .sp-live__dot,\n .sp-spin {\n animation: none;\n }\n}\n\n/* ============================================\n CSS Custom Properties (Theming)\n ============================================ */\n:root {\n --sp-accent: #e50914;\n --sp-color: #fff;\n --sp-bg: rgba(0, 0, 0, 0.8);\n --sp-control-height: 48px;\n --sp-icon-size: 24px;\n}\n";
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Formatting utility functions
|
|
111
|
+
*/
|
|
112
|
+
/**
|
|
113
|
+
* Format seconds as time string (mm:ss or h:mm:ss).
|
|
114
|
+
*/
|
|
115
|
+
declare function formatTime(seconds: number): string;
|
|
116
|
+
/**
|
|
117
|
+
* Format time for live streams (relative to live edge).
|
|
118
|
+
*/
|
|
119
|
+
declare function formatLiveTime(behindLive: number): string;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* UI Controls Plugin for Scarlett Player
|
|
123
|
+
*
|
|
124
|
+
* Provides a complete, themeable video player control interface.
|
|
125
|
+
* Modern design with progress bar above controls.
|
|
126
|
+
*
|
|
127
|
+
* @packageDocumentation
|
|
128
|
+
*/
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Create a UI controls plugin instance.
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```ts
|
|
135
|
+
* import { uiPlugin } from '@scarlett-player/ui';
|
|
136
|
+
*
|
|
137
|
+
* const player = new ScarlettPlayer({
|
|
138
|
+
* container: '#player',
|
|
139
|
+
* plugins: [
|
|
140
|
+
* uiPlugin({
|
|
141
|
+
* hideDelay: 3000,
|
|
142
|
+
* theme: { accentColor: '#e50914' },
|
|
143
|
+
* }),
|
|
144
|
+
* ],
|
|
145
|
+
* });
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
declare function uiPlugin(config?: UIPluginConfig): IUIPlugin;
|
|
149
|
+
|
|
150
|
+
export { type Control, type ControlSlot, type IUIPlugin, type LayoutConfig, type ThemeConfig, type UIPluginConfig, uiPlugin as default, formatLiveTime, formatTime, icons, styles, uiPlugin };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { Plugin } from '@scarlett-player/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* UI Controls Plugin Types
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Available control slot identifiers.
|
|
9
|
+
*/
|
|
10
|
+
type ControlSlot = 'play' | 'volume' | 'progress' | 'time' | 'live-indicator' | 'quality' | 'airplay' | 'chromecast' | 'pip' | 'fullscreen' | 'spacer';
|
|
11
|
+
/**
|
|
12
|
+
* Layout configuration for the control bar.
|
|
13
|
+
*/
|
|
14
|
+
interface LayoutConfig {
|
|
15
|
+
/** Order of controls in the control bar */
|
|
16
|
+
controls?: ControlSlot[];
|
|
17
|
+
/** Delay in ms before hiding controls (default: 3000) */
|
|
18
|
+
hideDelay?: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Theme configuration for styling the controls.
|
|
22
|
+
*/
|
|
23
|
+
interface ThemeConfig {
|
|
24
|
+
/** Primary text/icon color (default: '#fff') */
|
|
25
|
+
primaryColor?: string;
|
|
26
|
+
/** Accent color for active states (default: '#e50914') */
|
|
27
|
+
accentColor?: string;
|
|
28
|
+
/** Control bar background (default: 'rgba(0,0,0,0.7)') */
|
|
29
|
+
backgroundColor?: string;
|
|
30
|
+
/** Control bar height in px (default: 48) */
|
|
31
|
+
controlBarHeight?: number;
|
|
32
|
+
/** Icon size in px (default: 24) */
|
|
33
|
+
iconSize?: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* UI plugin configuration.
|
|
37
|
+
*/
|
|
38
|
+
interface UIPluginConfig extends LayoutConfig {
|
|
39
|
+
/** Theme configuration */
|
|
40
|
+
theme?: ThemeConfig;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Base interface for all control components.
|
|
44
|
+
*/
|
|
45
|
+
interface Control {
|
|
46
|
+
/** Render the control element */
|
|
47
|
+
render(): HTMLElement;
|
|
48
|
+
/** Update control state */
|
|
49
|
+
update(): void;
|
|
50
|
+
/** Cleanup when control is destroyed */
|
|
51
|
+
destroy(): void;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* UI Controls Plugin interface.
|
|
55
|
+
*/
|
|
56
|
+
interface IUIPlugin extends Plugin {
|
|
57
|
+
readonly id: 'ui-controls';
|
|
58
|
+
/** Show the controls */
|
|
59
|
+
show(): void;
|
|
60
|
+
/** Hide the controls */
|
|
61
|
+
hide(): void;
|
|
62
|
+
/** Apply a theme configuration */
|
|
63
|
+
setTheme(theme: ThemeConfig): void;
|
|
64
|
+
/** Get the control bar element */
|
|
65
|
+
getControlBar(): HTMLElement | null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* SVG Icons for UI Controls
|
|
70
|
+
*
|
|
71
|
+
* Clean, minimal icons - 24x24 viewBox with currentColor fill.
|
|
72
|
+
* Optimized for clarity at small sizes.
|
|
73
|
+
*/
|
|
74
|
+
declare const icons: {
|
|
75
|
+
readonly play: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M8 5v14l11-7z\"/></svg>";
|
|
76
|
+
readonly pause: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M6 4h4v16H6V4zm8 0h4v16h-4V4z\"/></svg>";
|
|
77
|
+
readonly replay: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z\"/></svg>";
|
|
78
|
+
readonly volumeHigh: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z\"/></svg>";
|
|
79
|
+
readonly volumeLow: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z\"/></svg>";
|
|
80
|
+
readonly volumeMute: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z\"/></svg>";
|
|
81
|
+
readonly fullscreen: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z\"/></svg>";
|
|
82
|
+
readonly exitFullscreen: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z\"/></svg>";
|
|
83
|
+
readonly pip: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M19 7h-8v6h8V7zm2-4H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3V5h18v14z\"/></svg>";
|
|
84
|
+
readonly exitPip: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3V5h18v14zM9 9h6v2H9z\"/></svg>";
|
|
85
|
+
readonly settings: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M19.14 12.94c.04-.31.06-.63.06-.94 0-.31-.02-.63-.06-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.04.31-.06.63-.06.94s.02.63.06.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z\"/></svg>";
|
|
86
|
+
readonly chromecast: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M1 18v3h3c0-1.66-1.34-3-3-3zm0-4v2c2.76 0 5 2.24 5 5h2c0-3.87-3.13-7-7-7zm0-4v2c4.97 0 9 4.03 9 9h2c0-6.08-4.93-11-11-11zm20-7H3c-1.1 0-2 .9-2 2v3h2V5h18v14h-7v2h7c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z\"/></svg>";
|
|
87
|
+
readonly chromecastConnected: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M1 18v3h3c0-1.66-1.34-3-3-3zm0-4v2c2.76 0 5 2.24 5 5h2c0-3.87-3.13-7-7-7zm18-7H5v1.63c3.96 1.28 7.09 4.41 8.37 8.37H19V7zM1 10v2c4.97 0 9 4.03 9 9h2c0-6.08-4.93-11-11-11zm20-7H3c-1.1 0-2 .9-2 2v3h2V5h18v14h-7v2h7c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z\"/></svg>";
|
|
88
|
+
readonly airplay: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M6 22h12l-6-6-6 6zM21 3H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h4v-2H3V5h18v12h-4v2h4c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z\"/></svg>";
|
|
89
|
+
readonly captions: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 7H9.5v-.5h-2v3h2V13H11v1c0 .55-.45 1-1 1H7c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1zm7 0h-1.5v-.5h-2v3h2V13H18v1c0 .55-.45 1-1 1h-3c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1z\"/></svg>";
|
|
90
|
+
readonly captionsOff: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M19.5 5.5v13h-15v-13h15zM19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2z\"/></svg>";
|
|
91
|
+
readonly checkmark: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\"/></svg>";
|
|
92
|
+
readonly chevronUp: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 8l-6 6 1.41 1.41L12 10.83l4.59 4.58L18 14z\"/></svg>";
|
|
93
|
+
readonly chevronDown: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z\"/></svg>";
|
|
94
|
+
readonly spinner: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" class=\"sp-spin\"><path d=\"M12 4V2A10 10 0 0 0 2 12h2a8 8 0 0 1 8-8z\"/></svg>";
|
|
95
|
+
readonly skipForward: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M4 18l8.5-6L4 6v12zm9-12v12l8.5-6L13 6z\"/></svg>";
|
|
96
|
+
readonly skipBack: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M11 18V6l-8.5 6 8.5 6zm.5-6l8.5 6V6l-8.5 6z\"/></svg>";
|
|
97
|
+
readonly forward10: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M18 13c0 3.31-2.69 6-6 6s-6-2.69-6-6 2.69-6 6-6v4l5-5-5-5v4c-4.42 0-8 3.58-8 8s3.58 8 8 8 8-3.58 8-8h-2z\"/><text x=\"12\" y=\"15\" text-anchor=\"middle\" font-size=\"7\" font-weight=\"600\">10</text></svg>";
|
|
98
|
+
readonly replay10: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z\"/><text x=\"12\" y=\"15\" text-anchor=\"middle\" font-size=\"7\" font-weight=\"600\">10</text></svg>";
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* CSS Styles for UI Controls
|
|
103
|
+
*
|
|
104
|
+
* Modern, minimal design inspired by Mux Player and Vidstack.
|
|
105
|
+
* Uses CSS custom properties for theming.
|
|
106
|
+
*/
|
|
107
|
+
declare const styles = "\n/* ============================================\n Container & Base\n ============================================ */\n.sp-container {\n position: relative;\n width: 100%;\n height: 100%;\n background: #000;\n overflow: hidden;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n}\n\n.sp-container video {\n width: 100%;\n height: 100%;\n display: block;\n object-fit: contain;\n}\n\n.sp-container:focus {\n outline: none;\n}\n\n/* ============================================\n Gradient Overlay\n ============================================ */\n.sp-gradient {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 160px;\n background: linear-gradient(\n to top,\n rgba(0, 0, 0, 0.8) 0%,\n rgba(0, 0, 0, 0.4) 50%,\n transparent 100%\n );\n pointer-events: none;\n opacity: 0;\n transition: opacity 0.25s ease;\n z-index: 5;\n}\n\n.sp-gradient--visible {\n opacity: 1;\n}\n\n/* ============================================\n Controls Container\n ============================================ */\n.sp-controls {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n display: flex;\n align-items: center;\n padding: 0 12px 12px;\n gap: 4px;\n opacity: 0;\n transform: translateY(4px);\n transition: opacity 0.25s ease, transform 0.25s ease;\n z-index: 10;\n}\n\n.sp-controls--visible {\n opacity: 1;\n transform: translateY(0);\n}\n\n.sp-controls--hidden {\n opacity: 0;\n transform: translateY(4px);\n pointer-events: none;\n}\n\n/* ============================================\n Progress Bar (Above Controls)\n ============================================ */\n.sp-progress-wrapper {\n position: absolute;\n bottom: 48px;\n left: 12px;\n right: 12px;\n height: 20px;\n display: flex;\n align-items: center;\n cursor: pointer;\n z-index: 10;\n opacity: 0;\n transition: opacity 0.25s ease;\n}\n\n.sp-progress-wrapper--visible {\n opacity: 1;\n}\n\n.sp-progress {\n position: relative;\n width: 100%;\n height: 3px;\n background: rgba(255, 255, 255, 0.3);\n border-radius: 1.5px;\n transition: height 0.15s ease;\n}\n\n.sp-progress-wrapper:hover .sp-progress,\n.sp-progress--dragging {\n height: 5px;\n}\n\n.sp-progress__track {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n border-radius: inherit;\n overflow: hidden;\n}\n\n.sp-progress__buffered {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: rgba(255, 255, 255, 0.4);\n border-radius: inherit;\n transition: width 0.1s linear;\n}\n\n.sp-progress__filled {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: var(--sp-accent, #e50914);\n border-radius: inherit;\n}\n\n.sp-progress__handle {\n position: absolute;\n top: 50%;\n width: 14px;\n height: 14px;\n background: var(--sp-accent, #e50914);\n border-radius: 50%;\n transform: translate(-50%, -50%) scale(0);\n transition: transform 0.15s ease;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);\n}\n\n.sp-progress-wrapper:hover .sp-progress__handle,\n.sp-progress--dragging .sp-progress__handle {\n transform: translate(-50%, -50%) scale(1);\n}\n\n/* Progress Tooltip */\n.sp-progress__tooltip {\n position: absolute;\n bottom: calc(100% + 8px);\n padding: 6px 10px;\n background: rgba(20, 20, 20, 0.95);\n color: #fff;\n font-size: 12px;\n font-weight: 500;\n font-variant-numeric: tabular-nums;\n border-radius: 4px;\n white-space: nowrap;\n transform: translateX(-50%);\n pointer-events: none;\n opacity: 0;\n transition: opacity 0.15s ease;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);\n}\n\n.sp-progress-wrapper:hover .sp-progress__tooltip {\n opacity: 1;\n}\n\n/* ============================================\n Control Buttons\n ============================================ */\n.sp-control {\n background: none;\n border: none;\n color: rgba(255, 255, 255, 0.9);\n cursor: pointer;\n padding: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n transition: color 0.15s ease, transform 0.15s ease, background 0.15s ease;\n flex-shrink: 0;\n}\n\n.sp-control:hover {\n color: #fff;\n background: rgba(255, 255, 255, 0.1);\n}\n\n.sp-control:active {\n transform: scale(0.92);\n}\n\n.sp-control:focus-visible {\n outline: 2px solid var(--sp-accent, #e50914);\n outline-offset: 2px;\n}\n\n.sp-control:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n transform: none;\n}\n\n.sp-control:disabled:hover {\n background: none;\n}\n\n.sp-control svg {\n width: 24px;\n height: 24px;\n fill: currentColor;\n display: block;\n}\n\n.sp-control--small svg {\n width: 20px;\n height: 20px;\n}\n\n/* ============================================\n Spacer\n ============================================ */\n.sp-spacer {\n flex: 1;\n min-width: 0;\n}\n\n/* ============================================\n Time Display\n ============================================ */\n.sp-time {\n font-size: 13px;\n font-variant-numeric: tabular-nums;\n color: rgba(255, 255, 255, 0.9);\n white-space: nowrap;\n padding: 0 4px;\n letter-spacing: 0.02em;\n}\n\n/* ============================================\n Volume Control\n ============================================ */\n.sp-volume {\n display: flex;\n align-items: center;\n position: relative;\n}\n\n.sp-volume__slider-wrap {\n width: 0;\n overflow: hidden;\n transition: width 0.2s ease;\n}\n\n.sp-volume:hover .sp-volume__slider-wrap,\n.sp-volume:focus-within .sp-volume__slider-wrap {\n width: 64px;\n}\n\n.sp-volume__slider {\n width: 64px;\n height: 3px;\n background: rgba(255, 255, 255, 0.3);\n border-radius: 1.5px;\n cursor: pointer;\n position: relative;\n margin: 0 8px 0 4px;\n}\n\n.sp-volume__level {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: #fff;\n border-radius: inherit;\n transition: width 0.1s ease;\n}\n\n/* ============================================\n Live Indicator\n ============================================ */\n.sp-live {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--sp-accent, #e50914);\n cursor: pointer;\n padding: 6px 10px;\n border-radius: 4px;\n transition: background 0.15s ease, opacity 0.15s ease;\n}\n\n.sp-live:hover {\n background: rgba(255, 255, 255, 0.1);\n}\n\n.sp-live__dot {\n width: 8px;\n height: 8px;\n background: currentColor;\n border-radius: 50%;\n animation: sp-pulse 2s ease-in-out infinite;\n}\n\n.sp-live--behind {\n opacity: 0.6;\n}\n\n.sp-live--behind .sp-live__dot {\n animation: none;\n}\n\n@keyframes sp-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.4; }\n}\n\n/* ============================================\n Quality / Settings Menu\n ============================================ */\n.sp-quality {\n position: relative;\n}\n\n.sp-quality__btn {\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.sp-quality__label {\n font-size: 12px;\n font-weight: 500;\n opacity: 0.9;\n}\n\n.sp-quality-menu {\n position: absolute;\n bottom: calc(100% + 8px);\n right: 0;\n background: rgba(20, 20, 20, 0.95);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n border-radius: 8px;\n padding: 8px 0;\n min-width: 150px;\n box-shadow: 0 4px 24px rgba(0, 0, 0, 0.4);\n opacity: 0;\n visibility: hidden;\n transform: translateY(8px);\n transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s;\n z-index: 20;\n}\n\n.sp-quality-menu--open {\n opacity: 1;\n visibility: visible;\n transform: translateY(0);\n}\n\n.sp-quality-menu__item {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 16px;\n font-size: 13px;\n color: rgba(255, 255, 255, 0.8);\n cursor: pointer;\n transition: background 0.1s ease, color 0.1s ease;\n}\n\n.sp-quality-menu__item:hover {\n background: rgba(255, 255, 255, 0.1);\n color: #fff;\n}\n\n.sp-quality-menu__item--active {\n color: var(--sp-accent, #e50914);\n}\n\n.sp-quality-menu__check {\n width: 16px;\n height: 16px;\n fill: currentColor;\n margin-left: 8px;\n opacity: 0;\n}\n\n.sp-quality-menu__item--active .sp-quality-menu__check {\n opacity: 1;\n}\n\n/* ============================================\n Cast Button States\n ============================================ */\n.sp-cast--active {\n color: var(--sp-accent, #e50914);\n}\n\n.sp-cast--unavailable {\n opacity: 0.4;\n}\n\n/* ============================================\n Buffering Indicator\n ============================================ */\n.sp-buffering {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n z-index: 15;\n pointer-events: none;\n opacity: 0;\n transition: opacity 0.2s ease;\n}\n\n.sp-buffering--visible {\n opacity: 1;\n}\n\n.sp-buffering svg {\n width: 48px;\n height: 48px;\n fill: rgba(255, 255, 255, 0.9);\n filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3));\n}\n\n@keyframes sp-spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n}\n\n.sp-spin {\n animation: sp-spin 0.8s linear infinite;\n}\n\n/* ============================================\n Reduced Motion\n ============================================ */\n@media (prefers-reduced-motion: reduce) {\n .sp-gradient,\n .sp-controls,\n .sp-progress-wrapper,\n .sp-progress,\n .sp-progress__handle,\n .sp-progress__tooltip,\n .sp-control,\n .sp-volume__slider-wrap,\n .sp-quality-menu,\n .sp-buffering {\n transition: none;\n }\n\n .sp-live__dot,\n .sp-spin {\n animation: none;\n }\n}\n\n/* ============================================\n CSS Custom Properties (Theming)\n ============================================ */\n:root {\n --sp-accent: #e50914;\n --sp-color: #fff;\n --sp-bg: rgba(0, 0, 0, 0.8);\n --sp-control-height: 48px;\n --sp-icon-size: 24px;\n}\n";
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Formatting utility functions
|
|
111
|
+
*/
|
|
112
|
+
/**
|
|
113
|
+
* Format seconds as time string (mm:ss or h:mm:ss).
|
|
114
|
+
*/
|
|
115
|
+
declare function formatTime(seconds: number): string;
|
|
116
|
+
/**
|
|
117
|
+
* Format time for live streams (relative to live edge).
|
|
118
|
+
*/
|
|
119
|
+
declare function formatLiveTime(behindLive: number): string;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* UI Controls Plugin for Scarlett Player
|
|
123
|
+
*
|
|
124
|
+
* Provides a complete, themeable video player control interface.
|
|
125
|
+
* Modern design with progress bar above controls.
|
|
126
|
+
*
|
|
127
|
+
* @packageDocumentation
|
|
128
|
+
*/
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Create a UI controls plugin instance.
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```ts
|
|
135
|
+
* import { uiPlugin } from '@scarlett-player/ui';
|
|
136
|
+
*
|
|
137
|
+
* const player = new ScarlettPlayer({
|
|
138
|
+
* container: '#player',
|
|
139
|
+
* plugins: [
|
|
140
|
+
* uiPlugin({
|
|
141
|
+
* hideDelay: 3000,
|
|
142
|
+
* theme: { accentColor: '#e50914' },
|
|
143
|
+
* }),
|
|
144
|
+
* ],
|
|
145
|
+
* });
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
declare function uiPlugin(config?: UIPluginConfig): IUIPlugin;
|
|
149
|
+
|
|
150
|
+
export { type Control, type ControlSlot, type IUIPlugin, type LayoutConfig, type ThemeConfig, type UIPluginConfig, uiPlugin as default, formatLiveTime, formatTime, icons, styles, uiPlugin };
|