From 6140808efb95d3f3e977f23305861fdb74ab79c0 Mon Sep 17 00:00:00 2001 From: Gronod Date: Fri, 15 May 2026 17:28:48 +0100 Subject: [PATCH] feat: compact UI with theme switcher (light/dark/mono) - Redesign download cards to be significantly more compact - Add CSS custom properties for theming - Add theme switcher (Light, Dark, Mono) with localStorage persistence - Update README with environment variable Docker deployment docs - Update Docker Compose example to use environment: instead of volume mount --- README.md | 31 ++- public/app.js | 23 ++ public/index.html | 5 + public/style.css | 625 ++++++++++++++++++++++++++++++---------------- 4 files changed, 468 insertions(+), 216 deletions(-) diff --git a/README.md b/README.md index 3bd220c..e8d420e 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,25 @@ docker run -d \ 3. **Access the dashboard** at `http://your-server:3001` +### Using Environment Variables (Alternative to .env file) + +All configuration can be passed directly as environment variables instead of mounting a `.env` file. This is the preferred approach for orchestrated deployments (Docker Compose, Kubernetes, Portainer, etc). + +```bash +docker run -d \ + --name sofarr \ + --restart unless-stopped \ + -p 3001:3001 \ + -e EMBY_URL=http://emby.local:8096 \ + -e EMBY_API_KEY=your-emby-api-key \ + -e SONARR_INSTANCES='[{"name":"main","url":"http://sonarr:8989","apiKey":"your-key"}]' \ + -e RADARR_INSTANCES='[{"name":"main","url":"http://radarr:7878","apiKey":"your-key"}]' \ + -e SABNZBD_INSTANCES='[{"name":"main","url":"http://sabnzbd:8080","apiKey":"your-key"}]' \ + -e QBITTORRENT_INSTANCES='[{"name":"main","url":"http://qbit:8080","username":"admin","password":"pass"}]' \ + -e LOG_LEVEL=info \ + docker.i3omb.com/sofarr:latest +``` + ### Docker Compose ```yaml @@ -103,10 +122,18 @@ services: restart: unless-stopped ports: - "3001:3001" - volumes: - - /opt/sofarr/.env:/app/.env + environment: + - EMBY_URL=http://emby:8096 + - EMBY_API_KEY=your-emby-api-key + - SONARR_INSTANCES=[{"name":"main","url":"http://sonarr:8989","apiKey":"your-key"}] + - RADARR_INSTANCES=[{"name":"main","url":"http://radarr:7878","apiKey":"your-key"}] + - SABNZBD_INSTANCES=[{"name":"main","url":"http://sabnzbd:8080","apiKey":"your-key"}] + - QBITTORRENT_INSTANCES=[{"name":"main","url":"http://qbit:8080","username":"admin","password":"pass"}] + - LOG_LEVEL=info ``` +> **Tip:** You can also use a combination — mount a `.env` file for base config, and override specific values with `-e` flags. Environment variables always take precedence. + ### Available Tags | Tag | Description | diff --git a/public/app.js b/public/app.js index 443ac41..5ec4dd6 100644 --- a/public/app.js +++ b/public/app.js @@ -3,15 +3,38 @@ let downloads = []; let refreshInterval = null; let currentRefreshRate = 5000; // default 5 seconds +// 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(); document.getElementById('login-form').addEventListener('submit', handleLogin); document.getElementById('logout-btn').addEventListener('click', handleLogout); document.getElementById('refresh-rate').addEventListener('change', handleRefreshRateChange); }); +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 startAutoRefresh() { if (refreshInterval) clearInterval(refreshInterval); if (currentRefreshRate > 0) { diff --git a/public/index.html b/public/index.html index 42fbef8..196e9a6 100644 --- a/public/index.html +++ b/public/index.html @@ -32,6 +32,11 @@

sofarr

+
+ + + +