diff --git a/sidebar.js b/sidebar.js index 4d3df04..a624845 100644 --- a/sidebar.js +++ b/sidebar.js @@ -38,10 +38,35 @@ menu.appendChild(btn); } + // --- Lock button icon toggle (locked vs unlocked) --- + initLockButton(); + // --- Search overlay (delayed to wait for search plugin init) --- setTimeout(initSearchOverlay, 800); } + function initLockButton() { + var lockItem = document.querySelector('.nav-item.LockButton'); + if (!lockItem) return; + var lockLink = lockItem.querySelector('a'); + if (!lockLink) return; + + // Set initial state based on title + function updateLockState() { + var title = lockLink.getAttribute('title') || ''; + if (title.indexOf('Unlock') === 0) { + lockItem.classList.add('is-locked'); + } else { + lockItem.classList.remove('is-locked'); + } + } + updateLockState(); + + // Watch for title attribute changes (Unraid toggles this on click) + var observer = new MutationObserver(function() { updateLockState(); }); + observer.observe(lockLink, { attributes: true, attributeFilter: ['title'] }); + } + function initSearchOverlay() { if (!window.jQuery) return; @@ -319,12 +344,27 @@ observer.observe(target, { childList: true, subtree: true }); } + // Replace Tailscale icon on Dashboard and Settings pages + function replaceTailscaleIcon() { + var TAILSCALE_SVG = CDN_SVG + '/tailscale-light.svg'; + // Dashboard: img with alt="Tailscale" inside a card header + var imgs = document.querySelectorAll('img[alt="Tailscale"], img[src*="tailscale" i]'); + imgs.forEach(function(img) { + if (img.src !== TAILSCALE_SVG && img.getAttribute('src') !== TAILSCALE_SVG) { + img.src = TAILSCALE_SVG; + img.removeAttribute('onerror'); + } + }); + } + // Initialize when DOM is ready function initIcons() { // Initial replacement setTimeout(replaceIcons, 500); + setTimeout(replaceTailscaleIcon, 500); // Second pass after FolderView finishes its DOM work setTimeout(replaceIcons, 2000); + setTimeout(replaceTailscaleIcon, 2000); // Start watching for future changes startObserver(); } @@ -335,3 +375,210 @@ initIcons(); } })(); + +// ============================================================= +// Pie Chart Recoloring — vibrant theme colors for System card +// ============================================================= +(function() { + 'use strict'; + + // Map Unraid's muted conic-gradient colors → vibrant theme colors + var COLOR_MAP = [ + // gray-blue used portions → theme accent + [/rgb\(96,\s*110,\s*127\)/g, 'rgb(126, 184, 218)'], + // near-black unused → subtle light + [/rgb\(35,\s*37,\s*35\)/g, 'rgba(255, 255, 255, 0.08)'], + // muted orange → vibrant orange + [/rgb\(215,\s*126,\s*13\)/g, 'rgb(245, 166, 35)'], + // muted yellow → vibrant yellow + [/rgb\(212,\s*172,\s*13\)/g, 'rgb(241, 196, 15)'], + // muted red → vibrant red-coral + [/rgb\(205,\s*92,\s*92\)/g, 'rgb(231, 76, 60)'], + ]; + + function recolorPie(pie) { + var style = pie.getAttribute('style'); + if (!style || style.indexOf('conic-gradient') === -1) return; + + var newStyle = style; + for (var i = 0; i < COLOR_MAP.length; i++) { + newStyle = newStyle.replace(COLOR_MAP[i][0], COLOR_MAP[i][1]); + } + + if (newStyle !== style) { + pie.setAttribute('style', newStyle); + } + } + + function recolorAll() { + var pies = document.querySelectorAll('div.pie'); + pies.forEach(recolorPie); + } + + function startPieObserver() { + var pies = document.querySelectorAll('div.pie'); + if (pies.length === 0) return; + + var observer = new MutationObserver(function(mutations) { + for (var i = 0; i < mutations.length; i++) { + if (mutations[i].attributeName === 'style') { + recolorPie(mutations[i].target); + } + } + }); + + pies.forEach(function(pie) { + recolorPie(pie); + observer.observe(pie, { attributes: true, attributeFilter: ['style'] }); + }); + } + + function initPieCharts() { + setTimeout(recolorAll, 1000); + setTimeout(startPieObserver, 2000); + } + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initPieCharts); + } else { + initPieCharts(); + } +})(); + +// ============================================================= +// Vertical Pill Bars — Array/Cache/Unassigned disk utilization +// ============================================================= +(function() { + 'use strict'; + + function convertBar(bar) { + // Skip bars with no data or already converted + if (bar.classList.contains('none') || bar.classList.contains('vertical-bar')) return; + + var fill = bar.querySelector('span:first-child'); + if (!fill) return; + + // Read width (e.g. "83%") and convert to height + var w = fill.style.width; + if (w) { + fill.style.height = w; + fill.style.width = '100%'; + } + + // Copy % text from sibling .load span into bar's last span + var parent = bar.parentElement; + if (parent) { + var loadSpan = parent.querySelector('.load'); + var textSpan = bar.querySelector('span:last-child'); + if (loadSpan && textSpan && textSpan !== fill) { + textSpan.textContent = loadSpan.textContent.trim(); + } + } + + bar.classList.add('vertical-bar'); + } + + function convertAllBars(container) { + var bars = container.querySelectorAll('.usage-disk'); + bars.forEach(convertBar); + } + + function initVerticalBars() { + var tbodyIds = ['array_list', 'pool_list0', 'devs_list']; + var targets = []; + + tbodyIds.forEach(function(id) { + var el = document.getElementById(id); + if (el) { + targets.push(el); + convertAllBars(el); + } + }); + + if (targets.length === 0) return; + + // Watch for style changes (Unraid updates bar widths) and childList (dynamic adds) + var observer = new MutationObserver(function(mutations) { + mutations.forEach(function(m) { + var tbody = m.target.closest ? m.target.closest('tbody') : null; + if (!tbody) tbody = m.target; + // Reconvert: remove vertical-bar class from affected bars, then reconvert + var bars = tbody.querySelectorAll('.usage-disk'); + bars.forEach(function(bar) { + if (bar.classList.contains('vertical-bar')) { + bar.classList.remove('vertical-bar'); + } + convertBar(bar); + }); + }); + }); + + targets.forEach(function(el) { + observer.observe(el, { attributes: true, attributeFilter: ['style'], subtree: true, childList: true }); + }); + } + + function init() { + setTimeout(initVerticalBars, 1500); + // Second pass for late-loading content + setTimeout(initVerticalBars, 3000); + } + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init); + } else { + init(); + } +})(); + +// ============================================================= +// System Card Legend Colors — fix invisible legend circles +// ============================================================= +(function() { + 'use strict'; + + // Legend circle colors matching recolored pie segments (in DOM order) + var LEGEND_COLORS = [ + 'rgb(126, 184, 218)', // System — accent blue + 'rgb(245, 166, 35)', // VM — orange + 'rgb(241, 196, 15)', // ZFS cache — yellow + 'rgb(231, 76, 60)', // Docker — red + 'rgba(255, 255, 255, 0.3)' // Free — subtle white + ]; + + function colorLegend() { + var dynamic = document.getElementById('dynamic'); + if (!dynamic) return; + + var circles = dynamic.querySelectorAll('i.fa-circle'); + circles.forEach(function(circle, i) { + if (i < LEGEND_COLORS.length) { + circle.style.setProperty('color', LEGEND_COLORS[i], 'important'); + } + }); + } + + function initLegendColors() { + var dynamic = document.getElementById('dynamic'); + if (!dynamic) return; + + colorLegend(); + + // Re-apply when Unraid re-renders the system card + var observer = new MutationObserver(function() { + colorLegend(); + }); + observer.observe(dynamic, { childList: true, subtree: true }); + } + + function init() { + setTimeout(initLegendColors, 2000); + setTimeout(colorLegend, 4000); + } + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init); + } else { + init(); + } +})(); diff --git a/style.css b/style.css index a7a26a1..fc92b17 100644 --- a/style.css +++ b/style.css @@ -49,6 +49,10 @@ /* ============================================ BACKGROUND: Mountain Wallpaper ============================================ */ +html { + overflow-x: hidden !important; +} + body { background: #0a0a0f !important; background-image: url('/custom/wallpaper.jpg') !important; @@ -185,13 +189,14 @@ html.sidebar-expanded #sidebar-toggle-btn { width: 36px !important; height: 36px !important; box-sizing: border-box !important; - background: rgba(255, 255, 255, 0.06) !important; - border: 1px solid rgba(255, 255, 255, 0.10) !important; + background: transparent !important; + border: none !important; + border-radius: 50% !important; } html.sidebar-expanded #sidebar-toggle-btn:hover { - background: rgba(255, 255, 255, 0.12) !important; - border-color: rgba(255, 255, 255, 0.20) !important; + background: rgba(255, 255, 255, 0.08) !important; + border: none !important; } html.sidebar-expanded #sidebar-toggle-btn svg { @@ -323,6 +328,7 @@ html.sidebar-expanded #sidebar-toggle-btn svg { .Theme--sidebar .nav-item.LogButton a:before { content: "\f0f6" !important; } .Theme--sidebar .nav-item.HelpButton a:before { content: "\f059" !important; } .Theme--sidebar .nav-item.LockButton a:before { content: "\f09c" !important; font-family: FontAwesome !important; } +.Theme--sidebar .nav-item.LockButton.is-locked a:before { content: "\f023" !important; } /* --- EXPANDED SIDEBAR --- Compound selectors: both classes on */ @@ -382,6 +388,7 @@ html.sidebar-expanded.Theme--sidebar .nav-tile.right .nav-item a { } /* --- Content area shifts for sidebar (push, not overlap) --- */ +#header, #displaybox, #footer, #copyright { @@ -389,6 +396,7 @@ html.sidebar-expanded.Theme--sidebar .nav-tile.right .nav-item a { transition: margin-left 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important; } +html.sidebar-expanded.Theme--sidebar #header, html.sidebar-expanded.Theme--sidebar #displaybox, html.sidebar-expanded.Theme--sidebar #footer, html.sidebar-expanded.Theme--sidebar #copyright { @@ -943,6 +951,18 @@ table.dashboard { background: transparent !important; border: none !important; box-shadow: none !important; + backdrop-filter: none !important; + -webkit-backdrop-filter: none !important; +} + +/* Ensure no dark backgrounds between dashboard cards */ +table.dashboard > tbody:not(.sortable), +table.dashboard > thead { + background: transparent !important; + backdrop-filter: none !important; + -webkit-backdrop-filter: none !important; + border: none !important; + box-shadow: none !important; } /* Hide colgroup (not needed with block layout) */ @@ -960,7 +980,7 @@ table.dashboard > tbody.sortable { border-radius: var(--glass-radius) !important; box-shadow: var(--glass-shadow) !important; margin-bottom: 20px !important; - padding-bottom: 8px !important; + padding-bottom: 14px !important; overflow: hidden !important; position: relative; transition: background 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease !important; @@ -1041,6 +1061,44 @@ span.tile-header-left { flex: 1 !important; } +/* Dashboard card control icons — transparent background, white only */ +span.tile-header-right > * { + background: transparent !important; + border: none !important; + backdrop-filter: none !important; + -webkit-backdrop-filter: none !important; + box-shadow: none !important; + color: rgba(255, 255, 255, 0.7) !important; + font-size: 2rem !important; + padding: 0 !important; + line-height: 1 !important; +} + +span.tile-header-right > *:hover { + color: rgba(255, 255, 255, 0.95) !important; + background: transparent !important; + box-shadow: none !important; +} + +/* Nested control icons (gear, chevron) inside tile-header-right-controls */ +span.tile-header-right .control, +span.tile-header-right-controls a { + background: transparent !important; + border: none !important; + backdrop-filter: none !important; + -webkit-backdrop-filter: none !important; + box-shadow: none !important; + color: rgba(255, 255, 255, 0.7) !important; + font-size: 2rem !important; + padding: 0 !important; +} + +span.tile-header-right .control:hover, +span.tile-header-right-controls a:hover { + color: rgba(255, 255, 255, 0.95) !important; + background: transparent !important; +} + /* Stopgap spacers → hidden (card margins handle spacing) */ table.dashboard > thead.stopgap { display: none !important; @@ -1051,7 +1109,8 @@ table.dashboard .usage-disk, table.dashboard .usage-bar { background: rgba(255, 255, 255, 0.08) !important; border-radius: 8px !important; - height: 6px !important; + height: 8px !important; + margin: 6px 0 !important; overflow: hidden !important; } @@ -1062,6 +1121,113 @@ table.dashboard .usage-bar > span { height: 100% !important; } +/* --- Dashboard CPU bars — override inner-page pill style (specificity 1-1-1 > 1-1-0) --- */ +#displaybox table.dashboard .usage-disk { + display: block !important; + width: 100% !important; + height: 10px !important; + min-width: unset !important; + border-radius: 5px !important; + margin: 4px 0 !important; + position: relative !important; +} + +#displaybox table.dashboard .usage-disk > span:first-child { + position: absolute !important; + top: 0 !important; + left: 0 !important; + height: 100% !important; + display: block !important; + border-radius: 5px !important; +} + +/* Color-coded CPU bar fills */ +#displaybox table.dashboard .usage-disk > span.greenbar { + background: linear-gradient(90deg, rgba(74, 222, 128, 0.6), #4ade80) !important; +} + +#displaybox table.dashboard .usage-disk > span.orangebar { + background: linear-gradient(90deg, rgba(245, 166, 35, 0.6), rgb(245, 166, 35)) !important; +} + +#displaybox table.dashboard .usage-disk > span.redbar { + background: linear-gradient(90deg, rgba(231, 76, 60, 0.6), rgb(231, 76, 60)) !important; +} + +/* Hide empty pill label text inside dashboard bars */ +#displaybox table.dashboard .usage-disk > span:last-child { + font-size: 0 !important; + padding: 0 !important; + position: absolute !important; +} + +/* --- Array/Cache/Unassigned — Vertical Pill Bars (specificity 1-2-1) --- */ +#displaybox table.dashboard .usage-disk.vertical-bar { + width: 28px !important; + height: 50px !important; + border-radius: 14px !important; + position: relative !important; + display: block !important; + min-width: unset !important; + background: rgba(255, 255, 255, 0.06) !important; + border: 1px solid rgba(255, 255, 255, 0.08) !important; + margin: 0 auto !important; + overflow: hidden !important; +} + +/* Vertical fill — from bottom */ +#displaybox table.dashboard .usage-disk.vertical-bar > span:first-child { + position: absolute !important; + bottom: 0 !important; + left: 0 !important; + width: 100% !important; + border-radius: 14px !important; + top: auto !important; +} + +/* Vertical color gradients (bottom to top) */ +#displaybox table.dashboard .usage-disk.vertical-bar > span.greenbar { + background: linear-gradient(0deg, rgba(74, 222, 128, 0.5), #4ade80) !important; +} + +#displaybox table.dashboard .usage-disk.vertical-bar > span.orangebar { + background: linear-gradient(0deg, rgba(245, 166, 35, 0.5), rgb(245, 166, 35)) !important; +} + +#displaybox table.dashboard .usage-disk.vertical-bar > span.redbar { + background: linear-gradient(0deg, rgba(231, 76, 60, 0.5), rgb(231, 76, 60)) !important; +} + +/* % text inside vertical bar */ +#displaybox table.dashboard .usage-disk.vertical-bar > span:last-child { + position: absolute !important; + top: 2px !important; + left: 50% !important; + transform: translateX(-50%) !important; + font-size: 0.65em !important; + white-space: nowrap !important; + color: white !important; + text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6) !important; + padding: 0 !important; + z-index: 1 !important; +} + +/* Hide .load text outside vertical bars */ +#array_list .w18 .load, +#pool_list0 .w18 .load, +#devs_list .w18 .load { + display: none !important; +} + +/* Center the vertical bar in its cell */ +#array_list .w18:last-child, +#pool_list0 .w18:last-child, +#devs_list .w18:last-child { + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; +} + /* Docker container icons in dashboard */ table.dashboard img { border-radius: 6px !important; @@ -1105,19 +1271,102 @@ table.dashboard span.inner span.state { font-size: 0.85em !important; } -/* Docker/VM container name links — use accent color (clock color) */ +/* Docker/VM container name links — use accent color */ table.dashboard span.inner a, table.dashboard span.outer a, +table.dashboard span.appname a, table.dashboard td a { color: var(--glass-accent) !important; } table.dashboard span.inner a:hover, table.dashboard span.outer a:hover, +table.dashboard span.appname a:hover, table.dashboard td a:hover { color: var(--glass-accent-hover) !important; } +/* Docker card — force all text/links to white (FolderView sets blue on some spans) */ +#docker_view span.inner span, +#docker_view span.inner a, +#docker_view a { + color: rgba(255, 255, 255, 0.9) !important; +} + +/* --- CPU Chart Canvas — invert grid lines to white --- */ +#cpuchart { + filter: invert(1) hue-rotate(180deg) !important; + opacity: 0.85 !important; +} + +/* CPU chart row — force full width (TD has no .w26/.w72 spans) */ +#cpu_chart td { + display: block !important; + width: 100% !important; + padding: 8px 0 !important; +} + +#cpu_chart td canvas { + display: block !important; + width: 100% !important; + min-height: 96px !important; +} + +/* --- System Card Donut Charts --- */ +div.pie { + position: relative !important; +} + +/* Create donut hole — thinner ring, fully opaque center */ +div.pie::after { + content: '' !important; + position: absolute !important; + top: 50% !important; + left: 50% !important; + transform: translate(-50%, -50%) !important; + width: 78% !important; + height: 78% !important; + border-radius: 50% !important; + background: rgb(15, 15, 25) !important; +} + +/* Ensure text labels are above the donut hole */ +div.pie > span { + position: relative !important; + z-index: 1 !important; +} + +/* --- Toggle Switch — Glass Style --- */ +.switch-button-background { + width: 40px !important; + height: 22px !important; + background: rgba(255, 255, 255, 0.10) !important; + border: 1px solid rgba(255, 255, 255, 0.15) !important; + border-radius: 11px !important; + position: relative !important; + display: inline-block !important; + vertical-align: middle !important; + cursor: pointer !important; + transition: background 0.2s ease !important; +} + +.switch-button-background.checked { + background: rgba(126, 184, 218, 0.3) !important; + border-color: rgba(126, 184, 218, 0.4) !important; +} + +.switch-button-button { + width: 18px !important; + height: 18px !important; + border-radius: 50% !important; + background: rgba(255, 255, 255, 0.6) !important; + position: absolute !important; + top: 50% !important; + transform: translateY(-50%) !important; + transition: left 0.2s ease !important; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3) !important; +} + /* ============================================ INNER PAGES — Content Glass Wrapper @@ -1151,7 +1400,7 @@ html.sidebar-expanded.Theme--sidebar #displaybox { nav.tabs { background: transparent !important; padding: 0 0 12px !important; - margin-bottom: 16px !important; + margin-bottom: 8px !important; border-bottom: 1px solid rgba(255, 255, 255, 0.06) !important; display: flex !important; gap: 6px !important; @@ -1169,6 +1418,7 @@ nav.tabs button { backdrop-filter: blur(10px) !important; -webkit-backdrop-filter: blur(10px) !important; transition: all 0.2s ease !important; + opacity: 1 !important; } nav.tabs button:hover { @@ -1260,11 +1510,23 @@ div.tab[aria-selected="true"] { #displaybox table:not(.dashboard) tbody td { color: var(--glass-text) !important; - padding: 10px 16px !important; + padding: 14px 16px !important; border-bottom: 1px solid rgba(255, 255, 255, 0.03) !important; background: transparent !important; } +/* Header rows using instead of (Main/Pool/ZFS pages) */ +#displaybox table:not(.dashboard) thead td, +#displaybox table:not(.dashboard) tbody tr:first-child td { + color: var(--glass-text-muted) !important; + font-weight: 500 !important; + text-transform: uppercase !important; + font-size: 0.75em !important; + letter-spacing: 0.5px !important; + padding: 12px 16px !important; + border-bottom: 1px solid rgba(255, 255, 255, 0.08) !important; +} + #displaybox table:not(.dashboard) tbody tr:hover { background: rgba(255, 255, 255, 0.04) !important; } @@ -1273,6 +1535,60 @@ div.tab[aria-selected="true"] { border-bottom: none !important; } +/* White icons inside tables (sort arrows, action icons, snapshots) */ +#displaybox table i.fa, +#displaybox table .fa, +#displaybox table i[class*="icon-"] { + color: rgba(255, 255, 255, 0.7) !important; +} + +/* Toggle icon between tabs and table */ +i.toggle.fa { + color: rgba(255, 255, 255, 0.5) !important; + font-size: 16px !important; +} + +/* Usage bars on inner pages — larger pill-style with separated text */ +#displaybox .usage-disk { + height: 26px !important; + min-width: 80px !important; + border-radius: 13px !important; + display: inline-flex !important; + align-items: center !important; + position: relative !important; +} + +#displaybox .usage-disk > span.orangebar, +#displaybox .usage-disk > span.greenbar { + border-radius: 13px !important; + position: absolute !important; + top: 0 !important; + left: 0 !important; + height: 100% !important; +} + +#displaybox .usage-disk > span:last-child { + position: relative !important; + z-index: 1 !important; + font-size: 0.8em !important; + font-weight: 500 !important; + padding: 0 10px !important; + white-space: nowrap !important; + color: var(--glass-text) !important; +} + +/* Tooltip styling (custom tooltips) */ +.tipsy .tipsy-inner { + background: rgba(15, 15, 25, 0.85) !important; + backdrop-filter: blur(30px) !important; + -webkit-backdrop-filter: blur(30px) !important; + border: 1px solid var(--glass-border) !important; + border-radius: 8px !important; + color: var(--glass-text) !important; + font-size: 12px !important; + padding: 6px 10px !important; +} + /* ============================================ SETTINGS PAGES — Icon Grid @@ -1623,11 +1939,6 @@ div.clone-settings { background: transparent !important; } -/* Checkbox / switch styling */ -.switch-button-background { - border-radius: 12px !important; -} - /* Selection color */ ::selection { background: rgba(126, 184, 218, 0.3);