diff --git a/core/automation/api.py b/core/automation/api.py index a6f78afe..a99e08ce 100644 --- a/core/automation/api.py +++ b/core/automation/api.py @@ -225,6 +225,13 @@ def update_automation( if cycle_path: return {'error': f'Signal cycle detected: {cycle_path}. This would cause an infinite loop.'}, 400 + trigger_changed = ( + 'trigger_type' in update_fields + or 'trigger_config' in update_fields + ) + if trigger_changed: + update_fields['next_run'] = None + success = database.update_automation(automation_id, **update_fields) if not success: return {'error': 'Automation not found'}, 404 diff --git a/webui/static/helper.js b/webui/static/helper.js index 3f9568f6..1e304a8b 100644 --- a/webui/static/helper.js +++ b/webui/static/helper.js @@ -3413,6 +3413,11 @@ function closeHelperSearch() { // projects that span multiple commits before shipping. Strip the flag at // release time and add a real `date:` line at the top of the version block. const WHATS_NEW = { + '2.6.2': [ + { date: 'May 24, 2026 — 2.6.2 release' }, + { title: 'Fix: Auto-Sync "next in 8h" timezone bug', desc: 'scheduled Auto-Sync playlists were all showing "next in 8h" regardless of the interval — Every 1 hour, Every 1 day, anything. backend stores next-run as a naive UTC string and the frontend was parsing it as local time, which on Pacific time offset the displayed countdown by ~8 hours. Auto-Sync now uses the existing UTC-aware parser the rest of the Automations page already uses. as a separate correctness fix, the automation update endpoint also now blanks the stored next-run whenever the trigger type or trigger config changes, so the engine recomputes from scratch instead of preserving a leftover timestamp from the previous schedule.' }, + { title: 'Auto-Sync modal restyled to your accent color', desc: 'the Playlist Auto-Sync manager now picks up your chosen accent (the same one used everywhere else in the app) instead of the hardcoded sky-blue palette it shipped with. tabs, drop zones, scheduled-playlist chips, scrollbars, and the modal glow all follow the accent theme. drop targets also light up clearly while dragging.' }, + ], '2.6.1': [ { date: 'May 24, 2026 — 2.6.1 release' }, { title: 'React Import page polish', desc: 'Import now runs through the React route stack with album, singles, and auto-import tabs plus the state fixes needed for reliable Vite builds.' }, diff --git a/webui/static/stats-automations.js b/webui/static/stats-automations.js index 25bc01ce..fa22d51b 100644 --- a/webui/static/stats-automations.js +++ b/webui/static/stats-automations.js @@ -818,7 +818,7 @@ function renderAutoSyncSchedulePanel(playlists, playlistSchedules) { const bucketHtml = AUTO_SYNC_BUCKETS.map(hours => { const assigned = schedulablePlaylists.filter(p => playlistSchedules[p.id]?.hours === hours); return ` -
+
${autoSyncBucketLabel(hours)} ${assigned.length} playlist${assigned.length === 1 ? '' : 's'} @@ -911,7 +911,7 @@ function autoSyncScheduledCardHtml(playlist, schedule) { function autoSyncNextRunLabel(nextRun) { if (!nextRun) return ''; - const ts = new Date(nextRun).getTime(); + const ts = _autoParseUTC(nextRun); if (!Number.isFinite(ts)) return ''; const diff = ts - Date.now(); if (diff <= 0) return 'due now'; @@ -932,10 +932,23 @@ function autoSyncDragStart(event) { function autoSyncDragOver(event) { event.preventDefault(); event.dataTransfer.dropEffect = 'move'; + const col = event.currentTarget; + if (col && !col.classList.contains('drag-over')) { + col.classList.add('drag-over'); + } +} + +function autoSyncDragLeave(event) { + const col = event.currentTarget; + if (!col) return; + if (col.contains(event.relatedTarget)) return; + col.classList.remove('drag-over'); } async function autoSyncDrop(event, hours) { event.preventDefault(); + const col = event.currentTarget; + if (col) col.classList.remove('drag-over'); const playlistId = parseInt(event.dataTransfer.getData('text/plain'), 10); if (!playlistId) return; await saveAutoSyncPlaylistSchedule(playlistId, hours); diff --git a/webui/static/style.css b/webui/static/style.css index d32f772d..f2784d42 100644 --- a/webui/static/style.css +++ b/webui/static/style.css @@ -11179,9 +11179,15 @@ body.helper-mode-active #dashboard-activity-feed:hover { .sync-history-btn:hover { color: rgb(var(--accent-rgb)); background: rgba(var(--accent-rgb), 0.1); border-color: rgba(var(--accent-rgb), 0.3); } .auto-sync-manager-btn { - color: #7dd3fc; - border-color: rgba(56, 189, 248, 0.28); - background: rgba(56, 189, 248, 0.1); + color: rgb(var(--accent-light-rgb)); + border-color: rgba(var(--accent-rgb), 0.32); + background: rgba(var(--accent-rgb), 0.12); +} + +.auto-sync-manager-btn:hover { + border-color: rgba(var(--accent-rgb), 0.5); + background: rgba(var(--accent-rgb), 0.2); + color: rgb(var(--accent-neon-rgb)); } .auto-sync-overlay { @@ -11198,10 +11204,14 @@ body.helper-mode-active #dashboard-activity-feed:hover { .auto-sync-modal { width: min(1500px, calc(100vw - 40px)); height: min(860px, calc(100vh - 40px)); - background: rgba(17, 19, 27, 0.98); - border: 1px solid rgba(255, 255, 255, 0.12); - border-radius: 8px; - box-shadow: 0 28px 80px rgba(0, 0, 0, 0.5); + background: + radial-gradient(circle at top right, rgba(var(--accent-rgb), 0.08) 0%, transparent 60%), + rgba(17, 19, 27, 0.98); + border: 1px solid rgba(var(--accent-rgb), 0.2); + border-radius: 10px; + box-shadow: + 0 28px 80px rgba(0, 0, 0, 0.5), + 0 0 60px rgba(var(--accent-rgb), 0.08); display: flex; flex-direction: column; overflow: hidden; @@ -11219,10 +11229,11 @@ body.helper-mode-active #dashboard-activity-feed:hover { .auto-sync-eyebrow { margin-bottom: 6px; - color: #7dd3fc; + color: rgb(var(--accent-light-rgb)); font-size: 11px; font-weight: 800; text-transform: uppercase; + letter-spacing: 0.06em; } .auto-sync-header h3 { @@ -11308,9 +11319,10 @@ body.helper-mode-active #dashboard-activity-feed:hover { .auto-sync-tabs button:hover, .auto-sync-tabs button.active { - border-color: rgba(56, 189, 248, 0.35); - background: rgba(56, 189, 248, 0.12); - color: #e0f2fe; + border-color: rgba(var(--accent-rgb), 0.4); + background: rgba(var(--accent-rgb), 0.14); + color: rgb(var(--accent-neon-rgb)); + box-shadow: 0 0 0 1px rgba(var(--accent-rgb), 0.08), 0 2px 10px rgba(var(--accent-rgb), 0.12); } .auto-sync-tab-panel { @@ -11331,8 +11343,8 @@ body.helper-mode-active #dashboard-activity-feed:hover { justify-content: space-between; gap: 16px; padding: 12px 18px; - border-bottom: 1px solid rgba(255, 255, 255, 0.07); - background: rgba(56, 189, 248, 0.035); + border-bottom: 1px solid rgba(var(--accent-rgb), 0.16); + background: linear-gradient(90deg, rgba(var(--accent-rgb), 0.08) 0%, rgba(var(--accent-rgb), 0.03) 60%, transparent 100%); flex-shrink: 0; } @@ -11423,12 +11435,14 @@ body.helper-mode-active #dashboard-activity-feed:hover { .auto-sync-playlist:hover, .auto-sync-scheduled-card:hover { - border-color: rgba(56, 189, 248, 0.32); - background: rgba(56, 189, 248, 0.08); + border-color: rgba(var(--accent-rgb), 0.4); + background: rgba(var(--accent-rgb), 0.1); + box-shadow: 0 4px 14px rgba(var(--accent-rgb), 0.12); } .auto-sync-playlist.scheduled { - border-color: rgba(34, 197, 94, 0.22); + border-color: rgba(var(--accent-rgb), 0.3); + background: rgba(var(--accent-rgb), 0.05); } .auto-sync-playlist.unavailable { @@ -11480,8 +11494,9 @@ body.helper-mode-active #dashboard-activity-feed:hover { } .auto-sync-scheduled-timing span { - background: rgba(56, 189, 248, 0.14); - color: #7dd3fc; + background: rgba(var(--accent-rgb), 0.18); + color: rgb(var(--accent-light-rgb)); + border: 1px solid rgba(var(--accent-rgb), 0.25); } .auto-sync-scheduled-timing small { @@ -11510,6 +11525,13 @@ body.helper-mode-active #dashboard-activity-feed:hover { background: rgba(255, 255, 255, 0.025); display: flex; flex-direction: column; + transition: border-color 0.15s ease, background 0.15s ease, box-shadow 0.15s ease; +} + +.auto-sync-column.drag-over { + border-color: rgba(var(--accent-rgb), 0.6); + background: rgba(var(--accent-rgb), 0.08); + box-shadow: inset 0 0 0 1px rgba(var(--accent-rgb), 0.3), 0 6px 20px rgba(var(--accent-rgb), 0.15); } .auto-sync-column-head { @@ -11524,7 +11546,7 @@ body.helper-mode-active #dashboard-activity-feed:hover { } .auto-sync-column-head small { - color: #7dd3fc; + color: rgb(var(--accent-light-rgb)); font-size: 11px; font-weight: 700; } @@ -11548,10 +11570,17 @@ body.helper-mode-active #dashboard-activity-feed:hover { .auto-sync-board::-webkit-scrollbar-thumb, .auto-sync-source-list::-webkit-scrollbar-thumb, .auto-sync-automation-list::-webkit-scrollbar-thumb { - background: rgba(125, 211, 252, 0.22); + background: rgba(var(--accent-rgb), 0.3); border-radius: 999px; } +.auto-sync-column-list::-webkit-scrollbar-thumb:hover, +.auto-sync-board::-webkit-scrollbar-thumb:hover, +.auto-sync-source-list::-webkit-scrollbar-thumb:hover, +.auto-sync-automation-list::-webkit-scrollbar-thumb:hover { + background: rgba(var(--accent-rgb), 0.5); +} + .auto-sync-drop-hint, .auto-sync-empty, .auto-sync-loading,