horeg-audio 1.0.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/README.md +190 -0
- package/dist/horeg-audio.cjs +828 -0
- package/dist/horeg-audio.cjs.map +1 -0
- package/dist/horeg-audio.global.js +828 -0
- package/dist/horeg-audio.global.js.map +1 -0
- package/dist/horeg-audio.js +1677 -0
- package/dist/horeg-audio.js.map +1 -0
- package/dist/index.d.ts +152 -0
- package/package.json +40 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"horeg-audio.js","sources":["../src/styles.ts","../src/utils/time.ts","../src/AudioEngine.ts","../src/utils/dom.ts","../src/UI.ts","../src/visualizer.ts","../src/Player.ts"],"sourcesContent":["import { HoregTheme, ThemeVariant } from './types';\r\n\r\nexport const THEME_PRESETS: Record<ThemeVariant, Partial<HoregTheme>> = {\r\n 'horeg-classic': {\r\n primaryGlowColor: '#f59e0b',\r\n accentColor: '#ef4444',\r\n cardBackground: '#121214',\r\n textColor: '#f4f4f5',\r\n sliderProgressColor: '#f59e0b',\r\n borderRadius: '14px'\r\n },\r\n 'horeg-nightclub': {\r\n primaryGlowColor: '#06b6d4',\r\n accentColor: '#ec4899',\r\n cardBackground: '#0b0c10',\r\n textColor: '#f8fafc',\r\n sliderProgressColor: '#06b6d4',\r\n borderRadius: '14px'\r\n },\r\n 'horeg-stealth': {\r\n primaryGlowColor: '#94a3b8',\r\n accentColor: '#e2e8f0',\r\n cardBackground: '#0f1115',\r\n textColor: '#e2e8f0',\r\n sliderProgressColor: '#94a3b8',\r\n borderRadius: '8px'\r\n }\r\n};\r\n\r\n/**\r\n * Returns complete encapsulated CSS string for the Shadow DOM.\r\n */\r\nexport function generateStyles(theme: HoregTheme = {}): string {\r\n const variant = theme.variant || 'horeg-classic';\r\n const preset = THEME_PRESETS[variant] || THEME_PRESETS['horeg-classic'];\r\n\r\n const glow = theme.primaryGlowColor || preset.primaryGlowColor || '#f59e0b';\r\n const accent = theme.accentColor || preset.accentColor || '#ef4444';\r\n const bg = theme.cardBackground || preset.cardBackground || '#121214';\r\n const text = theme.textColor || preset.textColor || '#f4f4f5';\r\n const progress = theme.sliderProgressColor || preset.sliderProgressColor || glow;\r\n const radius = theme.borderRadius || preset.borderRadius || '14px';\r\n\r\n return `\r\n :host {\r\n --horeg-bg: ${bg};\r\n --horeg-surface: #18181b;\r\n --horeg-surface-hover: #222226;\r\n --horeg-border: #27272a;\r\n --horeg-glow: ${glow};\r\n --horeg-glow-rgb: 245, 158, 11;\r\n --horeg-accent: ${accent};\r\n --horeg-progress: ${progress};\r\n --horeg-text-main: ${text};\r\n --horeg-text-muted: #a1a1aa;\r\n --horeg-radius: ${radius};\r\n display: block;\r\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\r\n box-sizing: border-box;\r\n outline: none;\r\n user-select: none;\r\n -webkit-user-select: none;\r\n }\r\n\r\n *, *::before, *::after {\r\n box-sizing: border-box;\r\n margin: 0;\r\n padding: 0;\r\n }\r\n\r\n .horeg-player-box {\r\n background: var(--horeg-bg);\r\n border: 2px solid var(--horeg-border);\r\n border-radius: var(--horeg-radius);\r\n box-shadow: 0 16px 40px -10px rgba(0, 0, 0, 0.9), 0 0 20px -5px var(--horeg-glow);\r\n color: var(--horeg-text-main);\r\n position: relative;\r\n overflow: hidden;\r\n max-width: 520px;\r\n width: 100%;\r\n margin: 0 auto;\r\n transition: box-shadow 0.3s ease, border-color 0.3s ease;\r\n }\r\n\r\n /* Player Box active glow when focused within */\r\n :host(:focus) .horeg-player-box,\r\n :host(:focus-within) .horeg-player-box,\r\n .horeg-player-box:focus-within {\r\n border-color: var(--horeg-glow);\r\n box-shadow: 0 20px 48px -10px rgba(0, 0, 0, 0.95), 0 0 30px -2px var(--horeg-glow), 0 0 10px var(--horeg-glow);\r\n }\r\n\r\n :host(:focus) .horeg-player-box .horeg-bolt,\r\n :host(:focus-within) .horeg-player-box .horeg-bolt,\r\n .horeg-player-box:focus-within .horeg-bolt {\r\n box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.3), 0 0 4px var(--horeg-glow);\r\n }\r\n\r\n .horeg-speaker-grill {\r\n position: absolute;\r\n inset: 0;\r\n background-image: radial-gradient(#2c2d33 1.2px, transparent 1.2px);\r\n background-size: 8px 8px;\r\n opacity: 0.3;\r\n pointer-events: none;\r\n z-index: 1;\r\n }\r\n\r\n /* Metal Corner Brackets / Screws (Sound System Rig Rigging) */\r\n .horeg-bolt {\r\n position: absolute;\r\n width: 8px;\r\n height: 8px;\r\n background: #3f3f46;\r\n border: 1px solid #18181b;\r\n border-radius: 50%;\r\n box-shadow: inset 0 1px 1px rgba(255,255,255,0.2), 0 1px 2px rgba(0,0,0,0.8);\r\n z-index: 2;\r\n }\r\n .horeg-bolt::after {\r\n content: '';\r\n position: absolute;\r\n top: 50%;\r\n left: 50%;\r\n width: 4px;\r\n height: 1px;\r\n background: #18181b;\r\n transform: translate(-50%, -50%) rotate(45deg);\r\n }\r\n .horeg-bolt.top-left { top: 7px; left: 7px; }\r\n .horeg-bolt.top-right { top: 7px; right: 7px; }\r\n .horeg-bolt.bottom-left { bottom: 7px; left: 7px; }\r\n .horeg-bolt.bottom-right { bottom: 7px; right: 7px; }\r\n\r\n .horeg-content-wrap {\r\n position: relative;\r\n z-index: 3;\r\n padding: 18px 20px 16px 20px;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 14px;\r\n }\r\n\r\n /* Top Rig Header */\r\n .horeg-header {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n border-bottom: 1px solid #27272a;\r\n padding-bottom: 8px;\r\n }\r\n\r\n .horeg-rig-badge {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n font-size: 10px;\r\n letter-spacing: 1.5px;\r\n text-transform: uppercase;\r\n font-weight: 800;\r\n color: var(--horeg-glow);\r\n text-shadow: 0 0 8px var(--horeg-glow);\r\n }\r\n\r\n .horeg-badge-led {\r\n width: 7px;\r\n height: 7px;\r\n background-color: var(--horeg-glow);\r\n border-radius: 50%;\r\n box-shadow: 0 0 6px var(--horeg-glow);\r\n animation: ledPulse 2s infinite ease-in-out;\r\n }\r\n\r\n @keyframes ledPulse {\r\n 0%, 100% { opacity: 0.5; transform: scale(0.9); }\r\n 50% { opacity: 1; transform: scale(1.1); box-shadow: 0 0 10px var(--horeg-glow); }\r\n }\r\n\r\n /* Track Display & Visualizer Section */\r\n .horeg-track-row {\r\n display: flex;\r\n align-items: center;\r\n gap: 14px;\r\n }\r\n\r\n .horeg-cover-container {\r\n position: relative;\r\n width: 64px;\r\n height: 64px;\r\n min-width: 64px;\r\n border-radius: 8px;\r\n overflow: hidden;\r\n background: #18181b;\r\n border: 1px solid #3f3f46;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n box-shadow: inset 0 0 10px rgba(0,0,0,0.8), 0 2px 6px rgba(0,0,0,0.6);\r\n }\r\n\r\n .horeg-cover-img {\r\n width: 100%;\r\n height: 100%;\r\n object-fit: cover;\r\n }\r\n\r\n .horeg-cover-fallback {\r\n color: var(--horeg-glow);\r\n opacity: 0.7;\r\n }\r\n\r\n .horeg-track-info {\r\n flex: 1;\r\n min-width: 0;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 2px;\r\n }\r\n\r\n .horeg-track-title {\r\n font-size: 15px;\r\n font-weight: 700;\r\n white-space: nowrap;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n color: var(--horeg-text-main);\r\n letter-spacing: 0.2px;\r\n }\r\n\r\n .horeg-track-artist {\r\n font-size: 12px;\r\n font-weight: 500;\r\n color: var(--horeg-text-muted);\r\n white-space: nowrap;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n }\r\n\r\n .horeg-track-album {\r\n font-size: 10px;\r\n color: #71717a;\r\n white-space: nowrap;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n }\r\n\r\n /* Mini EQ Visualizer */\r\n .horeg-eq-wrapper {\r\n display: flex;\r\n align-items: flex-end;\r\n gap: 2px;\r\n height: 24px;\r\n padding: 0 4px;\r\n }\r\n\r\n .horeg-eq-bar {\r\n width: 3px;\r\n height: 4px;\r\n background: var(--horeg-glow);\r\n border-radius: 1px;\r\n transition: height 0.1s ease;\r\n box-shadow: 0 0 4px var(--horeg-glow);\r\n }\r\n\r\n /* Scrubbing / Fader Progress Bar */\r\n .horeg-progress-container {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 5px;\r\n }\r\n\r\n .horeg-time-row {\r\n display: flex;\r\n justify-content: space-between;\r\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", monospace;\r\n font-size: 11px;\r\n color: var(--horeg-text-muted);\r\n }\r\n\r\n .horeg-slider-track {\r\n position: relative;\r\n width: 100%;\r\n height: 7px;\r\n background: #27272a;\r\n border-radius: 4px;\r\n cursor: pointer;\r\n overflow: hidden;\r\n box-shadow: inset 0 1px 3px rgba(0,0,0,0.8);\r\n }\r\n\r\n .horeg-buffer-bar {\r\n position: absolute;\r\n top: 0;\r\n bottom: 0;\r\n left: 0;\r\n width: 0%;\r\n background: #3f3f46;\r\n border-radius: 4px;\r\n transition: width 0.2s linear;\r\n }\r\n\r\n .horeg-fill-bar {\r\n position: absolute;\r\n top: 0;\r\n bottom: 0;\r\n left: 0;\r\n width: 0%;\r\n background: linear-gradient(90deg, var(--horeg-accent) 0%, var(--horeg-progress) 100%);\r\n border-radius: 4px;\r\n box-shadow: 0 0 8px var(--horeg-glow);\r\n transition: width 0.08s linear;\r\n }\r\n\r\n /* Controls Row */\r\n .horeg-controls-row {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding-top: 4px;\r\n }\r\n\r\n .horeg-side-controls {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n }\r\n\r\n .horeg-center-controls {\r\n display: flex;\r\n align-items: center;\r\n gap: 12px;\r\n }\r\n\r\n .horeg-btn {\r\n background: transparent;\r\n border: none;\r\n outline: none;\r\n color: var(--horeg-text-muted);\r\n cursor: pointer;\r\n padding: 6px;\r\n border-radius: 8px;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n transition: all 0.15s ease;\r\n touch-action: manipulation;\r\n }\r\n\r\n .horeg-btn:hover {\r\n color: var(--horeg-text-main);\r\n background: var(--horeg-surface-hover);\r\n }\r\n\r\n .horeg-btn:active {\r\n transform: scale(0.92);\r\n filter: brightness(1.2);\r\n }\r\n\r\n .horeg-btn:focus-visible {\r\n outline: 2px solid var(--horeg-glow);\r\n outline-offset: 1px;\r\n }\r\n\r\n .horeg-btn.active {\r\n color: var(--horeg-glow);\r\n background: rgba(255, 255, 255, 0.06);\r\n text-shadow: 0 0 8px var(--horeg-glow);\r\n box-shadow: inset 0 0 8px rgba(255, 255, 255, 0.05);\r\n }\r\n\r\n /* Primary Play / Pause Button with Neon Glow Pulse */\r\n .horeg-btn-play {\r\n width: 44px;\r\n height: 44px;\r\n border-radius: 50%;\r\n background: #1f1f23;\r\n border: 2px solid var(--horeg-glow);\r\n color: var(--horeg-glow);\r\n box-shadow: 0 0 12px -2px var(--horeg-glow);\r\n transition: transform 0.12s ease, box-shadow 0.2s ease, background 0.2s ease;\r\n }\r\n\r\n .horeg-btn-play:hover {\r\n background: #27272a;\r\n transform: scale(1.06);\r\n box-shadow: 0 0 18px 2px var(--horeg-glow);\r\n }\r\n\r\n .horeg-btn-play:active {\r\n transform: scale(0.96);\r\n }\r\n\r\n .horeg-btn-play.playing {\r\n background: var(--horeg-glow);\r\n color: #09090b;\r\n box-shadow: 0 0 20px var(--horeg-glow), inset 0 0 8px rgba(255, 255, 255, 0.4);\r\n animation: playPulse 2.2s infinite ease-in-out;\r\n }\r\n\r\n @keyframes playPulse {\r\n 0%, 100% {\r\n box-shadow: 0 0 15px var(--horeg-glow);\r\n }\r\n 50% {\r\n box-shadow: 0 0 25px var(--horeg-glow), 0 0 8px #fff;\r\n }\r\n }\r\n\r\n /* Volume Slider Rel Mixer */\r\n .horeg-volume-wrap {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n }\r\n\r\n .horeg-volume-slider {\r\n -webkit-appearance: none;\r\n appearance: none;\r\n width: 70px;\r\n height: 5px;\r\n background: #27272a;\r\n border-radius: 3px;\r\n outline: none;\r\n cursor: pointer;\r\n }\r\n\r\n .horeg-volume-slider::-webkit-slider-thumb {\r\n -webkit-appearance: none;\r\n appearance: none;\r\n width: 12px;\r\n height: 12px;\r\n border-radius: 50%;\r\n background: var(--horeg-glow);\r\n border: 1px solid #18181b;\r\n box-shadow: 0 0 6px var(--horeg-glow);\r\n cursor: pointer;\r\n transition: transform 0.1s ease;\r\n }\r\n\r\n .horeg-volume-slider::-webkit-slider-thumb:hover {\r\n transform: scale(1.2);\r\n }\r\n\r\n .horeg-volume-slider::-moz-range-thumb {\r\n width: 12px;\r\n height: 12px;\r\n border-radius: 50%;\r\n background: var(--horeg-glow);\r\n border: 1px solid #18181b;\r\n box-shadow: 0 0 6px var(--horeg-glow);\r\n cursor: pointer;\r\n }\r\n\r\n /* Drag Over Highlight on main player */\r\n .horeg-player-box.drag-over {\r\n border-color: var(--horeg-glow);\r\n box-shadow: 0 0 25px var(--horeg-glow), 0 8px 32px rgba(0, 0, 0, 0.9);\r\n }\r\n\r\n /* Rack Mount Playlist Drawer */\r\n .horeg-drawer {\r\n max-height: 0;\r\n opacity: 0;\r\n overflow-y: auto;\r\n transition: max-height 0.35s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.25s ease;\r\n background: #141416;\r\n border-top: 1px solid #27272a;\r\n border-radius: 0 0 calc(var(--horeg-radius) - 2px) calc(var(--horeg-radius) - 2px);\r\n scrollbar-width: thin;\r\n scrollbar-color: #3f3f46 #18181b;\r\n position: relative;\r\n z-index: 3;\r\n }\r\n\r\n .horeg-drawer.open {\r\n max-height: 220px;\r\n max-height: 380px;\r\n opacity: 1;\r\n }\r\n\r\n /* Drawer Header */\r\n .horeg-drawer-header {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 10px 14px 8px 14px;\r\n border-bottom: 1px solid #27272a;\r\n position: sticky;\r\n top: 0;\r\n background: #141416;\r\n z-index: 2;\r\n }\r\n\r\n .horeg-drawer-heading {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n font-size: 11px;\r\n font-weight: 700;\r\n letter-spacing: 0.8px;\r\n color: var(--horeg-text-muted);\r\n text-transform: uppercase;\r\n font-family: ui-monospace, monospace;\r\n }\r\n\r\n .horeg-drawer-count {\r\n padding: 1px 6px;\r\n background: #27272a;\r\n color: var(--horeg-glow);\r\n border-radius: 10px;\r\n font-size: 10px;\r\n font-weight: 700;\r\n }\r\n\r\n .horeg-btn-add-track {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 5px;\r\n padding: 4px 9px;\r\n font-size: 11px;\r\n font-weight: 600;\r\n color: var(--horeg-text-main);\r\n background: #1f1f23;\r\n border: 1px solid #3f3f46;\r\n border-radius: 5px;\r\n cursor: pointer;\r\n transition: all 0.15s ease;\r\n }\r\n\r\n .horeg-btn-add-track:hover {\r\n background: #27272a;\r\n border-color: var(--horeg-glow);\r\n color: var(--horeg-glow);\r\n box-shadow: 0 0 8px rgba(245, 158, 11, 0.2);\r\n }\r\n\r\n .horeg-btn-add-track.active {\r\n background: #27272a;\r\n border-color: var(--horeg-glow);\r\n color: var(--horeg-glow);\r\n box-shadow: 0 0 12px var(--horeg-glow);\r\n text-shadow: 0 0 6px var(--horeg-glow);\r\n }\r\n\r\n .horeg-btn-add-track:active {\r\n transform: scale(0.95);\r\n }\r\n\r\n /* Add Track Panel Form */\r\n .horeg-add-panel {\r\n display: none;\r\n flex-direction: column;\r\n gap: 10px;\r\n margin: 10px 12px;\r\n padding: 12px;\r\n background: #18181b;\r\n border: 1px solid #3f3f46;\r\n border-radius: 8px;\r\n box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);\r\n animation: fadeIn 0.2s ease;\r\n }\r\n\r\n .horeg-add-panel.open {\r\n display: flex;\r\n }\r\n\r\n .horeg-add-tabs {\r\n display: flex;\r\n gap: 6px;\r\n border-bottom: 1px solid #27272a;\r\n padding-bottom: 8px;\r\n }\r\n\r\n .horeg-tab-btn {\r\n flex: 1;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 6px;\r\n padding: 6px 10px;\r\n font-size: 11px;\r\n font-weight: 600;\r\n background: #202024;\r\n color: var(--horeg-text-muted);\r\n border: 1px solid #2e2e33;\r\n border-radius: 5px;\r\n cursor: pointer;\r\n transition: all 0.15s ease;\r\n }\r\n\r\n .horeg-tab-btn:hover {\r\n color: var(--horeg-text-main);\r\n border-color: #52525b;\r\n }\r\n\r\n .horeg-tab-btn.active {\r\n background: #27272a;\r\n color: var(--horeg-glow);\r\n border-color: var(--horeg-glow);\r\n box-shadow: 0 0 10px var(--horeg-glow);\r\n text-shadow: 0 0 6px var(--horeg-glow);\r\n }\r\n\r\n .horeg-tab-btn:active {\r\n transform: scale(0.97);\r\n }\r\n\r\n .horeg-tab-pane {\r\n display: none;\r\n flex-direction: column;\r\n gap: 8px;\r\n }\r\n\r\n .horeg-tab-pane.active {\r\n display: flex;\r\n }\r\n\r\n /* Dropzone for local file */\r\n .horeg-dropzone {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 6px;\r\n padding: 16px 10px;\r\n border: 2px dashed #3f3f46;\r\n border-radius: 6px;\r\n background: #141416;\r\n cursor: pointer;\r\n transition: all 0.2s ease;\r\n text-align: center;\r\n }\r\n\r\n .horeg-dropzone:hover,\r\n .horeg-dropzone.drag-active {\r\n border-color: var(--horeg-glow);\r\n background: rgba(245, 158, 11, 0.05);\r\n box-shadow: 0 0 10px rgba(245, 158, 11, 0.15);\r\n }\r\n\r\n .horeg-dropzone-icon {\r\n color: var(--horeg-glow);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n }\r\n\r\n .horeg-dropzone-text {\r\n font-size: 11px;\r\n font-weight: 600;\r\n color: var(--horeg-text-main);\r\n }\r\n\r\n .horeg-dropzone-hint {\r\n font-size: 10px;\r\n color: var(--horeg-text-muted);\r\n }\r\n\r\n /* Inputs for URL / Meta */\r\n .horeg-input-field {\r\n width: 100%;\r\n box-sizing: border-box;\r\n padding: 7px 10px;\r\n font-size: 12px;\r\n background: #121214;\r\n border: 1px solid #3f3f46;\r\n border-radius: 5px;\r\n color: var(--horeg-text-main);\r\n outline: none;\r\n transition: border-color 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;\r\n }\r\n\r\n .horeg-input-field:focus {\r\n border-color: var(--horeg-glow);\r\n background: #18181b;\r\n box-shadow: 0 0 0 1px var(--horeg-glow), 0 0 10px var(--horeg-glow);\r\n outline: none;\r\n }\r\n\r\n .horeg-input-field::placeholder {\r\n color: #71717a;\r\n font-size: 11px;\r\n }\r\n\r\n .horeg-form-actions {\r\n display: flex;\r\n align-items: center;\r\n justify-content: flex-end;\r\n gap: 6px;\r\n margin-top: 4px;\r\n }\r\n\r\n .horeg-btn-submit {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 5px;\r\n padding: 6px 12px;\r\n font-size: 11px;\r\n font-weight: 700;\r\n background: var(--horeg-glow);\r\n color: #09090b;\r\n border: none;\r\n border-radius: 5px;\r\n cursor: pointer;\r\n transition: opacity 0.15s ease, transform 0.1s ease;\r\n }\r\n\r\n .horeg-btn-submit:hover {\r\n opacity: 0.9;\r\n transform: translateY(-1px);\r\n }\r\n\r\n .horeg-btn-submit:active {\r\n transform: scale(0.96);\r\n filter: brightness(1.15);\r\n }\r\n\r\n .horeg-btn-cancel {\r\n padding: 6px 10px;\r\n font-size: 11px;\r\n font-weight: 600;\r\n background: transparent;\r\n color: var(--horeg-text-muted);\r\n border: 1px solid transparent;\r\n border-radius: 5px;\r\n cursor: pointer;\r\n transition: color 0.15s ease, transform 0.1s ease;\r\n }\r\n\r\n .horeg-btn-cancel:hover {\r\n color: var(--horeg-text-main);\r\n }\r\n\r\n .horeg-btn-cancel:active {\r\n transform: scale(0.96);\r\n }\r\n\r\n .horeg-drawer-inner {\r\n padding: 6px 10px 10px 10px;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 4px;\r\n }\r\n\r\n .horeg-track-item {\r\n display: flex;\r\n align-items: center;\r\n gap: 10px;\r\n padding: 8px 10px;\r\n border-radius: 6px;\r\n cursor: pointer;\r\n transition: background 0.15s ease;\r\n font-size: 13px;\r\n }\r\n\r\n .horeg-track-item:hover {\r\n background: #1f1f23;\r\n }\r\n\r\n .horeg-track-item.active {\r\n background: linear-gradient(90deg, rgba(255, 255, 255, 0.08) 0%, rgba(255, 255, 255, 0.02) 100%);\r\n color: var(--horeg-glow);\r\n border-left: 3px solid var(--horeg-glow);\r\n box-shadow: inset 3px 0 6px -2px var(--horeg-glow), 0 2px 8px rgba(0, 0, 0, 0.4);\r\n position: relative;\r\n }\r\n\r\n .horeg-track-item.active .horeg-track-num {\r\n color: var(--horeg-glow);\r\n font-weight: 700;\r\n text-shadow: 0 0 8px var(--horeg-glow);\r\n }\r\n\r\n .horeg-track-item.active .horeg-item-title {\r\n color: var(--horeg-glow);\r\n font-weight: 700;\r\n text-shadow: 0 0 8px var(--horeg-glow);\r\n }\r\n\r\n .horeg-track-item.active .horeg-item-artist {\r\n color: var(--horeg-text-main);\r\n opacity: 0.9;\r\n }\r\n\r\n .horeg-track-item.active .horeg-item-duration {\r\n color: var(--horeg-glow);\r\n opacity: 0.9;\r\n }\r\n\r\n .horeg-track-item:focus-visible {\r\n outline: 2px solid var(--horeg-glow);\r\n outline-offset: -1px;\r\n }\r\n\r\n .horeg-track-num {\r\n font-family: ui-monospace, monospace;\r\n font-size: 11px;\r\n color: var(--horeg-text-muted);\r\n width: 18px;\r\n }\r\n\r\n .horeg-track-details {\r\n flex: 1;\r\n min-width: 0;\r\n }\r\n\r\n .horeg-item-title {\r\n font-weight: 600;\r\n white-space: nowrap;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n }\r\n\r\n .horeg-item-artist {\r\n font-size: 11px;\r\n color: var(--horeg-text-muted);\r\n white-space: nowrap;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n }\r\n\r\n .horeg-item-duration {\r\n font-family: ui-monospace, monospace;\r\n font-size: 11px;\r\n color: var(--horeg-text-muted);\r\n margin-right: 4px;\r\n }\r\n\r\n /* Track Item Actions (Remove button) */\r\n .horeg-track-actions {\r\n display: flex;\r\n align-items: center;\r\n opacity: 0.5;\r\n transition: opacity 0.15s ease;\r\n }\r\n\r\n .horeg-track-item:hover .horeg-track-actions {\r\n opacity: 1;\r\n }\r\n\r\n .horeg-btn-remove {\r\n background: transparent;\r\n border: none;\r\n color: var(--horeg-text-muted);\r\n cursor: pointer;\r\n padding: 4px;\r\n border-radius: 4px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n transition: all 0.15s ease;\r\n }\r\n\r\n .horeg-btn-remove:hover {\r\n color: #ef4444;\r\n background: rgba(239, 68, 68, 0.15);\r\n }\r\n\r\n .horeg-empty-playlist {\r\n padding: 24px 12px;\r\n text-align: center;\r\n color: var(--horeg-text-muted);\r\n font-size: 12px;\r\n font-style: italic;\r\n }\r\n `;\r\n}\r\n\r\n","/**\n * Formats a time in seconds to mm:ss or hh:mm:ss string.\n * @param seconds Time in seconds\n * @returns Formatted time string\n */\nexport function formatTime(seconds: number): string {\n if (isNaN(seconds) || !isFinite(seconds) || seconds < 0) {\n return '00:00';\n }\n\n const roundedSeconds = Math.floor(seconds);\n const hours = Math.floor(roundedSeconds / 3600);\n const minutes = Math.floor((roundedSeconds % 3600) / 60);\n const remainingSeconds = roundedSeconds % 60;\n\n const pad = (num: number) => num.toString().padStart(2, '0');\n\n if (hours > 0) {\n return `${hours}:${pad(minutes)}:${pad(remainingSeconds)}`;\n }\n\n return `${pad(minutes)}:${pad(remainingSeconds)}`;\n}\n\n/**\n * Clamps a number between a minimum and maximum value.\n */\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\n","import { Track, LoopMode, HoregPlayerOptions } from './types';\r\nimport { clamp } from './utils/time';\r\n\r\nexport interface AudioEngineCallbacks {\r\n onPlay?: (track: Track) => void;\r\n onPause?: () => void;\r\n onTrackChange?: (track: Track, index: number) => void;\r\n onPlaylistChange?: (playlist: Track[], currentIndex: number) => void;\r\n onTimeUpdate?: (currentTime: number, duration: number) => void;\r\n onBufferUpdate?: (bufferedPercent: number) => void;\r\n onEnded?: (track: Track) => void;\r\n onError?: (error: MediaError | Error) => void;\r\n onLoadingChange?: (isLoading: boolean) => void;\r\n}\r\n\r\nexport class AudioEngine {\r\n private audio: HTMLAudioElement;\r\n private playlist: Track[] = [];\r\n private currentIndex: number = 0;\r\n private loopMode: LoopMode = 'all';\r\n private shuffleMode: boolean = false;\r\n private volumeLevel: number = 0.8;\r\n private previousVolume: number = 0.8;\r\n private callbacks: AudioEngineCallbacks = {};\r\n\r\n constructor(options: HoregPlayerOptions, callbacks: AudioEngineCallbacks = {}) {\r\n this.audio = new Audio();\r\n this.playlist = [...(options.playlist || [])];\r\n this.currentIndex = options.initialIndex !== undefined ? clamp(options.initialIndex, 0, Math.max(0, this.playlist.length - 1)) : 0;\r\n this.loopMode = options.loop || 'all';\r\n this.shuffleMode = !!options.shuffle;\r\n this.volumeLevel = options.volume !== undefined ? clamp(options.volume, 0, 1) : 0.8;\r\n this.callbacks = callbacks || {\r\n onPlay: options.onPlay,\r\n onPause: options.onPause,\r\n onTrackChange: options.onTrackChange,\r\n onPlaylistChange: options.onPlaylistChange,\r\n onTimeUpdate: options.onTimeUpdate,\r\n onEnded: options.onEnded,\r\n onError: options.onError\r\n };\r\n\r\n this.audio.volume = this.volumeLevel;\r\n this.attachEvents();\r\n\r\n if (this.playlist.length > 0) {\r\n this.loadTrack(this.currentIndex, !!options.autoplay);\r\n }\r\n }\r\n\r\n private attachEvents(): void {\r\n this.audio.addEventListener('play', this.handlePlay);\r\n this.audio.addEventListener('pause', this.handlePause);\r\n this.audio.addEventListener('timeupdate', this.handleTimeUpdate);\r\n this.audio.addEventListener('progress', this.handleProgress);\r\n this.audio.addEventListener('ended', this.handleEnded);\r\n this.audio.addEventListener('error', this.handleError);\r\n this.audio.addEventListener('waiting', this.handleWaiting);\r\n this.audio.addEventListener('playing', this.handlePlaying);\r\n this.audio.addEventListener('loadedmetadata', this.handleLoadedMetadata);\r\n }\r\n\r\n private detachEvents(): void {\r\n this.audio.removeEventListener('play', this.handlePlay);\r\n this.audio.removeEventListener('pause', this.handlePause);\r\n this.audio.removeEventListener('timeupdate', this.handleTimeUpdate);\r\n this.audio.removeEventListener('progress', this.handleProgress);\r\n this.audio.removeEventListener('ended', this.handleEnded);\r\n this.audio.removeEventListener('error', this.handleError);\r\n this.audio.removeEventListener('waiting', this.handleWaiting);\r\n this.audio.removeEventListener('playing', this.handlePlaying);\r\n this.audio.removeEventListener('loadedmetadata', this.handleLoadedMetadata);\r\n }\r\n\r\n private handlePlay = (): void => {\r\n const current = this.getCurrentTrack();\r\n if (current && this.callbacks.onPlay) {\r\n this.callbacks.onPlay(current);\r\n }\r\n };\r\n\r\n private handlePause = (): void => {\r\n if (this.callbacks.onPause) {\r\n this.callbacks.onPause();\r\n }\r\n };\r\n\r\n private handleTimeUpdate = (): void => {\r\n const current = this.audio.currentTime || 0;\r\n const duration = this.getDuration();\r\n if (this.callbacks.onTimeUpdate) {\r\n this.callbacks.onTimeUpdate(current, duration);\r\n }\r\n };\r\n\r\n private handleProgress = (): void => {\r\n if (this.audio.buffered.length > 0 && this.audio.duration > 0) {\r\n const bufferedEnd = this.audio.buffered.end(this.audio.buffered.length - 1);\r\n const percent = clamp((bufferedEnd / this.audio.duration) * 100, 0, 100);\r\n if (this.callbacks.onBufferUpdate) {\r\n this.callbacks.onBufferUpdate(percent);\r\n }\r\n }\r\n };\r\n\r\n private handleLoadedMetadata = (): void => {\r\n const current = this.audio.currentTime || 0;\r\n const duration = this.getDuration();\r\n if (this.callbacks.onTimeUpdate) {\r\n this.callbacks.onTimeUpdate(current, duration);\r\n }\r\n this.handleProgress();\r\n };\r\n\r\n private handleWaiting = (): void => {\r\n if (this.callbacks.onLoadingChange) {\r\n this.callbacks.onLoadingChange(true);\r\n }\r\n };\r\n\r\n private handlePlaying = (): void => {\r\n if (this.callbacks.onLoadingChange) {\r\n this.callbacks.onLoadingChange(false);\r\n }\r\n };\r\n\r\n private handleEnded = (): void => {\r\n const current = this.getCurrentTrack();\r\n if (current && this.callbacks.onEnded) {\r\n this.callbacks.onEnded(current);\r\n }\r\n\r\n if (this.loopMode === 'one') {\r\n this.audio.currentTime = 0;\r\n this.play();\r\n } else {\r\n this.next(true);\r\n }\r\n };\r\n\r\n private handleError = (): void => {\r\n const err = this.audio.error || new Error('Unknown audio playback error');\r\n if (this.callbacks.onError) {\r\n this.callbacks.onError(err);\r\n }\r\n };\r\n\r\n public getCurrentTrack(): Track | undefined {\r\n return this.playlist[this.currentIndex];\r\n }\r\n\r\n public getCurrentIndex(): number {\r\n return this.currentIndex;\r\n }\r\n\r\n public getPlaylist(): Track[] {\r\n return this.playlist;\r\n }\r\n\r\n public setPlaylist(newPlaylist: Track[], startIndex: number = 0): void {\r\n this.playlist = [...newPlaylist];\r\n this.currentIndex = clamp(startIndex, 0, Math.max(0, this.playlist.length - 1));\r\n this.loadTrack(this.currentIndex, false);\r\n if (this.callbacks.onPlaylistChange) {\r\n this.callbacks.onPlaylistChange([...this.playlist], this.currentIndex);\r\n }\r\n if (this.playlist.length > 0) {\r\n this.loadTrack(this.currentIndex, false);\r\n } else {\r\n this.audio.pause();\r\n this.audio.src = '';\r\n if (this.callbacks.onTrackChange) {\r\n this.callbacks.onTrackChange({ title: 'No Track Loaded', src: '' }, 0);\r\n }\r\n }\r\n }\r\n\r\n public addTrack(track: Track, autoPlay: boolean = false): number {\r\n const wasEmpty = this.playlist.length === 0;\r\n this.playlist.push(track);\r\n const newIndex = this.playlist.length - 1;\r\n\r\n if (this.callbacks.onPlaylistChange) {\r\n this.callbacks.onPlaylistChange([...this.playlist], this.currentIndex);\r\n }\r\n\r\n if (wasEmpty) {\r\n this.loadTrack(0, autoPlay);\r\n } else if (autoPlay) {\r\n this.loadTrack(newIndex, true);\r\n }\r\n\r\n return newIndex;\r\n }\r\n\r\n public addTracks(tracks: Track[], autoPlay: boolean = false): void {\r\n if (!tracks || tracks.length === 0) return;\r\n const wasEmpty = this.playlist.length === 0;\r\n const firstAddedIndex = this.playlist.length;\r\n this.playlist.push(...tracks);\r\n\r\n if (this.callbacks.onPlaylistChange) {\r\n this.callbacks.onPlaylistChange([...this.playlist], this.currentIndex);\r\n }\r\n\r\n if (wasEmpty) {\r\n this.loadTrack(0, autoPlay);\r\n } else if (autoPlay) {\r\n this.loadTrack(firstAddedIndex, true);\r\n }\r\n }\r\n\r\n public removeTrack(index: number): void {\r\n if (index < 0 || index >= this.playlist.length) return;\r\n const isCurrentTrack = index === this.currentIndex;\r\n this.playlist.splice(index, 1);\r\n\r\n if (this.playlist.length === 0) {\r\n this.currentIndex = 0;\r\n this.audio.pause();\r\n this.audio.src = '';\r\n if (this.callbacks.onTrackChange) {\r\n this.callbacks.onTrackChange({ title: 'No Track Loaded', src: '' }, 0);\r\n }\r\n } else if (isCurrentTrack) {\r\n const nextIndex = clamp(index, 0, this.playlist.length - 1);\r\n this.loadTrack(nextIndex, false);\r\n } else if (index < this.currentIndex) {\r\n this.currentIndex--;\r\n }\r\n\r\n if (this.callbacks.onPlaylistChange) {\r\n this.callbacks.onPlaylistChange([...this.playlist], this.currentIndex);\r\n }\r\n }\r\n\r\n public getDuration(): number {\r\n const dur = this.audio.duration;\r\n if (dur && !isNaN(dur) && isFinite(dur)) {\r\n return dur;\r\n }\r\n const currentTrack = this.getCurrentTrack();\r\n return currentTrack?.duration || 0;\r\n }\r\n\r\n public getCurrentTime(): number {\r\n return this.audio.currentTime || 0;\r\n }\r\n\r\n public isPlaying(): boolean {\r\n return !this.audio.paused && !this.audio.ended;\r\n }\r\n\r\n public isMuted(): boolean {\r\n return this.audio.muted || this.audio.volume === 0;\r\n }\r\n\r\n public getVolume(): number {\r\n return this.audio.volume;\r\n }\r\n\r\n public getLoop(): LoopMode {\r\n return this.loopMode;\r\n }\r\n\r\n public isShuffle(): boolean {\r\n return this.shuffleMode;\r\n }\r\n\r\n public loadTrack(indexOrTrack: number | Track, autoPlay: boolean = false): void {\r\n let targetIndex: number;\r\n\r\n if (typeof indexOrTrack === 'number') {\r\n targetIndex = clamp(indexOrTrack, 0, Math.max(0, this.playlist.length - 1));\r\n } else {\r\n targetIndex = this.playlist.findIndex((t) => t.src === indexOrTrack.src);\r\n if (targetIndex === -1) {\r\n this.playlist.push(indexOrTrack);\r\n targetIndex = this.playlist.length - 1;\r\n }\r\n }\r\n\r\n this.currentIndex = targetIndex;\r\n const track = this.playlist[this.currentIndex];\r\n\r\n if (!track) return;\r\n\r\n this.audio.src = track.src;\r\n\r\n if (this.callbacks.onTrackChange) {\r\n this.callbacks.onTrackChange(track, this.currentIndex);\r\n }\r\n\r\n if (autoPlay) {\r\n this.play();\r\n }\r\n }\r\n\r\n public async play(): Promise<void> {\r\n try {\r\n await this.audio.play();\r\n } catch (err) {\r\n // Modern browser autoplay policy rejection handling\r\n // Prevents unhandled promise rejection error in developer console\r\n console.warn('[HoregAudio] Autoplay / Audio play was prevented by browser policy:', err);\r\n }\r\n }\r\n\r\n public pause(): void {\r\n this.audio.pause();\r\n }\r\n\r\n public toggle(): void {\r\n if (this.audio.paused) {\r\n this.play();\r\n } else {\r\n this.pause();\r\n }\r\n }\r\n\r\n public seek(seconds: number): void {\r\n const duration = this.getDuration();\r\n const clampedTime = clamp(seconds, 0, duration > 0 ? duration : Infinity);\r\n this.audio.currentTime = clampedTime;\r\n }\r\n\r\n public setVolume(level: number): void {\r\n const clamped = clamp(level, 0, 1);\r\n this.volumeLevel = clamped;\r\n this.audio.volume = clamped;\r\n if (clamped > 0) {\r\n this.audio.muted = false;\r\n this.previousVolume = clamped;\r\n }\r\n }\r\n\r\n public toggleMute(): boolean {\r\n if (this.isMuted()) {\r\n this.audio.muted = false;\r\n this.setVolume(this.previousVolume > 0 ? this.previousVolume : 0.5);\r\n return false;\r\n } else {\r\n this.previousVolume = this.audio.volume;\r\n this.audio.muted = true;\r\n return true;\r\n }\r\n }\r\n\r\n public next(isFromEnded: boolean = false): void {\r\n if (this.playlist.length === 0) return;\r\n\r\n if (this.shuffleMode && this.playlist.length > 1) {\r\n let randomIndex: number;\r\n do {\r\n randomIndex = Math.floor(Math.random() * this.playlist.length);\r\n } while (randomIndex === this.currentIndex);\r\n this.loadTrack(randomIndex, true);\r\n return;\r\n }\r\n\r\n const nextIndex = this.currentIndex + 1;\r\n if (nextIndex < this.playlist.length) {\r\n this.loadTrack(nextIndex, true);\r\n } else if (this.loopMode === 'all') {\r\n this.loadTrack(0, true);\r\n } else if (!isFromEnded) {\r\n this.loadTrack(0, true);\r\n } else {\r\n this.pause();\r\n }\r\n }\r\n\r\n public prev(): void {\r\n if (this.playlist.length === 0) return;\r\n\r\n // If more than 2 seconds in, restart track\r\n if (this.audio.currentTime > 2) {\r\n this.seek(0);\r\n return;\r\n }\r\n\r\n if (this.shuffleMode && this.playlist.length > 1) {\r\n let randomIndex: number;\r\n do {\r\n randomIndex = Math.floor(Math.random() * this.playlist.length);\r\n } while (randomIndex === this.currentIndex);\r\n this.loadTrack(randomIndex, true);\r\n return;\r\n }\r\n\r\n const prevIndex = this.currentIndex - 1;\r\n if (prevIndex >= 0) {\r\n this.loadTrack(prevIndex, true);\r\n } else if (this.loopMode === 'all') {\r\n this.loadTrack(this.playlist.length - 1, true);\r\n } else {\r\n this.seek(0);\r\n }\r\n }\r\n\r\n public setLoop(mode: LoopMode): void {\r\n this.loopMode = mode;\r\n }\r\n\r\n public setShuffle(shuffle: boolean): void {\r\n this.shuffleMode = shuffle;\r\n }\r\n\r\n public setCallbacks(callbacks: AudioEngineCallbacks): void {\r\n this.callbacks = { ...this.callbacks, ...callbacks };\r\n }\r\n\r\n public destroy(): void {\r\n this.detachEvents();\r\n this.audio.pause();\r\n this.audio.src = '';\r\n this.audio.load();\r\n }\r\n}\r\n\r\n","export const ICONS = {\r\n play: `<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\"><path d=\"M8 5v14l11-7z\"/></svg>`,\r\n pause: `<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\"><path d=\"M6 19h4V5H6v14zm8-14v14h4V5h-4z\"/></svg>`,\r\n prev: `<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"18\" height=\"18\"><path d=\"M6 6h2v12H6zm3.5 6l8.5 6V6z\"/></svg>`,\r\n next: `<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"18\" height=\"18\"><path d=\"M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z\"/></svg>`,\r\n volumeHigh: `<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"18\" height=\"18\"><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>`,\r\n volumeMute: `<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"18\" height=\"18\"><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.27l4.73 4.73H3v6h4l5 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>`,\r\n shuffle: `<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"16\" height=\"16\"><path d=\"M10.59 9.17L5.41 4 4 5.41l5.17 5.17 1.42-1.41zM14.5 4l2.04 2.04L4 18.59 5.41 20 17.96 7.46 20 9.5V4h-5.5zm.33 9.41l-1.41 1.41 3.13 3.13L14.5 20H20v-5.5l-2.04 2.04-3.13-3.13z\"/></svg>`,\r\n repeat: `<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"16\" height=\"16\"><path d=\"M7 7h10v3l4-4-4-4v3H5v6h2V7zm10 10H7v-3l-4 4 4 4v-3h12v-6h-2v4z\"/></svg>`,\r\n repeatOne: `<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"16\" height=\"16\"><path d=\"M7 7h10v3l4-4-4-4v3H5v6h2V7zm10 10H7v-3l-4 4 4 4v-3h12v-6h-2v4zm-4-2V9h-1l-2 1v1h1.5v4H13z\"/></svg>`,\r\n playlist: `<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"18\" height=\"18\"><path d=\"M3 10h11v2H3zm0-4h11v2H3zm0 8h7v2H3zm13-1v8l6-4-6-4z\"/></svg>`,\r\n speaker: `<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"14\" height=\"14\"><path d=\"M17 2H7c-1.1 0-2 .9-2 2v16c0 1.1.9 1.99 2 1.99L17 22c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-5 2c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm0 16c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z\"/></svg>`,\r\n plus: `<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"14\" height=\"14\"><path d=\"M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\"/></svg>`,\r\n trash: `<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"14\" height=\"14\"><path d=\"M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z\"/></svg>`,\r\n upload: `<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"14\" height=\"14\"><path d=\"M9 16h6v-6h4l-7-7-7 7h4zm-4 2h14v2H5z\"/></svg>`,\r\n link: `<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"14\" height=\"14\"><path d=\"M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z\"/></svg>`,\r\n close: `<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"14\" height=\"14\"><path d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"/></svg>`\r\n};\r\n\r\n/**\r\n * Creates an element with optional class names and attributes.\r\n */\r\nexport function createElement<K extends keyof HTMLElementTagNameMap>(\r\n tagName: K,\r\n options?: {\r\n className?: string;\r\n attributes?: Record<string, string>;\r\n innerHTML?: string;\r\n textContent?: string;\r\n }\r\n): HTMLElementTagNameMap[K] {\r\n const el = document.createElement(tagName);\r\n if (options?.className) {\r\n el.className = options.className;\r\n }\r\n if (options?.attributes) {\r\n for (const [key, val] of Object.entries(options.attributes)) {\r\n el.setAttribute(key, val);\r\n }\r\n }\r\n if (options?.innerHTML) {\r\n el.innerHTML = options.innerHTML;\r\n } else if (options?.textContent) {\r\n el.textContent = options.textContent;\r\n }\r\n return el;\r\n}\r\n\r\n","import { Track, LoopMode } from './types';\r\nimport { formatTime, clamp } from './utils/time';\r\nimport { ICONS, createElement } from './utils/dom';\r\n\r\nexport interface UIEvents {\r\n onPlayPauseClick: () => void;\r\n onPrevClick: () => void;\r\n onNextClick: () => void;\r\n onSeek: (seconds: number) => void;\r\n onVolumeChange: (volume: number) => void;\r\n onMuteToggle: () => void;\r\n onShuffleToggle: () => void;\r\n onLoopToggle: () => void;\r\n onTrackSelect: (index: number) => void;\r\n onAddTrackFiles?: (files: File[]) => void;\r\n onAddTrackUrl?: (url: string, title?: string, artist?: string) => void;\r\n onRemoveTrack?: (index: number) => void;\r\n}\r\n\r\nexport class UI {\r\n public root: HTMLElement;\r\n public eqContainer: HTMLElement;\r\n\r\n private playBtn: HTMLButtonElement;\r\n private prevBtn: HTMLButtonElement;\r\n private nextBtn: HTMLButtonElement;\r\n private shuffleBtn: HTMLButtonElement;\r\n private loopBtn: HTMLButtonElement;\r\n private muteBtn: HTMLButtonElement;\r\n private drawerBtn: HTMLButtonElement;\r\n\r\n private titleEl: HTMLElement;\r\n private artistEl: HTMLElement;\r\n private albumEl: HTMLElement;\r\n private coverImgEl: HTMLImageElement | null = null;\r\n private coverContainer: HTMLElement;\r\n\r\n private currentTimeEl: HTMLElement;\r\n private durationEl: HTMLElement;\r\n private sliderTrack: HTMLElement;\r\n private fillBar: HTMLElement;\r\n private bufferBar: HTMLElement;\r\n\r\n private volumeSlider: HTMLInputElement;\r\n private drawerEl: HTMLElement;\r\n private drawerCountEl: HTMLElement;\r\n private addTrackBtn: HTMLButtonElement;\r\n private addPanelEl: HTMLElement;\r\n private fileInputEl: HTMLInputElement;\r\n private tabBtnFile: HTMLButtonElement;\r\n private tabBtnUrl: HTMLButtonElement;\r\n private tabPaneFile: HTMLElement;\r\n private tabPaneUrl: HTMLElement;\r\n private urlInput: HTMLInputElement;\r\n private titleInput: HTMLInputElement;\r\n private artistInput: HTMLInputElement;\r\n private submitUrlBtn: HTMLButtonElement;\r\n private cancelAddBtn: HTMLButtonElement;\r\n private dropzoneEl: HTMLElement;\r\n private drawerInner: HTMLElement;\r\n\r\n private isScrubbing: boolean = false;\r\n private currentDuration: number = 0;\r\n private events: UIEvents;\r\n\r\n constructor(events: UIEvents) {\r\n this.events = events;\r\n\r\n // Create main player container\r\n this.root = createElement('div', { className: 'horeg-player-box' });\r\n\r\n // Speaker grill overlay\r\n const grill = createElement('div', { className: 'horeg-speaker-grill' });\r\n this.root.appendChild(grill);\r\n\r\n // Rigging bolts\r\n ['top-left', 'top-right', 'bottom-left', 'bottom-right'].forEach((pos) => {\r\n this.root.appendChild(createElement('div', { className: `horeg-bolt ${pos}` }));\r\n });\r\n\r\n // Content wrapper\r\n const contentWrap = createElement('div', { className: 'horeg-content-wrap' });\r\n this.root.appendChild(contentWrap);\r\n\r\n // 1. Rig Header\r\n const header = createElement('div', { className: 'horeg-header' });\r\n const badge = createElement('div', {\r\n className: 'horeg-rig-badge',\r\n innerHTML: `<span class=\"horeg-badge-led\"></span> HOREG RIG • HIGH VOLTAGE`\r\n });\r\n header.appendChild(badge);\r\n contentWrap.appendChild(header);\r\n\r\n // 2. Track row (Cover art, metadata, visualizer)\r\n const trackRow = createElement('div', { className: 'horeg-track-row' });\r\n this.coverContainer = createElement('div', { className: 'horeg-cover-container' });\r\n this.coverContainer.innerHTML = `<span class=\"horeg-cover-fallback\">${ICONS.speaker}</span>`;\r\n trackRow.appendChild(this.coverContainer);\r\n\r\n const trackInfo = createElement('div', { className: 'horeg-track-info' });\r\n this.titleEl = createElement('div', { className: 'horeg-track-title', textContent: 'No Track Loaded' });\r\n this.artistEl = createElement('div', { className: 'horeg-track-artist', textContent: 'Unknown Artist' });\r\n this.albumEl = createElement('div', { className: 'horeg-track-album', textContent: '' });\r\n trackInfo.appendChild(this.titleEl);\r\n trackInfo.appendChild(this.artistEl);\r\n trackInfo.appendChild(this.albumEl);\r\n trackRow.appendChild(trackInfo);\r\n\r\n this.eqContainer = createElement('div', { className: 'horeg-eq-wrapper' });\r\n trackRow.appendChild(this.eqContainer);\r\n contentWrap.appendChild(trackRow);\r\n\r\n // 3. Progress / Scrubber Row\r\n const progressContainer = createElement('div', { className: 'horeg-progress-container' });\r\n const timeRow = createElement('div', { className: 'horeg-time-row' });\r\n this.currentTimeEl = createElement('span', { textContent: '00:00' });\r\n this.durationEl = createElement('span', { textContent: '00:00' });\r\n timeRow.appendChild(this.currentTimeEl);\r\n timeRow.appendChild(this.durationEl);\r\n progressContainer.appendChild(timeRow);\r\n\r\n this.sliderTrack = createElement('div', {\r\n className: 'horeg-slider-track',\r\n attributes: {\r\n role: 'slider',\r\n 'aria-label': 'Seek progress',\r\n 'aria-valuemin': '0',\r\n 'aria-valuemax': '100',\r\n 'aria-valuenow': '0',\r\n tabindex: '0'\r\n }\r\n });\r\n this.bufferBar = createElement('div', { className: 'horeg-buffer-bar' });\r\n this.fillBar = createElement('div', { className: 'horeg-fill-bar' });\r\n this.sliderTrack.appendChild(this.bufferBar);\r\n this.sliderTrack.appendChild(this.fillBar);\r\n progressContainer.appendChild(this.sliderTrack);\r\n contentWrap.appendChild(progressContainer);\r\n\r\n // 4. Controls Row\r\n const controlsRow = createElement('div', { className: 'horeg-controls-row' });\r\n\r\n // Side left: Shuffle & Repeat\r\n const leftControls = createElement('div', { className: 'horeg-side-controls' });\r\n this.shuffleBtn = createElement('button', {\r\n className: 'horeg-btn',\r\n attributes: { 'aria-label': 'Toggle shuffle', type: 'button' },\r\n innerHTML: ICONS.shuffle\r\n });\r\n this.loopBtn = createElement('button', {\r\n className: 'horeg-btn active',\r\n attributes: { 'aria-label': 'Toggle loop mode', type: 'button' },\r\n innerHTML: ICONS.repeat\r\n });\r\n leftControls.appendChild(this.shuffleBtn);\r\n leftControls.appendChild(this.loopBtn);\r\n controlsRow.appendChild(leftControls);\r\n\r\n // Center: Prev, Play, Next\r\n const centerControls = createElement('div', { className: 'horeg-center-controls' });\r\n this.prevBtn = createElement('button', {\r\n className: 'horeg-btn',\r\n attributes: { 'aria-label': 'Previous track', type: 'button' },\r\n innerHTML: ICONS.prev\r\n });\r\n this.playBtn = createElement('button', {\r\n className: 'horeg-btn horeg-btn-play',\r\n attributes: { 'aria-label': 'Play track', type: 'button' },\r\n innerHTML: ICONS.play\r\n });\r\n this.nextBtn = createElement('button', {\r\n className: 'horeg-btn',\r\n attributes: { 'aria-label': 'Next track', type: 'button' },\r\n innerHTML: ICONS.next\r\n });\r\n centerControls.appendChild(this.prevBtn);\r\n centerControls.appendChild(this.playBtn);\r\n centerControls.appendChild(this.nextBtn);\r\n controlsRow.appendChild(centerControls);\r\n\r\n // Side right: Volume & Drawer toggle\r\n const rightControls = createElement('div', { className: 'horeg-side-controls' });\r\n const volumeWrap = createElement('div', { className: 'horeg-volume-wrap' });\r\n this.muteBtn = createElement('button', {\r\n className: 'horeg-btn',\r\n attributes: { 'aria-label': 'Toggle mute', type: 'button' },\r\n innerHTML: ICONS.volumeHigh\r\n });\r\n this.volumeSlider = createElement('input', {\r\n className: 'horeg-volume-slider',\r\n attributes: {\r\n type: 'range',\r\n min: '0',\r\n max: '1',\r\n step: '0.01',\r\n value: '0.8',\r\n 'aria-label': 'Volume control'\r\n }\r\n });\r\n volumeWrap.appendChild(this.muteBtn);\r\n volumeWrap.appendChild(this.volumeSlider);\r\n rightControls.appendChild(volumeWrap);\r\n\r\n this.drawerBtn = createElement('button', {\r\n className: 'horeg-btn',\r\n attributes: { 'aria-label': 'Toggle playlist drawer', type: 'button' },\r\n innerHTML: ICONS.playlist\r\n });\r\n rightControls.appendChild(this.drawerBtn);\r\n controlsRow.appendChild(rightControls);\r\n\r\n contentWrap.appendChild(controlsRow);\r\n\r\n // 5. Drawer panel\r\n this.drawerEl = createElement('div', { className: 'horeg-drawer' });\r\n\r\n // Drawer Header with count & Add Track button\r\n const drawerHeader = createElement('div', { className: 'horeg-drawer-header' });\r\n const drawerHeading = createElement('div', { className: 'horeg-drawer-heading' });\r\n drawerHeading.innerHTML = `<span>PLAYLIST</span>`;\r\n this.drawerCountEl = createElement('span', { className: 'horeg-drawer-count', textContent: '0' });\r\n drawerHeading.appendChild(this.drawerCountEl);\r\n drawerHeader.appendChild(drawerHeading);\r\n\r\n this.addTrackBtn = createElement('button', {\r\n className: 'horeg-btn-add-track',\r\n attributes: { type: 'button', 'aria-label': 'Add track to playlist' },\r\n innerHTML: `${ICONS.plus} <span>Add Track</span>`\r\n });\r\n drawerHeader.appendChild(this.addTrackBtn);\r\n this.drawerEl.appendChild(drawerHeader);\r\n\r\n // Add Track Accordion Panel\r\n this.addPanelEl = createElement('div', { className: 'horeg-add-panel' });\r\n\r\n // Tabs\r\n const tabs = createElement('div', { className: 'horeg-add-tabs' });\r\n this.tabBtnFile = createElement('button', {\r\n className: 'horeg-tab-btn active',\r\n attributes: { type: 'button' },\r\n innerHTML: `${ICONS.upload} <span>File Lokal</span>`\r\n });\r\n this.tabBtnUrl = createElement('button', {\r\n className: 'horeg-tab-btn',\r\n attributes: { type: 'button' },\r\n innerHTML: `${ICONS.link} <span>Audio URL</span>`\r\n });\r\n tabs.appendChild(this.tabBtnFile);\r\n tabs.appendChild(this.tabBtnUrl);\r\n this.addPanelEl.appendChild(tabs);\r\n\r\n // Tab 1: Local File Dropzone & Hidden Input\r\n this.tabPaneFile = createElement('div', { className: 'horeg-tab-pane active' });\r\n this.fileInputEl = createElement('input', {\r\n attributes: { type: 'file', accept: 'audio/*', multiple: 'true', style: 'display: none;' }\r\n });\r\n this.dropzoneEl = createElement('div', { className: 'horeg-dropzone' });\r\n this.dropzoneEl.innerHTML = `\r\n <div class=\"horeg-dropzone-icon\">${ICONS.upload}</div>\r\n <div class=\"horeg-dropzone-text\">Pilih File Audio Komputer</div>\r\n <div class=\"horeg-dropzone-hint\">Klik di sini atau drag & drop file (.mp3, .wav, .flac, .ogg, .m4a)</div>\r\n `;\r\n this.tabPaneFile.appendChild(this.fileInputEl);\r\n this.tabPaneFile.appendChild(this.dropzoneEl);\r\n this.addPanelEl.appendChild(this.tabPaneFile);\r\n\r\n // Tab 2: URL Form\r\n this.tabPaneUrl = createElement('div', { className: 'horeg-tab-pane' });\r\n this.urlInput = createElement('input', {\r\n className: 'horeg-input-field',\r\n attributes: { type: 'url', placeholder: 'Audio URL (https://...mp3/wav)', required: 'true' }\r\n });\r\n this.titleInput = createElement('input', {\r\n className: 'horeg-input-field',\r\n attributes: { type: 'text', placeholder: 'Judul Lagu (Opsional)' }\r\n });\r\n this.artistInput = createElement('input', {\r\n className: 'horeg-input-field',\r\n attributes: { type: 'text', placeholder: 'Nama Artis (Opsional)' }\r\n });\r\n const formActions = createElement('div', { className: 'horeg-form-actions' });\r\n this.cancelAddBtn = createElement('button', {\r\n className: 'horeg-btn-cancel',\r\n attributes: { type: 'button' },\r\n textContent: 'Batal'\r\n });\r\n this.submitUrlBtn = createElement('button', {\r\n className: 'horeg-btn-submit',\r\n attributes: { type: 'button' },\r\n innerHTML: `${ICONS.plus} Tambah`\r\n });\r\n formActions.appendChild(this.cancelAddBtn);\r\n formActions.appendChild(this.submitUrlBtn);\r\n\r\n this.tabPaneUrl.appendChild(this.urlInput);\r\n this.tabPaneUrl.appendChild(this.titleInput);\r\n this.tabPaneUrl.appendChild(this.artistInput);\r\n this.tabPaneUrl.appendChild(formActions);\r\n this.addPanelEl.appendChild(this.tabPaneUrl);\r\n\r\n this.drawerEl.appendChild(this.addPanelEl);\r\n\r\n // Track list container\r\n this.drawerInner = createElement('div', { className: 'horeg-drawer-inner' });\r\n this.drawerEl.appendChild(this.drawerInner);\r\n this.root.appendChild(this.drawerEl);\r\n\r\n this.bindDOMEvents();\r\n }\r\n\r\n private bindDOMEvents(): void {\r\n this.playBtn.addEventListener('click', () => this.events.onPlayPauseClick());\r\n this.prevBtn.addEventListener('click', () => this.events.onPrevClick());\r\n this.nextBtn.addEventListener('click', () => this.events.onNextClick());\r\n this.shuffleBtn.addEventListener('click', () => this.events.onShuffleToggle());\r\n this.loopBtn.addEventListener('click', () => this.events.onLoopToggle());\r\n this.muteBtn.addEventListener('click', () => this.events.onMuteToggle());\r\n\r\n this.volumeSlider.addEventListener('input', () => {\r\n this.events.onVolumeChange(parseFloat(this.volumeSlider.value));\r\n });\r\n\r\n this.drawerBtn.addEventListener('click', () => {\r\n this.toggleDrawer();\r\n });\r\n\r\n // Toggle Add Track Form\r\n this.addTrackBtn.addEventListener('click', () => {\r\n const isOpen = this.addPanelEl.classList.toggle('open');\r\n this.addTrackBtn.classList.toggle('active', isOpen);\r\n if (isOpen) {\r\n this.toggleDrawer(true);\r\n }\r\n });\r\n\r\n // Tab Switchers\r\n this.tabBtnFile.addEventListener('click', () => {\r\n this.tabBtnFile.classList.add('active');\r\n this.tabBtnUrl.classList.remove('active');\r\n this.tabPaneFile.classList.add('active');\r\n this.tabPaneUrl.classList.remove('active');\r\n });\r\n\r\n this.tabBtnUrl.addEventListener('click', () => {\r\n this.tabBtnUrl.classList.add('active');\r\n this.tabBtnFile.classList.remove('active');\r\n this.tabPaneUrl.classList.add('active');\r\n this.tabPaneFile.classList.remove('active');\r\n this.urlInput.focus();\r\n });\r\n\r\n // Local file picker\r\n this.dropzoneEl.addEventListener('click', () => {\r\n this.fileInputEl.click();\r\n });\r\n\r\n this.fileInputEl.addEventListener('change', () => {\r\n if (this.fileInputEl.files && this.fileInputEl.files.length > 0) {\r\n const files = Array.from(this.fileInputEl.files);\r\n if (this.events.onAddTrackFiles) {\r\n this.events.onAddTrackFiles(files);\r\n }\r\n this.fileInputEl.value = '';\r\n this.closeAddPanel();\r\n }\r\n });\r\n\r\n // Dropzone drag-and-drop feedback\r\n this.dropzoneEl.addEventListener('dragover', (e) => {\r\n e.preventDefault();\r\n this.dropzoneEl.classList.add('drag-active');\r\n });\r\n\r\n this.dropzoneEl.addEventListener('dragleave', () => {\r\n this.dropzoneEl.classList.remove('drag-active');\r\n });\r\n\r\n this.dropzoneEl.addEventListener('drop', (e) => {\r\n e.preventDefault();\r\n this.dropzoneEl.classList.remove('drag-active');\r\n if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) {\r\n const audioFiles = Array.from(e.dataTransfer.files).filter(\r\n (f) => f.type.startsWith('audio/') || /\\.(mp3|wav|ogg|flac|m4a|aac)$/i.test(f.name)\r\n );\r\n if (audioFiles.length > 0 && this.events.onAddTrackFiles) {\r\n this.events.onAddTrackFiles(audioFiles);\r\n this.closeAddPanel();\r\n }\r\n }\r\n });\r\n\r\n // Whole player Drag & Drop listener\r\n this.root.addEventListener('dragover', (e) => {\r\n e.preventDefault();\r\n this.root.classList.add('drag-over');\r\n });\r\n\r\n this.root.addEventListener('dragleave', (e) => {\r\n if (!this.root.contains(e.relatedTarget as Node)) {\r\n this.root.classList.remove('drag-over');\r\n }\r\n });\r\n\r\n this.root.addEventListener('drop', (e) => {\r\n e.preventDefault();\r\n this.root.classList.remove('drag-over');\r\n if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) {\r\n const audioFiles = Array.from(e.dataTransfer.files).filter(\r\n (f) => f.type.startsWith('audio/') || /\\.(mp3|wav|ogg|flac|m4a|aac)$/i.test(f.name)\r\n );\r\n if (audioFiles.length > 0 && this.events.onAddTrackFiles) {\r\n this.events.onAddTrackFiles(audioFiles);\r\n this.closeAddPanel();\r\n }\r\n }\r\n });\r\n\r\n // URL Form Submission\r\n const handleUrlSubmit = () => {\r\n const url = this.urlInput.value.trim();\r\n if (!url) {\r\n this.urlInput.focus();\r\n return;\r\n }\r\n const title = this.titleInput.value.trim();\r\n const artist = this.artistInput.value.trim();\r\n if (this.events.onAddTrackUrl) {\r\n this.events.onAddTrackUrl(url, title || undefined, artist || undefined);\r\n }\r\n this.urlInput.value = '';\r\n this.titleInput.value = '';\r\n this.artistInput.value = '';\r\n this.closeAddPanel();\r\n };\r\n\r\n this.submitUrlBtn.addEventListener('click', handleUrlSubmit);\r\n const handleUrlInputKeyDown = (e: KeyboardEvent) => {\r\n // Allow space, arrows, and text navigation without triggering player shortcuts\r\n e.stopPropagation();\r\n if (e.key === 'Enter') {\r\n e.preventDefault();\r\n handleUrlSubmit();\r\n }\r\n };\r\n\r\n this.urlInput.addEventListener('keydown', handleUrlInputKeyDown);\r\n this.titleInput.addEventListener('keydown', handleUrlInputKeyDown);\r\n this.artistInput.addEventListener('keydown', handleUrlInputKeyDown);\r\n\r\n this.cancelAddBtn.addEventListener('click', () => {\r\n this.closeAddPanel();\r\n });\r\n\r\n // Scrubbing on slider track\r\n this.sliderTrack.addEventListener('mousedown', this.startScrubbing);\r\n this.sliderTrack.addEventListener('touchstart', this.startScrubbing, { passive: false });\r\n\r\n // Keyboard support on slider\r\n this.sliderTrack.addEventListener('keydown', (e: KeyboardEvent) => {\r\n if (!this.currentDuration) return;\r\n if (e.key === 'ArrowRight' || e.key === 'ArrowUp') {\r\n e.preventDefault();\r\n const cur = (parseFloat(this.fillBar.style.width) / 100) * this.currentDuration;\r\n this.events.onSeek(Math.min(cur + 5, this.currentDuration));\r\n } else if (e.key === 'ArrowLeft' || e.key === 'ArrowDown') {\r\n e.preventDefault();\r\n const cur = (parseFloat(this.fillBar.style.width) / 100) * this.currentDuration;\r\n this.events.onSeek(Math.max(cur - 5, 0));\r\n }\r\n });\r\n }\r\n\r\n private startScrubbing = (e: MouseEvent | TouchEvent): void => {\r\n e.preventDefault();\r\n this.isScrubbing = true;\r\n this.handleScrubbingMove(e);\r\n\r\n const onMove = (moveEvent: MouseEvent | TouchEvent) => {\r\n if (this.isScrubbing) {\r\n this.handleScrubbingMove(moveEvent);\r\n }\r\n };\r\n\r\n const onUp = (upEvent: MouseEvent | TouchEvent) => {\r\n if (this.isScrubbing) {\r\n this.isScrubbing = false;\r\n this.finishScrubbing(upEvent);\r\n window.removeEventListener('mousemove', onMove);\r\n window.removeEventListener('mouseup', onUp);\r\n window.removeEventListener('touchmove', onMove);\r\n window.removeEventListener('touchend', onUp);\r\n }\r\n };\r\n\r\n window.addEventListener('mousemove', onMove);\r\n window.addEventListener('mouseup', onUp);\r\n window.addEventListener('touchmove', onMove, { passive: false });\r\n window.addEventListener('touchend', onUp);\r\n };\r\n\r\n private calculateProgressPercent(e: MouseEvent | TouchEvent): number {\r\n const rect = this.sliderTrack.getBoundingClientRect();\r\n const clientX = 'touches' in e ? (e.touches[0] || e.changedTouches[0]).clientX : e.clientX;\r\n const offsetX = clamp(clientX - rect.left, 0, rect.width);\r\n return rect.width > 0 ? (offsetX / rect.width) * 100 : 0;\r\n }\r\n\r\n private handleScrubbingMove(e: MouseEvent | TouchEvent): void {\r\n const percent = this.calculateProgressPercent(e);\r\n this.fillBar.style.width = `${percent}%`;\r\n if (this.currentDuration > 0) {\r\n const seekTime = (percent / 100) * this.currentDuration;\r\n this.currentTimeEl.textContent = formatTime(seekTime);\r\n }\r\n }\r\n\r\n private finishScrubbing(e: MouseEvent | TouchEvent): void {\r\n const percent = this.calculateProgressPercent(e);\r\n if (this.currentDuration > 0) {\r\n const targetSeconds = (percent / 100) * this.currentDuration;\r\n this.events.onSeek(targetSeconds);\r\n }\r\n }\r\n\r\n public updateTrackInfo(track: Track): void {\r\n this.titleEl.textContent = track.title || 'Untitled Track';\r\n this.artistEl.textContent = track.artist || 'Unknown Artist';\r\n this.albumEl.textContent = track.album || '';\r\n\r\n if (track.coverArt) {\r\n this.coverContainer.innerHTML = '';\r\n this.coverImgEl = createElement('img', {\r\n className: 'horeg-cover-img',\r\n attributes: {\r\n src: track.coverArt,\r\n alt: `${track.title} cover`\r\n }\r\n });\r\n this.coverImgEl.onerror = () => {\r\n this.coverContainer.innerHTML = `<span class=\"horeg-cover-fallback\">${ICONS.speaker}</span>`;\r\n };\r\n this.coverContainer.appendChild(this.coverImgEl);\r\n } else {\r\n this.coverContainer.innerHTML = `<span class=\"horeg-cover-fallback\">${ICONS.speaker}</span>`;\r\n }\r\n }\r\n\r\n public updatePlayState(isPlaying: boolean): void {\r\n if (isPlaying) {\r\n this.playBtn.classList.add('playing');\r\n this.playBtn.innerHTML = ICONS.pause;\r\n this.playBtn.setAttribute('aria-label', 'Pause track');\r\n } else {\r\n this.playBtn.classList.remove('playing');\r\n this.playBtn.innerHTML = ICONS.play;\r\n this.playBtn.setAttribute('aria-label', 'Play track');\r\n }\r\n }\r\n\r\n public updateProgress(currentTime: number, duration: number): void {\r\n this.currentDuration = duration;\r\n this.durationEl.textContent = formatTime(duration);\r\n\r\n if (!this.isScrubbing) {\r\n this.currentTimeEl.textContent = formatTime(currentTime);\r\n const percent = duration > 0 ? (currentTime / duration) * 100 : 0;\r\n this.fillBar.style.width = `${clamp(percent, 0, 100)}%`;\r\n this.sliderTrack.setAttribute('aria-valuenow', Math.round(percent).toString());\r\n }\r\n }\r\n\r\n public updateBuffer(percent: number): void {\r\n this.bufferBar.style.width = `${clamp(percent, 0, 100)}%`;\r\n }\r\n\r\n public updateVolume(volume: number, isMuted: boolean): void {\r\n this.volumeSlider.value = volume.toString();\r\n if (isMuted || volume === 0) {\r\n this.muteBtn.innerHTML = ICONS.volumeMute;\r\n this.muteBtn.setAttribute('aria-label', 'Unmute');\r\n } else {\r\n this.muteBtn.innerHTML = ICONS.volumeHigh;\r\n this.muteBtn.setAttribute('aria-label', 'Mute');\r\n }\r\n }\r\n\r\n public updateLoopState(mode: LoopMode): void {\r\n if (mode === 'none') {\r\n this.loopBtn.classList.remove('active');\r\n this.loopBtn.innerHTML = ICONS.repeat;\r\n } else if (mode === 'all') {\r\n this.loopBtn.classList.add('active');\r\n this.loopBtn.innerHTML = ICONS.repeat;\r\n } else if (mode === 'one') {\r\n this.loopBtn.classList.add('active');\r\n this.loopBtn.innerHTML = ICONS.repeatOne;\r\n }\r\n this.loopBtn.setAttribute('aria-label', `Repeat mode: ${mode}`);\r\n }\r\n\r\n public updateShuffleState(isShuffle: boolean): void {\r\n if (isShuffle) {\r\n this.shuffleBtn.classList.add('active');\r\n } else {\r\n this.shuffleBtn.classList.remove('active');\r\n }\r\n }\r\n\r\n public toggleDrawer(open?: boolean): void {\r\n const shouldOpen = open !== undefined ? open : !this.drawerEl.classList.contains('open');\r\n if (shouldOpen) {\r\n this.drawerEl.classList.add('open');\r\n this.drawerBtn.classList.add('active');\r\n } else {\r\n this.drawerEl.classList.remove('open');\r\n this.drawerBtn.classList.remove('active');\r\n this.closeAddPanel();\r\n }\r\n }\r\n\r\n public closeAddPanel(): void {\r\n this.addPanelEl.classList.remove('open');\r\n this.addTrackBtn.classList.remove('active');\r\n }\r\n\r\n public renderPlaylist(playlist: Track[], currentIndex: number): void {\r\n this.drawerCountEl.textContent = playlist.length.toString();\r\n this.drawerInner.innerHTML = '';\r\n\r\n if (playlist.length === 0) {\r\n const emptyEl = createElement('div', {\r\n className: 'horeg-empty-playlist',\r\n textContent: 'Playlist masih kosong. Klik \"Add Track\" untuk menambahkan lagu.'\r\n });\r\n this.drawerInner.appendChild(emptyEl);\r\n return;\r\n }\r\n\r\n playlist.forEach((track, idx) => {\r\n const item = createElement('div', {\r\n className: `horeg-track-item ${idx === currentIndex ? 'active' : ''}`\r\n });\r\n\r\n const num = createElement('div', {\r\n className: 'horeg-track-num',\r\n textContent: (idx + 1).toString().padStart(2, '0')\r\n });\r\n item.appendChild(num);\r\n\r\n const details = createElement('div', { className: 'horeg-track-details' });\r\n const title = createElement('div', {\r\n className: 'horeg-item-title',\r\n textContent: track.title\r\n });\r\n const artist = createElement('div', {\r\n className: 'horeg-item-artist',\r\n textContent: track.artist || 'Unknown Artist'\r\n });\r\n details.appendChild(title);\r\n details.appendChild(artist);\r\n item.appendChild(details);\r\n\r\n if (track.duration) {\r\n const dur = createElement('div', {\r\n className: 'horeg-item-duration',\r\n textContent: formatTime(track.duration)\r\n });\r\n item.appendChild(dur);\r\n }\r\n\r\n // Remove / delete button\r\n const actions = createElement('div', { className: 'horeg-track-actions' });\r\n const removeBtn = createElement('button', {\r\n className: 'horeg-btn-remove',\r\n attributes: { type: 'button', 'aria-label': 'Hapus lagu dari playlist' },\r\n innerHTML: ICONS.trash\r\n });\r\n removeBtn.addEventListener('click', (e) => {\r\n e.stopPropagation();\r\n if (this.events.onRemoveTrack) {\r\n this.events.onRemoveTrack(idx);\r\n }\r\n });\r\n actions.appendChild(removeBtn);\r\n item.appendChild(actions);\r\n\r\n item.addEventListener('click', () => {\r\n this.events.onTrackSelect(idx);\r\n });\r\n\r\n this.drawerInner.appendChild(item);\r\n });\r\n }\r\n\r\n public destroy(): void {\r\n this.root.remove();\r\n }\r\n}\r\n\r\n","export interface VisualizerOptions {\n barCount?: number;\n container: HTMLElement;\n enableAnimation?: boolean;\n}\n\nexport class Visualizer {\n private container: HTMLElement;\n private bars: HTMLElement[] = [];\n private isRunning: boolean = false;\n private animFrameId: number | null = null;\n private isEnabled: boolean = true;\n private baseHeights: number[] = [];\n\n constructor(options: VisualizerOptions) {\n this.container = options.container;\n this.isEnabled = options.enableAnimation !== false;\n const barCount = options.barCount || 5;\n\n this.initBars(barCount);\n }\n\n private initBars(count: number): void {\n this.container.innerHTML = '';\n this.bars = [];\n this.baseHeights = [];\n\n for (let i = 0; i < count; i++) {\n const bar = document.createElement('span');\n bar.className = 'horeg-eq-bar';\n this.container.appendChild(bar);\n this.bars.push(bar);\n this.baseHeights.push(3 + (i % 2) * 2);\n bar.style.height = `${this.baseHeights[i]}px`;\n }\n }\n\n public setEnabled(enabled: boolean): void {\n this.isEnabled = enabled;\n if (!enabled) {\n this.stop();\n }\n }\n\n public start(): void {\n if (this.isRunning || !this.isEnabled) return;\n this.isRunning = true;\n this.loop();\n }\n\n public stop(): void {\n this.isRunning = false;\n if (this.animFrameId !== null) {\n cancelAnimationFrame(this.animFrameId);\n this.animFrameId = null;\n }\n // Smooth reset\n for (let i = 0; i < this.bars.length; i++) {\n this.bars[i].style.height = `${this.baseHeights[i] || 3}px`;\n }\n }\n\n private loop = (): void => {\n if (!this.isRunning) return;\n\n const time = Date.now() / 150;\n for (let i = 0; i < this.bars.length; i++) {\n // Horeg rhythmic bass / mid / high pulsation formula\n const wave1 = Math.sin(time * 1.8 + i * 1.2);\n const wave2 = Math.cos(time * 2.5 + i * 0.8);\n const intensity = Math.abs(wave1 * 0.6 + wave2 * 0.4);\n // Height between 4px and 22px\n const height = Math.floor(4 + intensity * 18);\n this.bars[i].style.height = `${height}px`;\n }\n\n this.animFrameId = requestAnimationFrame(this.loop);\n };\n\n public destroy(): void {\n this.stop();\n this.bars = [];\n this.container.innerHTML = '';\n }\n}\n\n","import { HoregPlayerOptions, HoregTheme, Track, LoopMode } from './types';\r\nimport { generateStyles } from './styles';\r\nimport { AudioEngine } from './AudioEngine';\r\nimport { UI } from './UI';\r\nimport { Visualizer } from './visualizer';\r\n\r\nexport class HoregAudio {\r\n private container: HTMLElement;\r\n private shadow: ShadowRoot;\r\n private styleEl: HTMLStyleElement;\r\n private audioEngine: AudioEngine;\r\n private ui: UI;\r\n private visualizer: Visualizer;\r\n private theme: HoregTheme;\r\n private boundKeyHandler: (e: KeyboardEvent) => void;\r\n\r\n constructor(options: HoregPlayerOptions) {\r\n if (typeof options.container === 'string') {\r\n const el = document.querySelector(options.container);\r\n if (!el) {\r\n throw new Error(`[HoregAudio] Container \"${options.container}\" not found in DOM.`);\r\n }\r\n this.container = el as HTMLElement;\r\n } else if (options.container instanceof HTMLElement) {\r\n this.container = options.container;\r\n } else {\r\n throw new Error('[HoregAudio] Invalid container option provided.');\r\n }\r\n\r\n this.theme = options.theme || { variant: 'horeg-classic' };\r\n\r\n // 1. Shadow DOM attachment\r\n this.shadow = this.container.attachShadow({ mode: 'open' });\r\n\r\n // 2. Inject encapsulated styles\r\n this.styleEl = document.createElement('style');\r\n this.styleEl.textContent = generateStyles(this.theme);\r\n this.shadow.appendChild(this.styleEl);\r\n\r\n // 3. Instantiate UI\r\n this.ui = new UI({\r\n onPlayPauseClick: () => this.toggle(),\r\n onPrevClick: () => this.prev(),\r\n onNextClick: () => this.next(),\r\n onSeek: (seconds) => this.seek(seconds),\r\n onVolumeChange: (vol) => this.setVolume(vol),\r\n onMuteToggle: () => {\r\n const isMuted = this.audioEngine.toggleMute();\r\n this.ui.updateVolume(this.audioEngine.getVolume(), isMuted);\r\n },\r\n onShuffleToggle: () => {\r\n const nextShuffle = !this.audioEngine.isShuffle();\r\n this.audioEngine.setShuffle(nextShuffle);\r\n this.ui.updateShuffleState(nextShuffle);\r\n },\r\n onLoopToggle: () => {\r\n const currentLoop = this.audioEngine.getLoop();\r\n const nextLoop: LoopMode = currentLoop === 'all' ? 'one' : currentLoop === 'one' ? 'none' : 'all';\r\n this.audioEngine.setLoop(nextLoop);\r\n this.ui.updateLoopState(nextLoop);\r\n },\r\n onTrackSelect: (index) => {\r\n this.loadTrack(index, true);\r\n },\r\n onAddTrackFiles: (files) => {\r\n this.addTrackFromFiles(files, false);\r\n },\r\n onAddTrackUrl: (url, title, artist) => {\r\n this.addTrackFromUrl(url, { title, artist }, false);\r\n },\r\n onRemoveTrack: (index) => {\r\n this.removeTrack(index);\r\n }\r\n });\r\n\r\n this.shadow.appendChild(this.ui.root);\r\n\r\n // 4. Initialize Visualizer\r\n this.visualizer = new Visualizer({\r\n container: this.ui.eqContainer,\r\n enableAnimation: this.theme.enableEqAnimation !== false\r\n });\r\n\r\n // 5. Initialize AudioEngine\r\n this.audioEngine = new AudioEngine(options, {\r\n onPlay: (track) => {\r\n this.ui.updatePlayState(true);\r\n this.visualizer.start();\r\n if (options.onPlay) options.onPlay(track);\r\n },\r\n onPause: () => {\r\n this.ui.updatePlayState(false);\r\n this.visualizer.stop();\r\n if (options.onPause) options.onPause();\r\n },\r\n onTrackChange: (track, index) => {\r\n this.ui.updateTrackInfo(track);\r\n const playlist = this.audioEngine ? this.audioEngine.getPlaylist() : (options.playlist || []);\r\n this.ui.renderPlaylist(playlist, index);\r\n this.ui.updateProgress(0, track.duration || 0);\r\n if (options.onTrackChange) options.onTrackChange(track, index);\r\n },\r\n onPlaylistChange: (playlist, index) => {\r\n this.ui.renderPlaylist(playlist, index);\r\n if (options.onPlaylistChange) options.onPlaylistChange(playlist, index);\r\n },\r\n onTimeUpdate: (currentTime, duration) => {\r\n this.ui.updateProgress(currentTime, duration);\r\n if (options.onTimeUpdate) options.onTimeUpdate(currentTime, duration);\r\n },\r\n onBufferUpdate: (percent) => {\r\n this.ui.updateBuffer(percent);\r\n },\r\n onEnded: (track) => {\r\n this.visualizer.stop();\r\n if (options.onEnded) options.onEnded(track);\r\n },\r\n onError: (err) => {\r\n this.ui.updatePlayState(false);\r\n this.visualizer.stop();\r\n if (options.onError) options.onError(err);\r\n }\r\n });\r\n\r\n // Initial states\r\n this.ui.updateVolume(this.audioEngine.getVolume(), this.audioEngine.isMuted());\r\n this.ui.updateLoopState(this.audioEngine.getLoop());\r\n this.ui.updateShuffleState(this.audioEngine.isShuffle());\r\n this.ui.renderPlaylist(this.audioEngine.getPlaylist(), this.audioEngine.getCurrentIndex());\r\n\r\n const currentTrack = this.audioEngine.getCurrentTrack();\r\n if (currentTrack) {\r\n this.ui.updateTrackInfo(currentTrack);\r\n this.ui.renderPlaylist(this.audioEngine.getPlaylist(), this.audioEngine.getCurrentIndex());\r\n }\r\n\r\n // Keyboard controls when container is active\r\n this.boundKeyHandler = this.handleKeyDown.bind(this);\r\n this.container.tabIndex = this.container.tabIndex >= 0 ? this.container.tabIndex : 0;\r\n this.container.addEventListener('keydown', this.boundKeyHandler);\r\n }\r\n\r\n private handleKeyDown(e: KeyboardEvent): void {\r\n const path = e.composedPath ? e.composedPath() : [];\r\n const target = (path.length > 0 ? path[0] : e.target) as HTMLElement | null;\r\n const activeEl = (this.shadow?.activeElement || document.activeElement) as HTMLElement | null;\r\n\r\n const isInputElement = (el: HTMLElement | null): boolean => {\r\n if (!el) return false;\r\n const tag = el.tagName;\r\n return tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' || el.isContentEditable;\r\n };\r\n\r\n // Do not intercept if user is typing or interacting with form controls\r\n if (isInputElement(target) || isInputElement(activeEl)) {\r\n return;\r\n }\r\n\r\n if (e.code === 'Space') {\r\n // If a button is focused, let space trigger native button click\r\n if (target?.tagName === 'BUTTON' || activeEl?.tagName === 'BUTTON') {\r\n return;\r\n }\r\n e.preventDefault();\r\n this.toggle();\r\n } else if (e.key === 'ArrowRight' && !e.shiftKey) {\r\n e.preventDefault();\r\n this.seek(this.audioEngine.getCurrentTime() + 5);\r\n } else if (e.key === 'ArrowLeft' && !e.shiftKey) {\r\n e.preventDefault();\r\n this.seek(this.audioEngine.getCurrentTime() - 5);\r\n } else if (e.key === 'ArrowUp') {\r\n e.preventDefault();\r\n this.setVolume(this.audioEngine.getVolume() + 0.05);\r\n } else if (e.key === 'ArrowDown') {\r\n e.preventDefault();\r\n this.setVolume(this.audioEngine.getVolume() - 0.05);\r\n }\r\n }\r\n\r\n // --- Public API Methods ---\r\n\r\n public async play(): Promise<void> {\r\n await this.audioEngine.play();\r\n }\r\n\r\n public pause(): void {\r\n this.audioEngine.pause();\r\n }\r\n\r\n public toggle(): void {\r\n this.audioEngine.toggle();\r\n }\r\n\r\n public next(): void {\r\n this.audioEngine.next();\r\n }\r\n\r\n public prev(): void {\r\n this.audioEngine.prev();\r\n }\r\n\r\n public seek(seconds: number): void {\r\n this.audioEngine.seek(seconds);\r\n }\r\n\r\n public setVolume(level: number): void {\r\n this.audioEngine.setVolume(level);\r\n this.ui.updateVolume(this.audioEngine.getVolume(), this.audioEngine.isMuted());\r\n }\r\n\r\n public loadTrack(indexOrTrack: number | Track, autoPlay: boolean = false): void {\r\n this.audioEngine.loadTrack(indexOrTrack, autoPlay);\r\n }\r\n\r\n public addTrack(track: Track, autoPlay: boolean = false): number {\r\n const index = this.audioEngine.addTrack(track, autoPlay);\r\n this.ui.renderPlaylist(this.audioEngine.getPlaylist(), this.audioEngine.getCurrentIndex());\r\n return index;\r\n }\r\n\r\n public addTracks(tracks: Track[], autoPlay: boolean = false): void {\r\n this.audioEngine.addTracks(tracks, autoPlay);\r\n this.ui.renderPlaylist(this.audioEngine.getPlaylist(), this.audioEngine.getCurrentIndex());\r\n }\r\n\r\n public async addTrackFromFile(file: File, autoPlay: boolean = false): Promise<Track> {\r\n const src = URL.createObjectURL(file);\r\n const nameWithoutExt = file.name.replace(/\\.[^/.]+$/, '');\r\n const track: Track = {\r\n id: `local-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`,\r\n title: nameWithoutExt,\r\n artist: 'File Audio Lokal',\r\n src\r\n };\r\n\r\n try {\r\n const dur = await this.probeAudioDuration(src);\r\n if (dur > 0) {\r\n track.duration = dur;\r\n }\r\n } catch {\r\n // AudioEngine will pick up duration on loadedmetadata\r\n }\r\n\r\n this.addTrack(track, autoPlay);\r\n return track;\r\n }\r\n\r\n public async addTrackFromFiles(files: FileList | File[], autoPlay: boolean = false): Promise<Track[]> {\r\n const fileArray = Array.from(files);\r\n const addedTracks: Track[] = [];\r\n for (let i = 0; i < fileArray.length; i++) {\r\n const file = fileArray[i];\r\n const shouldAutoPlay = autoPlay && i === 0;\r\n const track = await this.addTrackFromFile(file, shouldAutoPlay);\r\n addedTracks.push(track);\r\n }\r\n return addedTracks;\r\n }\r\n\r\n public async addTrackFromUrl(url: string, meta: Partial<Track> = {}, autoPlay: boolean = false): Promise<Track> {\r\n let fallbackTitle = 'Online Track';\r\n try {\r\n const parsed = new URL(url);\r\n const pathname = parsed.pathname;\r\n const lastPart = pathname.substring(pathname.lastIndexOf('/') + 1);\r\n if (lastPart) {\r\n fallbackTitle = decodeURIComponent(lastPart).replace(/\\.[^/.]+$/, '');\r\n }\r\n } catch {\r\n // In case of non-standard URL or data URI\r\n }\r\n\r\n const track: Track = {\r\n id: meta.id || `url-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`,\r\n title: meta.title?.trim() || fallbackTitle,\r\n artist: meta.artist?.trim() || 'Audio Stream',\r\n album: meta.album,\r\n coverArt: meta.coverArt,\r\n src: url,\r\n duration: meta.duration\r\n };\r\n\r\n if (!track.duration) {\r\n try {\r\n const dur = await this.probeAudioDuration(url);\r\n if (dur > 0) {\r\n track.duration = dur;\r\n }\r\n } catch {\r\n // Fallback to loadedmetadata\r\n }\r\n }\r\n\r\n this.addTrack(track, autoPlay);\r\n return track;\r\n }\r\n\r\n public removeTrack(index: number): void {\r\n this.audioEngine.removeTrack(index);\r\n this.ui.renderPlaylist(this.audioEngine.getPlaylist(), this.audioEngine.getCurrentIndex());\r\n const current = this.audioEngine.getCurrentTrack();\r\n if (current) {\r\n this.ui.updateTrackInfo(current);\r\n } else {\r\n this.ui.updateTrackInfo({ title: 'No Track Loaded', src: '' });\r\n }\r\n }\r\n\r\n public getPlaylist(): Track[] {\r\n return this.audioEngine.getPlaylist();\r\n }\r\n\r\n private probeAudioDuration(src: string): Promise<number> {\r\n return new Promise((resolve) => {\r\n const tempAudio = new Audio();\r\n let isResolved = false;\r\n\r\n const finish = (dur: number) => {\r\n if (!isResolved) {\r\n isResolved = true;\r\n tempAudio.removeEventListener('loadedmetadata', onLoaded);\r\n tempAudio.removeEventListener('error', onError);\r\n tempAudio.src = '';\r\n resolve(dur);\r\n }\r\n };\r\n\r\n const onLoaded = () => {\r\n const d = tempAudio.duration;\r\n finish(d && !isNaN(d) && isFinite(d) ? d : 0);\r\n };\r\n\r\n const onError = () => finish(0);\r\n\r\n tempAudio.addEventListener('loadedmetadata', onLoaded);\r\n tempAudio.addEventListener('error', onError);\r\n tempAudio.src = src;\r\n\r\n setTimeout(() => finish(0), 3000);\r\n });\r\n }\r\n\r\n public setTheme(themeConfig: Partial<HoregTheme>): void {\r\n this.theme = { ...this.theme, ...themeConfig };\r\n this.styleEl.textContent = generateStyles(this.theme);\r\n if (this.theme.enableEqAnimation !== undefined) {\r\n this.visualizer.setEnabled(this.theme.enableEqAnimation);\r\n }\r\n }\r\n\r\n public getAudioEngine(): AudioEngine {\r\n return this.audioEngine;\r\n }\r\n\r\n public destroy(): void {\r\n this.container.removeEventListener('keydown', this.boundKeyHandler);\r\n this.audioEngine.destroy();\r\n this.visualizer.destroy();\r\n this.ui.destroy();\r\n this.shadow.innerHTML = '';\r\n }\r\n}\r\n\r\n"],"names":["THEME_PRESETS","generateStyles","theme","variant","preset","glow","accent","bg","text","progress","radius","formatTime","seconds","roundedSeconds","hours","minutes","remainingSeconds","pad","num","clamp","value","min","max","AudioEngine","options","callbacks","__publicField","current","duration","bufferedEnd","percent","err","newPlaylist","startIndex","track","autoPlay","wasEmpty","newIndex","tracks","firstAddedIndex","index","isCurrentTrack","nextIndex","dur","currentTrack","indexOrTrack","targetIndex","t","clampedTime","level","clamped","isFromEnded","randomIndex","prevIndex","mode","shuffle","ICONS","createElement","tagName","el","key","val","UI","events","onMove","moveEvent","onUp","upEvent","grill","pos","contentWrap","header","badge","trackRow","trackInfo","progressContainer","timeRow","controlsRow","leftControls","centerControls","rightControls","volumeWrap","drawerHeader","drawerHeading","tabs","formActions","isOpen","files","e","_a","audioFiles","f","handleUrlSubmit","url","title","artist","handleUrlInputKeyDown","cur","rect","clientX","offsetX","seekTime","targetSeconds","isPlaying","currentTime","volume","isMuted","isShuffle","open","playlist","currentIndex","emptyEl","idx","item","details","actions","removeBtn","Visualizer","time","wave1","wave2","intensity","height","barCount","count","bar","enabled","i","HoregAudio","vol","nextShuffle","currentLoop","nextLoop","path","target","activeEl","isInputElement","tag","file","src","nameWithoutExt","fileArray","addedTracks","shouldAutoPlay","meta","_b","fallbackTitle","pathname","lastPart","resolve","tempAudio","isResolved","finish","onLoaded","onError","d","themeConfig"],"mappings":";;;AAEO,MAAMA,IAA2D;AAAA,EACtE,iBAAiB;AAAA,IACf,kBAAkB;AAAA,IAClB,aAAa;AAAA,IACb,gBAAgB;AAAA,IAChB,WAAW;AAAA,IACX,qBAAqB;AAAA,IACrB,cAAc;AAAA,EAAA;AAAA,EAEhB,mBAAmB;AAAA,IACjB,kBAAkB;AAAA,IAClB,aAAa;AAAA,IACb,gBAAgB;AAAA,IAChB,WAAW;AAAA,IACX,qBAAqB;AAAA,IACrB,cAAc;AAAA,EAAA;AAAA,EAEhB,iBAAiB;AAAA,IACf,kBAAkB;AAAA,IAClB,aAAa;AAAA,IACb,gBAAgB;AAAA,IAChB,WAAW;AAAA,IACX,qBAAqB;AAAA,IACrB,cAAc;AAAA,EAAA;AAElB;AAKO,SAASC,EAAeC,IAAoB,IAAY;AAC7D,QAAMC,IAAUD,EAAM,WAAW,iBAC3BE,IAASJ,EAAcG,CAAO,KAAKH,EAAc,eAAe,GAEhEK,IAAOH,EAAM,oBAAoBE,EAAO,oBAAoB,WAC5DE,IAASJ,EAAM,eAAeE,EAAO,eAAe,WACpDG,IAAKL,EAAM,kBAAkBE,EAAO,kBAAkB,WACtDI,IAAON,EAAM,aAAaE,EAAO,aAAa,WAC9CK,IAAWP,EAAM,uBAAuBE,EAAO,uBAAuBC,GACtEK,IAASR,EAAM,gBAAgBE,EAAO,gBAAgB;AAE5D,SAAO;AAAA;AAAA,oBAEWG,CAAE;AAAA;AAAA;AAAA;AAAA,sBAIAF,CAAI;AAAA;AAAA,wBAEFC,CAAM;AAAA,0BACJG,CAAQ;AAAA,2BACPD,CAAI;AAAA;AAAA,wBAEPE,CAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;A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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
declare class AudioEngine {
|
|
2
|
+
private audio;
|
|
3
|
+
private playlist;
|
|
4
|
+
private currentIndex;
|
|
5
|
+
private loopMode;
|
|
6
|
+
private shuffleMode;
|
|
7
|
+
private volumeLevel;
|
|
8
|
+
private previousVolume;
|
|
9
|
+
private callbacks;
|
|
10
|
+
constructor(options: HoregPlayerOptions, callbacks?: AudioEngineCallbacks);
|
|
11
|
+
private attachEvents;
|
|
12
|
+
private detachEvents;
|
|
13
|
+
private handlePlay;
|
|
14
|
+
private handlePause;
|
|
15
|
+
private handleTimeUpdate;
|
|
16
|
+
private handleProgress;
|
|
17
|
+
private handleLoadedMetadata;
|
|
18
|
+
private handleWaiting;
|
|
19
|
+
private handlePlaying;
|
|
20
|
+
private handleEnded;
|
|
21
|
+
private handleError;
|
|
22
|
+
getCurrentTrack(): Track | undefined;
|
|
23
|
+
getCurrentIndex(): number;
|
|
24
|
+
getPlaylist(): Track[];
|
|
25
|
+
setPlaylist(newPlaylist: Track[], startIndex?: number): void;
|
|
26
|
+
addTrack(track: Track, autoPlay?: boolean): number;
|
|
27
|
+
addTracks(tracks: Track[], autoPlay?: boolean): void;
|
|
28
|
+
removeTrack(index: number): void;
|
|
29
|
+
getDuration(): number;
|
|
30
|
+
getCurrentTime(): number;
|
|
31
|
+
isPlaying(): boolean;
|
|
32
|
+
isMuted(): boolean;
|
|
33
|
+
getVolume(): number;
|
|
34
|
+
getLoop(): LoopMode;
|
|
35
|
+
isShuffle(): boolean;
|
|
36
|
+
loadTrack(indexOrTrack: number | Track, autoPlay?: boolean): void;
|
|
37
|
+
play(): Promise<void>;
|
|
38
|
+
pause(): void;
|
|
39
|
+
toggle(): void;
|
|
40
|
+
seek(seconds: number): void;
|
|
41
|
+
setVolume(level: number): void;
|
|
42
|
+
toggleMute(): boolean;
|
|
43
|
+
next(isFromEnded?: boolean): void;
|
|
44
|
+
prev(): void;
|
|
45
|
+
setLoop(mode: LoopMode): void;
|
|
46
|
+
setShuffle(shuffle: boolean): void;
|
|
47
|
+
setCallbacks(callbacks: AudioEngineCallbacks): void;
|
|
48
|
+
destroy(): void;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
declare interface AudioEngineCallbacks {
|
|
52
|
+
onPlay?: (track: Track) => void;
|
|
53
|
+
onPause?: () => void;
|
|
54
|
+
onTrackChange?: (track: Track, index: number) => void;
|
|
55
|
+
onPlaylistChange?: (playlist: Track[], currentIndex: number) => void;
|
|
56
|
+
onTimeUpdate?: (currentTime: number, duration: number) => void;
|
|
57
|
+
onBufferUpdate?: (bufferedPercent: number) => void;
|
|
58
|
+
onEnded?: (track: Track) => void;
|
|
59
|
+
onError?: (error: MediaError | Error) => void;
|
|
60
|
+
onLoadingChange?: (isLoading: boolean) => void;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
declare class HoregAudio {
|
|
64
|
+
private container;
|
|
65
|
+
private shadow;
|
|
66
|
+
private styleEl;
|
|
67
|
+
private audioEngine;
|
|
68
|
+
private ui;
|
|
69
|
+
private visualizer;
|
|
70
|
+
private theme;
|
|
71
|
+
private boundKeyHandler;
|
|
72
|
+
constructor(options: HoregPlayerOptions);
|
|
73
|
+
private handleKeyDown;
|
|
74
|
+
play(): Promise<void>;
|
|
75
|
+
pause(): void;
|
|
76
|
+
toggle(): void;
|
|
77
|
+
next(): void;
|
|
78
|
+
prev(): void;
|
|
79
|
+
seek(seconds: number): void;
|
|
80
|
+
setVolume(level: number): void;
|
|
81
|
+
loadTrack(indexOrTrack: number | Track, autoPlay?: boolean): void;
|
|
82
|
+
addTrack(track: Track, autoPlay?: boolean): number;
|
|
83
|
+
addTracks(tracks: Track[], autoPlay?: boolean): void;
|
|
84
|
+
addTrackFromFile(file: File, autoPlay?: boolean): Promise<Track>;
|
|
85
|
+
addTrackFromFiles(files: FileList | File[], autoPlay?: boolean): Promise<Track[]>;
|
|
86
|
+
addTrackFromUrl(url: string, meta?: Partial<Track>, autoPlay?: boolean): Promise<Track>;
|
|
87
|
+
removeTrack(index: number): void;
|
|
88
|
+
getPlaylist(): Track[];
|
|
89
|
+
private probeAudioDuration;
|
|
90
|
+
setTheme(themeConfig: Partial<HoregTheme>): void;
|
|
91
|
+
getAudioEngine(): AudioEngine;
|
|
92
|
+
destroy(): void;
|
|
93
|
+
}
|
|
94
|
+
export { HoregAudio }
|
|
95
|
+
export default HoregAudio;
|
|
96
|
+
|
|
97
|
+
export declare interface HoregPlayerOptions {
|
|
98
|
+
container: string | HTMLElement;
|
|
99
|
+
playlist: Track[];
|
|
100
|
+
initialIndex?: number;
|
|
101
|
+
autoplay?: boolean;
|
|
102
|
+
loop?: LoopMode;
|
|
103
|
+
shuffle?: boolean;
|
|
104
|
+
volume?: number;
|
|
105
|
+
theme?: HoregTheme;
|
|
106
|
+
onPlay?: (track: Track) => void;
|
|
107
|
+
onPause?: () => void;
|
|
108
|
+
onTrackChange?: (track: Track, index: number) => void;
|
|
109
|
+
onPlaylistChange?: (playlist: Track[], currentIndex: number) => void;
|
|
110
|
+
onTimeUpdate?: (currentTime: number, duration: number) => void;
|
|
111
|
+
onEnded?: (track: Track) => void;
|
|
112
|
+
onError?: (error: MediaError | Error) => void;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export declare interface HoregPlayerState {
|
|
116
|
+
isPlaying: boolean;
|
|
117
|
+
isLoading: boolean;
|
|
118
|
+
currentIndex: number;
|
|
119
|
+
currentTime: number;
|
|
120
|
+
duration: number;
|
|
121
|
+
volume: number;
|
|
122
|
+
isMuted: boolean;
|
|
123
|
+
loop: LoopMode;
|
|
124
|
+
shuffle: boolean;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export declare interface HoregTheme {
|
|
128
|
+
variant?: ThemeVariant;
|
|
129
|
+
primaryGlowColor?: string;
|
|
130
|
+
accentColor?: string;
|
|
131
|
+
cardBackground?: string;
|
|
132
|
+
textColor?: string;
|
|
133
|
+
sliderProgressColor?: string;
|
|
134
|
+
enableEqAnimation?: boolean;
|
|
135
|
+
borderRadius?: string;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export declare type LoopMode = 'none' | 'all' | 'one';
|
|
139
|
+
|
|
140
|
+
export declare type ThemeVariant = 'horeg-classic' | 'horeg-nightclub' | 'horeg-stealth';
|
|
141
|
+
|
|
142
|
+
export declare interface Track {
|
|
143
|
+
id?: string | number;
|
|
144
|
+
title: string;
|
|
145
|
+
artist?: string;
|
|
146
|
+
album?: string;
|
|
147
|
+
src: string;
|
|
148
|
+
coverArt?: string;
|
|
149
|
+
duration?: number;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export { }
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "horeg-audio",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Standalone Vanilla TypeScript audio player library with encapsulated Shadow DOM and sound horeg aesthetics.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/horeg-audio.cjs",
|
|
7
|
+
"module": "./dist/horeg-audio.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"unpkg": "./dist/horeg-audio.global.js",
|
|
10
|
+
"jsdelivr": "./dist/horeg-audio.global.js",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/horeg-audio.js",
|
|
15
|
+
"require": "./dist/horeg-audio.cjs",
|
|
16
|
+
"default": "./dist/horeg-audio.global.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"keywords": [
|
|
23
|
+
"audio-player",
|
|
24
|
+
"music-player",
|
|
25
|
+
"sound-horeg",
|
|
26
|
+
"typescript",
|
|
27
|
+
"shadow-dom",
|
|
28
|
+
"vanilla-js"
|
|
29
|
+
],
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"typescript": "^5.4.0",
|
|
32
|
+
"vite": "^5.4.0",
|
|
33
|
+
"vite-plugin-dts": "^3.9.1"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"dev": "vite",
|
|
37
|
+
"build": "tsc && vite build",
|
|
38
|
+
"preview": "vite preview"
|
|
39
|
+
}
|
|
40
|
+
}
|