From a7363fcb3a0c1a919953bbec3508e9e75778ab4c Mon Sep 17 00:00:00 2001 From: Gronod Date: Tue, 19 May 2026 20:27:11 +0100 Subject: [PATCH] v1.5.2: Build and deploy React client with Webhooks Configuration panel --- package.json | 2 +- public/app.js | 1200 ++--------------------------------- public/index.html | 132 +--- public/style.css | 1519 +-------------------------------------------- 4 files changed, 60 insertions(+), 2793 deletions(-) diff --git a/package.json b/package.json index b2f38f5..7bab45c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sofarr", - "version": "1.5.1", + "version": "1.5.2", "description": "A personal media download dashboard that shows your downloads 'so far' while you relax on the sofa waiting for your *arr services to finish", "main": "server/index.js", "scripts": { diff --git a/public/app.js b/public/app.js index f89cd7f..7151fc9 100644 --- a/public/app.js +++ b/public/app.js @@ -1,1152 +1,48 @@ -// Copyright (c) 2026 Gordon Bolton. MIT License. -let currentUser = null; -let downloads = []; -let isAdmin = false; -let showAll = false; -let csrfToken = null; // double-submit CSRF token, sent as X-CSRF-Token on mutating requests -const SPLASH_MIN_MS = 1200; // minimum splash display time - -// History section state -let historyDays = parseInt(localStorage.getItem('sofarr-history-days'), 10) || 7; -let historyRefreshHandle = null; -const HISTORY_REFRESH_MS = 5 * 60 * 1000; // auto-refresh history every 5 min -let ignoreAvailable = localStorage.getItem('sofarr-ignore-available') === 'true'; -let lastHistoryItems = []; // raw items from last fetch, for re-filtering without a network round-trip - -// SSE stream state -let sseSource = null; -let sseReconnectTimer = null; -const SSE_RECONNECT_MS = 3000; // browser already retries, but we add explicit backoff too - -// Apply saved theme immediately (before DOMContentLoaded to avoid flash) -(function() { - const saved = localStorage.getItem('sofarr-theme') || 'light'; - document.documentElement.setAttribute('data-theme', saved); -})(); - -// Check authentication on load -document.addEventListener('DOMContentLoaded', () => { - checkAuthentication(); - initThemeSwitcher(); - initTabs(); - initHistoryControls(); - loadAppVersion(); - - document.getElementById('login-form').addEventListener('submit', handleLogin); - document.getElementById('logout-btn').addEventListener('click', handleLogout); - document.getElementById('show-all-toggle').addEventListener('change', handleShowAllToggle); - document.getElementById('status-btn').addEventListener('click', toggleStatusPanel); - document.getElementById('title-home-link').addEventListener('click', e => { e.preventDefault(); goHome(); }); -}); - -function loadAppVersion() { - fetch('/health') - .then(r => r.json()) - .then(data => { - if (data.version) { - document.getElementById('app-version').textContent = `sofarr v${data.version}`; - } - }) - .catch(() => {}); -} - -function initThemeSwitcher() { - const saved = localStorage.getItem('sofarr-theme') || 'light'; - document.querySelectorAll('.theme-btn').forEach(btn => { - btn.classList.toggle('active', btn.dataset.theme === saved); - btn.addEventListener('click', () => setTheme(btn.dataset.theme)); - }); -} - -function setTheme(theme) { - document.documentElement.setAttribute('data-theme', theme); - localStorage.setItem('sofarr-theme', theme); - document.querySelectorAll('.theme-btn').forEach(btn => { - btn.classList.toggle('active', btn.dataset.theme === theme); - }); -} - -function goHome() { - closeStatusPanel(); - // Reset showAll if active - if (showAll) { - showAll = false; - const toggle = document.getElementById('show-all-toggle'); - if (toggle) toggle.checked = false; - startSSE(); - } - activateTab('downloads', true); -} - -function initTabs() { - const savedTab = localStorage.getItem('sofarr-active-tab') || 'downloads'; - activateTab(savedTab, false); - - document.querySelectorAll('.tab-btn').forEach(btn => { - btn.addEventListener('click', () => { - const tab = btn.dataset.tab; - activateTab(tab, true); - }); - }); -} - -function activateTab(tabName, save) { - document.querySelectorAll('.tab-btn').forEach(btn => { - btn.classList.toggle('active', btn.dataset.tab === tabName); - }); - document.querySelectorAll('.tab-panel').forEach(panel => { - panel.style.display = panel.id === `tab-${tabName}` ? '' : 'none'; - }); - if (save) localStorage.setItem('sofarr-active-tab', tabName); - // Load history the first time the history tab is shown - if (tabName === 'history') loadHistory(); -} - -// --- SSE connection management --- - -function startSSE() { - stopSSE(); - const params = showAll ? '?showAll=true' : ''; - const source = new EventSource('/api/dashboard/stream' + params); - sseSource = source; - - let firstMessage = true; - source.onmessage = (event) => { - try { - const data = JSON.parse(event.data); - currentUser = data.user; - isAdmin = !!data.isAdmin; - downloads = data.downloads; - document.getElementById('currentUser').textContent = currentUser || '-'; - renderDownloads(); - hideError(); - if (firstMessage) { firstMessage = false; hideLoading(); } - } catch (err) { - console.error('[SSE] Failed to parse message:', err); - } - }; - - source.onerror = () => { - // EventSource retries automatically; we just log and show a reconnecting indicator - console.warn('[SSE] Connection lost, browser will retry...'); - }; - - console.log('[SSE] Stream connected'); -} - -function stopSSE() { - if (sseReconnectTimer) { clearTimeout(sseReconnectTimer); sseReconnectTimer = null; } - if (sseSource) { - sseSource.close(); - sseSource = null; - console.log('[SSE] Stream closed'); - } -} - -function handleShowAllToggle(e) { - showAll = e.target.checked; - // Re-open stream with updated showAll param - startSSE(); - // Reload history with updated showAll param - loadHistory(); -} - -function fadeOutLogin() { - return new Promise(resolve => { - const login = document.getElementById('login-container'); - login.classList.add('fade-out'); - login.addEventListener('transitionend', () => { - login.style.display = 'none'; - login.classList.remove('fade-out'); - resolve(); - }, { once: true }); - }); -} - -function showSplash() { - const splash = document.getElementById('splash-screen'); - splash.style.display = 'flex'; - splash.style.opacity = '1'; - splash.classList.remove('fade-out'); -} - -function dismissSplash(startTime) { - return new Promise(resolve => { - const elapsed = Date.now() - (startTime || 0); - const remaining = Math.max(0, SPLASH_MIN_MS - elapsed); - setTimeout(() => { - const splash = document.getElementById('splash-screen'); - splash.classList.add('fade-out'); - // Fallback: resolve after transition duration + buffer in case - // transitionend never fires (e.g. display was toggled in same frame) - const TRANSITION_MS = 400; - const fallback = setTimeout(() => { - splash.style.display = 'none'; - resolve(); - }, TRANSITION_MS + 100); - splash.addEventListener('transitionend', () => { - clearTimeout(fallback); - splash.style.display = 'none'; - resolve(); - }, { once: true }); - }, remaining); - }); -} - -async function checkAuthentication() { - const splashStart = Date.now(); - try { - // Fetch both auth state and a fresh CSRF token in parallel - const [meRes, csrfRes] = await Promise.all([ - fetch('/api/auth/me'), - fetch('/api/auth/csrf') - ]); - const data = await meRes.json(); - const csrfData = await csrfRes.json(); - if (csrfData.csrfToken) csrfToken = csrfData.csrfToken; - - if (data.authenticated) { - currentUser = data.user; - isAdmin = !!data.user.isAdmin; - showDashboard(); - showLoading(); - startSSE(); - await dismissSplash(splashStart); - } else { - await dismissSplash(splashStart); - showLogin(); - } - } catch (err) { - console.error('Authentication check failed:', err); - await dismissSplash(splashStart); - showLogin(); - } -} - -async function handleLogin(e) { - e.preventDefault(); - - const username = document.getElementById('username').value; - const password = document.getElementById('password').value; - const rememberMe = document.getElementById('remember-me').checked; - - try { - const response = await fetch('/api/auth/login', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ username, password, rememberMe }) - }); - - const data = await response.json(); - - if (data.success) { - currentUser = data.user; - isAdmin = !!data.user.isAdmin; - // Store CSRF token returned by login for use in subsequent requests - if (data.csrfToken) csrfToken = data.csrfToken; - // Fade out login, then show splash while opening SSE stream. - // requestAnimationFrame ensures the browser paints the splash at - // opacity:1 before dismissSplash adds fade-out, so the CSS - // transition fires and transitionend is guaranteed. - await fadeOutLogin(); - showSplash(); - await new Promise(r => requestAnimationFrame(() => requestAnimationFrame(r))); - showDashboard(); - showLoading(); - const splashStart = Date.now(); - startSSE(); - await dismissSplash(splashStart); - } else { - showLoginError(data.error || 'Login failed'); - } - } catch (err) { - showLoginError('Login failed. Please try again.'); - console.error(err); - } -} - -async function handleLogout() { - try { - stopSSE(); - stopHistoryRefresh(); - if (statusRefreshHandle) { clearInterval(statusRefreshHandle); statusRefreshHandle = null; } - await fetch('/api/auth/logout', { - method: 'POST', - headers: csrfToken ? { 'X-CSRF-Token': csrfToken } : {} - }); - currentUser = null; - csrfToken = null; - downloads = []; - clearHistory(); - showLogin(); - } catch (err) { - console.error('Logout failed:', err); - } -} - -function showLogin() { - document.getElementById('login-container').style.display = 'flex'; - document.getElementById('dashboard-container').style.display = 'none'; - hideLoginError(); -} - -function showDashboard() { - document.getElementById('login-container').style.display = 'none'; - document.getElementById('dashboard-container').style.display = 'block'; - document.getElementById('currentUser').textContent = currentUser.name || '-'; - // Always start with status panel hidden (guards against stale display value on re-login) - const sp = document.getElementById('status-panel'); - sp.style.display = 'none'; - sp.innerHTML = ''; - document.getElementById('admin-controls').style.display = isAdmin ? 'flex' : 'none'; - // Initialise days input from saved value - const daysInput = document.getElementById('history-days'); - if (daysInput) daysInput.value = historyDays; - startHistoryRefresh(); -} - -function showLoginError(message) { - const errorDiv = document.getElementById('login-error'); - errorDiv.textContent = message; - errorDiv.style.display = 'block'; -} - -function hideLoginError() { - const errorDiv = document.getElementById('login-error'); - errorDiv.style.display = 'none'; -} - -// Build an episode-info element for series downloads/history. -// Single episode: "S01E05 — Episode Title" -// Multiple episodes: "Multiple episodes" with tooltip listing them all. -// Returns null if no episode data. -function formatEpisodeInfo(episodes) { - if (!episodes || episodes.length === 0) return null; - const el = document.createElement('p'); - el.className = 'episode-info'; - if (episodes.length === 1) { - const ep = episodes[0]; - const code = 'S' + String(ep.season).padStart(2, '0') + 'E' + String(ep.episode).padStart(2, '0'); - el.textContent = ep.title ? code + ' \u2014 ' + ep.title : code; - } else { - el.textContent = 'Multiple episodes'; - el.classList.add('multi-episode'); - const lines = episodes.map(ep => { - const code = 'S' + String(ep.season).padStart(2, '0') + 'E' + String(ep.episode).padStart(2, '0'); - return ep.title ? code + ' \u2014 ' + ep.title : code; - }); - el.setAttribute('data-tooltip', lines.join('\n')); - } - return el; -} - -// fetchUserDownloads is kept for the showAll toggle re-connection case -// but the primary data path is now via SSE (startSSE / EventSource). - -function renderDownloads() { - const downloadsList = document.getElementById('downloads-list'); - const noDownloads = document.getElementById('no-downloads'); - - if (downloads.length === 0) { - noDownloads.style.display = 'block'; - downloadsList.innerHTML = ''; - return; - } - - noDownloads.style.display = 'none'; - - // Get existing cards - const existingCards = new Map(); - downloadsList.querySelectorAll('.download-card').forEach(card => { - existingCards.set(card.dataset.id, card); - }); - - // Track which downloads we've processed - const processedIds = new Set(); - - downloads.forEach(download => { - const id = download.title; - processedIds.add(id); - - const existingCard = existingCards.get(id); - if (existingCard) { - // Update existing card - updateDownloadCard(existingCard, download); - } else { - // Create new card - const card = createDownloadCard(download); - downloadsList.appendChild(card); - } - }); - - // Remove cards for downloads that no longer exist - existingCards.forEach((card, id) => { - if (!processedIds.has(id)) { - card.remove(); - } - }); -} - -function updateDownloadCard(card, download) { - // Update status - const statusEl = card.querySelector('.download-status'); - if (statusEl && statusEl.textContent !== download.status) { - statusEl.textContent = download.status; - statusEl.className = `download-status ${download.status}`; - } - - // Update progress bar and missing pieces - const progressContainer = card.querySelector('.progress-container'); - if (progressContainer && download.progress !== undefined) { - const progressBar = progressContainer.querySelector('.progress-bar'); - const progressText = progressContainer.querySelector('.progress-text'); - const missingText = progressContainer.querySelector('.missing-text'); - - if (progressBar) { - const downloaded = progressBar.querySelector('.downloaded'); - if (downloaded) { - downloaded.style.width = download.progress + '%'; - } - } - - if (progressText) { - progressText.textContent = download.progress + '%'; - } - - if (missingText) { - const totalMb = parseFloat(download.mb) || parseFloat(download.size); - const missingMb = parseFloat(download.mbmissing) || 0; - if (missingMb > 0 && totalMb > 0) { - missingText.textContent = `(missing ${missingMb.toFixed(1)} of ${totalMb.toFixed(1)} MB)`; - } else { - missingText.textContent = ''; - } - } - } - - // Update speed - const speedEl = card.querySelector('.detail-item[data-label="Speed"] .detail-value'); - if (speedEl && download.speed !== undefined) { - speedEl.textContent = download.speed; - } - - // Update ETA - const etaEl = card.querySelector('.detail-item[data-label="ETA"] .detail-value'); - if (etaEl && download.eta !== undefined) { - etaEl.textContent = download.eta; - } - - // Update qBittorrent-specific fields - if (download.qbittorrent) { - const seedsEl = card.querySelector('.detail-item[data-label="Seeds"] .detail-value'); - if (seedsEl && download.seeds !== undefined) { - seedsEl.textContent = download.seeds; - } - - const peersEl = card.querySelector('.detail-item[data-label="Peers"] .detail-value'); - if (peersEl && download.peers !== undefined) { - peersEl.textContent = download.peers; - } - - const availabilityItem = card.querySelector('.detail-item[data-label="Availability"]'); - if (availabilityItem && download.availability !== undefined) { - availabilityItem.querySelector('.detail-value').textContent = `${download.availability}%`; - availabilityItem.classList.toggle('availability-warning', parseFloat(download.availability) < 100); - } - } -} - -async function handleBlocklistSearch(btn, download) { - if (!confirm(`Blocklist "${download.title}" and trigger a new search?\n\nThis will:\n• Remove the download from the download client\n• Add this release to the blocklist\n• Trigger an automatic search for a new release`)) return; - - btn.disabled = true; - btn.textContent = '⏳ Working…'; - - try { - const res = await fetch('/api/dashboard/blocklist-search', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-CSRF-Token': csrfToken - }, - body: JSON.stringify({ - arrQueueId: download.arrQueueId, - arrType: download.arrType, - arrInstanceUrl: download.arrInstanceUrl, - arrInstanceKey: download.arrInstanceKey, - arrContentId: download.arrContentId, - arrContentType: download.arrContentType - }) - }); - - if (!res.ok) { - const data = await res.json().catch(() => ({})); - throw new Error(data.error || `HTTP ${res.status}`); - } - - btn.textContent = '✓ Done — searching…'; - btn.className = 'blocklist-search-btn success'; - } catch (err) { - console.error('[Blocklist] Error:', err); - btn.disabled = false; - btn.textContent = '⛔ Blocklist & Search'; - btn.className = 'blocklist-search-btn error'; - btn.title = `Failed: ${err.message}`; - setTimeout(() => { - btn.className = 'blocklist-search-btn'; - btn.title = 'Remove this release from the download client, add it to the blocklist, and trigger a new automatic search'; - }, 4000); - } -} - -function createDownloadCard(download) { - const card = document.createElement('div'); - card.className = `download-card ${download.type}`; - card.dataset.id = download.title; - - // Cover art - if (download.coverArt) { - const coverDiv = document.createElement('div'); - coverDiv.className = 'download-cover'; - const coverImg = document.createElement('img'); - // Proxy cover art through the server so the CSP img-src 'self' rule - // is satisfied (external poster URLs would be blocked otherwise). - coverImg.src = download.coverArt - ? '/api/dashboard/cover-art?url=' + encodeURIComponent(download.coverArt) - : ''; - coverImg.alt = download.movieName || download.seriesName || download.title; - coverImg.loading = 'lazy'; - coverDiv.appendChild(coverImg); - card.appendChild(coverDiv); - } - - // Info wrapper - const infoDiv = document.createElement('div'); - infoDiv.className = 'download-info'; - - const header = document.createElement('div'); - header.className = 'download-header'; - - const type = document.createElement('span'); - type.className = `download-type ${download.type}`; - if (download.type === 'series') { - type.textContent = '📺 Series'; - } else if (download.type === 'movie') { - type.textContent = '🎬 Movie'; - } else if (download.type === 'torrent') { - const instName = download.instanceName ? ` (${download.instanceName})` : ''; - type.textContent = `📥 Torrent${instName}`; - } else { - type.textContent = download.type; - } - - const status = document.createElement('span'); - status.className = `download-status ${download.status}`; - status.textContent = download.status; - - header.appendChild(type); - header.appendChild(status); - - if (download.importIssues && download.importIssues.length > 0) { - const issueBadge = document.createElement('span'); - issueBadge.className = 'import-issue-badge'; - issueBadge.textContent = 'Import Pending'; - issueBadge.setAttribute('data-tooltip', download.importIssues.join('\n')); - header.appendChild(issueBadge); - } - - if ((isAdmin || download.canBlocklist) && download.arrQueueId) { - const blBtn = document.createElement('button'); - blBtn.className = 'blocklist-search-btn'; - blBtn.textContent = '⛔ Blocklist & Search'; - blBtn.title = 'Remove this release from the download client, add it to the blocklist, and trigger a new automatic search'; - blBtn.addEventListener('click', () => handleBlocklistSearch(blBtn, download)); - header.appendChild(blBtn); - } - - const title = document.createElement('h3'); - title.className = 'download-title'; - title.textContent = download.title; - - infoDiv.appendChild(header); - infoDiv.appendChild(title); - - if (download.seriesName) { - const series = document.createElement('p'); - series.className = 'download-series'; - if (isAdmin && download.arrLink) { - series.innerHTML = 'Series: ' + escapeHtml(download.seriesName) + ''; - } else { - series.textContent = `Series: ${download.seriesName}`; - } - infoDiv.appendChild(series); - const epEl = formatEpisodeInfo(download.episodes); - if (epEl) infoDiv.appendChild(epEl); - } - - if (download.movieName) { - const movie = document.createElement('p'); - movie.className = 'download-movie'; - if (isAdmin && download.arrLink) { - movie.innerHTML = 'Movie: ' + escapeHtml(download.movieName) + ''; - } else { - movie.textContent = `Movie: ${download.movieName}`; - } - infoDiv.appendChild(movie); - } - - if (showAll && download.tagBadges && download.tagBadges.length > 0) { - // In showAll mode: render all tags classified by whether they match an Emby user. - // Unmatched (no known Emby user) → amber, leftmost. - // Matched → show Emby display name in accent colour, rightmost. - const unmatched = download.tagBadges.filter(b => !b.matchedUser); - const matched = download.tagBadges.filter(b => b.matchedUser); - for (const b of unmatched) { - const badge = document.createElement('span'); - badge.className = 'download-user-badge unmatched'; - badge.textContent = b.label; - header.appendChild(badge); - } - for (const b of matched) { - const badge = document.createElement('span'); - badge.className = 'download-user-badge'; - badge.textContent = b.matchedUser; - header.appendChild(badge); - } - } else if (download.matchedUserTag) { - // Normal (non-showAll) view: show only the current user's matched tag - const matchedBadge = document.createElement('span'); - matchedBadge.className = 'download-user-badge'; - matchedBadge.textContent = download.matchedUserTag; - header.appendChild(matchedBadge); - } - - const details = document.createElement('div'); - details.className = 'download-details'; - - const size = createDetailItem('Size', formatSize(download.size)); - details.appendChild(size); - - if (download.progress !== undefined) { - const progressItem = document.createElement('div'); - progressItem.className = 'detail-item progress-item'; - progressItem.dataset.label = 'Progress'; - - const labelSpan = document.createElement('span'); - labelSpan.className = 'detail-label'; - labelSpan.textContent = 'Progress'; - - const valueDiv = document.createElement('div'); - valueDiv.className = 'progress-container'; - - // Progress bar with segments - const totalMb = parseFloat(download.mb) || parseFloat(download.size); - const missingMb = parseFloat(download.mbmissing) || 0; - const downloadedMb = totalMb - missingMb; - const progressPercent = parseFloat(download.progress) || 0; - const missingPercent = totalMb > 0 ? (missingMb / totalMb) * 100 : 0; - - const progressBar = document.createElement('div'); - progressBar.className = 'progress-bar'; - - // Downloaded portion (green) - if (progressPercent > 0) { - const downloaded = document.createElement('div'); - downloaded.className = 'progress-segment downloaded'; - downloaded.style.width = progressPercent + '%'; - progressBar.appendChild(downloaded); - } - - valueDiv.appendChild(progressBar); - - // Text showing percentage - const progressText = document.createElement('span'); - progressText.className = 'progress-text'; - progressText.textContent = download.progress + '%'; - valueDiv.appendChild(progressText); - - // Missing pieces text - if (missingMb > 0 && totalMb > 0) { - const missingText = document.createElement('span'); - missingText.className = 'missing-text'; - missingText.textContent = `(missing ${missingMb.toFixed(1)} of ${totalMb.toFixed(1)} MB)`; - valueDiv.appendChild(missingText); - } - - progressItem.appendChild(labelSpan); - progressItem.appendChild(valueDiv); - details.appendChild(progressItem); - } - - if (download.speed) { - const speed = createDetailItem('Speed', download.speed); - details.appendChild(speed); - } - - if (download.eta) { - const eta = createDetailItem('ETA', download.eta); - details.appendChild(eta); - } - - // qBittorrent-specific fields - if (download.qbittorrent) { - if (download.seeds !== undefined) { - const seeds = createDetailItem('Seeds', download.seeds); - details.appendChild(seeds); - } - - if (download.peers !== undefined) { - const peers = createDetailItem('Peers', download.peers); - details.appendChild(peers); - } - - if (download.availability !== undefined) { - const availability = createDetailItem('Availability', `${download.availability}%`); - if (parseFloat(download.availability) < 100) availability.classList.add('availability-warning'); - details.appendChild(availability); - } - } - - if (download.completedAt) { - const completed = createDetailItem('Completed', formatDate(download.completedAt)); - details.appendChild(completed); - } - - if (isAdmin && (download.downloadPath || download.targetPath)) { - const pathsDiv = document.createElement('div'); - pathsDiv.className = 'download-paths'; - if (download.downloadPath) { - const dlPath = document.createElement('div'); - dlPath.className = 'path-item'; - dlPath.innerHTML = 'Download: ' + escapeHtml(download.downloadPath) + ''; - pathsDiv.appendChild(dlPath); - } - if (download.targetPath) { - const tgtPath = document.createElement('div'); - tgtPath.className = 'path-item'; - tgtPath.innerHTML = 'Target: ' + escapeHtml(download.targetPath) + ''; - pathsDiv.appendChild(tgtPath); - } - details.appendChild(pathsDiv); - } - - infoDiv.appendChild(details); - card.appendChild(infoDiv); - - return card; -} - -function createDetailItem(label, value) { - const item = document.createElement('div'); - item.className = 'detail-item'; - item.dataset.label = label; - - const labelSpan = document.createElement('span'); - labelSpan.className = 'detail-label'; - labelSpan.textContent = label; - - const valueSpan = document.createElement('span'); - valueSpan.className = 'detail-value'; - valueSpan.textContent = value; - - item.appendChild(labelSpan); - item.appendChild(valueSpan); - - return item; -} - -function escapeHtml(str) { - const div = document.createElement('div'); - div.textContent = str; - return div.innerHTML; -} - -let statusRefreshHandle = null; -const STATUS_REFRESH_MS = 5000; - -async function toggleStatusPanel() { - const panel = document.getElementById('status-panel'); - if (panel.style.display !== 'none') { - panel.style.display = 'none'; - if (statusRefreshHandle) { clearInterval(statusRefreshHandle); statusRefreshHandle = null; } - return; - } - panel.style.display = 'block'; - await refreshStatusPanel(); - if (statusRefreshHandle) clearInterval(statusRefreshHandle); - statusRefreshHandle = setInterval(refreshStatusPanel, STATUS_REFRESH_MS); -} - -function closeStatusPanel() { - document.getElementById('status-panel').style.display = 'none'; - if (statusRefreshHandle) { clearInterval(statusRefreshHandle); statusRefreshHandle = null; } -} - -async function refreshStatusPanel() { - const panel = document.getElementById('status-panel'); - if (!panel || panel.style.display === 'none') return; - try { - const res = await fetch('/api/dashboard/status'); - if (!res.ok) throw new Error('Failed to fetch status'); - const data = await res.json(); - renderStatusPanel(data, panel); - } catch (err) { - // Don't overwrite panel on transient error during auto-refresh - if (!panel.innerHTML || panel.innerHTML.includes('status-loading')) { - panel.innerHTML = '

Failed to load status.

'; - } - } -} - -function renderStatusPanel(data, panel) { - const s = data.server; - const hrs = Math.floor(s.uptimeSeconds / 3600); - const mins = Math.floor((s.uptimeSeconds % 3600) / 60); - const secs = s.uptimeSeconds % 60; - const uptime = `${hrs}h ${mins}m ${secs}s`; - - const totalKB = (data.cache.totalSizeBytes / 1024).toFixed(1); - - let html = ` -
-

Server Status

- -
-
-
-
Server
-
Uptime${uptime}
-
Node${escapeHtml(s.nodeVersion)}
-
Memory (RSS)${s.memoryUsageMB} MB
-
Heap${s.heapUsedMB} / ${s.heapTotalMB} MB
-
-
-
Data Refresh
`; - - const pollIntervalMs = data.polling.intervalMs; - const clients = data.clients || []; - const sseClients = clients.filter(c => c.type === 'sse'); - - if (data.polling.enabled) { - html += `
Background poll${pollIntervalMs / 1000}s
`; - } else { - html += `
Background pollDisabled
`; - } - - const mode = sseClients.length > 0 - ? `SSE push` - : (data.polling.enabled ? 'Background' : 'On-demand (idle)'); - html += `
Delivery mode${mode}
`; - - html += `
SSE clients${sseClients.length}
`; - for (const c of sseClients) { - const age = Math.round((Date.now() - c.connectedAt) / 1000); - html += `
${escapeHtml(c.user)}connected ${age}s ago
`; - } - - html += `
`; - - // Poll timings card - const lp = data.polling.lastPoll; - if (lp) { - const pollAge = Math.round((Date.now() - new Date(lp.timestamp).getTime()) / 1000); - html += ` -
-
Last Poll (${lp.totalMs}ms total, ${pollAge}s ago)
-
`; - const maxTaskMs = lp.tasks.reduce((max, t) => Math.max(max, t.ms), 1); - for (const t of lp.tasks) { - const barWidth = Math.max(2, (t.ms / maxTaskMs) * 100); - html += ` -
- ${escapeHtml(t.label)} -
- ${t.ms}ms -
`; - } - html += `
`; - } - - // Cache table - html += ` -
-
Cache (${data.cache.entryCount} entries, ${totalKB} KB)
- - - `; - - for (const e of data.cache.entries) { - const sizeStr = e.sizeBytes > 1024 ? (e.sizeBytes / 1024).toFixed(1) + ' KB' : e.sizeBytes + ' B'; - const ttlStr = e.expired ? 'expired' : (e.ttlRemainingMs / 1000).toFixed(0) + 's'; - const items = e.itemCount !== null ? e.itemCount : '—'; - html += ``; - } - - html += `
KeyItemsSizeTTL
${escapeHtml(e.key)}${items}${sizeStr}${ttlStr}
`; - panel.innerHTML = html; - // Wire close button — addEventListener avoids CSP inline handler restrictions - const closeBtn = panel.querySelector('#status-close-btn'); - if (closeBtn) closeBtn.addEventListener('click', closeStatusPanel); - // Set bar widths via JS DOM assignment — immune to CSP style-src restrictions - panel.querySelectorAll('.timing-bar[data-w]').forEach(el => { - el.style.width = el.dataset.w + '%'; - }); -} - -function formatSize(size) { - if (!size) return 'N/A'; - // If already a formatted string (e.g., "21.5 GB"), return as-is - if (typeof size === 'string') { - return size; - } - // If it's a number (bytes), format it - const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; - const i = Math.floor(Math.log(size) / Math.log(1024)); - return Math.round(size / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i]; -} - -function formatDate(dateString) { - if (!dateString) return 'N/A'; - return new Date(dateString).toLocaleString(); -} - -function showError(message) { - const errorDiv = document.getElementById('error-message'); - errorDiv.textContent = message; - errorDiv.style.display = 'block'; -} - -function hideError() { - const errorDiv = document.getElementById('error-message'); - errorDiv.style.display = 'none'; -} - -function showLoading() { - const loading = document.getElementById('loading'); - loading.style.display = 'block'; -} - -function hideLoading() { - const loading = document.getElementById('loading'); - loading.style.display = 'none'; -} - -// ============================================================================= -// History section -// ============================================================================= - -function initHistoryControls() { - const daysInput = document.getElementById('history-days'); - const refreshBtn = document.getElementById('history-refresh-btn'); - const ignoreToggle = document.getElementById('ignore-available-toggle'); - if (daysInput) { - daysInput.addEventListener('change', () => { - const v = parseInt(daysInput.value, 10); - if (v > 0 && v <= 90) { - historyDays = v; - localStorage.setItem('sofarr-history-days', v); - loadHistory(); - } - }); - } - if (refreshBtn) { - refreshBtn.addEventListener('click', () => loadHistory(true)); - } - if (ignoreToggle) { - ignoreToggle.checked = ignoreAvailable; - ignoreToggle.addEventListener('change', () => { - ignoreAvailable = ignoreToggle.checked; - localStorage.setItem('sofarr-ignore-available', ignoreAvailable); - renderHistory(lastHistoryItems); - }); - } -} - -function startHistoryRefresh() { - stopHistoryRefresh(); - historyRefreshHandle = setInterval(() => loadHistory(), HISTORY_REFRESH_MS); -} - -function stopHistoryRefresh() { - if (historyRefreshHandle) { - clearInterval(historyRefreshHandle); - historyRefreshHandle = null; - } -} - -function clearHistory() { - lastHistoryItems = []; - document.getElementById('history-list').innerHTML = ''; - document.getElementById('no-history').style.display = 'none'; - document.getElementById('history-error').style.display = 'none'; -} - -async function loadHistory(forceRefresh = false) { - const listEl = document.getElementById('history-list'); - const loadingEl = document.getElementById('history-loading'); - const errorEl = document.getElementById('history-error'); - const noHistoryEl = document.getElementById('no-history'); - - loadingEl.style.display = 'block'; - errorEl.style.display = 'none'; - noHistoryEl.style.display = 'none'; - - try { - const params = new URLSearchParams({ days: historyDays }); - if (showAll) params.set('showAll', 'true'); - if (forceRefresh) params.set('_t', Date.now()); - const res = await fetch(`/api/history/recent?${params}`); - if (!res.ok) throw new Error(`HTTP ${res.status}`); - const data = await res.json(); - loadingEl.style.display = 'none'; - lastHistoryItems = data.history || []; - renderHistory(lastHistoryItems); - } catch (err) { - loadingEl.style.display = 'none'; - errorEl.textContent = 'Failed to load history.'; - errorEl.style.display = 'block'; - console.error('[History] Load error:', err); - } -} - -function renderHistory(items) { - const listEl = document.getElementById('history-list'); - const noHistoryEl = document.getElementById('no-history'); - listEl.innerHTML = ''; - const visible = ignoreAvailable - ? items.filter(item => !(item.outcome === 'failed' && item.availableForUpgrade)) - : items; - if (!visible.length) { - noHistoryEl.style.display = 'block'; - return; - } - noHistoryEl.style.display = 'none'; - visible.forEach(item => listEl.appendChild(createHistoryCard(item))); -} - -function createHistoryCard(item) { - const card = document.createElement('div'); - card.className = `history-card ${item.type} ${item.outcome}`; - - if (item.coverArt) { - const coverDiv = document.createElement('div'); - coverDiv.className = 'history-cover'; - const img = document.createElement('img'); - img.src = '/api/dashboard/cover-art?url=' + encodeURIComponent(item.coverArt); - img.alt = item.movieName || item.seriesName || item.title; - img.loading = 'lazy'; - coverDiv.appendChild(img); - card.appendChild(coverDiv); - } - - const info = document.createElement('div'); - info.className = 'history-info'; - - // Header row: type badge + outcome badge - const header = document.createElement('div'); - header.className = 'history-card-header'; - - const typeBadge = document.createElement('span'); - typeBadge.className = `history-type-badge ${item.type}`; - typeBadge.textContent = item.type === 'series' ? '📺 Series' : '🎬 Movie'; - header.appendChild(typeBadge); - - const outcomeBadge = document.createElement('span'); - outcomeBadge.className = `history-outcome-badge ${item.outcome}`; - outcomeBadge.textContent = item.outcome === 'imported' ? '✓ Imported' : '✗ Failed'; - header.appendChild(outcomeBadge); - - if (item.availableForUpgrade) { - const upgradeBadge = document.createElement('span'); - upgradeBadge.className = 'history-upgrade-badge'; - upgradeBadge.title = 'A previous version of this item is available. An upgrade download has failed.'; - upgradeBadge.textContent = '⬆ Available'; - header.appendChild(upgradeBadge); - } - - if (item.instanceName) { - const instBadge = document.createElement('span'); - instBadge.className = 'history-instance-badge'; - instBadge.textContent = item.instanceName; - header.appendChild(instBadge); - } - - if (showAll && item.tagBadges && item.tagBadges.length > 0) { - const unmatched = item.tagBadges.filter(b => !b.matchedUser); - const matched = item.tagBadges.filter(b => b.matchedUser); - for (const b of unmatched) { - const badge = document.createElement('span'); - badge.className = 'download-user-badge unmatched'; - badge.textContent = b.label; - header.appendChild(badge); - } - for (const b of matched) { - const badge = document.createElement('span'); - badge.className = 'download-user-badge'; - badge.textContent = b.matchedUser; - header.appendChild(badge); - } - } else if (item.matchedUserTag) { - const badge = document.createElement('span'); - badge.className = 'download-user-badge'; - badge.textContent = item.matchedUserTag; - header.appendChild(badge); - } - - info.appendChild(header); - - // Title - const title = document.createElement('h3'); - title.className = 'history-title'; - title.textContent = item.title; - info.appendChild(title); - - // Series/movie name with optional arr link - if (item.seriesName) { - const p = document.createElement('p'); - p.className = 'history-media-name'; - if (isAdmin && item.arrLink) { - p.innerHTML = 'Series: ' + escapeHtml(item.seriesName) + ''; - } else { - p.textContent = 'Series: ' + item.seriesName; - } - info.appendChild(p); - const epEl = formatEpisodeInfo(item.episodes); - if (epEl) info.appendChild(epEl); - } - if (item.movieName) { - const p = document.createElement('p'); - p.className = 'history-media-name'; - if (isAdmin && item.arrLink) { - p.innerHTML = 'Movie: ' + escapeHtml(item.movieName) + ''; - } else { - p.textContent = 'Movie: ' + item.movieName; - } - info.appendChild(p); - } - - // Detail pills - const details = document.createElement('div'); - details.className = 'history-details'; - - if (item.completedAt) { - details.appendChild(createDetailItem('Completed', formatDate(item.completedAt))); - } - if (item.quality) { - details.appendChild(createDetailItem('Quality', item.quality)); - } - - // Failed imports: show failure message - if (item.outcome === 'failed' && item.failureMessage) { - const failItem = document.createElement('div'); - failItem.className = 'history-failure-message'; - failItem.textContent = item.failureMessage; - details.appendChild(failItem); - } - - info.appendChild(details); - card.appendChild(info); - return card; -} +(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))r(o);new MutationObserver(o=>{for(const l of o)if(l.type==="childList")for(const i of l.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&r(i)}).observe(document,{childList:!0,subtree:!0});function n(o){const l={};return o.integrity&&(l.integrity=o.integrity),o.referrerPolicy&&(l.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?l.credentials="include":o.crossOrigin==="anonymous"?l.credentials="omit":l.credentials="same-origin",l}function r(o){if(o.ep)return;o.ep=!0;const l=n(o);fetch(o.href,l)}})();function Wf(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var aa={exports:{}},jo={},ca={exports:{}},F={};/** + * @license React + * react.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var Nr=Symbol.for("react.element"),Qf=Symbol.for("react.portal"),Kf=Symbol.for("react.fragment"),qf=Symbol.for("react.strict_mode"),Gf=Symbol.for("react.profiler"),Xf=Symbol.for("react.provider"),Jf=Symbol.for("react.context"),Yf=Symbol.for("react.forward_ref"),Zf=Symbol.for("react.suspense"),bf=Symbol.for("react.memo"),ed=Symbol.for("react.lazy"),Bs=Symbol.iterator;function td(e){return e===null||typeof e!="object"?null:(e=Bs&&e[Bs]||e["@@iterator"],typeof e=="function"?e:null)}var fa={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},da=Object.assign,pa={};function An(e,t,n){this.props=e,this.context=t,this.refs=pa,this.updater=n||fa}An.prototype.isReactComponent={};An.prototype.setState=function(e,t){if(typeof e!="object"&&typeof e!="function"&&e!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")};An.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function ha(){}ha.prototype=An.prototype;function Ii(e,t,n){this.props=e,this.context=t,this.refs=pa,this.updater=n||fa}var Ui=Ii.prototype=new ha;Ui.constructor=Ii;da(Ui,An.prototype);Ui.isPureReactComponent=!0;var $s=Array.isArray,ma=Object.prototype.hasOwnProperty,Mi={current:null},ya={key:!0,ref:!0,__self:!0,__source:!0};function ga(e,t,n){var r,o={},l=null,i=null;if(t!=null)for(r in t.ref!==void 0&&(i=t.ref),t.key!==void 0&&(l=""+t.key),t)ma.call(t,r)&&!ya.hasOwnProperty(r)&&(o[r]=t[r]);var s=arguments.length-2;if(s===1)o.children=n;else if(1>>1,W=g[I];if(0>>1;Io(se,x))Ceo(de,se)?(g[I]=de,g[Ce]=x,I=Ce):(g[I]=se,g[te]=x,I=te);else if(Ceo(de,x))g[I]=de,g[Ce]=x,I=Ce;else break e}}return C}function o(g,C){var x=g.sortIndex-C.sortIndex;return x!==0?x:g.id-C.id}if(typeof performance=="object"&&typeof performance.now=="function"){var l=performance;e.unstable_now=function(){return l.now()}}else{var i=Date,s=i.now();e.unstable_now=function(){return i.now()-s}}var u=[],a=[],d=1,m=null,y=3,E=!1,S=!1,v=!1,k=typeof setTimeout=="function"?setTimeout:null,c=typeof clearTimeout=="function"?clearTimeout:null,f=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function p(g){for(var C=n(a);C!==null;){if(C.callback===null)r(a);else if(C.startTime<=g)r(a),C.sortIndex=C.expirationTime,t(u,C);else break;C=n(a)}}function w(g){if(v=!1,p(g),!S)if(n(u)!==null)S=!0,Ge(_);else{var C=n(a);C!==null&&$t(w,C.startTime-g)}}function _(g,C){S=!1,v&&(v=!1,c(L),L=-1),E=!0;var x=y;try{for(p(C),m=n(u);m!==null&&(!(m.expirationTime>C)||g&&!J());){var I=m.callback;if(typeof I=="function"){m.callback=null,y=m.priorityLevel;var W=I(m.expirationTime<=C);C=e.unstable_now(),typeof W=="function"?m.callback=W:m===n(u)&&r(u),p(C)}else r(u);m=n(u)}if(m!==null)var Q=!0;else{var te=n(a);te!==null&&$t(w,te.startTime-C),Q=!1}return Q}finally{m=null,y=x,E=!1}}var O=!1,P=null,L=-1,B=5,z=-1;function J(){return!(e.unstable_now()-zg||125I?(g.sortIndex=x,t(a,g),n(u)===null&&g===n(a)&&(v?(c(L),L=-1):v=!0,$t(w,x-I))):(g.sortIndex=W,t(u,g),S||E||(S=!0,Ge(_))),g},e.unstable_shouldYield=J,e.unstable_wrapCallback=function(g){var C=y;return function(){var x=y;y=C;try{return g.apply(this,arguments)}finally{y=x}}}})(Na);ka.exports=Na;var pd=ka.exports;/** + * @license React + * react-dom.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var hd=we,Ie=pd;function N(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),jl=Object.prototype.hasOwnProperty,md=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,Vs={},Ws={};function yd(e){return jl.call(Ws,e)?!0:jl.call(Vs,e)?!1:md.test(e)?Ws[e]=!0:(Vs[e]=!0,!1)}function gd(e,t,n,r){if(n!==null&&n.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return r?!1:n!==null?!n.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function vd(e,t,n,r){if(t===null||typeof t>"u"||gd(e,t,n,r))return!0;if(r)return!1;if(n!==null)switch(n.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function Ne(e,t,n,r,o,l,i){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=r,this.attributeNamespace=o,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=l,this.removeEmptyString=i}var ce={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){ce[e]=new Ne(e,0,!1,e,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];ce[t]=new Ne(t,1,!1,e[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(e){ce[e]=new Ne(e,2,!1,e.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){ce[e]=new Ne(e,2,!1,e,null,!1,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){ce[e]=new Ne(e,3,!1,e.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(e){ce[e]=new Ne(e,3,!0,e,null,!1,!1)});["capture","download"].forEach(function(e){ce[e]=new Ne(e,4,!1,e,null,!1,!1)});["cols","rows","size","span"].forEach(function(e){ce[e]=new Ne(e,6,!1,e,null,!1,!1)});["rowSpan","start"].forEach(function(e){ce[e]=new Ne(e,5,!1,e.toLowerCase(),null,!1,!1)});var Bi=/[\-:]([a-z])/g;function $i(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(Bi,$i);ce[t]=new Ne(t,1,!1,e,null,!1,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(Bi,$i);ce[t]=new Ne(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)});["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(Bi,$i);ce[t]=new Ne(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(e){ce[e]=new Ne(e,1,!1,e.toLowerCase(),null,!1,!1)});ce.xlinkHref=new Ne("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(e){ce[e]=new Ne(e,1,!1,e.toLowerCase(),null,!0,!0)});function Hi(e,t,n,r){var o=ce.hasOwnProperty(t)?ce[t]:null;(o!==null?o.type!==0:r||!(2s||o[i]!==l[s]){var u=` +`+o[i].replace(" at new "," at ");return e.displayName&&u.includes("")&&(u=u.replace("",e.displayName)),u}while(1<=i&&0<=s);break}}}finally{cl=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?qn(e):""}function wd(e){switch(e.tag){case 5:return qn(e.type);case 16:return qn("Lazy");case 13:return qn("Suspense");case 19:return qn("SuspenseList");case 0:case 2:case 15:return e=fl(e.type,!1),e;case 11:return e=fl(e.type.render,!1),e;case 1:return e=fl(e.type,!0),e;default:return""}}function Vl(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case un:return"Fragment";case sn:return"Portal";case Bl:return"Profiler";case Vi:return"StrictMode";case $l:return"Suspense";case Hl:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case Ra:return(e.displayName||"Context")+".Consumer";case Ca:return(e._context.displayName||"Context")+".Provider";case Wi:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case Qi:return t=e.displayName||null,t!==null?t:Vl(e.type)||"Memo";case Et:t=e._payload,e=e._init;try{return Vl(e(t))}catch{}}return null}function Sd(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return Vl(t);case 8:return t===Vi?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function Ft(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function Oa(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function Ed(e){var t=Oa(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&typeof n.get=="function"&&typeof n.set=="function"){var o=n.get,l=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return o.call(this)},set:function(i){r=""+i,l.call(this,i)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(i){r=""+i},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function zr(e){e._valueTracker||(e._valueTracker=Ed(e))}function Pa(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=Oa(e)?e.checked?"true":"false":e.value),e=r,e!==n?(t.setValue(e),!0):!1}function po(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function Wl(e,t){var n=t.checked;return X({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function Ks(e,t){var n=t.defaultValue==null?"":t.defaultValue,r=t.checked!=null?t.checked:t.defaultChecked;n=Ft(t.value!=null?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function Ta(e,t){t=t.checked,t!=null&&Hi(e,"checked",t,!1)}function Ql(e,t){Ta(e,t);var n=Ft(t.value),r=t.type;if(n!=null)r==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if(r==="submit"||r==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?Kl(e,t.type,n):t.hasOwnProperty("defaultValue")&&Kl(e,t.type,Ft(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function qs(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!(r!=="submit"&&r!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}n=e.name,n!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,n!==""&&(e.name=n)}function Kl(e,t,n){(t!=="number"||po(e.ownerDocument)!==e)&&(n==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var Gn=Array.isArray;function wn(e,t,n,r){if(e=e.options,t){t={};for(var o=0;o"+t.valueOf().toString()+"",t=Fr.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function sr(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeType===3){n.nodeValue=t;return}}e.textContent=t}var Yn={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},kd=["Webkit","ms","Moz","O"];Object.keys(Yn).forEach(function(e){kd.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),Yn[t]=Yn[e]})});function za(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typeof t!="number"||t===0||Yn.hasOwnProperty(e)&&Yn[e]?(""+t).trim():t+"px"}function Fa(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var r=n.indexOf("--")===0,o=za(n,t[n],r);n==="float"&&(n="cssFloat"),r?e.setProperty(n,o):e[n]=o}}var Nd=X({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function Xl(e,t){if(t){if(Nd[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(N(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(N(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(N(61))}if(t.style!=null&&typeof t.style!="object")throw Error(N(62))}}function Jl(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var Yl=null;function Ki(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var Zl=null,Sn=null,En=null;function Js(e){if(e=Rr(e)){if(typeof Zl!="function")throw Error(N(280));var t=e.stateNode;t&&(t=Wo(t),Zl(e.stateNode,e.type,t))}}function Ia(e){Sn?En?En.push(e):En=[e]:Sn=e}function Ua(){if(Sn){var e=Sn,t=En;if(En=Sn=null,Js(e),t)for(e=0;e>>=0,e===0?32:31-(zd(e)/Fd|0)|0}var Ir=64,Ur=4194304;function Xn(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function go(e,t){var n=e.pendingLanes;if(n===0)return 0;var r=0,o=e.suspendedLanes,l=e.pingedLanes,i=n&268435455;if(i!==0){var s=i&~o;s!==0?r=Xn(s):(l&=i,l!==0&&(r=Xn(l)))}else i=n&~o,i!==0?r=Xn(i):l!==0&&(r=Xn(l));if(r===0)return 0;if(t!==0&&t!==r&&!(t&o)&&(o=r&-r,l=t&-t,o>=l||o===16&&(l&4194240)!==0))return t;if(r&4&&(r|=n&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=r;0n;n++)t.push(e);return t}function _r(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-et(t),e[t]=n}function jd(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0=bn),lu=String.fromCharCode(32),iu=!1;function rc(e,t){switch(e){case"keyup":return pp.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function oc(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var an=!1;function mp(e,t){switch(e){case"compositionend":return oc(t);case"keypress":return t.which!==32?null:(iu=!0,lu);case"textInput":return e=t.data,e===lu&&iu?null:e;default:return null}}function yp(e,t){if(an)return e==="compositionend"||!es&&rc(e,t)?(e=tc(),br=Yi=Ct=null,an=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=cu(n)}}function uc(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?uc(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function ac(){for(var e=window,t=po();t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=po(e.document)}return t}function ts(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function Cp(e){var t=ac(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&uc(n.ownerDocument.documentElement,n)){if(r!==null&&ts(n)){if(t=r.start,e=r.end,e===void 0&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if(e=(t=n.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var o=n.textContent.length,l=Math.min(r.start,o);r=r.end===void 0?l:Math.min(r.end,o),!e.extend&&l>r&&(o=r,r=l,l=o),o=fu(n,l);var i=fu(n,r);o&&i&&(e.rangeCount!==1||e.anchorNode!==o.node||e.anchorOffset!==o.offset||e.focusNode!==i.node||e.focusOffset!==i.offset)&&(t=t.createRange(),t.setStart(o.node,o.offset),e.removeAllRanges(),l>r?(e.addRange(t),e.extend(i.node,i.offset)):(t.setEnd(i.node,i.offset),e.addRange(t)))}}for(t=[],e=n;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof n.focus=="function"&&n.focus(),n=0;n=document.documentMode,cn=null,oi=null,tr=null,li=!1;function du(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;li||cn==null||cn!==po(r)||(r=cn,"selectionStart"in r&&ts(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),tr&&pr(tr,r)||(tr=r,r=So(oi,"onSelect"),0pn||(e.current=fi[pn],fi[pn]=null,pn--)}function $(e,t){pn++,fi[pn]=e.current,e.current=t}var It={},ge=Mt(It),Pe=Mt(!1),Jt=It;function Rn(e,t){var n=e.type.contextTypes;if(!n)return It;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var o={},l;for(l in n)o[l]=t[l];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=o),o}function Te(e){return e=e.childContextTypes,e!=null}function ko(){V(Pe),V(ge)}function wu(e,t,n){if(ge.current!==It)throw Error(N(168));$(ge,t),$(Pe,n)}function vc(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,typeof r.getChildContext!="function")return n;r=r.getChildContext();for(var o in r)if(!(o in t))throw Error(N(108,Sd(e)||"Unknown",o));return X({},n,r)}function No(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||It,Jt=ge.current,$(ge,e),$(Pe,Pe.current),!0}function Su(e,t,n){var r=e.stateNode;if(!r)throw Error(N(169));n?(e=vc(e,t,Jt),r.__reactInternalMemoizedMergedChildContext=e,V(Pe),V(ge),$(ge,e)):V(Pe),$(Pe,n)}var ct=null,Qo=!1,Cl=!1;function wc(e){ct===null?ct=[e]:ct.push(e)}function Up(e){Qo=!0,wc(e)}function jt(){if(!Cl&&ct!==null){Cl=!0;var e=0,t=M;try{var n=ct;for(M=1;e>=i,o-=i,ft=1<<32-et(t)+o|n<L?(B=P,P=null):B=P.sibling;var z=y(c,P,p[L],w);if(z===null){P===null&&(P=B);break}e&&P&&z.alternate===null&&t(c,P),f=l(z,f,L),O===null?_=z:O.sibling=z,O=z,P=B}if(L===p.length)return n(c,P),K&&Ht(c,L),_;if(P===null){for(;LL?(B=P,P=null):B=P.sibling;var J=y(c,P,z.value,w);if(J===null){P===null&&(P=B);break}e&&P&&J.alternate===null&&t(c,P),f=l(J,f,L),O===null?_=J:O.sibling=J,O=J,P=B}if(z.done)return n(c,P),K&&Ht(c,L),_;if(P===null){for(;!z.done;L++,z=p.next())z=m(c,z.value,w),z!==null&&(f=l(z,f,L),O===null?_=z:O.sibling=z,O=z);return K&&Ht(c,L),_}for(P=r(c,P);!z.done;L++,z=p.next())z=E(P,c,L,z.value,w),z!==null&&(e&&z.alternate!==null&&P.delete(z.key===null?L:z.key),f=l(z,f,L),O===null?_=z:O.sibling=z,O=z);return e&&P.forEach(function(je){return t(c,je)}),K&&Ht(c,L),_}function k(c,f,p,w){if(typeof p=="object"&&p!==null&&p.type===un&&p.key===null&&(p=p.props.children),typeof p=="object"&&p!==null){switch(p.$$typeof){case Ar:e:{for(var _=p.key,O=f;O!==null;){if(O.key===_){if(_=p.type,_===un){if(O.tag===7){n(c,O.sibling),f=o(O,p.props.children),f.return=c,c=f;break e}}else if(O.elementType===_||typeof _=="object"&&_!==null&&_.$$typeof===Et&&Nu(_)===O.type){n(c,O.sibling),f=o(O,p.props),f.ref=Vn(c,O,p),f.return=c,c=f;break e}n(c,O);break}else t(c,O);O=O.sibling}p.type===un?(f=Xt(p.props.children,c.mode,w,p.key),f.return=c,c=f):(w=so(p.type,p.key,p.props,null,c.mode,w),w.ref=Vn(c,f,p),w.return=c,c=w)}return i(c);case sn:e:{for(O=p.key;f!==null;){if(f.key===O)if(f.tag===4&&f.stateNode.containerInfo===p.containerInfo&&f.stateNode.implementation===p.implementation){n(c,f.sibling),f=o(f,p.children||[]),f.return=c,c=f;break e}else{n(c,f);break}else t(c,f);f=f.sibling}f=Al(p,c.mode,w),f.return=c,c=f}return i(c);case Et:return O=p._init,k(c,f,O(p._payload),w)}if(Gn(p))return S(c,f,p,w);if(Mn(p))return v(c,f,p,w);Wr(c,p)}return typeof p=="string"&&p!==""||typeof p=="number"?(p=""+p,f!==null&&f.tag===6?(n(c,f.sibling),f=o(f,p),f.return=c,c=f):(n(c,f),f=Dl(p,c.mode,w),f.return=c,c=f),i(c)):n(c,f)}return k}var On=Nc(!0),_c=Nc(!1),Ro=Mt(null),xo=null,yn=null,ls=null;function is(){ls=yn=xo=null}function ss(e){var t=Ro.current;V(Ro),e._currentValue=t}function hi(e,t,n){for(;e!==null;){var r=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,r!==null&&(r.childLanes|=t)):r!==null&&(r.childLanes&t)!==t&&(r.childLanes|=t),e===n)break;e=e.return}}function Nn(e,t){xo=e,ls=yn=null,e=e.dependencies,e!==null&&e.firstContext!==null&&(e.lanes&t&&(Oe=!0),e.firstContext=null)}function Ke(e){var t=e._currentValue;if(ls!==e)if(e={context:e,memoizedValue:t,next:null},yn===null){if(xo===null)throw Error(N(308));yn=e,xo.dependencies={lanes:0,firstContext:e}}else yn=yn.next=e;return t}var Qt=null;function us(e){Qt===null?Qt=[e]:Qt.push(e)}function Cc(e,t,n,r){var o=t.interleaved;return o===null?(n.next=n,us(t)):(n.next=o.next,o.next=n),t.interleaved=n,yt(e,r)}function yt(e,t){e.lanes|=t;var n=e.alternate;for(n!==null&&(n.lanes|=t),n=e,e=e.return;e!==null;)e.childLanes|=t,n=e.alternate,n!==null&&(n.childLanes|=t),n=e,e=e.return;return n.tag===3?n.stateNode:null}var kt=!1;function as(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Rc(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function pt(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function Lt(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,U&2){var o=r.pending;return o===null?t.next=t:(t.next=o.next,o.next=t),r.pending=t,yt(e,n)}return o=r.interleaved,o===null?(t.next=t,us(r)):(t.next=o.next,o.next=t),r.interleaved=t,yt(e,n)}function to(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194240)!==0)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,Gi(e,n)}}function _u(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var o=null,l=null;if(n=n.firstBaseUpdate,n!==null){do{var i={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};l===null?o=l=i:l=l.next=i,n=n.next}while(n!==null);l===null?o=l=t:l=l.next=t}else o=l=t;n={baseState:r.baseState,firstBaseUpdate:o,lastBaseUpdate:l,shared:r.shared,effects:r.effects},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function Oo(e,t,n,r){var o=e.updateQueue;kt=!1;var l=o.firstBaseUpdate,i=o.lastBaseUpdate,s=o.shared.pending;if(s!==null){o.shared.pending=null;var u=s,a=u.next;u.next=null,i===null?l=a:i.next=a,i=u;var d=e.alternate;d!==null&&(d=d.updateQueue,s=d.lastBaseUpdate,s!==i&&(s===null?d.firstBaseUpdate=a:s.next=a,d.lastBaseUpdate=u))}if(l!==null){var m=o.baseState;i=0,d=a=u=null,s=l;do{var y=s.lane,E=s.eventTime;if((r&y)===y){d!==null&&(d=d.next={eventTime:E,lane:0,tag:s.tag,payload:s.payload,callback:s.callback,next:null});e:{var S=e,v=s;switch(y=t,E=n,v.tag){case 1:if(S=v.payload,typeof S=="function"){m=S.call(E,m,y);break e}m=S;break e;case 3:S.flags=S.flags&-65537|128;case 0:if(S=v.payload,y=typeof S=="function"?S.call(E,m,y):S,y==null)break e;m=X({},m,y);break e;case 2:kt=!0}}s.callback!==null&&s.lane!==0&&(e.flags|=64,y=o.effects,y===null?o.effects=[s]:y.push(s))}else E={eventTime:E,lane:y,tag:s.tag,payload:s.payload,callback:s.callback,next:null},d===null?(a=d=E,u=m):d=d.next=E,i|=y;if(s=s.next,s===null){if(s=o.shared.pending,s===null)break;y=s,s=y.next,y.next=null,o.lastBaseUpdate=y,o.shared.pending=null}}while(1);if(d===null&&(u=m),o.baseState=u,o.firstBaseUpdate=a,o.lastBaseUpdate=d,t=o.shared.interleaved,t!==null){o=t;do i|=o.lane,o=o.next;while(o!==t)}else l===null&&(o.shared.lanes=0);bt|=i,e.lanes=i,e.memoizedState=m}}function Cu(e,t,n){if(e=t.effects,t.effects=null,e!==null)for(t=0;tn?n:4,e(!0);var r=xl.transition;xl.transition={};try{e(!1),t()}finally{M=n,xl.transition=r}}function Vc(){return qe().memoizedState}function $p(e,t,n){var r=At(e);if(n={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null},Wc(e))Qc(t,n);else if(n=Cc(e,t,n,r),n!==null){var o=Ee();tt(n,e,r,o),Kc(n,t,r)}}function Hp(e,t,n){var r=At(e),o={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null};if(Wc(e))Qc(t,o);else{var l=e.alternate;if(e.lanes===0&&(l===null||l.lanes===0)&&(l=t.lastRenderedReducer,l!==null))try{var i=t.lastRenderedState,s=l(i,n);if(o.hasEagerState=!0,o.eagerState=s,nt(s,i)){var u=t.interleaved;u===null?(o.next=o,us(t)):(o.next=u.next,u.next=o),t.interleaved=o;return}}catch{}finally{}n=Cc(e,t,o,r),n!==null&&(o=Ee(),tt(n,e,r,o),Kc(n,t,r))}}function Wc(e){var t=e.alternate;return e===G||t!==null&&t===G}function Qc(e,t){nr=To=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function Kc(e,t,n){if(n&4194240){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,Gi(e,n)}}var Lo={readContext:Ke,useCallback:pe,useContext:pe,useEffect:pe,useImperativeHandle:pe,useInsertionEffect:pe,useLayoutEffect:pe,useMemo:pe,useReducer:pe,useRef:pe,useState:pe,useDebugValue:pe,useDeferredValue:pe,useTransition:pe,useMutableSource:pe,useSyncExternalStore:pe,useId:pe,unstable_isNewReconciler:!1},Vp={readContext:Ke,useCallback:function(e,t){return lt().memoizedState=[e,t===void 0?null:t],e},useContext:Ke,useEffect:xu,useImperativeHandle:function(e,t,n){return n=n!=null?n.concat([e]):null,ro(4194308,4,Mc.bind(null,t,e),n)},useLayoutEffect:function(e,t){return ro(4194308,4,e,t)},useInsertionEffect:function(e,t){return ro(4,2,e,t)},useMemo:function(e,t){var n=lt();return t=t===void 0?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=lt();return t=n!==void 0?n(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=$p.bind(null,G,e),[r.memoizedState,e]},useRef:function(e){var t=lt();return e={current:e},t.memoizedState=e},useState:Ru,useDebugValue:gs,useDeferredValue:function(e){return lt().memoizedState=e},useTransition:function(){var e=Ru(!1),t=e[0];return e=Bp.bind(null,e[1]),lt().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var r=G,o=lt();if(K){if(n===void 0)throw Error(N(407));n=n()}else{if(n=t(),ie===null)throw Error(N(349));Zt&30||Tc(r,t,n)}o.memoizedState=n;var l={value:n,getSnapshot:t};return o.queue=l,xu(Dc.bind(null,r,l,e),[e]),r.flags|=2048,Er(9,Lc.bind(null,r,l,n,t),void 0,null),n},useId:function(){var e=lt(),t=ie.identifierPrefix;if(K){var n=dt,r=ft;n=(r&~(1<<32-et(r)-1)).toString(32)+n,t=":"+t+"R"+n,n=wr++,0<\/script>",e=e.removeChild(e.firstChild)):typeof r.is=="string"?e=i.createElement(n,{is:r.is}):(e=i.createElement(n),n==="select"&&(i=e,r.multiple?i.multiple=!0:r.size&&(i.size=r.size))):e=i.createElementNS(e,n),e[it]=t,e[yr]=r,nf(e,t,!1,!1),t.stateNode=e;e:{switch(i=Jl(n,r),n){case"dialog":H("cancel",e),H("close",e),o=r;break;case"iframe":case"object":case"embed":H("load",e),o=r;break;case"video":case"audio":for(o=0;oLn&&(t.flags|=128,r=!0,Wn(l,!1),t.lanes=4194304)}else{if(!r)if(e=Po(i),e!==null){if(t.flags|=128,r=!0,n=e.updateQueue,n!==null&&(t.updateQueue=n,t.flags|=4),Wn(l,!0),l.tail===null&&l.tailMode==="hidden"&&!i.alternate&&!K)return he(t),null}else 2*Z()-l.renderingStartTime>Ln&&n!==1073741824&&(t.flags|=128,r=!0,Wn(l,!1),t.lanes=4194304);l.isBackwards?(i.sibling=t.child,t.child=i):(n=l.last,n!==null?n.sibling=i:t.child=i,l.last=i)}return l.tail!==null?(t=l.tail,l.rendering=t,l.tail=t.sibling,l.renderingStartTime=Z(),t.sibling=null,n=q.current,$(q,r?n&1|2:n&1),t):(he(t),null);case 22:case 23:return Ns(),r=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==r&&(t.flags|=8192),r&&t.mode&1?Ae&1073741824&&(he(t),t.subtreeFlags&6&&(t.flags|=8192)):he(t),null;case 24:return null;case 25:return null}throw Error(N(156,t.tag))}function Yp(e,t){switch(rs(t),t.tag){case 1:return Te(t.type)&&ko(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return Pn(),V(Pe),V(ge),ds(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 5:return fs(t),null;case 13:if(V(q),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(N(340));xn()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return V(q),null;case 4:return Pn(),null;case 10:return ss(t.type._context),null;case 22:case 23:return Ns(),null;case 24:return null;default:return null}}var Kr=!1,me=!1,Zp=typeof WeakSet=="function"?WeakSet:Set,T=null;function gn(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(null)}catch(r){Y(e,t,r)}else n.current=null}function Ni(e,t,n){try{n()}catch(r){Y(e,t,r)}}var Mu=!1;function bp(e,t){if(ii=vo,e=ac(),ts(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var o=r.anchorOffset,l=r.focusNode;r=r.focusOffset;try{n.nodeType,l.nodeType}catch{n=null;break e}var i=0,s=-1,u=-1,a=0,d=0,m=e,y=null;t:for(;;){for(var E;m!==n||o!==0&&m.nodeType!==3||(s=i+o),m!==l||r!==0&&m.nodeType!==3||(u=i+r),m.nodeType===3&&(i+=m.nodeValue.length),(E=m.firstChild)!==null;)y=m,m=E;for(;;){if(m===e)break t;if(y===n&&++a===o&&(s=i),y===l&&++d===r&&(u=i),(E=m.nextSibling)!==null)break;m=y,y=m.parentNode}m=E}n=s===-1||u===-1?null:{start:s,end:u}}else n=null}n=n||{start:0,end:0}}else n=null;for(si={focusedElem:e,selectionRange:n},vo=!1,T=t;T!==null;)if(t=T,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,T=e;else for(;T!==null;){t=T;try{var S=t.alternate;if(t.flags&1024)switch(t.tag){case 0:case 11:case 15:break;case 1:if(S!==null){var v=S.memoizedProps,k=S.memoizedState,c=t.stateNode,f=c.getSnapshotBeforeUpdate(t.elementType===t.type?v:Ye(t.type,v),k);c.__reactInternalSnapshotBeforeUpdate=f}break;case 3:var p=t.stateNode.containerInfo;p.nodeType===1?p.textContent="":p.nodeType===9&&p.documentElement&&p.removeChild(p.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(N(163))}}catch(w){Y(t,t.return,w)}if(e=t.sibling,e!==null){e.return=t.return,T=e;break}T=t.return}return S=Mu,Mu=!1,S}function rr(e,t,n){var r=t.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var o=r=r.next;do{if((o.tag&e)===e){var l=o.destroy;o.destroy=void 0,l!==void 0&&Ni(t,n,l)}o=o.next}while(o!==r)}}function Go(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var n=t=t.next;do{if((n.tag&e)===e){var r=n.create;n.destroy=r()}n=n.next}while(n!==t)}}function _i(e){var t=e.ref;if(t!==null){var n=e.stateNode;switch(e.tag){case 5:e=n;break;default:e=n}typeof t=="function"?t(e):t.current=e}}function lf(e){var t=e.alternate;t!==null&&(e.alternate=null,lf(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[it],delete t[yr],delete t[ci],delete t[Fp],delete t[Ip])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function sf(e){return e.tag===5||e.tag===3||e.tag===4}function ju(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||sf(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function Ci(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.nodeType===8?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(n.nodeType===8?(t=n.parentNode,t.insertBefore(e,n)):(t=n,t.appendChild(e)),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=Eo));else if(r!==4&&(e=e.child,e!==null))for(Ci(e,t,n),e=e.sibling;e!==null;)Ci(e,t,n),e=e.sibling}function Ri(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(e=e.child,e!==null))for(Ri(e,t,n),e=e.sibling;e!==null;)Ri(e,t,n),e=e.sibling}var ue=null,Ze=!1;function St(e,t,n){for(n=n.child;n!==null;)uf(e,t,n),n=n.sibling}function uf(e,t,n){if(st&&typeof st.onCommitFiberUnmount=="function")try{st.onCommitFiberUnmount(Bo,n)}catch{}switch(n.tag){case 5:me||gn(n,t);case 6:var r=ue,o=Ze;ue=null,St(e,t,n),ue=r,Ze=o,ue!==null&&(Ze?(e=ue,n=n.stateNode,e.nodeType===8?e.parentNode.removeChild(n):e.removeChild(n)):ue.removeChild(n.stateNode));break;case 18:ue!==null&&(Ze?(e=ue,n=n.stateNode,e.nodeType===8?_l(e.parentNode,n):e.nodeType===1&&_l(e,n),fr(e)):_l(ue,n.stateNode));break;case 4:r=ue,o=Ze,ue=n.stateNode.containerInfo,Ze=!0,St(e,t,n),ue=r,Ze=o;break;case 0:case 11:case 14:case 15:if(!me&&(r=n.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){o=r=r.next;do{var l=o,i=l.destroy;l=l.tag,i!==void 0&&(l&2||l&4)&&Ni(n,t,i),o=o.next}while(o!==r)}St(e,t,n);break;case 1:if(!me&&(gn(n,t),r=n.stateNode,typeof r.componentWillUnmount=="function"))try{r.props=n.memoizedProps,r.state=n.memoizedState,r.componentWillUnmount()}catch(s){Y(n,t,s)}St(e,t,n);break;case 21:St(e,t,n);break;case 22:n.mode&1?(me=(r=me)||n.memoizedState!==null,St(e,t,n),me=r):St(e,t,n);break;default:St(e,t,n)}}function Bu(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n=e.stateNode;n===null&&(n=e.stateNode=new Zp),t.forEach(function(r){var o=uh.bind(null,e,r);n.has(r)||(n.add(r),r.then(o,o))})}}function Xe(e,t){var n=t.deletions;if(n!==null)for(var r=0;ro&&(o=i),r&=~l}if(r=o,r=Z()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*th(r/1960))-r,10e?16:e,Rt===null)var r=!1;else{if(e=Rt,Rt=null,zo=0,U&6)throw Error(N(331));var o=U;for(U|=4,T=e.current;T!==null;){var l=T,i=l.child;if(T.flags&16){var s=l.deletions;if(s!==null){for(var u=0;uZ()-Es?Gt(e,0):Ss|=n),Le(e,t)}function yf(e,t){t===0&&(e.mode&1?(t=Ur,Ur<<=1,!(Ur&130023424)&&(Ur=4194304)):t=1);var n=Ee();e=yt(e,t),e!==null&&(_r(e,t,n),Le(e,n))}function sh(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),yf(e,n)}function uh(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,o=e.memoizedState;o!==null&&(n=o.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(N(314))}r!==null&&r.delete(t),yf(e,n)}var gf;gf=function(e,t,n){if(e!==null)if(e.memoizedProps!==t.pendingProps||Pe.current)Oe=!0;else{if(!(e.lanes&n)&&!(t.flags&128))return Oe=!1,Xp(e,t,n);Oe=!!(e.flags&131072)}else Oe=!1,K&&t.flags&1048576&&Sc(t,Co,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;oo(e,t),e=t.pendingProps;var o=Rn(t,ge.current);Nn(t,n),o=hs(null,t,r,e,o,n);var l=ms();return t.flags|=1,typeof o=="object"&&o!==null&&typeof o.render=="function"&&o.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,Te(r)?(l=!0,No(t)):l=!1,t.memoizedState=o.state!==null&&o.state!==void 0?o.state:null,as(t),o.updater=qo,t.stateNode=o,o._reactInternals=t,yi(t,r,e,n),t=wi(null,t,r,!0,l,n)):(t.tag=0,K&&l&&ns(t),Se(null,t,o,n),t=t.child),t;case 16:r=t.elementType;e:{switch(oo(e,t),e=t.pendingProps,o=r._init,r=o(r._payload),t.type=r,o=t.tag=ch(r),e=Ye(r,e),o){case 0:t=vi(null,t,r,e,n);break e;case 1:t=Fu(null,t,r,e,n);break e;case 11:t=Au(null,t,r,e,n);break e;case 14:t=zu(null,t,r,Ye(r.type,e),n);break e}throw Error(N(306,r,""))}return t;case 0:return r=t.type,o=t.pendingProps,o=t.elementType===r?o:Ye(r,o),vi(e,t,r,o,n);case 1:return r=t.type,o=t.pendingProps,o=t.elementType===r?o:Ye(r,o),Fu(e,t,r,o,n);case 3:e:{if(bc(t),e===null)throw Error(N(387));r=t.pendingProps,l=t.memoizedState,o=l.element,Rc(e,t),Oo(t,r,null,n);var i=t.memoizedState;if(r=i.element,l.isDehydrated)if(l={element:r,isDehydrated:!1,cache:i.cache,pendingSuspenseBoundaries:i.pendingSuspenseBoundaries,transitions:i.transitions},t.updateQueue.baseState=l,t.memoizedState=l,t.flags&256){o=Tn(Error(N(423)),t),t=Iu(e,t,r,n,o);break e}else if(r!==o){o=Tn(Error(N(424)),t),t=Iu(e,t,r,n,o);break e}else for(ze=Tt(t.stateNode.containerInfo.firstChild),Fe=t,K=!0,be=null,n=_c(t,null,r,n),t.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling;else{if(xn(),r===o){t=gt(e,t,n);break e}Se(e,t,r,n)}t=t.child}return t;case 5:return xc(t),e===null&&pi(t),r=t.type,o=t.pendingProps,l=e!==null?e.memoizedProps:null,i=o.children,ui(r,o)?i=null:l!==null&&ui(r,l)&&(t.flags|=32),Zc(e,t),Se(e,t,i,n),t.child;case 6:return e===null&&pi(t),null;case 13:return ef(e,t,n);case 4:return cs(t,t.stateNode.containerInfo),r=t.pendingProps,e===null?t.child=On(t,null,r,n):Se(e,t,r,n),t.child;case 11:return r=t.type,o=t.pendingProps,o=t.elementType===r?o:Ye(r,o),Au(e,t,r,o,n);case 7:return Se(e,t,t.pendingProps,n),t.child;case 8:return Se(e,t,t.pendingProps.children,n),t.child;case 12:return Se(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,o=t.pendingProps,l=t.memoizedProps,i=o.value,$(Ro,r._currentValue),r._currentValue=i,l!==null)if(nt(l.value,i)){if(l.children===o.children&&!Pe.current){t=gt(e,t,n);break e}}else for(l=t.child,l!==null&&(l.return=t);l!==null;){var s=l.dependencies;if(s!==null){i=l.child;for(var u=s.firstContext;u!==null;){if(u.context===r){if(l.tag===1){u=pt(-1,n&-n),u.tag=2;var a=l.updateQueue;if(a!==null){a=a.shared;var d=a.pending;d===null?u.next=u:(u.next=d.next,d.next=u),a.pending=u}}l.lanes|=n,u=l.alternate,u!==null&&(u.lanes|=n),hi(l.return,n,t),s.lanes|=n;break}u=u.next}}else if(l.tag===10)i=l.type===t.type?null:l.child;else if(l.tag===18){if(i=l.return,i===null)throw Error(N(341));i.lanes|=n,s=i.alternate,s!==null&&(s.lanes|=n),hi(i,n,t),i=l.sibling}else i=l.child;if(i!==null)i.return=l;else for(i=l;i!==null;){if(i===t){i=null;break}if(l=i.sibling,l!==null){l.return=i.return,i=l;break}i=i.return}l=i}Se(e,t,o.children,n),t=t.child}return t;case 9:return o=t.type,r=t.pendingProps.children,Nn(t,n),o=Ke(o),r=r(o),t.flags|=1,Se(e,t,r,n),t.child;case 14:return r=t.type,o=Ye(r,t.pendingProps),o=Ye(r.type,o),zu(e,t,r,o,n);case 15:return Jc(e,t,t.type,t.pendingProps,n);case 17:return r=t.type,o=t.pendingProps,o=t.elementType===r?o:Ye(r,o),oo(e,t),t.tag=1,Te(r)?(e=!0,No(t)):e=!1,Nn(t,n),qc(t,r,o),yi(t,r,o,n),wi(null,t,r,!0,e,n);case 19:return tf(e,t,n);case 22:return Yc(e,t,n)}throw Error(N(156,t.tag))};function vf(e,t){return Wa(e,t)}function ah(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function Ve(e,t,n,r){return new ah(e,t,n,r)}function Cs(e){return e=e.prototype,!(!e||!e.isReactComponent)}function ch(e){if(typeof e=="function")return Cs(e)?1:0;if(e!=null){if(e=e.$$typeof,e===Wi)return 11;if(e===Qi)return 14}return 2}function zt(e,t){var n=e.alternate;return n===null?(n=Ve(e.tag,t,e.key,e.mode),n.elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=e.flags&14680064,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function so(e,t,n,r,o,l){var i=2;if(r=e,typeof e=="function")Cs(e)&&(i=1);else if(typeof e=="string")i=5;else e:switch(e){case un:return Xt(n.children,o,l,t);case Vi:i=8,o|=8;break;case Bl:return e=Ve(12,n,t,o|2),e.elementType=Bl,e.lanes=l,e;case $l:return e=Ve(13,n,t,o),e.elementType=$l,e.lanes=l,e;case Hl:return e=Ve(19,n,t,o),e.elementType=Hl,e.lanes=l,e;case xa:return Jo(n,o,l,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case Ca:i=10;break e;case Ra:i=9;break e;case Wi:i=11;break e;case Qi:i=14;break e;case Et:i=16,r=null;break e}throw Error(N(130,e==null?e:typeof e,""))}return t=Ve(i,n,t,o),t.elementType=e,t.type=r,t.lanes=l,t}function Xt(e,t,n,r){return e=Ve(7,e,r,t),e.lanes=n,e}function Jo(e,t,n,r){return e=Ve(22,e,r,t),e.elementType=xa,e.lanes=n,e.stateNode={isHidden:!1},e}function Dl(e,t,n){return e=Ve(6,e,null,t),e.lanes=n,e}function Al(e,t,n){return t=Ve(4,e.children!==null?e.children:[],e.key,t),t.lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function fh(e,t,n,r,o){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=pl(0),this.expirationTimes=pl(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=pl(0),this.identifierPrefix=r,this.onRecoverableError=o,this.mutableSourceEagerHydrationData=null}function Rs(e,t,n,r,o,l,i,s,u){return e=new fh(e,t,n,s,u),t===1?(t=1,l===!0&&(t|=8)):t=0,l=Ve(3,null,null,t),e.current=l,l.stateNode=e,l.memoizedState={element:r,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},as(l),e}function dh(e,t,n){var r=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(kf)}catch(e){console.error(e)}}kf(),Ea.exports=Ue;var gh=Ea.exports,Gu=gh;Ml.createRoot=Gu.createRoot,Ml.hydrateRoot=Gu.hydrateRoot;function Nf(e,t){return function(){return e.apply(t,arguments)}}const{toString:vh}=Object.prototype,{getPrototypeOf:tl}=Object,{iterator:nl,toStringTag:_f}=Symbol,rl=(e=>t=>{const n=vh.call(t);return e[n]||(e[n]=n.slice(8,-1).toLowerCase())})(Object.create(null)),rt=e=>(e=e.toLowerCase(),t=>rl(t)===e),ol=e=>t=>typeof t===e,{isArray:In}=Array,Dn=ol("undefined");function Or(e){return e!==null&&!Dn(e)&&e.constructor!==null&&!Dn(e.constructor)&&De(e.constructor.isBuffer)&&e.constructor.isBuffer(e)}const Cf=rt("ArrayBuffer");function wh(e){let t;return typeof ArrayBuffer<"u"&&ArrayBuffer.isView?t=ArrayBuffer.isView(e):t=e&&e.buffer&&Cf(e.buffer),t}const Sh=ol("string"),De=ol("function"),Rf=ol("number"),Pr=e=>e!==null&&typeof e=="object",Eh=e=>e===!0||e===!1,uo=e=>{if(rl(e)!=="object")return!1;const t=tl(e);return(t===null||t===Object.prototype||Object.getPrototypeOf(t)===null)&&!(_f in e)&&!(nl in e)},kh=e=>{if(!Pr(e)||Or(e))return!1;try{return Object.keys(e).length===0&&Object.getPrototypeOf(e)===Object.prototype}catch{return!1}},Nh=rt("Date"),_h=rt("File"),Ch=e=>!!(e&&typeof e.uri<"u"),Rh=e=>e&&typeof e.getParts<"u",xh=rt("Blob"),Oh=rt("FileList"),Ph=e=>Pr(e)&&De(e.pipe);function Th(){return typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{}}const Xu=Th(),Ju=typeof Xu.FormData<"u"?Xu.FormData:void 0,Lh=e=>{if(!e)return!1;if(Ju&&e instanceof Ju)return!0;const t=tl(e);if(!t||t===Object.prototype||!De(e.append))return!1;const n=rl(e);return n==="formdata"||n==="object"&&De(e.toString)&&e.toString()==="[object FormData]"},Dh=rt("URLSearchParams"),[Ah,zh,Fh,Ih]=["ReadableStream","Request","Response","Headers"].map(rt),Uh=e=>e.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"");function Tr(e,t,{allOwnKeys:n=!1}={}){if(e===null||typeof e>"u")return;let r,o;if(typeof e!="object"&&(e=[e]),In(e))for(r=0,o=e.length;r0;)if(o=n[r],t===o.toLowerCase())return o;return null}const qt=(()=>typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:global)(),Of=e=>!Dn(e)&&e!==qt;function Li(...e){const{caseless:t,skipUndefined:n}=Of(this)&&this||{},r={},o=(l,i)=>{if(i==="__proto__"||i==="constructor"||i==="prototype")return;const s=t&&xf(r,i)||i,u=Di(r,s)?r[s]:void 0;uo(u)&&uo(l)?r[s]=Li(u,l):uo(l)?r[s]=Li({},l):In(l)?r[s]=l.slice():(!n||!Dn(l))&&(r[s]=l)};for(let l=0,i=e.length;l(Tr(t,(o,l)=>{n&&De(o)?Object.defineProperty(e,l,{__proto__:null,value:Nf(o,n),writable:!0,enumerable:!0,configurable:!0}):Object.defineProperty(e,l,{__proto__:null,value:o,writable:!0,enumerable:!0,configurable:!0})},{allOwnKeys:r}),e),jh=e=>(e.charCodeAt(0)===65279&&(e=e.slice(1)),e),Bh=(e,t,n,r)=>{e.prototype=Object.create(t.prototype,r),Object.defineProperty(e.prototype,"constructor",{__proto__:null,value:e,writable:!0,enumerable:!1,configurable:!0}),Object.defineProperty(e,"super",{__proto__:null,value:t.prototype}),n&&Object.assign(e.prototype,n)},$h=(e,t,n,r)=>{let o,l,i;const s={};if(t=t||{},e==null)return t;do{for(o=Object.getOwnPropertyNames(e),l=o.length;l-- >0;)i=o[l],(!r||r(i,e,t))&&!s[i]&&(t[i]=e[i],s[i]=!0);e=n!==!1&&tl(e)}while(e&&(!n||n(e,t))&&e!==Object.prototype);return t},Hh=(e,t,n)=>{e=String(e),(n===void 0||n>e.length)&&(n=e.length),n-=t.length;const r=e.indexOf(t,n);return r!==-1&&r===n},Vh=e=>{if(!e)return null;if(In(e))return e;let t=e.length;if(!Rf(t))return null;const n=new Array(t);for(;t-- >0;)n[t]=e[t];return n},Wh=(e=>t=>e&&t instanceof e)(typeof Uint8Array<"u"&&tl(Uint8Array)),Qh=(e,t)=>{const r=(e&&e[nl]).call(e);let o;for(;(o=r.next())&&!o.done;){const l=o.value;t.call(e,l[0],l[1])}},Kh=(e,t)=>{let n;const r=[];for(;(n=e.exec(t))!==null;)r.push(n);return r},qh=rt("HTMLFormElement"),Gh=e=>e.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,function(n,r,o){return r.toUpperCase()+o}),Di=(({hasOwnProperty:e})=>(t,n)=>e.call(t,n))(Object.prototype),Xh=rt("RegExp"),Pf=(e,t)=>{const n=Object.getOwnPropertyDescriptors(e),r={};Tr(n,(o,l)=>{let i;(i=t(o,l,e))!==!1&&(r[l]=i||o)}),Object.defineProperties(e,r)},Jh=e=>{Pf(e,(t,n)=>{if(De(e)&&["arguments","caller","callee"].includes(n))return!1;const r=e[n];if(De(r)){if(t.enumerable=!1,"writable"in t){t.writable=!1;return}t.set||(t.set=()=>{throw Error("Can not rewrite read-only method '"+n+"'")})}})},Yh=(e,t)=>{const n={},r=o=>{o.forEach(l=>{n[l]=!0})};return In(e)?r(e):r(String(e).split(t)),n},Zh=()=>{},bh=(e,t)=>e!=null&&Number.isFinite(e=+e)?e:t;function em(e){return!!(e&&De(e.append)&&e[_f]==="FormData"&&e[nl])}const tm=e=>{const t=new WeakSet,n=r=>{if(Pr(r)){if(t.has(r))return;if(Or(r))return r;if(!("toJSON"in r)){t.add(r);const o=In(r)?[]:{};return Tr(r,(l,i)=>{const s=n(l);!Dn(s)&&(o[i]=s)}),t.delete(r),o}}return r};return n(e)},nm=rt("AsyncFunction"),rm=e=>e&&(Pr(e)||De(e))&&De(e.then)&&De(e.catch),Tf=((e,t)=>e?setImmediate:t?((n,r)=>(qt.addEventListener("message",({source:o,data:l})=>{o===qt&&l===n&&r.length&&r.shift()()},!1),o=>{r.push(o),qt.postMessage(n,"*")}))(`axios@${Math.random()}`,[]):n=>setTimeout(n))(typeof setImmediate=="function",De(qt.postMessage)),om=typeof queueMicrotask<"u"?queueMicrotask.bind(qt):typeof process<"u"&&process.nextTick||Tf,lm=e=>e!=null&&De(e[nl]),h={isArray:In,isArrayBuffer:Cf,isBuffer:Or,isFormData:Lh,isArrayBufferView:wh,isString:Sh,isNumber:Rf,isBoolean:Eh,isObject:Pr,isPlainObject:uo,isEmptyObject:kh,isReadableStream:Ah,isRequest:zh,isResponse:Fh,isHeaders:Ih,isUndefined:Dn,isDate:Nh,isFile:_h,isReactNativeBlob:Ch,isReactNative:Rh,isBlob:xh,isRegExp:Xh,isFunction:De,isStream:Ph,isURLSearchParams:Dh,isTypedArray:Wh,isFileList:Oh,forEach:Tr,merge:Li,extend:Mh,trim:Uh,stripBOM:jh,inherits:Bh,toFlatObject:$h,kindOf:rl,kindOfTest:rt,endsWith:Hh,toArray:Vh,forEachEntry:Qh,matchAll:Kh,isHTMLForm:qh,hasOwnProperty:Di,hasOwnProp:Di,reduceDescriptors:Pf,freezeMethods:Jh,toObjectSet:Yh,toCamelCase:Gh,noop:Zh,toFiniteNumber:bh,findKey:xf,global:qt,isContextDefined:Of,isSpecCompliantForm:em,toJSONObject:tm,isAsyncFn:nm,isThenable:rm,setImmediate:Tf,asap:om,isIterable:lm},im=h.toObjectSet(["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"]),sm=e=>{const t={};let n,r,o;return e&&e.split(` +`).forEach(function(i){o=i.indexOf(":"),n=i.substring(0,o).trim().toLowerCase(),r=i.substring(o+1).trim(),!(!n||t[n]&&im[n])&&(n==="set-cookie"?t[n]?t[n].push(r):t[n]=[r]:t[n]=t[n]?t[n]+", "+r:r)}),t};function um(e){let t=0,n=e.length;for(;tt;){const r=e.charCodeAt(n-1);if(r!==9&&r!==32)break;n-=1}return t===0&&n===e.length?e:e.slice(t,n)}const am=new RegExp("[\\u0000-\\u0008\\u000a-\\u001f\\u007f]+","g"),cm=new RegExp("[^\\u0009\\u0020-\\u007e\\u0080-\\u00ff]+","g");function Ts(e,t){return h.isArray(e)?e.map(n=>Ts(n,t)):um(String(e).replace(t,""))}const fm=e=>Ts(e,am),dm=e=>Ts(e,cm);function Lf(e){const t=Object.create(null);return h.forEach(e.toJSON(),(n,r)=>{t[r]=dm(n)}),t}const Yu=Symbol("internals");function Kn(e){return e&&String(e).trim().toLowerCase()}function ao(e){return e===!1||e==null?e:h.isArray(e)?e.map(ao):fm(String(e))}function pm(e){const t=Object.create(null),n=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;let r;for(;r=n.exec(e);)t[r[1]]=r[2];return t}const hm=e=>/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim());function zl(e,t,n,r,o){if(h.isFunction(r))return r.call(this,t,n);if(o&&(t=n),!!h.isString(t)){if(h.isString(r))return t.indexOf(r)!==-1;if(h.isRegExp(r))return r.test(t)}}function mm(e){return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(t,n,r)=>n.toUpperCase()+r)}function ym(e,t){const n=h.toCamelCase(" "+t);["get","set","has"].forEach(r=>{Object.defineProperty(e,r+n,{__proto__:null,value:function(o,l,i){return this[r].call(this,t,o,l,i)},configurable:!0})})}class ll{constructor(t){t&&this.set(t)}set(t,n,r){const o=this;function l(s,u,a){const d=Kn(u);if(!d)throw new Error("header name must be a non-empty string");const m=h.findKey(o,d);(!m||o[m]===void 0||a===!0||a===void 0&&o[m]!==!1)&&(o[m||u]=ao(s))}const i=(s,u)=>h.forEach(s,(a,d)=>l(a,d,u));if(h.isPlainObject(t)||t instanceof this.constructor)i(t,n);else if(h.isString(t)&&(t=t.trim())&&!hm(t))i(sm(t),n);else if(h.isObject(t)&&h.isIterable(t)){let s={},u,a;for(const d of t){if(!h.isArray(d))throw TypeError("Object iterator must return a key-value pair");s[a=d[0]]=(u=s[a])?h.isArray(u)?[...u,d[1]]:[u,d[1]]:d[1]}i(s,n)}else t!=null&&l(n,t,r);return this}get(t,n){if(t=Kn(t),t){const r=h.findKey(this,t);if(r){const o=this[r];if(!n)return o;if(n===!0)return pm(o);if(h.isFunction(n))return n.call(this,o,r);if(h.isRegExp(n))return n.exec(o);throw new TypeError("parser must be boolean|regexp|function")}}}has(t,n){if(t=Kn(t),t){const r=h.findKey(this,t);return!!(r&&this[r]!==void 0&&(!n||zl(this,this[r],r,n)))}return!1}delete(t,n){const r=this;let o=!1;function l(i){if(i=Kn(i),i){const s=h.findKey(r,i);s&&(!n||zl(r,r[s],s,n))&&(delete r[s],o=!0)}}return h.isArray(t)?t.forEach(l):l(t),o}clear(t){const n=Object.keys(this);let r=n.length,o=!1;for(;r--;){const l=n[r];(!t||zl(this,this[l],l,t,!0))&&(delete this[l],o=!0)}return o}normalize(t){const n=this,r={};return h.forEach(this,(o,l)=>{const i=h.findKey(r,l);if(i){n[i]=ao(o),delete n[l];return}const s=t?mm(l):String(l).trim();s!==l&&delete n[l],n[s]=ao(o),r[s]=!0}),this}concat(...t){return this.constructor.concat(this,...t)}toJSON(t){const n=Object.create(null);return h.forEach(this,(r,o)=>{r!=null&&r!==!1&&(n[o]=t&&h.isArray(r)?r.join(", "):r)}),n}[Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator]()}toString(){return Object.entries(this.toJSON()).map(([t,n])=>t+": "+n).join(` +`)}getSetCookie(){return this.get("set-cookie")||[]}get[Symbol.toStringTag](){return"AxiosHeaders"}static from(t){return t instanceof this?t:new this(t)}static concat(t,...n){const r=new this(t);return n.forEach(o=>r.set(o)),r}static accessor(t){const r=(this[Yu]=this[Yu]={accessors:{}}).accessors,o=this.prototype;function l(i){const s=Kn(i);r[s]||(ym(o,i),r[s]=!0)}return h.isArray(t)?t.forEach(l):l(t),this}}ll.accessor(["Content-Type","Content-Length","Accept","Accept-Encoding","User-Agent","Authorization"]);h.reduceDescriptors(ll.prototype,({value:e},t)=>{let n=t[0].toUpperCase()+t.slice(1);return{get:()=>e,set(r){this[n]=r}}});h.freezeMethods(ll);const Qe=ll,gm="[REDACTED ****]";function vm(e){if(h.hasOwnProp(e,"toJSON"))return!0;let t=Object.getPrototypeOf(e);for(;t&&t!==Object.prototype;){if(h.hasOwnProp(t,"toJSON"))return!0;t=Object.getPrototypeOf(t)}return!1}function wm(e,t){const n=new Set(t.map(l=>String(l).toLowerCase())),r=[],o=l=>{if(l===null||typeof l!="object"||h.isBuffer(l))return l;if(r.indexOf(l)!==-1)return;l instanceof Qe&&(l=l.toJSON()),r.push(l);let i;if(h.isArray(l))i=[],l.forEach((s,u)=>{const a=o(s);h.isUndefined(a)||(i[u]=a)});else{if(!h.isPlainObject(l)&&vm(l))return r.pop(),l;i=Object.create(null);for(const[s,u]of Object.entries(l)){const a=n.has(s.toLowerCase())?gm:o(u);h.isUndefined(a)||(i[s]=a)}}return r.pop(),i};return o(e)}class fe extends Error{static from(t,n,r,o,l,i){const s=new fe(t.message,n||t.code,r,o,l);return s.cause=t,s.name=t.name,t.status!=null&&s.status==null&&(s.status=t.status),i&&Object.assign(s,i),s}constructor(t,n,r,o,l){super(t),Object.defineProperty(this,"message",{__proto__:null,value:t,enumerable:!0,writable:!0,configurable:!0}),this.name="AxiosError",this.isAxiosError=!0,n&&(this.code=n),r&&(this.config=r),o&&(this.request=o),l&&(this.response=l,this.status=l.status)}toJSON(){const t=this.config,n=t&&h.hasOwnProp(t,"redact")?t.redact:void 0,r=h.isArray(n)&&n.length>0?wm(t,n):h.toJSONObject(t);return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:r,code:this.code,status:this.status}}}fe.ERR_BAD_OPTION_VALUE="ERR_BAD_OPTION_VALUE";fe.ERR_BAD_OPTION="ERR_BAD_OPTION";fe.ECONNABORTED="ECONNABORTED";fe.ETIMEDOUT="ETIMEDOUT";fe.ECONNREFUSED="ECONNREFUSED";fe.ERR_NETWORK="ERR_NETWORK";fe.ERR_FR_TOO_MANY_REDIRECTS="ERR_FR_TOO_MANY_REDIRECTS";fe.ERR_DEPRECATED="ERR_DEPRECATED";fe.ERR_BAD_RESPONSE="ERR_BAD_RESPONSE";fe.ERR_BAD_REQUEST="ERR_BAD_REQUEST";fe.ERR_CANCELED="ERR_CANCELED";fe.ERR_NOT_SUPPORT="ERR_NOT_SUPPORT";fe.ERR_INVALID_URL="ERR_INVALID_URL";fe.ERR_FORM_DATA_DEPTH_EXCEEDED="ERR_FORM_DATA_DEPTH_EXCEEDED";const D=fe,Sm=null;function Ai(e){return h.isPlainObject(e)||h.isArray(e)}function Df(e){return h.endsWith(e,"[]")?e.slice(0,-2):e}function Fl(e,t,n){return e?e.concat(t).map(function(o,l){return o=Df(o),!n&&l?"["+o+"]":o}).join(n?".":""):t}function Em(e){return h.isArray(e)&&!e.some(Ai)}const km=h.toFlatObject(h,{},null,function(t){return/^is[A-Z]/.test(t)});function il(e,t,n){if(!h.isObject(e))throw new TypeError("target must be an object");t=t||new FormData,n=h.toFlatObject(n,{metaTokens:!0,dots:!1,indexes:!1},!1,function(k,c){return!h.isUndefined(c[k])});const r=n.metaTokens,o=n.visitor||m,l=n.dots,i=n.indexes,s=n.Blob||typeof Blob<"u"&&Blob,u=n.maxDepth===void 0?100:n.maxDepth,a=s&&h.isSpecCompliantForm(t);if(!h.isFunction(o))throw new TypeError("visitor must be a function");function d(v){if(v===null)return"";if(h.isDate(v))return v.toISOString();if(h.isBoolean(v))return v.toString();if(!a&&h.isBlob(v))throw new D("Blob is not supported. Use a Buffer instead.");return h.isArrayBuffer(v)||h.isTypedArray(v)?a&&typeof Blob=="function"?new Blob([v]):Buffer.from(v):v}function m(v,k,c){let f=v;if(h.isReactNative(t)&&h.isReactNativeBlob(v))return t.append(Fl(c,k,l),d(v)),!1;if(v&&!c&&typeof v=="object"){if(h.endsWith(k,"{}"))k=r?k:k.slice(0,-2),v=JSON.stringify(v);else if(h.isArray(v)&&Em(v)||(h.isFileList(v)||h.endsWith(k,"[]"))&&(f=h.toArray(v)))return k=Df(k),f.forEach(function(w,_){!(h.isUndefined(w)||w===null)&&t.append(i===!0?Fl([k],_,l):i===null?k:k+"[]",d(w))}),!1}return Ai(v)?!0:(t.append(Fl(c,k,l),d(v)),!1)}const y=[],E=Object.assign(km,{defaultVisitor:m,convertValue:d,isVisitable:Ai});function S(v,k,c=0){if(!h.isUndefined(v)){if(c>u)throw new D("Object is too deeply nested ("+c+" levels). Max depth: "+u,D.ERR_FORM_DATA_DEPTH_EXCEEDED);if(y.indexOf(v)!==-1)throw Error("Circular reference detected in "+k.join("."));y.push(v),h.forEach(v,function(p,w){(!(h.isUndefined(p)||p===null)&&o.call(t,p,h.isString(w)?w.trim():w,k,E))===!0&&S(p,k?k.concat(w):[w],c+1)}),y.pop()}}if(!h.isObject(e))throw new TypeError("data must be an object");return S(e),t}function Zu(e){const t={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+"};return encodeURIComponent(e).replace(/[!'()~]|%20/g,function(r){return t[r]})}function Ls(e,t){this._pairs=[],e&&il(e,this,t)}const Af=Ls.prototype;Af.append=function(t,n){this._pairs.push([t,n])};Af.toString=function(t){const n=t?function(r){return t.call(this,r,Zu)}:Zu;return this._pairs.map(function(o){return n(o[0])+"="+n(o[1])},"").join("&")};function Nm(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+")}function zf(e,t,n){if(!t)return e;const r=n&&n.encode||Nm,o=h.isFunction(n)?{serialize:n}:n,l=o&&o.serialize;let i;if(l?i=l(t,o):i=h.isURLSearchParams(t)?t.toString():new Ls(t,o).toString(r),i){const s=e.indexOf("#");s!==-1&&(e=e.slice(0,s)),e+=(e.indexOf("?")===-1?"?":"&")+i}return e}class _m{constructor(){this.handlers=[]}use(t,n,r){return this.handlers.push({fulfilled:t,rejected:n,synchronous:r?r.synchronous:!1,runWhen:r?r.runWhen:null}),this.handlers.length-1}eject(t){this.handlers[t]&&(this.handlers[t]=null)}clear(){this.handlers&&(this.handlers=[])}forEach(t){h.forEach(this.handlers,function(r){r!==null&&t(r)})}}const bu=_m,Ds={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1,legacyInterceptorReqResOrdering:!0},Cm=typeof URLSearchParams<"u"?URLSearchParams:Ls,Rm=typeof FormData<"u"?FormData:null,xm=typeof Blob<"u"?Blob:null,Om={isBrowser:!0,classes:{URLSearchParams:Cm,FormData:Rm,Blob:xm},protocols:["http","https","file","blob","url","data"]},As=typeof window<"u"&&typeof document<"u",zi=typeof navigator=="object"&&navigator||void 0,Pm=As&&(!zi||["ReactNative","NativeScript","NS"].indexOf(zi.product)<0),Tm=(()=>typeof WorkerGlobalScope<"u"&&self instanceof WorkerGlobalScope&&typeof self.importScripts=="function")(),Lm=As&&window.location.href||"http://localhost",Dm=Object.freeze(Object.defineProperty({__proto__:null,hasBrowserEnv:As,hasStandardBrowserEnv:Pm,hasStandardBrowserWebWorkerEnv:Tm,navigator:zi,origin:Lm},Symbol.toStringTag,{value:"Module"})),ye={...Dm,...Om};function Am(e,t){return il(e,new ye.classes.URLSearchParams,{visitor:function(n,r,o,l){return ye.isNode&&h.isBuffer(n)?(this.append(r,n.toString("base64")),!1):l.defaultVisitor.apply(this,arguments)},...t})}function zm(e){return h.matchAll(/\w+|\[(\w*)]/g,e).map(t=>t[0]==="[]"?"":t[1]||t[0])}function Fm(e){const t={},n=Object.keys(e);let r;const o=n.length;let l;for(r=0;r=n.length;return i=!i&&h.isArray(o)?o.length:i,u?(h.hasOwnProp(o,i)?o[i]=h.isArray(o[i])?o[i].concat(r):[o[i],r]:o[i]=r,!s):((!h.hasOwnProp(o,i)||!h.isObject(o[i]))&&(o[i]=[]),t(n,r,o[i],l)&&h.isArray(o[i])&&(o[i]=Fm(o[i])),!s)}if(h.isFormData(e)&&h.isFunction(e.entries)){const n={};return h.forEachEntry(e,(r,o)=>{t(zm(r),o,n,0)}),n}return null}const ln=(e,t)=>e!=null&&h.hasOwnProp(e,t)?e[t]:void 0;function Im(e,t,n){if(h.isString(e))try{return(t||JSON.parse)(e),h.trim(e)}catch(r){if(r.name!=="SyntaxError")throw r}return(n||JSON.stringify)(e)}const zs={transitional:Ds,adapter:["xhr","http","fetch"],transformRequest:[function(t,n){const r=n.getContentType()||"",o=r.indexOf("application/json")>-1,l=h.isObject(t);if(l&&h.isHTMLForm(t)&&(t=new FormData(t)),h.isFormData(t))return o?JSON.stringify(Ff(t)):t;if(h.isArrayBuffer(t)||h.isBuffer(t)||h.isStream(t)||h.isFile(t)||h.isBlob(t)||h.isReadableStream(t))return t;if(h.isArrayBufferView(t))return t.buffer;if(h.isURLSearchParams(t))return n.setContentType("application/x-www-form-urlencoded;charset=utf-8",!1),t.toString();let s;if(l){const u=ln(this,"formSerializer");if(r.indexOf("application/x-www-form-urlencoded")>-1)return Am(t,u).toString();if((s=h.isFileList(t))||r.indexOf("multipart/form-data")>-1){const a=ln(this,"env"),d=a&&a.FormData;return il(s?{"files[]":t}:t,d&&new d,u)}}return l||o?(n.setContentType("application/json",!1),Im(t)):t}],transformResponse:[function(t){const n=ln(this,"transitional")||zs.transitional,r=n&&n.forcedJSONParsing,o=ln(this,"responseType"),l=o==="json";if(h.isResponse(t)||h.isReadableStream(t))return t;if(t&&h.isString(t)&&(r&&!o||l)){const s=!(n&&n.silentJSONParsing)&&l;try{return JSON.parse(t,ln(this,"parseReviver"))}catch(u){if(s)throw u.name==="SyntaxError"?D.from(u,D.ERR_BAD_RESPONSE,this,null,ln(this,"response")):u}}return t}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,env:{FormData:ye.classes.FormData,Blob:ye.classes.Blob},validateStatus:function(t){return t>=200&&t<300},headers:{common:{Accept:"application/json, text/plain, */*","Content-Type":void 0}}};h.forEach(["delete","get","head","post","put","patch","query"],e=>{zs.headers[e]={}});const Fs=zs;function Il(e,t){const n=this||Fs,r=t||n,o=Qe.from(r.headers);let l=r.data;return h.forEach(e,function(s){l=s.call(n,l,o.normalize(),t?t.status:void 0)}),o.normalize(),l}function If(e){return!!(e&&e.__CANCEL__)}class Um extends D{constructor(t,n,r){super(t??"canceled",D.ERR_CANCELED,n,r),this.name="CanceledError",this.__CANCEL__=!0}}const Lr=Um;function Uf(e,t,n){const r=n.config.validateStatus;!n.status||!r||r(n.status)?e(n):t(new D("Request failed with status code "+n.status,n.status>=400&&n.status<500?D.ERR_BAD_REQUEST:D.ERR_BAD_RESPONSE,n.config,n.request,n))}function Mm(e){const t=/^([-+\w]{1,25}):(?:\/\/)?/.exec(e);return t&&t[1]||""}function jm(e,t){e=e||10;const n=new Array(e),r=new Array(e);let o=0,l=0,i;return t=t!==void 0?t:1e3,function(u){const a=Date.now(),d=r[l];i||(i=a),n[o]=u,r[o]=a;let m=l,y=0;for(;m!==o;)y+=n[m++],m=m%e;if(o=(o+1)%e,o===l&&(l=(l+1)%e),a-i{n=d,o=null,l&&(clearTimeout(l),l=null),e(...a)};return[(...a)=>{const d=Date.now(),m=d-n;m>=r?i(a,d):(o=a,l||(l=setTimeout(()=>{l=null,i(o)},r-m)))},()=>o&&i(o)]}const Uo=(e,t,n=3)=>{let r=0;const o=jm(50,250);return Bm(l=>{if(!l||typeof l.loaded!="number")return;const i=l.loaded,s=l.lengthComputable?l.total:void 0,u=s!=null?Math.min(i,s):i,a=Math.max(0,u-r),d=o(a);r=Math.max(r,u);const m={loaded:u,total:s,progress:s?u/s:void 0,bytes:a,rate:d||void 0,estimated:d&&s?(s-u)/d:void 0,event:l,lengthComputable:s!=null,[t?"download":"upload"]:!0};e(m)},n)},ea=(e,t)=>{const n=e!=null;return[r=>t[0]({lengthComputable:n,total:e,loaded:r}),t[1]]},ta=e=>(...t)=>h.asap(()=>e(...t)),$m=ye.hasStandardBrowserEnv?((e,t)=>n=>(n=new URL(n,ye.origin),e.protocol===n.protocol&&e.host===n.host&&(t||e.port===n.port)))(new URL(ye.origin),ye.navigator&&/(msie|trident)/i.test(ye.navigator.userAgent)):()=>!0,Hm=ye.hasStandardBrowserEnv?{write(e,t,n,r,o,l,i){if(typeof document>"u")return;const s=[`${e}=${encodeURIComponent(t)}`];h.isNumber(n)&&s.push(`expires=${new Date(n).toUTCString()}`),h.isString(r)&&s.push(`path=${r}`),h.isString(o)&&s.push(`domain=${o}`),l===!0&&s.push("secure"),h.isString(i)&&s.push(`SameSite=${i}`),document.cookie=s.join("; ")},read(e){if(typeof document>"u")return null;const t=document.cookie.split(";");for(let n=0;ne instanceof Qe?{...e}:e;function tn(e,t){t=t||{};const n=Object.create(null);Object.defineProperty(n,"hasOwnProperty",{__proto__:null,value:Object.prototype.hasOwnProperty,enumerable:!1,writable:!0,configurable:!0});function r(a,d,m,y){return h.isPlainObject(a)&&h.isPlainObject(d)?h.merge.call({caseless:y},a,d):h.isPlainObject(d)?h.merge({},d):h.isArray(d)?d.slice():d}function o(a,d,m,y){if(h.isUndefined(d)){if(!h.isUndefined(a))return r(void 0,a,m,y)}else return r(a,d,m,y)}function l(a,d){if(!h.isUndefined(d))return r(void 0,d)}function i(a,d){if(h.isUndefined(d)){if(!h.isUndefined(a))return r(void 0,a)}else return r(void 0,d)}function s(a,d,m){if(h.hasOwnProp(t,m))return r(a,d);if(h.hasOwnProp(e,m))return r(void 0,a)}const u={url:l,method:l,data:l,baseURL:i,transformRequest:i,transformResponse:i,paramsSerializer:i,timeout:i,timeoutMessage:i,withCredentials:i,withXSRFToken:i,adapter:i,responseType:i,xsrfCookieName:i,xsrfHeaderName:i,onUploadProgress:i,onDownloadProgress:i,decompress:i,maxContentLength:i,maxBodyLength:i,beforeRedirect:i,transport:i,httpAgent:i,httpsAgent:i,cancelToken:i,socketPath:i,allowedSocketPaths:i,responseEncoding:i,validateStatus:s,headers:(a,d,m)=>o(na(a),na(d),m,!0)};return h.forEach(Object.keys({...e,...t}),function(d){if(d==="__proto__"||d==="constructor"||d==="prototype")return;const m=h.hasOwnProp(u,d)?u[d]:o,y=h.hasOwnProp(e,d)?e[d]:void 0,E=h.hasOwnProp(t,d)?t[d]:void 0,S=m(y,E,d);h.isUndefined(S)&&m!==s||(n[d]=S)}),n}const Qm=["content-type","content-length"];function Km(e,t,n){if(n!=="content-only"){e.set(t);return}Object.entries(t).forEach(([r,o])=>{Qm.includes(r.toLowerCase())&&e.set(r,o)})}const qm=e=>encodeURIComponent(e).replace(/%([0-9A-F]{2})/gi,(t,n)=>String.fromCharCode(parseInt(n,16))),jf=e=>{const t=tn({},e),n=y=>h.hasOwnProp(t,y)?t[y]:void 0,r=n("data");let o=n("withXSRFToken");const l=n("xsrfHeaderName"),i=n("xsrfCookieName");let s=n("headers");const u=n("auth"),a=n("baseURL"),d=n("allowAbsoluteUrls"),m=n("url");if(t.headers=s=Qe.from(s),t.url=zf(Mf(a,m,d),e.params,e.paramsSerializer),u&&s.set("Authorization","Basic "+btoa((u.username||"")+":"+(u.password?qm(u.password):""))),h.isFormData(r)&&(ye.hasStandardBrowserEnv||ye.hasStandardBrowserWebWorkerEnv?s.setContentType(void 0):h.isFunction(r.getHeaders)&&Km(s,r.getHeaders(),n("formDataHeaderPolicy"))),ye.hasStandardBrowserEnv&&(h.isFunction(o)&&(o=o(t)),o===!0||o==null&&$m(t.url))){const E=l&&i&&Hm.read(i);E&&s.set(l,E)}return t},Gm=typeof XMLHttpRequest<"u",Xm=Gm&&function(e){return new Promise(function(n,r){const o=jf(e);let l=o.data;const i=Qe.from(o.headers).normalize();let{responseType:s,onUploadProgress:u,onDownloadProgress:a}=o,d,m,y,E,S;function v(){E&&E(),S&&S(),o.cancelToken&&o.cancelToken.unsubscribe(d),o.signal&&o.signal.removeEventListener("abort",d)}let k=new XMLHttpRequest;k.open(o.method.toUpperCase(),o.url,!0),k.timeout=o.timeout;function c(){if(!k)return;const p=Qe.from("getAllResponseHeaders"in k&&k.getAllResponseHeaders()),_={data:!s||s==="text"||s==="json"?k.responseText:k.response,status:k.status,statusText:k.statusText,headers:p,config:e,request:k};Uf(function(P){n(P),v()},function(P){r(P),v()},_),k=null}"onloadend"in k?k.onloadend=c:k.onreadystatechange=function(){!k||k.readyState!==4||k.status===0&&!(k.responseURL&&k.responseURL.startsWith("file:"))||setTimeout(c)},k.onabort=function(){k&&(r(new D("Request aborted",D.ECONNABORTED,e,k)),v(),k=null)},k.onerror=function(w){const _=w&&w.message?w.message:"Network Error",O=new D(_,D.ERR_NETWORK,e,k);O.event=w||null,r(O),v(),k=null},k.ontimeout=function(){let w=o.timeout?"timeout of "+o.timeout+"ms exceeded":"timeout exceeded";const _=o.transitional||Ds;o.timeoutErrorMessage&&(w=o.timeoutErrorMessage),r(new D(w,_.clarifyTimeoutError?D.ETIMEDOUT:D.ECONNABORTED,e,k)),v(),k=null},l===void 0&&i.setContentType(null),"setRequestHeader"in k&&h.forEach(Lf(i),function(w,_){k.setRequestHeader(_,w)}),h.isUndefined(o.withCredentials)||(k.withCredentials=!!o.withCredentials),s&&s!=="json"&&(k.responseType=o.responseType),a&&([y,S]=Uo(a,!0),k.addEventListener("progress",y)),u&&k.upload&&([m,E]=Uo(u),k.upload.addEventListener("progress",m),k.upload.addEventListener("loadend",E)),(o.cancelToken||o.signal)&&(d=p=>{k&&(r(!p||p.type?new Lr(null,e,k):p),k.abort(),v(),k=null)},o.cancelToken&&o.cancelToken.subscribe(d),o.signal&&(o.signal.aborted?d():o.signal.addEventListener("abort",d)));const f=Mm(o.url);if(f&&!ye.protocols.includes(f)){r(new D("Unsupported protocol "+f+":",D.ERR_BAD_REQUEST,e));return}k.send(l||null)})},Jm=(e,t)=>{if(e=e?e.filter(Boolean):[],!t&&!e.length)return;const n=new AbortController;let r=!1;const o=function(u){if(!r){r=!0,i();const a=u instanceof Error?u:this.reason;n.abort(a instanceof D?a:new Lr(a instanceof Error?a.message:a))}};let l=t&&setTimeout(()=>{l=null,o(new D(`timeout of ${t}ms exceeded`,D.ETIMEDOUT))},t);const i=()=>{e&&(l&&clearTimeout(l),l=null,e.forEach(u=>{u.unsubscribe?u.unsubscribe(o):u.removeEventListener("abort",o)}),e=null)};e.forEach(u=>u.addEventListener("abort",o));const{signal:s}=n;return s.unsubscribe=()=>h.asap(i),s},Ym=Jm,Zm=function*(e,t){let n=e.byteLength;if(!t||n{const o=bm(e,t);let l=0,i,s=u=>{i||(i=!0,r&&r(u))};return new ReadableStream({async pull(u){try{const{done:a,value:d}=await o.next();if(a){s(),u.close();return}let m=d.byteLength;if(n){let y=l+=m;n(y)}u.enqueue(new Uint8Array(d))}catch(a){throw s(a),a}},cancel(u){return s(u),o.return()}},{highWaterMark:2})};function ty(e){if(!e||typeof e!="string"||!e.startsWith("data:"))return 0;const t=e.indexOf(",");if(t<0)return 0;const n=e.slice(5,t),r=e.slice(t+1);if(/;base64/i.test(n)){let i=r.length;const s=r.length;for(let E=0;E=48&&S<=57||S>=65&&S<=70||S>=97&&S<=102)&&(v>=48&&v<=57||v>=65&&v<=70||v>=97&&v<=102)&&(i-=2,E+=2)}let u=0,a=s-1;const d=E=>E>=2&&r.charCodeAt(E-2)===37&&r.charCodeAt(E-1)===51&&(r.charCodeAt(E)===68||r.charCodeAt(E)===100);a>=0&&(r.charCodeAt(a)===61?(u++,a--):d(a)&&(u++,a-=3)),u===1&&a>=0&&(r.charCodeAt(a)===61||d(a))&&u++;const y=Math.floor(i/4)*3-(u||0);return y>0?y:0}if(typeof Buffer<"u"&&typeof Buffer.byteLength=="function")return Buffer.byteLength(r,"utf8");let l=0;for(let i=0,s=r.length;i=55296&&u<=56319&&i+1=56320&&a<=57343?(l+=4,i++):l+=3}else l+=3}return l}const Is="1.16.1",oa=64*1024,{isFunction:Xr}=h,la=(e,...t)=>{try{return!!e(...t)}catch{return!1}},ny=e=>{const t=h.global!==void 0&&h.global!==null?h.global:globalThis,{ReadableStream:n,TextEncoder:r}=t;e=h.merge.call({skipUndefined:!0},{Request:t.Request,Response:t.Response},e);const{fetch:o,Request:l,Response:i}=e,s=o?Xr(o):typeof fetch=="function",u=Xr(l),a=Xr(i);if(!s)return!1;const d=s&&Xr(n),m=s&&(typeof r=="function"?(c=>f=>c.encode(f))(new r):async c=>new Uint8Array(await new l(c).arrayBuffer())),y=u&&d&&la(()=>{let c=!1;const f=new l(ye.origin,{body:new n,method:"POST",get duplex(){return c=!0,"half"}}),p=f.headers.has("Content-Type");return f.body!=null&&f.body.cancel(),c&&!p}),E=a&&d&&la(()=>h.isReadableStream(new i("").body)),S={stream:E&&(c=>c.body)};s&&["text","arrayBuffer","blob","formData","stream"].forEach(c=>{!S[c]&&(S[c]=(f,p)=>{let w=f&&f[c];if(w)return w.call(f);throw new D(`Response type '${c}' is not supported`,D.ERR_NOT_SUPPORT,p)})});const v=async c=>{if(c==null)return 0;if(h.isBlob(c))return c.size;if(h.isSpecCompliantForm(c))return(await new l(ye.origin,{method:"POST",body:c}).arrayBuffer()).byteLength;if(h.isArrayBufferView(c)||h.isArrayBuffer(c))return c.byteLength;if(h.isURLSearchParams(c)&&(c=c+""),h.isString(c))return(await m(c)).byteLength},k=async(c,f)=>{const p=h.toFiniteNumber(c.getContentLength());return p??v(f)};return async c=>{let{url:f,method:p,data:w,signal:_,cancelToken:O,timeout:P,onDownloadProgress:L,onUploadProgress:B,responseType:z,headers:J,withCredentials:je="same-origin",fetchOptions:_e,maxContentLength:ve,maxBodyLength:Bt}=jf(c);const Ge=h.isNumber(ve)&&ve>-1,$t=h.isNumber(Bt)&&Bt>-1;let g=o||fetch;z=z?(z+"").toLowerCase():"text";let C=Ym([_,O&&O.toAbortSignal()],P),x=null;const I=C&&C.unsubscribe&&(()=>{C.unsubscribe()});let W;try{if(Ge&&typeof f=="string"&&f.startsWith("data:")&&ty(f)>ve)throw new D("maxContentLength size of "+ve+" exceeded",D.ERR_BAD_RESPONSE,c,x);if($t&&p!=="get"&&p!=="head"){const j=await k(J,w);if(typeof j=="number"&&isFinite(j)&&j>Bt)throw new D("Request body larger than maxBodyLength limit",D.ERR_BAD_REQUEST,c,x)}if(B&&y&&p!=="get"&&p!=="head"&&(W=await k(J,w))!==0){let j=new l(f,{method:"POST",body:w,duplex:"half"}),Re;if(h.isFormData(w)&&(Re=j.headers.get("content-type"))&&J.setContentType(Re),j.body){const[oe,wt]=ea(W,Uo(ta(B)));w=ra(j.body,oa,oe,wt)}}h.isString(je)||(je=je?"include":"omit");const Q=u&&"credentials"in l.prototype;if(h.isFormData(w)){const j=J.getContentType();j&&/^multipart\/form-data/i.test(j)&&!/boundary=/i.test(j)&&J.delete("content-type")}J.set("User-Agent","axios/"+Is,!1);const te={..._e,signal:C,method:p.toUpperCase(),headers:Lf(J.normalize()),body:w,duplex:"half",credentials:Q?je:void 0};x=u&&new l(f,te);let se=await(u?g(x,_e):g(f,te));if(Ge){const j=h.toFiniteNumber(se.headers.get("content-length"));if(j!=null&&j>ve)throw new D("maxContentLength size of "+ve+" exceeded",D.ERR_BAD_RESPONSE,c,x)}const Ce=E&&(z==="stream"||z==="response");if(E&&se.body&&(L||Ge||Ce&&I)){const j={};["status","statusText","headers"].forEach(Un=>{j[Un]=se[Un]});const Re=h.toFiniteNumber(se.headers.get("content-length")),[oe,wt]=L&&ea(Re,Uo(ta(L),!0))||[];let js=0;const Vf=Un=>{if(Ge&&(js=Un,js>ve))throw new D("maxContentLength size of "+ve+" exceeded",D.ERR_BAD_RESPONSE,c,x);oe&&oe(Un)};se=new i(ra(se.body,oa,Vf,()=>{wt&&wt(),I&&I()}),j)}z=z||"text";let de=await S[h.findKey(S,z)||"text"](se,c);if(Ge&&!E&&!Ce){let j;if(de!=null&&(typeof de.byteLength=="number"?j=de.byteLength:typeof de.size=="number"?j=de.size:typeof de=="string"&&(j=typeof r=="function"?new r().encode(de).byteLength:de.length)),typeof j=="number"&&j>ve)throw new D("maxContentLength size of "+ve+" exceeded",D.ERR_BAD_RESPONSE,c,x)}return!Ce&&I&&I(),await new Promise((j,Re)=>{Uf(j,Re,{data:de,headers:Qe.from(se.headers),status:se.status,statusText:se.statusText,config:c,request:x})})}catch(Q){if(I&&I(),C&&C.aborted&&C.reason instanceof D){const te=C.reason;throw te.config=c,x&&(te.request=x),Q!==te&&(te.cause=Q),te}throw Q&&Q.name==="TypeError"&&/Load failed|fetch/i.test(Q.message)?Object.assign(new D("Network Error",D.ERR_NETWORK,c,x,Q&&Q.response),{cause:Q.cause||Q}):D.from(Q,Q&&Q.code,c,x,Q&&Q.response)}}},ry=new Map,Bf=e=>{let t=e&&e.env||{};const{fetch:n,Request:r,Response:o}=t,l=[r,o,n];let i=l.length,s=i,u,a,d=ry;for(;s--;)u=l[s],a=d.get(u),a===void 0&&d.set(u,a=s?new Map:ny(t)),d=a;return a};Bf();const Us={http:Sm,xhr:Xm,fetch:{get:Bf}};h.forEach(Us,(e,t)=>{if(e){try{Object.defineProperty(e,"name",{__proto__:null,value:t})}catch{}Object.defineProperty(e,"adapterName",{__proto__:null,value:t})}});const ia=e=>`- ${e}`,oy=e=>h.isFunction(e)||e===null||e===!1;function ly(e,t){e=h.isArray(e)?e:[e];const{length:n}=e;let r,o;const l={};for(let i=0;i`adapter ${u} `+(a===!1?"is not supported by the environment":"is not available in the build"));let s=n?i.length>1?`since : +`+i.map(ia).join(` +`):" "+ia(i[0]):"as no adapter specified";throw new D("There is no suitable adapter to dispatch the request "+s,"ERR_NOT_SUPPORT")}return o}const $f={getAdapter:ly,adapters:Us};function Ul(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new Lr(null,e)}function sa(e){return Ul(e),e.headers=Qe.from(e.headers),e.data=Il.call(e,e.transformRequest),["post","put","patch"].indexOf(e.method)!==-1&&e.headers.setContentType("application/x-www-form-urlencoded",!1),$f.getAdapter(e.adapter||Fs.adapter,e)(e).then(function(r){Ul(e),e.response=r;try{r.data=Il.call(e,e.transformResponse,r)}finally{delete e.response}return r.headers=Qe.from(r.headers),r},function(r){if(!If(r)&&(Ul(e),r&&r.response)){e.response=r.response;try{r.response.data=Il.call(e,e.transformResponse,r.response)}finally{delete e.response}r.response.headers=Qe.from(r.response.headers)}return Promise.reject(r)})}const sl={};["object","boolean","number","function","string","symbol"].forEach((e,t)=>{sl[e]=function(r){return typeof r===e||"a"+(t<1?"n ":" ")+e}});const ua={};sl.transitional=function(t,n,r){function o(l,i){return"[Axios v"+Is+"] Transitional option '"+l+"'"+i+(r?". "+r:"")}return(l,i,s)=>{if(t===!1)throw new D(o(i," has been removed"+(n?" in "+n:"")),D.ERR_DEPRECATED);return n&&!ua[i]&&(ua[i]=!0,console.warn(o(i," has been deprecated since v"+n+" and will be removed in the near future"))),t?t(l,i,s):!0}};sl.spelling=function(t){return(n,r)=>(console.warn(`${r} is likely a misspelling of ${t}`),!0)};function iy(e,t,n){if(typeof e!="object")throw new D("options must be an object",D.ERR_BAD_OPTION_VALUE);const r=Object.keys(e);let o=r.length;for(;o-- >0;){const l=r[o],i=Object.prototype.hasOwnProperty.call(t,l)?t[l]:void 0;if(i){const s=e[l],u=s===void 0||i(s,l,e);if(u!==!0)throw new D("option "+l+" must be "+u,D.ERR_BAD_OPTION_VALUE);continue}if(n!==!0)throw new D("Unknown option "+l,D.ERR_BAD_OPTION)}}const co={assertOptions:iy,validators:sl},Be=co.validators;class Mo{constructor(t){this.defaults=t||{},this.interceptors={request:new bu,response:new bu}}async request(t,n){try{return await this._request(t,n)}catch(r){if(r instanceof Error){let o={};Error.captureStackTrace?Error.captureStackTrace(o):o=new Error;const l=(()=>{if(!o.stack)return"";const i=o.stack.indexOf(` +`);return i===-1?"":o.stack.slice(i+1)})();try{if(!r.stack)r.stack=l;else if(l){const i=l.indexOf(` +`),s=i===-1?-1:l.indexOf(` +`,i+1),u=s===-1?"":l.slice(s+1);String(r.stack).endsWith(u)||(r.stack+=` +`+l)}}catch{}}throw r}}_request(t,n){typeof t=="string"?(n=n||{},n.url=t):n=t||{},n=tn(this.defaults,n);const{transitional:r,paramsSerializer:o,headers:l}=n;r!==void 0&&co.assertOptions(r,{silentJSONParsing:Be.transitional(Be.boolean),forcedJSONParsing:Be.transitional(Be.boolean),clarifyTimeoutError:Be.transitional(Be.boolean),legacyInterceptorReqResOrdering:Be.transitional(Be.boolean)},!1),o!=null&&(h.isFunction(o)?n.paramsSerializer={serialize:o}:co.assertOptions(o,{encode:Be.function,serialize:Be.function},!0)),n.allowAbsoluteUrls!==void 0||(this.defaults.allowAbsoluteUrls!==void 0?n.allowAbsoluteUrls=this.defaults.allowAbsoluteUrls:n.allowAbsoluteUrls=!0),co.assertOptions(n,{baseUrl:Be.spelling("baseURL"),withXsrfToken:Be.spelling("withXSRFToken")},!0),n.method=(n.method||this.defaults.method||"get").toLowerCase();let i=l&&h.merge(l.common,l[n.method]);l&&h.forEach(["delete","get","head","post","put","patch","query","common"],S=>{delete l[S]}),n.headers=Qe.concat(i,l);const s=[];let u=!0;this.interceptors.request.forEach(function(v){if(typeof v.runWhen=="function"&&v.runWhen(n)===!1)return;u=u&&v.synchronous;const k=n.transitional||Ds;k&&k.legacyInterceptorReqResOrdering?s.unshift(v.fulfilled,v.rejected):s.push(v.fulfilled,v.rejected)});const a=[];this.interceptors.response.forEach(function(v){a.push(v.fulfilled,v.rejected)});let d,m=0,y;if(!u){const S=[sa.bind(this),void 0];for(S.unshift(...s),S.push(...a),y=S.length,d=Promise.resolve(n);m{if(!r._listeners)return;let l=r._listeners.length;for(;l-- >0;)r._listeners[l](o);r._listeners=null}),this.promise.then=o=>{let l;const i=new Promise(s=>{r.subscribe(s),l=s}).then(o);return i.cancel=function(){r.unsubscribe(l)},i},t(function(l,i,s){r.reason||(r.reason=new Lr(l,i,s),n(r.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(t){if(this.reason){t(this.reason);return}this._listeners?this._listeners.push(t):this._listeners=[t]}unsubscribe(t){if(!this._listeners)return;const n=this._listeners.indexOf(t);n!==-1&&this._listeners.splice(n,1)}toAbortSignal(){const t=new AbortController,n=r=>{t.abort(r)};return this.subscribe(n),t.signal.unsubscribe=()=>this.unsubscribe(n),t.signal}static source(){let t;return{token:new Ms(function(o){t=o}),cancel:t}}}const sy=Ms;function uy(e){return function(n){return e.apply(null,n)}}function ay(e){return h.isObject(e)&&e.isAxiosError===!0}const Fi={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511,WebServerIsDown:521,ConnectionTimedOut:522,OriginIsUnreachable:523,TimeoutOccurred:524,SslHandshakeFailed:525,InvalidSslCertificate:526};Object.entries(Fi).forEach(([e,t])=>{Fi[t]=e});const cy=Fi;function Hf(e){const t=new fo(e),n=Nf(fo.prototype.request,t);return h.extend(n,fo.prototype,t,{allOwnKeys:!0}),h.extend(n,t,null,{allOwnKeys:!0}),n.create=function(o){return Hf(tn(e,o))},n}const ee=Hf(Fs);ee.Axios=fo;ee.CanceledError=Lr;ee.CancelToken=sy;ee.isCancel=If;ee.VERSION=Is;ee.toFormData=il;ee.AxiosError=D;ee.Cancel=ee.CanceledError;ee.all=function(t){return Promise.all(t)};ee.spread=uy;ee.isAxiosError=ay;ee.mergeConfig=tn;ee.AxiosHeaders=Qe;ee.formToJSON=e=>Ff(h.isHTMLForm(e)?new FormData(e):e);ee.getAdapter=$f.getAdapter;ee.HttpStatusCode=cy;ee.default=ee;const Je=ee;function fy(){const[e,t]=we.useState(""),[n,r]=we.useState(null),[o,l]=we.useState([]),[i,s]=we.useState(!1),[u,a]=we.useState(null),[d,m]=we.useState([]),[y,E]=we.useState(!1),[S,v]=we.useState({enabled:!1,triggers:{onGrab:!1,onDownload:!1,onImport:!1,onUpgrade:!1},stats:null}),[k,c]=we.useState({enabled:!1,triggers:{onGrab:!1,onDownload:!1,onImport:!1,onUpgrade:!1},stats:null}),[f,p]=we.useState(null),[w,_]=we.useState(!1);we.useEffect(()=>{O(),_e()},[]);const O=async()=>{try{const g=await Je.get("/api/emby/sessions");m(g.data);const C=g.data.find(x=>x.NowPlayingItem||x.Active);C&&(t(C.Id),P(C.Id))}catch(g){a("Failed to fetch Emby sessions. Make sure Emby is running and configured."),console.error(g)}},P=async g=>{s(!0),a(null);try{const C=await Je.get(`/api/dashboard/user-downloads/${g}`);r(C.data.user),l(C.data.downloads)}catch(C){a("Failed to fetch downloads. Make sure all services are configured."),console.error(C)}finally{s(!1)}},L=g=>{const C=g.target.value;t(C),C&&P(C)},B=g=>{if(!g)return"N/A";const C=["B","KB","MB","GB","TB"],x=Math.floor(Math.log(g)/Math.log(1024));return Math.round(g/Math.pow(1024,x)*100)/100+" "+C[x]},z=g=>g?new Date(g).toLocaleString():"N/A",J=g=>{if(!g)return"Never";const C=Math.floor((Date.now()-g)/1e3);if(C<60)return`${C}s ago`;const x=Math.floor(C/60);if(x<60)return`${x}m ago`;const I=Math.floor(x/60);return I<24?`${I}h ago`:`${Math.floor(I/24)}d ago`},je=async()=>{try{const g=await Je.get("/api/dashboard/webhook-metrics");return p(g.data),g.data}catch{return null}},_e=async()=>{var g,C;try{const x=je();let I=!1,W={onGrab:!1,onDownload:!1,onImport:!1,onUpgrade:!1};try{const oe=(await Je.get("/api/sonarr/notifications")).data.find(wt=>wt.name==="Sofarr");I=!!oe,oe&&(W={onGrab:oe.onGrab,onDownload:oe.onDownload,onImport:oe.onImport,onUpgrade:oe.onUpgrade})}catch{}let Q=!1,te={onGrab:!1,onDownload:!1,onImport:!1,onUpgrade:!1};try{const oe=(await Je.get("/api/radarr/notifications")).data.find(wt=>wt.name==="Sofarr");Q=!!oe,oe&&(te={onGrab:oe.onGrab,onDownload:oe.onDownload,onImport:oe.onImport,onUpgrade:oe.onUpgrade})}catch{}const se=await x,Ce=se?Object.entries(se.instances||{}):[],de=((g=Ce.find(([Re])=>Re.includes("sonarr")))==null?void 0:g[1])||null,j=((C=Ce.find(([Re])=>Re.includes("radarr")))==null?void 0:C[1])||null;v({enabled:I,triggers:W,stats:de}),c({enabled:Q,triggers:te,stats:j})}catch(x){console.error("Failed to fetch webhook status:",x)}},ve=async()=>{_(!0);try{await Je.post("/api/sonarr/notifications/sofarr-webhook"),await _e()}catch(g){console.error("Failed to enable Sonarr webhook:",g),alert("Failed to enable Sonarr webhook. Check console for details.")}finally{_(!1)}},Bt=async()=>{_(!0);try{await Je.post("/api/radarr/notifications/sofarr-webhook"),await _e()}catch(g){console.error("Failed to enable Radarr webhook:",g),alert("Failed to enable Radarr webhook. Check console for details.")}finally{_(!1)}},Ge=async()=>{_(!0);try{const C=(await Je.get("/api/sonarr/notifications")).data.find(x=>x.name==="Sofarr");C?(await Je.post("/api/sonarr/notifications/test",{id:C.id}),await _e(),alert("Sonarr webhook test sent successfully!")):alert("Sofarr webhook not configured for Sonarr.")}catch(g){console.error("Failed to test Sonarr webhook:",g),alert("Failed to test Sonarr webhook. Check console for details.")}finally{_(!1)}},$t=async()=>{_(!0);try{const C=(await Je.get("/api/radarr/notifications")).data.find(x=>x.name==="Sofarr");C?(await Je.post("/api/radarr/notifications/test",{id:C.id}),await _e(),alert("Radarr webhook test sent successfully!")):alert("Sofarr webhook not configured for Radarr.")}catch(g){console.error("Failed to test Radarr webhook:",g),alert("Failed to test Radarr webhook. Check console for details.")}finally{_(!1)}};return A("div",{className:"app",children:[A("header",{className:"app-header",children:[R("h1",{children:"Media Download Dashboard"}),n&&A("div",{className:"user-info",children:[R("span",{className:"user-label",children:"Current User:"}),R("span",{className:"user-name",children:n})]})]}),A("div",{className:"controls",children:[R("label",{htmlFor:"session-select",children:"Select Emby Session:"}),A("select",{id:"session-select",value:e,onChange:L,className:"session-select",children:[R("option",{value:"",children:"-- Select Session --"}),d.map(g=>A("option",{value:g.Id,children:[g.UserName," - ",g.Client," ",g.NowPlayingItem?`(Playing: ${g.NowPlayingItem.Name})`:"(Idle)"]},g.Id))]}),R("button",{onClick:O,className:"refresh-btn",children:"Refresh Sessions"})]}),u&&R("div",{className:"error-message",children:u}),i&&R("div",{className:"loading",children:"Loading downloads..."}),!i&&!u&&A("div",{className:"downloads-container",children:[R("h2",{children:"Your Downloads"}),o.length===0?A("div",{className:"no-downloads",children:[R("p",{children:"No downloads found for your user."}),R("p",{children:'Make sure your shows and movies are tagged with "user:yourusername" in Sonarr/Radarr.'})]}):R("div",{className:"downloads-list",children:o.map((g,C)=>A("div",{className:`download-card ${g.type}`,children:[g.coverArt&&R("div",{className:"download-cover",children:R("img",{src:g.coverArt,alt:g.movieName||g.seriesName||g.title})}),A("div",{className:"download-info",children:[A("div",{className:"download-header",children:[R("span",{className:`download-type ${g.type}`,children:g.type==="series"?"📺 Series":"🎬 Movie"}),R("span",{className:`download-status ${g.status}`,children:g.status})]}),R("h3",{className:"download-title",children:g.title}),g.seriesName&&A("p",{className:"download-series",children:["Series: ",g.seriesName]}),g.movieName&&A("p",{className:"download-movie",children:["Movie: ",g.movieName]}),A("div",{className:"download-details",children:[A("div",{className:"detail-item",children:[R("span",{className:"detail-label",children:"Size:"}),R("span",{className:"detail-value",children:B(g.size)})]}),g.progress&&A("div",{className:"detail-item",children:[R("span",{className:"detail-label",children:"Progress:"}),A("span",{className:"detail-value",children:[g.progress,"%"]})]}),g.speed&&A("div",{className:"detail-item",children:[R("span",{className:"detail-label",children:"Speed:"}),R("span",{className:"detail-value",children:g.speed})]}),g.eta&&A("div",{className:"detail-item",children:[R("span",{className:"detail-label",children:"ETA:"}),R("span",{className:"detail-value",children:g.eta})]}),g.completedAt&&A("div",{className:"detail-item",children:[R("span",{className:"detail-label",children:"Completed:"}),R("span",{className:"detail-value",children:z(g.completedAt)})]})]})]})]},C))})]}),A("div",{className:"webhooks-section",children:[A("div",{className:"webhooks-header",onClick:()=>E(!y),children:[R("h2",{children:"⚡ Webhooks Configuration"}),R("span",{className:`webhooks-toggle ${y?"expanded":""}`,children:"▼"})]}),y&&A("div",{className:"webhooks-content",children:[w&&R("div",{className:"loading",children:"Loading webhook status..."}),A("div",{className:"webhook-instance",children:[R("h3",{children:"Sonarr"}),A("div",{className:"webhook-status",children:[R("span",{className:`status-indicator ${S.enabled?"enabled":"disabled"}`,children:S.enabled?"● Enabled":"○ Disabled"}),!S.enabled&&R("button",{onClick:ve,className:"enable-webhook-btn",disabled:w,children:"Enable Sofarr Webhooks"}),S.enabled&&R("button",{onClick:Ge,className:"test-webhook-btn",disabled:w,children:"Test"})]}),S.enabled&&A("div",{className:"webhook-triggers",children:[A("div",{className:"trigger-item",children:[R("span",{className:"trigger-label",children:"On Grab"}),R("span",{className:`trigger-value ${S.triggers.onGrab?"active":"inactive"}`,children:S.triggers.onGrab?"✓":"✗"})]}),A("div",{className:"trigger-item",children:[R("span",{className:"trigger-label",children:"On Download"}),R("span",{className:`trigger-value ${S.triggers.onDownload?"active":"inactive"}`,children:S.triggers.onDownload?"✓":"✗"})]}),A("div",{className:"trigger-item",children:[R("span",{className:"trigger-label",children:"On Import"}),R("span",{className:`trigger-value ${S.triggers.onImport?"active":"inactive"}`,children:S.triggers.onImport?"✓":"✗"})]}),A("div",{className:"trigger-item",children:[R("span",{className:"trigger-label",children:"On Upgrade"}),R("span",{className:`trigger-value ${S.triggers.onUpgrade?"active":"inactive"}`,children:S.triggers.onUpgrade?"✓":"✗"})]})]}),S.stats&&A("div",{className:"webhook-stats",children:[R("div",{className:"webhook-stats-title",children:"Statistics"}),A("div",{className:"webhook-stats-grid",children:[A("div",{className:"webhook-stat",children:[R("span",{className:"webhook-stat-label",children:"Events Received"}),R("span",{className:"webhook-stat-value",children:S.stats.eventsReceived??0})]}),A("div",{className:"webhook-stat",children:[R("span",{className:"webhook-stat-label",children:"Polls Skipped"}),R("span",{className:"webhook-stat-value",children:S.stats.pollsSkipped??0})]}),A("div",{className:"webhook-stat",children:[R("span",{className:"webhook-stat-label",children:"Last Event"}),R("span",{className:"webhook-stat-value",children:J(S.stats.lastWebhookTimestamp)})]})]})]})]}),A("div",{className:"webhook-instance",children:[R("h3",{children:"Radarr"}),A("div",{className:"webhook-status",children:[R("span",{className:`status-indicator ${k.enabled?"enabled":"disabled"}`,children:k.enabled?"● Enabled":"○ Disabled"}),!k.enabled&&R("button",{onClick:Bt,className:"enable-webhook-btn",disabled:w,children:"Enable Sofarr Webhooks"}),k.enabled&&R("button",{onClick:$t,className:"test-webhook-btn",disabled:w,children:"Test"})]}),k.enabled&&A("div",{className:"webhook-triggers",children:[A("div",{className:"trigger-item",children:[R("span",{className:"trigger-label",children:"On Grab"}),R("span",{className:`trigger-value ${k.triggers.onGrab?"active":"inactive"}`,children:k.triggers.onGrab?"✓":"✗"})]}),A("div",{className:"trigger-item",children:[R("span",{className:"trigger-label",children:"On Download"}),R("span",{className:`trigger-value ${k.triggers.onDownload?"active":"inactive"}`,children:k.triggers.onDownload?"✓":"✗"})]}),A("div",{className:"trigger-item",children:[R("span",{className:"trigger-label",children:"On Import"}),R("span",{className:`trigger-value ${k.triggers.onImport?"active":"inactive"}`,children:k.triggers.onImport?"✓":"✗"})]}),A("div",{className:"trigger-item",children:[R("span",{className:"trigger-label",children:"On Upgrade"}),R("span",{className:`trigger-value ${k.triggers.onUpgrade?"active":"inactive"}`,children:k.triggers.onUpgrade?"✓":"✗"})]})]}),k.stats&&A("div",{className:"webhook-stats",children:[R("div",{className:"webhook-stats-title",children:"Statistics"}),A("div",{className:"webhook-stats-grid",children:[A("div",{className:"webhook-stat",children:[R("span",{className:"webhook-stat-label",children:"Events Received"}),R("span",{className:"webhook-stat-value",children:k.stats.eventsReceived??0})]}),A("div",{className:"webhook-stat",children:[R("span",{className:"webhook-stat-label",children:"Polls Skipped"}),R("span",{className:"webhook-stat-value",children:k.stats.pollsSkipped??0})]}),A("div",{className:"webhook-stat",children:[R("span",{className:"webhook-stat-label",children:"Last Event"}),R("span",{className:"webhook-stat-value",children:J(k.stats.lastWebhookTimestamp)})]})]})]})]})]})]}),R("footer",{className:"app-footer",children:R("p",{children:'Ensure your media is tagged with "user:username" in Sonarr/Radarr to match downloads to users.'})})]})}Ml.createRoot(document.getElementById("root")).render(R(id.StrictMode,{children:R(fy,{})})); diff --git a/public/index.html b/public/index.html index 68f1457..02a2df5 100644 --- a/public/index.html +++ b/public/index.html @@ -1,126 +1,14 @@ - - - - sofarr - Your Downloads Dashboard - - - - + + + + Media Download Dashboard + - - - -
- -
- -
- - - - - -
- - - + + +
+ + diff --git a/public/style.css b/public/style.css index 622fe4f..8aacd85 100644 --- a/public/style.css +++ b/public/style.css @@ -1,1518 +1 @@ -/* ===== Splash Screen ===== */ -.splash-screen { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - background: #f8f9fa; - z-index: 9999; - opacity: 1; - transition: opacity 0.4s ease-out; -} - -.splash-screen.fade-out { - opacity: 0; -} - -.splash-logo { - max-width: 280px; - width: 60%; - animation: splashPulse 1.8s ease-in-out infinite; -} - -@keyframes splashPulse { - 0%, 100% { transform: scale(1); opacity: 1; } - 50% { transform: scale(1.03); opacity: 0.85; } -} - -/* ===== Theme Variables ===== */ -:root, [data-theme="light"] { - /* Page background — clean off-white matching logo backdrop */ - --bg-gradient-start: #e8eef3; - --bg-gradient-end: #d4dee8; - - /* Surfaces */ - --surface: #ffffff; - --surface-alt: #f0f4f7; - - /* Typography — charcoal from logo, all meet WCAG AA on white */ - --text-primary: #2b2f33; /* ~14:1 on white */ - --text-secondary: #4d5760; /* ~7.5:1 on white */ - --text-muted: #6b7784; /* ~4.6:1 on white — AA compliant */ - - /* Borders */ - --border: #c8d3db; - - /* Accent — primary teal from couch outline */ - --accent: #1f7d94; /* ~4.6:1 on white — AA compliant */ - --accent-hover: #165f70; /* darker for hover */ - --accent-light: #e0f0f4; /* very light teal tint for backgrounds */ - - /* Series — steel blue from sofa body */ - --series-color: #1e6b8a; /* ~5.0:1 on white — AA */ - --series-bg: #dceef5; - - /* Movie — warm coral (complementary to teal, accessible) */ - --movie-color: #b5451b; /* ~5.5:1 on white — AA */ - --movie-bg: #fdeee8; - - /* Torrent — mid teal-green */ - --torrent-color: #1a7a6e; /* ~4.7:1 on white — AA */ - --torrent-bg: #ddf2ef; - - /* State colours */ - --success: #2e7d32; /* ~7.1:1 on white — AA */ - --success-bg: #e8f5e9; - --info: #1565c0; /* ~7.3:1 on white — AA */ - --info-bg: #e3f0fb; - --danger: #c62828; /* ~6.5:1 on white — AA */ - --danger-bg: #fdecea; - --danger-border: #f5c6c2; - - /* Progress bar */ - --progress-bg: #eaf2f5; - --progress-border: #c8d3db; - --progress-fill-start: #1f7d94; - --progress-fill-end: #2da0bc; - - /* Shadows */ - --shadow: rgba(30, 60, 80, 0.10); - --shadow-strong: rgba(30, 60, 80, 0.18); - - /* Footer — dark text on light page background */ - --footer-text: #4d5760; - - /* Inputs */ - --input-bg: #ffffff; - --select-bg: #ffffff; - - /* Unmatched tag — amber, accessible on its bg */ - --unmatched-tag-bg: #fff3e0; - --unmatched-tag-color: #7a4000; /* ~7.1:1 on #fff3e0 — AA */ -} - -[data-theme="dark"] { - --bg-gradient-start: #1a1a2e; - --bg-gradient-end: #16213e; - --surface: #1e1e2f; - --surface-alt: #2a2a3d; - --text-primary: #e0e0e0; - --text-secondary: #a0a0b0; - --text-muted: #707080; - --border: #3a3a4d; - --accent: #7c8cf0; - --accent-hover: #6b7ce0; - --accent-light: #2a2a4d; - --series-color: #7c8cf0; - --series-bg: #2a2a4d; - --movie-color: #f080c0; - --movie-bg: #3d2035; - --torrent-color: #4dd0c8; - --torrent-bg: #1a3d3a; - --success: #66bb6a; - --success-bg: #1a3d1e; - --info: #64b5f6; - --info-bg: #1a2d3d; - --danger: #ef5350; - --danger-bg: #3d1a1a; - --danger-border: #5a2a2a; - --progress-bg: #2a2020; - --progress-border: #4a3030; - --progress-fill-start: #66bb6a; - --progress-fill-end: #81c784; - --shadow: rgba(0, 0, 0, 0.3); - --shadow-strong: rgba(0, 0, 0, 0.4); - --footer-text: rgba(200, 200, 220, 0.8); - --input-bg: #2a2a3d; - --select-bg: #2a2a3d; - --unmatched-tag-bg: #3d2a00; - --unmatched-tag-color: #ffb74d; -} - -[data-theme="mono"] { - --bg-gradient-start: #222222; - --bg-gradient-end: #333333; - --surface: #1a1a1a; - --surface-alt: #252525; - --text-primary: #d0d0d0; - --text-secondary: #909090; - --text-muted: #606060; - --border: #404040; - --accent: #b0b0b0; - --accent-hover: #c8c8c8; - --accent-light: #303030; - --series-color: #c0c0c0; - --series-bg: #303030; - --movie-color: #c0c0c0; - --movie-bg: #303030; - --torrent-color: #c0c0c0; - --torrent-bg: #303030; - --success: #a0a0a0; - --success-bg: #2a2a2a; - --info: #a0a0a0; - --info-bg: #2a2a2a; - --danger: #909090; - --danger-bg: #2a2a2a; - --danger-border: #484848; - --progress-bg: #2a2a2a; - --progress-border: #404040; - --progress-fill-start: #808080; - --progress-fill-end: #a0a0a0; - --shadow: rgba(0, 0, 0, 0.4); - --shadow-strong: rgba(0, 0, 0, 0.5); - --footer-text: rgba(180, 180, 180, 0.7); - --input-bg: #252525; - --select-bg: #252525; - --unmatched-tag-bg: #2a2a2a; - --unmatched-tag-color: #a0a0a0; -} - -/* ===== Base ===== */ -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - background: linear-gradient(135deg, var(--bg-gradient-start) 0%, var(--bg-gradient-end) 100%); - min-height: 100vh; - transition: background 0.3s; -} - -.app { - min-height: 100vh; - padding: 16px; - max-width: 1200px; - margin: 0 auto; -} - -/* ===== Header ===== */ -.app-header { - background: var(--surface); - padding: 14px 20px; - border-radius: 8px; - box-shadow: 0 2px 4px var(--shadow); - margin-bottom: 12px; - display: flex; - justify-content: space-between; - align-items: center; - flex-wrap: wrap; - gap: 12px; - transition: background 0.3s; -} - -.app-header h1 { - color: var(--text-primary); - font-size: 1.4rem; -} - -.header-controls { - display: flex; - align-items: center; - gap: 14px; -} - -.user-info { - display: flex; - align-items: center; - gap: 8px; - background: var(--surface-alt); - padding: 6px 14px; - border-radius: 16px; - font-size: 0.85rem; -} - -.user-label { - color: var(--text-muted); - font-weight: 500; -} - -.user-name { - color: var(--accent); - font-weight: bold; - font-size: 0.9rem; -} - -.logout-btn { - padding: 4px 12px; - background: var(--danger); - color: white; - border: none; - border-radius: 4px; - cursor: pointer; - font-size: 0.8rem; - transition: opacity 0.2s; -} - -.logout-btn:hover { - opacity: 0.85; -} - -/* ===== Theme Switcher ===== */ -.theme-switcher { - display: flex; - align-items: center; - gap: 4px; - background: var(--surface-alt); - padding: 3px; - border-radius: 16px; -} - -.theme-btn { - padding: 4px 10px; - border: none; - border-radius: 14px; - cursor: pointer; - font-size: 0.75rem; - background: transparent; - color: var(--text-muted); - transition: all 0.2s; -} - -.theme-btn.active { - background: var(--accent); - color: white; -} - -.theme-btn:hover:not(.active) { - color: var(--text-primary); -} - -/* ===== Refresh Control ===== */ -.refresh-control { - display: flex; - align-items: center; - gap: 6px; -} - -.refresh-control label { - color: var(--text-muted); - font-size: 0.8rem; -} - -.refresh-control select { - padding: 4px 8px; - border: 1px solid var(--border); - border-radius: 4px; - background: var(--select-bg); - color: var(--text-primary); - cursor: pointer; - font-size: 0.8rem; -} - -/* ===== Error / Loading ===== */ -.error-message { - background: var(--danger-bg); - color: var(--danger); - padding: 10px 14px; - border-radius: 6px; - margin-bottom: 12px; - border-left: 3px solid var(--danger); - font-size: 0.9rem; -} - -.loading { - text-align: center; - padding: 30px; - color: var(--footer-text); - font-size: 1rem; -} - -/* ===== Downloads Container ===== */ -.downloads-container { - background: var(--surface); - padding: 16px 20px; - border-radius: 8px; - box-shadow: 0 2px 4px var(--shadow); - transition: background 0.3s; -} - -.downloads-container h2 { - color: var(--text-primary); - margin-bottom: 12px; - font-size: 1.1rem; -} - -.no-downloads { - text-align: center; - padding: 30px; - color: var(--text-secondary); - font-size: 0.9rem; -} - -.no-downloads p { - margin: 6px 0; -} - -.downloads-list { - display: flex; - flex-direction: column; - gap: 8px; -} - -/* ===== Download Card (Compact) ===== */ -.download-card { - border: 1px solid var(--border); - border-radius: 6px; - padding: 10px 14px; - display: flex; - gap: 12px; - align-items: flex-start; - transition: box-shadow 0.2s, background 0.3s; - background: var(--surface); -} - -.download-card:hover { - box-shadow: 0 2px 8px var(--shadow); -} - -.download-card.series { - border-left: 3px solid var(--series-color); -} - -.download-card.movie { - border-left: 3px solid var(--movie-color); -} - -.download-card.torrent { - border-left: 3px solid var(--torrent-color); -} - -/* ===== Cover Art ===== */ -.download-cover { - flex-shrink: 0; - width: 50px; - border-radius: 4px; - overflow: hidden; - box-shadow: 0 1px 4px var(--shadow-strong); -} - -.download-cover img { - width: 100%; - height: auto; - display: block; -} - -/* ===== Download Info ===== */ -.download-info { - flex: 1; - min-width: 0; -} - -.download-header { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 4px; - margin-bottom: 4px; -} - -.download-type { - padding: 2px 8px; - border-radius: 10px; - font-size: 0.7rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.3px; -} - -.download-type.series { - background: var(--series-bg); - color: var(--series-color); -} - -.download-type.movie { - background: var(--movie-bg); - color: var(--movie-color); -} - -.download-type.torrent { - background: var(--torrent-bg); - color: var(--torrent-color); -} - -.download-status { - padding: 2px 8px; - border-radius: 10px; - font-size: 0.7rem; - font-weight: 600; - text-transform: capitalize; -} - -.download-status.downloading, -.download-status.Downloading { - background: var(--success-bg); - color: var(--success); -} - -.download-status.completed, -.download-status.Completed { - background: var(--info-bg); - color: var(--info); -} - -.download-status.failed, -.download-status.Failed { - background: var(--danger-bg); - color: var(--danger); -} - -.download-title { - color: var(--text-primary); - margin-bottom: 2px; - font-size: 0.9rem; - font-weight: 600; - overflow-wrap: break-word; - word-break: break-word; - overflow: hidden; -} - -.download-series, -.download-movie { - color: var(--text-secondary); - margin-bottom: 6px; - font-style: italic; - font-size: 0.8rem; -} - -.episode-info { - color: var(--text-secondary); - font-size: 0.78rem; - margin: -2px 0 6px; -} - -.episode-info.multi-episode { - cursor: help; - text-decoration: underline dotted; - position: relative; -} - -.episode-info.multi-episode:hover::after { - content: attr(data-tooltip); - position: absolute; - left: 0; - top: 100%; - z-index: 20; - background: var(--surface); - color: var(--text-primary); - border: 1px solid var(--border); - border-radius: 6px; - padding: 8px 10px; - font-size: 0.75rem; - white-space: pre-line; - box-shadow: 0 4px 12px rgba(0,0,0,0.15); - max-width: 320px; - pointer-events: none; -} - -/* ===== Detail Row (Inline) ===== */ -.download-details { - display: flex; - flex-wrap: wrap; - gap: 4px 6px; - padding-top: 6px; - border-top: 1px solid var(--border); - align-items: center; -} - -.detail-item { - display: inline-flex; - align-items: baseline; - gap: 3px; - font-size: 0.78rem; - background: var(--bg-secondary, rgba(0,0,0,0.04)); - border-radius: 4px; - padding: 2px 6px; - white-space: nowrap; -} - -.detail-item.availability-warning .detail-value { - color: var(--danger, #e53e3e); - font-weight: 700; -} - -.detail-label { - color: var(--text-muted); - text-transform: uppercase; - letter-spacing: 0.3px; - font-size: 0.65rem; - font-weight: 600; -} - -.detail-value { - color: var(--text-primary); - font-weight: 500; -} - -/* ===== Progress Bar (Compact) ===== */ -.progress-item { - flex-basis: 100%; - display: flex; - align-items: center; - background: none; - padding: 0; - white-space: normal; - border-radius: 0; -} - -.progress-container { - display: flex; - flex: 1; - align-items: center; - gap: 8px; -} - -.progress-bar { - flex: 1; - height: 10px; - background: var(--progress-bg); - border-radius: 5px; - overflow: hidden; - position: relative; - border: 1px solid var(--progress-border); -} - -.progress-segment { - position: absolute; - top: 0; - left: 0; - height: 100%; - transition: width 0.3s ease; -} - -.progress-segment.downloaded { - background: linear-gradient(90deg, var(--progress-fill-start) 0%, var(--progress-fill-end) 100%); -} - -.progress-text { - font-weight: 600; - color: var(--text-primary); - font-size: 0.78rem; - white-space: nowrap; -} - -.missing-text { - color: var(--danger); - font-size: 0.72rem; - font-weight: 500; - overflow-wrap: break-word; - word-break: break-word; -} - -/* ===== Main Tabs ===== */ -.main-tabs { - max-width: 1200px; - margin: 16px auto 0; - padding: 0 16px; -} - -.tab-bar { - display: flex; - gap: 4px; - border-bottom: 2px solid var(--border); - margin-bottom: 0; -} - -.tab-btn { - background: none; - border: none; - border-bottom: 3px solid transparent; - margin-bottom: -2px; - padding: 10px 20px; - font-size: 0.95rem; - font-weight: 600; - color: var(--text-secondary); - cursor: pointer; - transition: color 0.2s, border-color 0.2s; - border-radius: 4px 4px 0 0; -} - -.tab-btn:hover { - color: var(--text-primary); - background: var(--surface); -} - -.tab-btn.active { - color: var(--accent); - border-bottom-color: var(--accent); - background: var(--surface); -} - -.tab-panel { - padding-top: 0; -} - -/* ===== Recently Completed History ===== */ -.history-container { - max-width: unset; - margin: 0; - padding: 0; -} - -.history-header { - display: flex; - align-items: center; - gap: 12px; - margin-bottom: 12px; - flex-wrap: wrap; -} - -.history-header h2 { - margin: 0; - font-size: 1.2rem; - color: var(--text-primary); - flex: 1 1 auto; -} - -.history-controls { - display: flex; - align-items: center; - gap: 6px; - flex-wrap: nowrap; -} - -.history-days-label { - font-size: 0.85rem; - color: var(--text-secondary); -} - -.history-days-input { - width: 52px; - padding: 3px 6px; - border: 1px solid var(--border); - border-radius: 4px; - background: var(--surface); - color: var(--text-primary); - font-size: 0.85rem; - text-align: center; -} - -.history-refresh-btn { - background: none; - border: 1px solid var(--border); - border-radius: 4px; - color: var(--text-secondary); - cursor: pointer; - font-size: 1rem; - padding: 2px 7px; - line-height: 1.4; - transition: background 0.15s, color 0.15s; -} - -.history-refresh-btn:hover { - background: var(--hover-bg); - color: var(--text-primary); -} - -.history-toggle-label { - display: inline-flex; - align-items: center; - gap: 5px; - font-size: 0.82rem; - color: var(--text-secondary); - cursor: pointer; - user-select: none; - margin-left: 4px; - position: relative; -} - -.history-toggle-label[data-tooltip]:hover::after { - content: attr(data-tooltip); - position: absolute; - left: 0; - top: calc(100% + 6px); - z-index: 20; - background: var(--surface); - color: var(--text-primary); - border: 1px solid var(--border); - border-radius: 6px; - padding: 8px 10px; - font-size: 0.75rem; - white-space: pre-line; - box-shadow: 0 4px 12px rgba(0,0,0,0.15); - max-width: 280px; - pointer-events: none; -} - -.history-toggle-label input[type="checkbox"] { - cursor: pointer; - accent-color: var(--accent, #2980b9); -} - -.history-loading, -.history-error, -.no-history { - color: var(--text-secondary); - font-size: 0.9rem; - padding: 8px 0; -} - -.history-error { - color: var(--error, #e74c3c); -} - -.history-list { - display: flex; - flex-direction: column; - gap: 8px; -} - -.history-card { - display: flex; - gap: 12px; - background: var(--surface); - border: 1px solid var(--border); - border-radius: 8px; - padding: 10px 12px; - transition: background 0.2s; - align-items: flex-start; -} - -.history-card.failed { - border-left: 3px solid var(--error, #e74c3c); -} - -.history-card.imported { - border-left: 3px solid var(--success, #27ae60); -} - -.history-cover { - flex: 0 0 48px; - width: 48px; -} - -.history-cover img { - width: 48px; - height: 68px; - object-fit: cover; - border-radius: 4px; - display: block; -} - -.history-info { - flex: 1 1 auto; - min-width: 0; -} - -.history-card-header { - display: flex; - flex-wrap: wrap; - gap: 5px; - align-items: center; - margin-bottom: 4px; -} - -.history-type-badge, -.history-outcome-badge, -.history-instance-badge, -.history-upgrade-badge { - font-size: 0.72rem; - font-weight: 600; - padding: 2px 7px; - border-radius: 10px; - white-space: nowrap; -} - -.history-upgrade-badge { - background: #e67e22; - color: #fff; - cursor: default; -} - -.history-type-badge.series { - background: var(--badge-series-bg, #2980b9); - color: #fff; -} - -.history-type-badge.movie { - background: var(--badge-movie-bg, #8e44ad); - color: #fff; -} - -.history-outcome-badge.imported { - background: var(--success, #27ae60); - color: #fff; -} - -.history-outcome-badge.failed { - background: var(--error, #e74c3c); - color: #fff; -} - -.history-instance-badge { - background: var(--tag-bg, #ecf0f1); - color: var(--text-secondary); - border: 1px solid var(--border); -} - -.history-title { - font-size: 0.9rem; - font-weight: 600; - margin: 0 0 2px; - color: var(--text-primary); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.history-media-name { - font-size: 0.82rem; - color: var(--text-secondary); - margin: 0 0 4px; -} - -.history-details { - display: flex; - flex-wrap: wrap; - gap: 5px; - margin-top: 4px; -} - -.history-failure-message { - font-size: 0.78rem; - color: var(--error, #e74c3c); - background: var(--error-bg, rgba(231, 76, 60, 0.08)); - border-radius: 4px; - padding: 3px 7px; - flex-basis: 100%; -} - -@media (max-width: 480px) { - .history-cover { - display: none; - } - .history-title { - white-space: normal; - } -} - -/* ===== Footer ===== */ -.app-footer { - margin-top: 12px; - text-align: center; - color: var(--footer-text); - font-size: 0.8rem; -} - -.app-footer p { - opacity: 0.8; -} - -.app-version { - font-size: 0.72rem; - opacity: 0.5; - margin-top: 4px; - color: inherit; - text-decoration: none; - display: inline-block; -} - -.app-version:hover { - opacity: 0.8; - text-decoration: underline; - text-underline-offset: 2px; -} - -.title-link { - color: inherit; - text-decoration: none; - display: inline-flex; - align-items: center; - gap: 8px; -} - -.title-link:hover { - text-decoration: underline; - text-underline-offset: 3px; -} - -.title-logo { - width: 56px; - height: 56px; - display: block; - flex-shrink: 0; -} - -/* ===== Login ===== */ -.login-container { - display: flex; - justify-content: center; - align-items: center; - min-height: 100vh; - padding: 20px; - opacity: 1; - transition: opacity 0.3s ease-out; -} - -.login-container.fade-out { - opacity: 0; -} - -.login-box { - background: var(--surface); - padding: 36px; - border-radius: 10px; - box-shadow: 0 4px 6px var(--shadow); - width: 100%; - max-width: 380px; - transition: background 0.3s; - text-align: center; -} - -.login-logo { - max-width: 180px; - width: 60%; - margin-bottom: 12px; -} - -.login-subtitle { - color: var(--text-secondary); - margin-bottom: 24px; - font-size: 0.85rem; -} - -.form-group { - margin-bottom: 16px; - text-align: left; -} - -.form-group label { - display: block; - color: var(--text-primary); - font-weight: 500; - margin-bottom: 6px; - font-size: 0.9rem; -} - -.form-group input { - width: 100%; - padding: 10px; - border: 1px solid var(--border); - border-radius: 4px; - font-size: 0.95rem; - background: var(--input-bg); - color: var(--text-primary); -} - -.form-group input:focus { - outline: none; - border-color: var(--accent); -} - -.form-group--checkbox { - margin-bottom: 20px; -} - -.checkbox-label { - display: flex; - align-items: center; - gap: 8px; - cursor: pointer; - font-size: 0.9rem; - color: var(--text-secondary); - user-select: none; -} - -.checkbox-label input[type="checkbox"] { - width: 16px; - height: 16px; - accent-color: var(--accent); - cursor: pointer; - flex-shrink: 0; -} - -.checkbox-label span { - line-height: 1; -} - -.login-btn { - width: 100%; - padding: 10px; - background: var(--accent); - color: white; - border: none; - border-radius: 5px; - cursor: pointer; - font-size: 0.95rem; - font-weight: 500; - transition: background 0.2s; -} - -.login-btn:hover { - background: var(--accent-hover); -} - -/* ===== Admin Controls ===== */ -.admin-controls { - display: flex; - align-items: center; -} - -.toggle-label { - display: flex; - align-items: center; - gap: 6px; - cursor: pointer; - font-size: 0.8rem; - color: var(--text-secondary); - background: var(--surface-alt); - padding: 4px 12px; - border-radius: 14px; -} - -.toggle-label input[type="checkbox"] { - width: 14px; - height: 14px; - cursor: pointer; - accent-color: var(--accent); -} - -/* ===== Arr Links (Admin) ===== */ -.arr-link { - color: var(--accent); - text-decoration: none; - border-bottom: 1px dotted var(--accent); -} - -.arr-link:hover { - opacity: 0.8; - border-bottom-style: solid; -} - -/* ===== Download Paths (Admin) ===== */ -.download-paths { - flex-basis: 100%; - display: flex; - flex-direction: column; - gap: 2px; - margin-top: 2px; -} - -.path-item { - font-size: 0.7rem; - font-family: 'SF Mono', 'Fira Code', 'Fira Mono', Menlo, Consolas, monospace; - color: var(--text-muted); - overflow-wrap: break-word; - word-break: break-all; - overflow: hidden; -} - -.path-label { - font-weight: 600; - color: var(--text-secondary); - font-size: 0.65rem; - text-transform: uppercase; - letter-spacing: 0.3px; -} - -.path-value { - color: var(--text-muted); -} - -/* ===== Import Issue Badge ===== */ -.import-issue-badge { - padding: 2px 8px; - border-radius: 10px; - font-size: 0.65rem; - font-weight: 600; - background: #ffebee; - color: #c62828; - cursor: help; - position: relative; - white-space: nowrap; -} - -.import-issue-badge:hover::after { - content: attr(data-tooltip); - position: absolute; - top: calc(100% + 6px); - left: 0; - z-index: 20; - background: var(--surface); - color: var(--text-primary); - border: 1px solid var(--border); - border-radius: 6px; - padding: 8px 10px; - font-size: 0.75rem; - font-weight: 400; - white-space: pre-line; - max-width: 320px; - box-shadow: 0 4px 12px rgba(0,0,0,0.15); - line-height: 1.4; - pointer-events: none; -} - -.blocklist-search-btn { - font-size: 0.68rem; - font-weight: 600; - padding: 2px 8px; - border-radius: 10px; - border: 1px solid var(--error, #e74c3c); - background: transparent; - color: var(--error, #e74c3c); - cursor: pointer; - white-space: nowrap; - transition: background 0.15s, color 0.15s; -} - -.blocklist-search-btn:hover:not(:disabled) { - background: var(--error, #e74c3c); - color: #fff; -} - -.blocklist-search-btn:disabled { - opacity: 0.6; - cursor: default; -} - -.blocklist-search-btn.success { - border-color: var(--success, #27ae60); - color: var(--success, #27ae60); -} - -.blocklist-search-btn.error { - background: var(--error, #e74c3c); - color: #fff; -} - -.download-user-badge { - padding: 2px 8px; - border-radius: 10px; - font-size: 0.65rem; - font-weight: 600; - text-transform: capitalize; - background: var(--accent-light); - color: var(--accent); - margin-left: auto; - white-space: nowrap; -} - -.download-user-badge.unmatched { - background: var(--unmatched-tag-bg); - color: var(--unmatched-tag-color); - margin-left: 0; -} - -/* ===== Status Button ===== */ -.status-btn { - padding: 4px 12px; - border: 1px solid var(--border); - border-radius: 5px; - background: var(--surface); - color: var(--text-secondary); - cursor: pointer; - font-size: 0.8rem; - font-weight: 500; - transition: background 0.2s, color 0.2s; -} - -.status-btn:hover { - background: var(--accent); - color: white; - border-color: var(--accent); -} - -/* ===== Status Panel ===== */ -.status-panel { - background: var(--surface); - border: 1px solid var(--border); - border-radius: 10px; - padding: 20px; - margin-bottom: 16px; - box-shadow: 0 2px 4px var(--shadow); -} - -.status-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 16px; -} - -.status-header h3 { - margin: 0; - color: var(--text-primary); - font-size: 1.1rem; -} - -.status-close { - background: none; - border: none; - font-size: 1.4rem; - color: var(--text-secondary); - cursor: pointer; - padding: 0 4px; - line-height: 1; -} - -.status-close:hover { - color: var(--text-primary); -} - -.status-grid { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 16px; -} - -.status-card { - background: var(--background); - border: 1px solid var(--border); - border-radius: 8px; - padding: 14px; -} - -.status-card-wide { - grid-column: 1 / -1; -} - -.status-card-title { - font-weight: 600; - font-size: 0.85rem; - color: var(--text-primary); - margin-bottom: 10px; - padding-bottom: 6px; - border-bottom: 1px solid var(--border); -} - -.status-row { - display: flex; - justify-content: space-between; - padding: 3px 0; - font-size: 0.8rem; - color: var(--text-secondary); -} - -.status-row span:last-child { - font-weight: 500; - color: var(--text-primary); -} - -.status-table { - width: 100%; - border-collapse: collapse; - font-size: 0.78rem; -} - -.status-table th { - text-align: left; - padding: 4px 8px; - color: var(--text-secondary); - font-weight: 600; - font-size: 0.7rem; - text-transform: uppercase; - letter-spacing: 0.3px; - border-bottom: 1px solid var(--border); -} - -.status-table td { - padding: 5px 8px; - color: var(--text-primary); - border-bottom: 1px solid var(--border); -} - -.status-table code { - font-size: 0.75rem; - background: var(--surface); - padding: 1px 4px; - border-radius: 3px; -} - -.status-expired { - color: #c62828; - font-weight: 600; - font-size: 0.7rem; -} - -.status-fg-badge { - background: #fff3e0; - color: #e65100; - padding: 1px 8px; - border-radius: 8px; - font-size: 0.75rem; - font-weight: 600; -} - -.status-row-sub { - padding-left: 12px; - font-size: 0.75rem; - opacity: 0.8; -} - -.status-row-sub span:first-child { - font-style: italic; -} - -.status-timings { - display: flex; - flex-direction: column; - gap: 6px; -} - -.timing-row { - display: flex; - align-items: center; - gap: 8px; - font-size: 0.78rem; -} - -.timing-label { - width: 110px; - flex-shrink: 0; - color: var(--text-secondary); - white-space: nowrap; -} - -.timing-bar-bg { - flex: 1; - height: 8px; - background: var(--border); - border-radius: 4px; - overflow: hidden; -} - -.timing-bar { - height: 100%; - background: var(--accent); - border-radius: 4px; - min-width: 2px; - transition: width 0.3s ease; -} - -.timing-value { - width: 50px; - flex-shrink: 0; - text-align: right; - color: var(--text-primary); - font-weight: 500; - font-size: 0.75rem; -} - -.status-loading, .status-error { - text-align: center; - padding: 20px; - color: var(--text-secondary); - font-size: 0.9rem; -} - -.status-error { - color: #c62828; -} - -/* ===== Mobile ===== */ -@media (max-width: 768px) { - .app { - padding: 10px; - } - - .app-header { - flex-direction: column; - align-items: flex-start; - padding: 10px 12px; - gap: 8px; - } - - .header-controls { - width: 100%; - flex-wrap: wrap; - gap: 8px; - } - - .user-info { - width: 100%; - justify-content: space-between; - box-sizing: border-box; - } - - .admin-controls { - width: 100%; - flex-wrap: wrap; - gap: 6px; - } - - .downloads-container { - padding: 12px; - } - - .download-card { - padding: 8px 10px; - } - - .download-cover { - width: 40px; - } - - .progress-container { - flex-wrap: wrap; - } - - .status-grid { - grid-template-columns: 1fr; - } - - .status-table { - font-size: 0.72rem; - } - - .status-table th, - .status-table td { - padding: 4px 4px; - } - - .status-table td code { - word-break: break-all; - } - - .timing-label { - width: 90px; - } - - .timing-value { - width: 40px; - } - - .import-issue-badge:hover::after { - left: auto; - right: 0; - max-width: calc(100vw - 24px); - } -} - -/* ===== Very small screens (≤ 400px) ===== */ -@media (max-width: 400px) { - .app { - padding: 6px; - } - - .download-cover { - display: none; - } - - .theme-switcher { - flex-shrink: 0; - } - - .user-info { - font-size: 0.78rem; - padding: 5px 10px; - } - - .download-card { - padding: 8px; - } - - .timing-label { - width: 75px; - font-size: 0.7rem; - } -} +*{margin:0;padding:0;box-sizing:border-box}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);min-height:100vh}.app{min-height:100vh;padding:20px;max-width:1200px;margin:0 auto}.app-header{background:white;padding:30px;border-radius:10px;box-shadow:0 4px 6px #0000001a;margin-bottom:20px;display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:20px}.app-header h1{color:#333;font-size:2rem}.user-info{display:flex;align-items:center;gap:10px;background:#f0f0f0;padding:10px 20px;border-radius:20px}.user-label{color:#666;font-weight:500}.user-name{color:#667eea;font-weight:700;font-size:1.1rem}.controls{background:white;padding:20px;border-radius:10px;box-shadow:0 4px 6px #0000001a;margin-bottom:20px;display:flex;gap:15px;align-items:center;flex-wrap:wrap}.controls label{color:#333;font-weight:500}.session-select{flex:1;min-width:200px;padding:10px;border:2px solid #e0e0e0;border-radius:5px;font-size:1rem;cursor:pointer}.session-select:focus{outline:none;border-color:#667eea}.refresh-btn{padding:10px 20px;background:#667eea;color:#fff;border:none;border-radius:5px;cursor:pointer;font-size:1rem;transition:background .3s}.refresh-btn:hover{background:#5568d3}.error-message{background:#fee;color:#c33;padding:15px;border-radius:10px;margin-bottom:20px;border-left:4px solid #c33}.loading{text-align:center;padding:40px;color:#fff;font-size:1.2rem}.downloads-container{background:white;padding:30px;border-radius:10px;box-shadow:0 4px 6px #0000001a}.downloads-container h2{color:#333;margin-bottom:20px;font-size:1.5rem}.no-downloads{text-align:center;padding:40px;color:#666}.no-downloads p{margin:10px 0}.downloads-list{display:grid;gap:20px}.download-card{border:2px solid #e0e0e0;border-radius:10px;padding:20px;transition:transform .2s,box-shadow .2s;display:flex;gap:20px;align-items:flex-start}.download-cover{flex-shrink:0;width:80px;border-radius:6px;overflow:hidden;box-shadow:0 2px 8px #00000026}.download-cover img{width:100%;height:auto;display:block}.download-info{flex:1;min-width:0}.download-card:hover{transform:translateY(-2px);box-shadow:0 6px 12px #0000001a}.download-card.series{border-left:4px solid #667eea}.download-card.movie{border-left:4px solid #f093fb}.download-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:15px}.download-type{padding:5px 15px;border-radius:20px;font-size:.9rem;font-weight:500}.download-type.series{background:#e8eaf6;color:#667eea}.download-type.movie{background:#fce4ec;color:#f093fb}.download-status{padding:5px 15px;border-radius:20px;font-size:.9rem;font-weight:500;text-transform:capitalize}.download-status.downloading{background:#e8f5e9;color:#4caf50}.download-status.completed{background:#e3f2fd;color:#2196f3}.download-status.failed{background:#ffebee;color:#f44336}.download-title{color:#333;margin-bottom:10px;font-size:1.2rem}.download-series,.download-movie{color:#666;margin-bottom:15px;font-style:italic}.download-details{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:15px;padding-top:15px;border-top:1px solid #e0e0e0}.detail-item{display:flex;flex-direction:column;gap:5px}.detail-label{color:#999;font-size:.85rem;text-transform:uppercase;letter-spacing:.5px}.detail-value{color:#333;font-weight:500}.app-footer{margin-top:20px;text-align:center;color:#fff;font-size:.9rem}.app-footer p{opacity:.9}@media (max-width: 768px){.app-header{flex-direction:column;align-items:flex-start}.controls{flex-direction:column;align-items:stretch}.download-details{grid-template-columns:1fr}}.webhooks-section{background:white;border-radius:10px;box-shadow:0 4px 6px #0000001a;margin-bottom:20px;overflow:hidden}.webhooks-header{padding:20px 30px;background:#f8f9fa;border-bottom:2px solid #e0e0e0;cursor:pointer;display:flex;justify-content:space-between;align-items:center;transition:background .3s}.webhooks-header:hover{background:#f0f1f2}.webhooks-header h2{color:#333;font-size:1.3rem;margin:0}.webhooks-toggle{font-size:1.2rem;color:#666;transition:transform .3s}.webhooks-toggle.expanded{transform:rotate(180deg)}.webhooks-content{padding:20px 30px}.webhook-instance{padding:20px 0;border-bottom:1px solid #e0e0e0}.webhook-instance:last-child{border-bottom:none}.webhook-instance h3{color:#333;font-size:1.1rem;margin-bottom:15px}.webhook-status{display:flex;align-items:center;gap:15px;margin-bottom:15px}.status-indicator{font-size:1rem;font-weight:500;padding:5px 15px;border-radius:20px}.status-indicator.enabled{background:#e8f5e9;color:#4caf50}.status-indicator.disabled{background:#f5f5f5;color:#999}.enable-webhook-btn{padding:8px 16px;background:#667eea;color:#fff;border:none;border-radius:5px;cursor:pointer;font-size:.95rem;transition:background .3s}.enable-webhook-btn:hover{background:#5568d3}.enable-webhook-btn:disabled{background:#ccc;cursor:not-allowed}.test-webhook-btn{padding:8px 16px;background:#f093fb;color:#fff;border:none;border-radius:5px;cursor:pointer;font-size:.95rem;transition:background .3s}.test-webhook-btn:hover{background:#d97ed8}.test-webhook-btn:disabled{background:#ccc;cursor:not-allowed}.webhook-triggers{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;padding-top:15px;border-top:1px solid #e0e0e0}.trigger-item{display:flex;justify-content:space-between;align-items:center}.trigger-label{color:#666;font-size:.9rem}.trigger-value{font-weight:500;font-size:1.1rem}.trigger-value.active{color:#4caf50}.trigger-value.inactive{color:#999}.webhook-stats{margin-top:15px;padding-top:15px;border-top:1px solid #e0e0e0}.webhook-stats-title{color:#999;font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.6px;margin-bottom:10px}.webhook-stats-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:10px}.webhook-stat{display:flex;flex-direction:column;gap:3px}.webhook-stat-label{color:#999;font-size:.8rem}.webhook-stat-value{color:#333;font-size:.95rem;font-weight:500}