From 83d7a24d1a726d8fa3a4f299c53d2c2c464c506d Mon Sep 17 00:00:00 2001 From: taqin Date: Sun, 12 Apr 2026 20:54:41 +0700 Subject: [PATCH] Redesign UI like Driver Navigator + fix CMD window UI changes: - Top title bar with logo, app name, and system status LED - Toolbar with 4 icon tabs: Scan, Drivers, Download, System - Scan page with computer SVG illustration, stats, and big SCAN NOW button - Step indicator bar: Scan Devices > Review Drivers > Install Updates - Driver list with per-row progress bars, Download + Install buttons - "Update All Drivers" CTA bar at bottom when outdated drivers found - Windows Update tab with pending/installed sections - System Info tab with resource bars - Footer with About/Help buttons Backend fixes: - Add cmdutil.HiddenCommand() using CREATE_NO_WINDOW (0x08000000) - All PowerShell subprocesses now run without visible console window - Build with -ldflags "-H windowsgui" to hide main CMD window Co-Authored-By: Claude Opus 4.6 (1M context) --- internal/cmdutil/cmdutil.go | 16 + internal/drivers/drivers.go | 5 +- internal/sysinfo/sysinfo.go | 7 +- internal/winupdate/winupdate.go | 7 +- ui/app.js | 408 +++++----- ui/index.html | 512 +++++------- ui/style.css | 1353 +++++++++++-------------------- 7 files changed, 922 insertions(+), 1386 deletions(-) create mode 100644 internal/cmdutil/cmdutil.go diff --git a/internal/cmdutil/cmdutil.go b/internal/cmdutil/cmdutil.go new file mode 100644 index 0000000..19def7e --- /dev/null +++ b/internal/cmdutil/cmdutil.go @@ -0,0 +1,16 @@ +package cmdutil + +import ( + "os/exec" + "syscall" +) + +// HiddenCommand creates an exec.Cmd that runs without showing a console window. +// This prevents PowerShell/cmd flashing when the app runs as a GUI application. +func HiddenCommand(name string, args ...string) *exec.Cmd { + cmd := exec.Command(name, args...) + cmd.SysProcAttr = &syscall.SysProcAttr{ + CreationFlags: 0x08000000, // CREATE_NO_WINDOW + } + return cmd +} diff --git a/internal/drivers/drivers.go b/internal/drivers/drivers.go index 61f0622..21a6f0e 100644 --- a/internal/drivers/drivers.go +++ b/internal/drivers/drivers.go @@ -2,9 +2,10 @@ package drivers import ( "encoding/json" - "os/exec" "strings" "time" + + "github.com/mumur/driver-booster/internal/cmdutil" ) type Driver struct { @@ -63,7 +64,7 @@ func (s *Scanner) enumDriversPnP() []Driver { psScript := ` Get-CimInstance Win32_PnPSignedDriver | Where-Object { $_.DeviceName -ne $null } | Select-Object -First 100 DeviceName, DeviceClass, Manufacturer, DriverVersion, DriverDate, InfName, IsSigned, Status | ConvertTo-Json -Compress ` - out, err := exec.Command("powershell", "-NoProfile", "-Command", psScript).Output() + out, err := cmdutil.HiddenCommand("powershell", "-NoProfile", "-Command", psScript).Output() if err != nil { return []Driver{} } diff --git a/internal/sysinfo/sysinfo.go b/internal/sysinfo/sysinfo.go index 56333a8..0ed77ea 100644 --- a/internal/sysinfo/sysinfo.go +++ b/internal/sysinfo/sysinfo.go @@ -2,11 +2,12 @@ package sysinfo import ( "fmt" - "os/exec" "runtime" "strings" "syscall" "unsafe" + + "github.com/mumur/driver-booster/internal/cmdutil" ) var ( @@ -117,7 +118,7 @@ func getComputerName() string { } func getCPUName() string { - out, err := exec.Command("powershell", "-NoProfile", "-Command", + out, err := cmdutil.HiddenCommand("powershell", "-NoProfile", "-Command", "(Get-CimInstance Win32_Processor).Name").Output() if err != nil { return "Unknown CPU" @@ -126,7 +127,7 @@ func getCPUName() string { } func getWindowsProductName() string { - out, err := exec.Command("powershell", "-NoProfile", "-Command", + out, err := cmdutil.HiddenCommand("powershell", "-NoProfile", "-Command", "(Get-CimInstance Win32_OperatingSystem).Caption").Output() if err != nil { return "Windows" diff --git a/internal/winupdate/winupdate.go b/internal/winupdate/winupdate.go index a918594..caf25f2 100644 --- a/internal/winupdate/winupdate.go +++ b/internal/winupdate/winupdate.go @@ -2,8 +2,9 @@ package winupdate import ( "encoding/json" - "os/exec" "time" + + "github.com/mumur/driver-booster/internal/cmdutil" ) type Update struct { @@ -101,7 +102,7 @@ try { Pending = $pending } | ConvertTo-Json -Depth 3 -Compress ` - out, err := exec.Command("powershell", "-NoProfile", "-Command", psScript).Output() + out, err := cmdutil.HiddenCommand("powershell", "-NoProfile", "-Command", psScript).Output() if err != nil { result.Error = "Failed to check updates: " + err.Error() result.CheckTime = time.Since(start).Round(time.Millisecond).String() @@ -193,7 +194,7 @@ if ($ToInstall.Count -gt 0) { Write-Output "No updates to install." } ` - out, err := exec.Command("powershell", "-NoProfile", "-Command", psScript).Output() + out, err := cmdutil.HiddenCommand("powershell", "-NoProfile", "-Command", psScript).Output() if err != nil { return InstallResult{ Success: false, diff --git a/ui/app.js b/ui/app.js index e24a7dc..1bbfa38 100644 --- a/ui/app.js +++ b/ui/app.js @@ -1,307 +1,266 @@ /* ============================================================ Driver Booster Pro - Application Logic + Driver Navigator Style: tabs, driver rows, action buttons ============================================================ */ -const App = { - state: { - sysInfo: null, - drivers: null, - updates: null, - }, +var App = { + state: { sysInfo:null, drivers:null, updates:null }, - init() { - this.setupNavigation(); + init: function() { + this.setupTabs(); this.setupFilters(); this.refreshSysInfo(); - // Re-render Lucide icons after DOM mutations this.refreshIcons(); }, - refreshIcons() { - if (window.lucide) { - lucide.createIcons(); - } + refreshIcons: function() { + if (window.lucide) lucide.createIcons(); }, - // ---- Navigation ---- - setupNavigation() { - document.querySelectorAll('.nav-item[data-page]').forEach(function(item) { - item.addEventListener('click', function() { - var page = item.dataset.page; - document.querySelectorAll('.nav-item').forEach(function(n) { n.classList.remove('active'); }); - item.classList.add('active'); - document.querySelectorAll('.page').forEach(function(p) { p.classList.remove('active'); }); - var target = document.getElementById('page-' + page); + // ---- Tab Navigation ---- + setupTabs: function() { + var self = this; + document.querySelectorAll('.toolbar-tab').forEach(function(tab) { + tab.addEventListener('click', function() { + var id = tab.dataset.tab; + document.querySelectorAll('.toolbar-tab').forEach(function(t){ t.classList.remove('active'); }); + tab.classList.add('active'); + document.querySelectorAll('.tab-page').forEach(function(p){ p.classList.remove('active'); }); + var target = document.getElementById('tab-' + id); if (target) target.classList.add('active'); }); }); }, - // ---- Filters ---- - setupFilters() { + // ---- Filter Pills ---- + setupFilters: function() { var self = this; - document.querySelectorAll('.filter-btn').forEach(function(btn) { + document.querySelectorAll('.pill').forEach(function(btn) { btn.addEventListener('click', function() { - document.querySelectorAll('.filter-btn').forEach(function(b) { b.classList.remove('active'); }); + document.querySelectorAll('.pill').forEach(function(b){ b.classList.remove('active'); }); btn.classList.add('active'); self.filterDrivers(btn.dataset.filter); }); }); }, - filterDrivers(filter) { - document.querySelectorAll('.driver-card').forEach(function(card) { - var show = filter === 'all' || - (filter === 'outdated' && card.classList.contains('outdated')) || - (filter === 'error' && card.classList.contains('error')) || - (filter === 'signed' && card.dataset.signed === 'true'); - card.style.display = show ? '' : 'none'; + filterDrivers: function(f) { + document.querySelectorAll('.drv-row').forEach(function(row) { + var show = f === 'all' || + (f === 'outdated' && row.classList.contains('outdated')) || + (f === 'error' && row.classList.contains('error')) || + (f === 'signed' && row.dataset.signed === 'true'); + row.style.display = show ? '' : 'none'; }); }, // ---- Loading ---- - showLoading(text) { + showLoading: function(text) { document.getElementById('loading-text').textContent = text; document.getElementById('loading-overlay').style.display = 'flex'; this.refreshIcons(); }, - - hideLoading() { + hideLoading: function() { document.getElementById('loading-overlay').style.display = 'none'; }, // ---- System Info ---- - async refreshSysInfo() { + refreshSysInfo: function() { + var self = this; this.showLoading('Collecting system information...'); - try { - var res = await fetch('/api/sysinfo'); - var info = await res.json(); - this.state.sysInfo = info; - this.renderSysInfo(info); - this.updateDashboardResources(info); - } catch (e) { - console.error('Failed to get sysinfo:', e); - } - this.hideLoading(); + fetch('/api/sysinfo').then(function(r){ return r.json(); }).then(function(info) { + self.state.sysInfo = info; + self.renderSysInfo(info); + self.hideLoading(); + }).catch(function(e) { + console.error(e); + self.hideLoading(); + }); }, - renderSysInfo(info) { - this.setText('sys-name', info.computerName); - this.setText('sys-os', info.osName); - this.setText('sys-version', info.osVersion); - this.setText('sys-build', info.osBuild); - this.setText('sys-arch', info.architecture); - this.setText('sys-cpu', info.cpuName); - this.setText('sys-cores', info.cpuCores); - this.setText('sys-ram-total', info.totalRam); - this.setText('sys-ram-used', info.usedRam); - this.setText('sys-ram-free', info.freeRam); - this.setText('sys-ram-pct', (info.ramPercent || 0) + '%'); - this.setText('sys-disk-total', info.diskTotal); - this.setText('sys-disk-used', info.diskUsed); - this.setText('sys-disk-free', info.diskFree); - this.setText('sys-disk-pct', (info.diskPercent || 0) + '%'); + renderSysInfo: function(d) { + this.set('sys-name', d.computerName); + this.set('sys-os', d.osName); + this.set('sys-version', d.osVersion); + this.set('sys-build', d.osBuild); + this.set('sys-arch', d.architecture); + this.set('sys-cpu', d.cpuName); + this.set('sys-cores', d.cpuCores); + this.set('sys-ram-total', d.totalRam); + this.set('sys-ram-used', d.usedRam); + this.set('sys-ram-free', d.freeRam); + this.set('sys-ram-pct', (d.ramPercent||0)+'%'); + this.set('sys-disk-total', d.diskTotal); + this.set('sys-disk-used', d.diskUsed); + this.set('sys-disk-free', d.diskFree); + this.set('sys-disk-pct', (d.diskPercent||0)+'%'); + var rb = document.getElementById('sys-ram-bar'); + var db = document.getElementById('sys-disk-bar'); + if(rb) rb.style.width = (d.ramPercent||0)+'%'; + if(db) db.style.width = (d.diskPercent||0)+'%'; }, - updateDashboardResources(info) { - this.setText('dash-ram-percent', (info.ramPercent || 0) + '%'); - this.setText('dash-ram-detail', (info.usedRam || '--') + ' / ' + (info.totalRam || '--')); - document.getElementById('dash-ram-bar').style.width = (info.ramPercent || 0) + '%'; - this.setText('dash-disk-detail', (info.diskUsed || '--') + ' / ' + (info.diskTotal || '--')); - document.getElementById('dash-disk-bar').style.width = (info.diskPercent || 0) + '%'; - - // Warn color for high usage - var ramBar = document.getElementById('dash-ram-bar'); - var diskBar = document.getElementById('dash-disk-bar'); - if (info.ramPercent > 80) { - ramBar.style.background = 'linear-gradient(90deg, #f59e0b, #ef4444)'; - } else { - ramBar.style.background = ''; - } - if (info.diskPercent > 85) { - diskBar.style.background = 'linear-gradient(90deg, #f59e0b, #ef4444)'; - } else { - diskBar.style.background = ''; - } - }, - - // ---- Drivers ---- - async scanDrivers() { + // ---- Driver Scan ---- + scanDrivers: function() { + var self = this; this.showLoading('Scanning drivers... This may take a moment.'); - try { - var res = await fetch('/api/drivers/scan'); - var result = await res.json(); - this.state.drivers = result; - this.renderDrivers(result); - this.updateDashboardDrivers(result); - } catch (e) { - console.error('Failed to scan drivers:', e); - } - this.hideLoading(); + // Switch to drivers tab + document.querySelectorAll('.toolbar-tab').forEach(function(t){ t.classList.remove('active'); }); + document.querySelector('[data-tab="drivers"]').classList.add('active'); + document.querySelectorAll('.tab-page').forEach(function(p){ p.classList.remove('active'); }); + document.getElementById('tab-drivers').classList.add('active'); + + // Update steps + this.setStep(2); + + fetch('/api/drivers/scan').then(function(r){ return r.json(); }).then(function(result) { + self.state.drivers = result; + self.renderDrivers(result); + self.updateScanStats(result); + self.hideLoading(); + }).catch(function(e) { + console.error(e); + self.hideLoading(); + }); }, - renderDrivers(result) { - document.getElementById('driver-summary').style.display = 'flex'; - document.getElementById('driver-filters').style.display = 'flex'; - this.setText('drv-total', result.totalCount); - this.setText('drv-outdated', result.outdatedCount); - this.setText('drv-errors', result.errorCount); - this.setText('drv-time', result.scanTime); + updateScanStats: function(r) { + this.set('scan-stat-total', r.totalCount); + this.set('scan-stat-outdated', r.outdatedCount); + this.set('scan-stat-errors', r.errorCount); + }, + + renderDrivers: function(result) { + document.getElementById('driver-result-count').style.display = 'block'; + document.getElementById('driver-filter-strip').style.display = 'flex'; + this.set('drv-total-2', result.totalCount); + this.set('drv-outdated-2', result.outdatedCount); + this.set('drv-time-2', result.scanTime); var list = document.getElementById('driver-list'); if (!result.drivers || result.drivers.length === 0) { - list.innerHTML = - '
' + - '
' + - '

No Drivers Found

' + - '

No driver information was returned.

' + - '
'; + list.innerHTML = '

No drivers found

'; this.refreshIcons(); return; } var self = this; list.innerHTML = result.drivers.map(function(d) { - var classes = ['driver-card']; - if (d.needsUpdate) classes.push('outdated'); - if (d.status === 'Error' || d.status === 'Degraded') classes.push('error'); + var cls = ['drv-row']; + if (d.needsUpdate) cls.push('outdated'); + if (d.status === 'Error' || d.status === 'Degraded') cls.push('error'); - var iconName = self.getClassIconName(d.deviceClass); + var icon = self.driverIcon(d.deviceClass); + var barClass = d.needsUpdate ? 'warn' : (d.status === 'Error' ? 'err' : 'ok'); - var badges = ''; - if (d.isSigned) { - badges += ' Signed'; - } else { - badges += ' Unsigned'; - } - if (d.needsUpdate) { - badges += ' Outdated'; - } else { - badges += ' OK'; - } + var signBadge = d.isSigned + ? ' Signed' + : ''; - return '
' + - '
' + - '
' + - '
' + self.esc(d.deviceName) + '
' + - '
' + - self.esc(d.manufacturer || 'Unknown') + - ' · v' + self.esc(d.driverVersion || '?') + - ' · ' + self.esc(d.driverDate) + - '
' + + var actionBtn = d.needsUpdate + ? '' + + '' + : ''; + + return '
' + + '
' + + '
' + + '
' + self.esc(d.deviceName) + '
' + + '
' + self.esc(d.manufacturer||'Unknown') + ' · v' + self.esc(d.driverVersion||'?') + ' · ' + self.esc(d.driverDate) + ' ' + signBadge + '
' + '
' + - '
' + badges + '
' + + '
' + + '
' + + '
' + + '
' + actionBtn + '
' + '
'; }).join(''); + // Show CTA if there are outdated drivers + var cta = document.getElementById('cta-update-all'); + if (result.outdatedCount > 0) { + cta.style.display = 'flex'; + this.set('cta-count', result.outdatedCount); + } else { + cta.style.display = 'none'; + } + this.refreshIcons(); }, - updateDashboardDrivers(result) { - this.setText('dash-driver-count', result.totalCount); - this.setText('dash-outdated-count', result.outdatedCount); - }, - - getClassIconName(cls) { + driverIcon: function(cls) { if (!cls) return 'package'; - var upper = cls.toUpperCase(); - var map = { - 'DISPLAY': 'monitor', - 'MEDIA': 'volume-2', - 'AUDIO': 'volume-2', - 'SOUND': 'volume-2', - 'NET': 'wifi', - 'NETWORK': 'wifi', - 'USB': 'usb', - 'HID': 'mouse', - 'KEYBOARD': 'keyboard', - 'DISK': 'hard-drive', - 'STORAGE': 'hard-drive', - 'PROCESSOR': 'cpu', - 'SYSTEM': 'settings', - 'BLUETOOTH': 'bluetooth', - 'CAMERA': 'camera', - 'IMAGE': 'camera', - 'PRINT': 'printer', - 'BATTERY': 'battery-charging', - 'BIOMETRIC': 'fingerprint', - 'FIRMWARE': 'circuit-board', - 'SECURITY': 'shield', - 'SENSOR': 'thermometer', + var u = cls.toUpperCase(); + var m = { + 'DISPLAY':'monitor','MEDIA':'volume-2','AUDIO':'volume-2','SOUND':'volume-2', + 'NET':'wifi','NETWORK':'wifi','USB':'usb','HID':'mouse','KEYBOARD':'keyboard', + 'DISK':'hard-drive','STORAGE':'hard-drive','PROCESSOR':'cpu','SYSTEM':'settings', + 'BLUETOOTH':'bluetooth','CAMERA':'camera','IMAGE':'camera','PRINT':'printer', + 'BATTERY':'battery-charging','FIRMWARE':'circuit-board','SECURITY':'shield', + 'SENSOR':'thermometer' }; - for (var key in map) { - if (upper.indexOf(key) !== -1) return map[key]; - } + for (var k in m) { if (u.indexOf(k) !== -1) return m[k]; } return 'package'; }, - // ---- Updates ---- - async checkUpdates() { + // ---- Windows Update ---- + checkUpdates: function() { + var self = this; this.showLoading('Checking for Windows updates...'); - try { - var res = await fetch('/api/updates/check'); - var result = await res.json(); - this.state.updates = result; - this.renderUpdates(result); - this.setText('dash-update-count', result.pendingCount); - } catch (e) { - console.error('Failed to check updates:', e); - } - this.hideLoading(); + // Switch to updates tab + document.querySelectorAll('.toolbar-tab').forEach(function(t){ t.classList.remove('active'); }); + document.querySelector('[data-tab="updates"]').classList.add('active'); + document.querySelectorAll('.tab-page').forEach(function(p){ p.classList.remove('active'); }); + document.getElementById('tab-updates').classList.add('active'); + + fetch('/api/updates/check').then(function(r){ return r.json(); }).then(function(result) { + self.state.updates = result; + self.renderUpdates(result); + self.hideLoading(); + }).catch(function(e) { + console.error(e); + self.hideLoading(); + }); }, - renderUpdates(result) { + renderUpdates: function(result) { var list = document.getElementById('update-list'); if (result.error) { - list.innerHTML = - '
' + - '
' + - '

Error

' + - '

' + this.esc(result.error) + '

' + - '
'; + list.innerHTML = '

' + this.esc(result.error) + '

'; this.refreshIcons(); return; } if (!result.updates || result.updates.length === 0) { - list.innerHTML = - '
' + - '
' + - '

All Up to Date

' + - '

Your system is fully updated.

' + - '
'; + list.innerHTML = '

Your system is fully up to date!

'; this.refreshIcons(); return; } - var pending = result.updates.filter(function(u) { return !u.isInstalled; }); - var installed = result.updates.filter(function(u) { return u.isInstalled; }); + var pending = result.updates.filter(function(u){return !u.isInstalled;}); + var installed = result.updates.filter(function(u){return u.isInstalled;}); var self = this; var html = ''; if (pending.length > 0) { - html += '
Pending Updates (' + pending.length + ')
'; + html += '
Pending (' + pending.length + ')
'; html += pending.map(function(u) { - return '
' + - '
' + self.esc(u.title) + '
' + - '
' + - (u.kbArticle ? ' ' + self.esc(u.kbArticle) + '' : '') + - (u.category ? ' ' + self.esc(u.category) + '' : '') + - (u.size ? ' ' + self.esc(u.size) + '' : '') + - (u.severity && u.severity !== 'Unspecified' ? ' ' + self.esc(u.severity) + '' : '') + - (u.isMandatory ? ' Mandatory' : '') + + return '
' + + '
' + self.esc(u.title) + '
' + + '
' + + (u.kbArticle ? ' '+self.esc(u.kbArticle)+'' : '') + + (u.category ? ' '+self.esc(u.category)+'' : '') + + (u.size ? ' '+self.esc(u.size)+'' : '') + + (u.severity && u.severity !== 'Unspecified' ? ' '+self.esc(u.severity)+'' : '') + '
' + '
'; }).join(''); } if (installed.length > 0) { - html += '
Recently Installed (' + installed.length + ')
'; + html += '
Recently Installed (' + installed.length + ')
'; html += installed.map(function(u) { - return '
' + - '
' + self.esc(u.title) + '
' + - '
'; + return '
' + self.esc(u.title) + '
'; }).join(''); } @@ -309,21 +268,28 @@ const App = { this.refreshIcons(); }, - // ---- Utility ---- - setText(id, value) { - var el = document.getElementById(id); - if (el) el.textContent = (value != null && value !== '') ? value : '--'; + // ---- Step bar ---- + setStep: function(n) { + for (var i = 1; i <= 3; i++) { + var el = document.getElementById('step-' + i); + if (!el) continue; + el.classList.remove('active', 'done'); + if (i < n) el.classList.add('done'); + if (i === n) el.classList.add('active'); + } }, - esc(str) { - if (!str) return ''; - var div = document.createElement('div'); - div.textContent = str; - return div.innerHTML; + // ---- Helpers ---- + set: function(id, val) { + var el = document.getElementById(id); + if (el) el.textContent = (val != null && val !== '') ? val : '--'; }, + esc: function(s) { + if (!s) return ''; + var d = document.createElement('div'); + d.textContent = s; + return d.innerHTML; + } }; -// Boot -document.addEventListener('DOMContentLoaded', function() { - App.init(); -}); +document.addEventListener('DOMContentLoaded', function() { App.init(); }); diff --git a/ui/index.html b/ui/index.html index 43dade2..6636d5f 100644 --- a/ui/index.html +++ b/ui/index.html @@ -8,342 +8,257 @@ - -
-
-
-
- - - - -
- -
- -
-
-
-
- -

Quick Actions

+ +
+ + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+

Scan Your System

+

Detect outdated, missing, and faulty drivers. Keep your PC running at peak performance.

+
+
+ + -- + +
+
+ + -- + +
+
+ + -- +
-
- - - -
-
- -
-
-
- -

System Resources

-
-
-
-
-
-
- - Memory (RAM) -
- -- / -- -
-
-
-
-
-
-
-
-
-
- - Storage (C:) -
- -- / -- -
-
-
-
-
-
-
-
-
-
-
- - -
- -
+
+ + +
@@ -354,7 +269,6 @@

Scanning...

-

Please wait while we analyze your system

diff --git a/ui/style.css b/ui/style.css index 484e932..20033b5 100644 --- a/ui/style.css +++ b/ui/style.css @@ -1,976 +1,613 @@ /* ============================================================ - Driver Booster Pro - Professional Dark Theme + Driver Booster Pro - Driver Navigator Style + Toolbar tabs + driver list + Download/Install buttons ============================================================ */ :root { - /* Core palette */ - --bg-base: #080b14; - --bg-primary: #0c1021; - --bg-secondary: #111827; - --bg-card: rgba(17, 24, 39, 0.7); - --bg-card-solid: #111827; - --bg-elevated: rgba(30, 41, 59, 0.5); - --bg-hover: rgba(55, 65, 81, 0.3); - --bg-active: rgba(99, 102, 241, 0.08); + --bg: linear-gradient(180deg, #0a1628 0%, #0f1d32 50%, #0a1628 100%); + --surface: #111d33; + --surface-light: #162442; + --surface-hover: #1a2d50; + --border: rgba(255,255,255,0.07); + --border-accent: rgba(59,130,246,0.3); - /* Borders */ - --border: rgba(255, 255, 255, 0.06); - --border-light: rgba(255, 255, 255, 0.1); - --border-accent: rgba(99, 102, 241, 0.3); + --text: #e8edf5; + --text-sec: #8899b4; + --text-dim: #56687e; - /* Text */ - --text-primary: #f1f5f9; - --text-secondary: #94a3b8; - --text-muted: #64748b; - --text-dim: #475569; + --accent: #3b82f6; + --accent-light: #60a5fa; + --accent-dark: #2563eb; + --accent-glow: rgba(59,130,246,0.25); - /* Brand */ - --accent: #6366f1; - --accent-light: #818cf8; - --accent-dark: #4f46e5; - --accent-glow: rgba(99, 102, 241, 0.2); - --accent-glow-strong: rgba(99, 102, 241, 0.4); + --green: #10b981; + --green-bg: rgba(16,185,129,0.12); + --yellow: #f59e0b; + --yellow-bg: rgba(245,158,11,0.12); + --red: #ef4444; + --red-bg: rgba(239,68,68,0.12); + --cyan: #06b6d4; - /* Semantic */ - --success: #10b981; - --success-bg: rgba(16, 185, 129, 0.12); - --warning: #f59e0b; - --warning-bg: rgba(245, 158, 11, 0.12); - --danger: #ef4444; - --danger-bg: rgba(239, 68, 68, 0.12); - --info: #3b82f6; - --info-bg: rgba(59, 130, 246, 0.12); - - /* Layout */ - --radius: 14px; - --radius-sm: 10px; - --radius-xs: 6px; - --sidebar-width: 260px; - - /* Motion */ - --ease: cubic-bezier(0.4, 0, 0.2, 1); - --duration: 0.2s; + --r: 10px; + --r-sm: 6px; } -/* ---- Reset ---- */ -*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; } +* { margin:0; padding:0; box-sizing:border-box; } body { - font-family: 'Segoe UI Variable', 'Segoe UI', -apple-system, BlinkMacSystemFont, Inter, sans-serif; - background: var(--bg-base); - color: var(--text-primary); - overflow: hidden; + font-family: 'Segoe UI Variable','Segoe UI',-apple-system,BlinkMacSystemFont,sans-serif; + background: var(--bg); + background-attachment: fixed; + color: var(--text); height: 100vh; + overflow: hidden; -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; } -/* ---- Animated Background ---- */ -.bg-grid { - position: fixed; - inset: 0; - background-image: - linear-gradient(rgba(99, 102, 241, 0.03) 1px, transparent 1px), - linear-gradient(90deg, rgba(99, 102, 241, 0.03) 1px, transparent 1px); - background-size: 60px 60px; - z-index: 0; - pointer-events: none; -} - -.bg-glow { - position: fixed; - border-radius: 50%; - filter: blur(120px); - opacity: 0.4; - z-index: 0; - pointer-events: none; - animation: glowFloat 20s ease-in-out infinite; -} - -.bg-glow-1 { - width: 500px; - height: 500px; - background: radial-gradient(circle, rgba(99, 102, 241, 0.15), transparent 70%); - top: -100px; - right: -100px; -} - -.bg-glow-2 { - width: 400px; - height: 400px; - background: radial-gradient(circle, rgba(139, 92, 246, 0.1), transparent 70%); - bottom: -100px; - left: 20%; - animation-delay: -10s; -} - -@keyframes glowFloat { - 0%, 100% { transform: translate(0, 0) scale(1); } - 33% { transform: translate(30px, -20px) scale(1.05); } - 66% { transform: translate(-20px, 15px) scale(0.95); } -} - -/* ---- App Layout ---- */ +/* ---- App Shell ---- */ .app { - display: flex; - height: 100vh; - position: relative; - z-index: 1; -} - -/* ---- Sidebar ---- */ -.sidebar { - width: var(--sidebar-width); - background: rgba(8, 11, 20, 0.85); - backdrop-filter: blur(20px) saturate(180%); - border-right: 1px solid var(--border); display: flex; flex-direction: column; - flex-shrink: 0; - z-index: 10; + height: 100vh; } -.sidebar-header { - padding: 24px 20px 20px; - border-bottom: 1px solid var(--border); -} - -.logo { +/* ---- Title Bar ---- */ +.titlebar { display: flex; + justify-content: space-between; align-items: center; - gap: 12px; + padding: 14px 24px 10px; + border-bottom: 1px solid var(--border); + background: rgba(10,22,40,0.8); + backdrop-filter: blur(12px); + flex-shrink: 0; } -.logo-icon { - width: 40px; - height: 40px; - background: linear-gradient(135deg, var(--accent), var(--accent-dark)); +.titlebar-left { display:flex; align-items:center; gap:12px; } + +.app-logo { + width: 36px; height: 36px; + background: linear-gradient(135deg, var(--accent), var(--cyan)); + border-radius: 10px; + display: flex; align-items:center; justify-content:center; + box-shadow: 0 2px 12px var(--accent-glow); +} +.app-logo svg { width:20px; height:20px; color:white; } + +.app-title { display:flex; align-items:baseline; gap:6px; } +.app-name { + font-size: 18px; font-weight: 800; + letter-spacing: 2px; + background: linear-gradient(90deg, #fff, #94a3b8); + -webkit-background-clip: text; -webkit-text-fill-color: transparent; +} +.app-edition { + font-size: 11px; font-weight: 700; + color: var(--cyan); + background: rgba(6,182,212,0.15); + padding: 2px 8px; border-radius: 4px; + letter-spacing: 1px; +} + +.sys-status { + display:flex; align-items:center; gap:6px; + font-size: 12px; color: var(--text-sec); +} +.status-led { + width:8px; height:8px; border-radius:50%; + background: var(--green); + box-shadow: 0 0 8px var(--green); + animation: pulse 2s ease-in-out infinite; +} +@keyframes pulse { 0%,100%{opacity:1} 50%{opacity:.4} } + +/* ---- Toolbar ---- */ +.toolbar { + display: flex; + gap: 4px; + padding: 12px 24px; + border-bottom: 1px solid var(--border); + background: rgba(10,22,40,0.5); + flex-shrink: 0; +} + +.toolbar-tab { + display: flex; flex-direction: column; + align-items: center; gap: 6px; + padding: 12px 28px; + border: 1px solid transparent; + border-radius: var(--r); + background: transparent; + color: var(--text-sec); + font-size: 11.5px; font-weight: 600; + letter-spacing: 0.5px; + text-transform: uppercase; + cursor: pointer; + transition: all .2s ease; + font-family: inherit; + position: relative; +} + +.toolbar-tab:hover { background: var(--surface); color: var(--text); } + +.toolbar-tab.active { + background: linear-gradient(180deg, var(--surface-light), var(--surface)); + border-color: var(--border-accent); + color: var(--accent-light); + box-shadow: 0 2px 12px var(--accent-glow), inset 0 1px 0 rgba(255,255,255,0.05); +} + +.tab-icon-wrap { + width: 44px; height: 44px; border-radius: 12px; + display: flex; align-items:center; justify-content:center; + transition: all .2s ease; +} + +.tab-icon-wrap svg { width: 22px; height: 22px; } + +.scan-icon-wrap { background: rgba(59,130,246,0.1); color: var(--accent); } +.drivers-icon-wrap { background: rgba(6,182,212,0.1); color: var(--cyan); } +.updates-icon-wrap { background: rgba(16,185,129,0.1); color: var(--green); } +.settings-icon-wrap { background: rgba(245,158,11,0.1); color: var(--yellow); } + +.toolbar-tab.active .scan-icon-wrap { background: rgba(59,130,246,0.2); } +.toolbar-tab.active .drivers-icon-wrap { background: rgba(6,182,212,0.2); } +.toolbar-tab.active .updates-icon-wrap { background: rgba(16,185,129,0.2); } +.toolbar-tab.active .settings-icon-wrap { background: rgba(245,158,11,0.2); } + +/* ---- Content Area ---- */ +.content-area { + flex: 1; + overflow: hidden; + position: relative; +} + +.tab-page { + display: none; + height: 100%; + animation: fadeUp .3s ease; +} +.tab-page.active { display: flex; flex-direction: column; } + +@keyframes fadeUp { + from { opacity:0; transform:translateY(8px); } + to { opacity:1; transform:translateY(0); } +} + +/* ---- Scan Hero ---- */ +.scan-hero { + flex: 1; display: flex; align-items: center; justify-content: center; - color: white; - box-shadow: 0 4px 12px var(--accent-glow-strong); + gap: 60px; + padding: 40px; } -.logo-icon svg { width: 22px; height: 22px; } +.scan-illustration { flex-shrink:0; } +.computer-svg { width: 220px; height: 200px; } -.logo-text-wrap { display: flex; flex-direction: column; } +.scan-info { max-width: 380px; } +.scan-info h2 { font-size: 28px; font-weight: 800; margin-bottom: 10px; letter-spacing:-0.5px; } +.scan-info p { color: var(--text-sec); font-size: 14px; line-height: 1.6; margin-bottom: 24px; } -.logo-text { - font-size: 15px; - font-weight: 700; - color: var(--text-primary); - letter-spacing: -0.3px; -} - -.logo-sub { - font-size: 11px; - color: var(--text-muted); - letter-spacing: 0.5px; - text-transform: uppercase; -} - -.nav-section-label { - font-size: 10px; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 1.2px; - color: var(--text-dim); - padding: 20px 24px 8px; -} - -.nav-items { list-style: none; padding: 0 12px; } - -.nav-item { - display: flex; - align-items: center; - gap: 12px; - padding: 10px 14px; - border-radius: var(--radius-sm); - cursor: pointer; - color: var(--text-secondary); - transition: all var(--duration) var(--ease); - margin-bottom: 2px; - font-size: 13.5px; - font-weight: 500; - user-select: none; - position: relative; - overflow: hidden; -} - -.nav-item svg { width: 18px; height: 18px; flex-shrink: 0; opacity: 0.7; } - -.nav-item:hover { - background: var(--bg-hover); - color: var(--text-primary); -} - -.nav-item:hover svg { opacity: 1; } - -.nav-item.active { - background: var(--bg-active); - color: var(--accent-light); - font-weight: 600; -} - -.nav-item.active svg { opacity: 1; color: var(--accent-light); } - -.nav-indicator { - position: absolute; - left: 0; - top: 50%; - transform: translateY(-50%) scaleY(0); - width: 3px; - height: 20px; - border-radius: 0 3px 3px 0; - background: var(--accent); - transition: transform var(--duration) var(--ease); -} - -.nav-item.active .nav-indicator { transform: translateY(-50%) scaleY(1); } - -.sidebar-footer { - padding: 16px 20px; - border-top: 1px solid var(--border); - margin-top: auto; - display: flex; - justify-content: space-between; - align-items: center; -} - -.app-status { - display: flex; - align-items: center; - gap: 6px; - font-size: 12px; - color: var(--text-muted); -} - -.status-dot { - width: 7px; - height: 7px; - border-radius: 50%; - background: var(--success); - box-shadow: 0 0 8px var(--success); - animation: pulse 2s ease-in-out infinite; -} - -@keyframes pulse { - 0%, 100% { opacity: 1; } - 50% { opacity: 0.5; } -} - -.version-badge { - font-size: 11px; - color: var(--text-dim); - background: var(--bg-elevated); - padding: 3px 10px; - border-radius: 20px; - border: 1px solid var(--border); -} - -/* ---- Content Area ---- */ -.content { - flex: 1; - overflow-y: auto; - padding: 32px 36px; - position: relative; -} - -.content::-webkit-scrollbar { width: 5px; } -.content::-webkit-scrollbar-track { background: transparent; } -.content::-webkit-scrollbar-thumb { background: var(--border-light); border-radius: 3px; } -.content::-webkit-scrollbar-thumb:hover { background: var(--text-dim); } - -/* ---- Pages ---- */ -.page { display: none; animation: pageIn 0.4s var(--ease); } -.page.active { display: block; } - -@keyframes pageIn { - from { opacity: 0; transform: translateY(12px); } - to { opacity: 1; transform: translateY(0); } -} - -.page-header { - display: flex; - justify-content: space-between; - align-items: flex-start; +.scan-stats { + display: flex; gap: 20px; margin-bottom: 28px; } -.page-header h1 { - font-size: 22px; - font-weight: 700; - letter-spacing: -0.5px; - display: flex; - align-items: center; - gap: 10px; -} - -.page-icon { width: 24px; height: 24px; color: var(--accent-light); } - -.subtitle { - color: var(--text-muted); - font-size: 13px; - margin-top: 4px; -} - -.header-actions { display: flex; gap: 8px; } - -.icon-btn { - width: 38px; - height: 38px; +.mini-stat { + display: flex; align-items:center; gap: 8px; + background: var(--surface); border: 1px solid var(--border); - border-radius: var(--radius-sm); - background: var(--bg-card); - color: var(--text-secondary); + border-radius: var(--r); + padding: 10px 16px; +} +.mini-stat svg { width:16px; height:16px; color: var(--accent-light); } +.mini-stat span { font-size:20px; font-weight:700; } +.mini-stat label { font-size:11px; color:var(--text-dim); text-transform:uppercase; letter-spacing:.5px; } +.mini-stat.warn svg { color: var(--yellow); } +.mini-stat.warn span { color: var(--yellow); } +.mini-stat.danger svg { color: var(--red); } +.mini-stat.danger span { color: var(--red); } + +.big-scan-btn { + display: flex; align-items:center; gap:12px; + padding: 16px 48px; + background: linear-gradient(135deg, var(--accent-dark), var(--accent)); + border: none; border-radius: 12px; + color: white; font-size: 16px; font-weight: 700; + letter-spacing: 2px; cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - transition: all var(--duration) var(--ease); + transition: all .25s ease; + font-family: inherit; + box-shadow: 0 4px 20px var(--accent-glow); } - -.icon-btn:hover { - border-color: var(--accent); - color: var(--accent-light); - background: var(--bg-active); -} - -.icon-btn svg { width: 16px; height: 16px; } - -/* ---- Glass Panel ---- */ -.glass-panel { - background: var(--bg-card); - backdrop-filter: blur(12px); - border: 1px solid var(--border); - border-radius: var(--radius); -} - -/* ---- Stats Grid ---- */ -.stats-grid { - display: grid; - grid-template-columns: repeat(4, 1fr); - gap: 16px; - margin-bottom: 24px; -} - -.stat-card { - background: var(--bg-card); - border: 1px solid var(--border); - border-radius: var(--radius); - padding: 22px 20px; - display: flex; - align-items: center; - gap: 16px; - transition: all 0.3s var(--ease); - position: relative; - overflow: hidden; -} - -.stat-card-bg { - position: absolute; - top: -30px; - right: -30px; - width: 100px; - height: 100px; - border-radius: 50%; - opacity: 0; - transition: opacity 0.3s var(--ease); -} - -.stat-card:hover .stat-card-bg { opacity: 1; } - -.stat-drivers .stat-card-bg { background: radial-gradient(circle, var(--accent-glow), transparent 70%); } -.stat-outdated .stat-card-bg { background: radial-gradient(circle, var(--warning-bg), transparent 70%); } -.stat-updates .stat-card-bg { background: radial-gradient(circle, var(--info-bg), transparent 70%); } -.stat-ram .stat-card-bg { background: radial-gradient(circle, var(--success-bg), transparent 70%); } - -.stat-card:hover { - border-color: var(--border-light); +.big-scan-btn svg { width:22px; height:22px; } +.big-scan-btn:hover { transform: translateY(-2px); - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3); + box-shadow: 0 6px 30px rgba(59,130,246,0.4); } +.big-scan-btn:active { transform:translateY(0); } -.stat-icon { - width: 48px; - height: 48px; - border-radius: 12px; - display: flex; - align-items: center; - justify-content: center; +/* ---- Step Bar ---- */ +.step-bar { + display: flex; align-items:center; justify-content:center; + gap: 0; + padding: 18px 24px; + border-top: 1px solid var(--border); + background: rgba(10,22,40,0.5); flex-shrink: 0; } -.stat-icon svg { width: 22px; height: 22px; } - -.stat-drivers .stat-icon { background: var(--accent-glow); color: var(--accent-light); } -.stat-outdated .stat-icon { background: var(--warning-bg); color: var(--warning); } -.stat-updates .stat-icon { background: var(--info-bg); color: var(--info); } -.stat-ram .stat-icon { background: var(--success-bg); color: var(--success); } - -.stat-info { flex: 1; } - -.stat-value { - font-size: 26px; - font-weight: 800; - display: block; - line-height: 1.1; - letter-spacing: -0.5px; +.step { + display:flex; align-items:center; gap:8px; + font-size: 12px; color: var(--text-dim); + font-weight: 500; } +.step.active { color: var(--accent-light); } +.step.done { color: var(--green); } -.stat-label { - font-size: 11.5px; - color: var(--text-muted); - text-transform: uppercase; - letter-spacing: 0.5px; - margin-top: 2px; +.step-num { + width:24px; height:24px; + border-radius:50%; + border: 2px solid var(--text-dim); + display:flex; align-items:center; justify-content:center; + font-size: 11px; font-weight:700; } +.step.active .step-num { border-color: var(--accent); background: var(--accent); color:white; } +.step.done .step-num { border-color: var(--green); background: var(--green); color:white; } -.stat-trend { - color: var(--text-dim); +.step-line { + width: 60px; height: 2px; + background: var(--text-dim); + margin: 0 12px; opacity: 0.3; } -.stat-trend svg { width: 32px; height: 32px; } - -/* ---- Dashboard Panels ---- */ -.dashboard-panels { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; } - -.panel { overflow: hidden; } - -.panel-header { - padding: 18px 22px; +/* ---- Tab Headers ---- */ +.tab-header { + display: flex; justify-content:space-between; align-items:center; + padding: 18px 24px; border-bottom: 1px solid var(--border); + flex-shrink: 0; +} +.tab-header-left h2 { + font-size: 16px; font-weight: 700; + display:flex; align-items:center; gap:8px; +} +.tab-header-left h2 svg { width:20px; height:20px; color:var(--accent-light); } +.result-count { font-size:12px; color:var(--text-dim); margin-top:3px; } +.warn-text { color: var(--yellow); } + +.header-btn { + display:flex; align-items:center; gap:6px; + padding: 8px 18px; + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--r-sm); + color: var(--text-sec); + font-size: 12.5px; font-weight:600; + cursor:pointer; + transition: all .2s ease; + font-family: inherit; +} +.header-btn svg { width:14px; height:14px; } +.header-btn:hover { border-color: var(--accent); color: var(--accent-light); background: var(--surface-light); } + +/* ---- Filter Strip ---- */ +.filter-strip { + display:flex; gap:6px; + padding: 12px 24px; + border-bottom: 1px solid var(--border); + flex-shrink: 0; } -.panel-title { - display: flex; - align-items: center; - gap: 10px; +.pill { + display:flex; align-items:center; gap:5px; + padding: 5px 14px; + border:1px solid var(--border); + border-radius:20px; + background:transparent; + color: var(--text-dim); + font-size:12px; font-weight:500; + cursor:pointer; + transition: all .15s ease; + font-family:inherit; } +.pill svg { width:12px; height:12px; } +.pill:hover { border-color: var(--accent); color: var(--accent-light); } +.pill.active { background:var(--accent); border-color:var(--accent); color:white; } -.panel-title svg { width: 16px; height: 16px; color: var(--accent-light); } - -.panel-title h3 { - font-size: 13px; - font-weight: 600; - color: var(--text-secondary); - text-transform: uppercase; - letter-spacing: 0.5px; +/* ---- Driver Scroll List ---- */ +.driver-scroll { + flex: 1; + overflow-y: auto; + padding: 12px 24px; } +.driver-scroll::-webkit-scrollbar { width:5px; } +.driver-scroll::-webkit-scrollbar-track { background:transparent; } +.driver-scroll::-webkit-scrollbar-thumb { background:var(--border); border-radius:3px; } -/* ---- Action Buttons ---- */ -.quick-actions { padding: 16px; display: flex; flex-direction: column; gap: 8px; } +/* Placeholder */ +.placeholder-msg { + text-align:center; + padding: 80px 20px; + color: var(--text-dim); +} +.placeholder-msg svg { width:48px; height:48px; margin-bottom:12px; opacity:.3; } +.placeholder-msg p { font-size:14px; } -.action-btn { +/* ---- Driver Row (like Driver Navigator) ---- */ +.drv-row { display: flex; align-items: center; gap: 14px; padding: 14px 18px; - border: 1px solid transparent; - border-radius: var(--radius-sm); - font-size: 14px; - font-weight: 600; - cursor: pointer; - transition: all var(--duration) var(--ease); - font-family: inherit; - text-align: left; - position: relative; - overflow: hidden; - color: white; - background: none; + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--r); + margin-bottom: 8px; + transition: all .2s ease; } +.drv-row:hover { border-color: rgba(59,130,246,0.2); background: var(--surface-hover); } +.drv-row.outdated { border-left: 3px solid var(--yellow); } +.drv-row.error { border-left: 3px solid var(--red); } -.action-btn.compact { - padding: 10px 18px; - gap: 8px; - border-radius: var(--radius-sm); - font-size: 13px; -} - -.action-btn.compact svg { width: 16px; height: 16px; } - -.action-btn-icon { - width: 40px; - height: 40px; +.drv-icon { + width:40px; height:40px; border-radius: 10px; - display: flex; - align-items: center; - justify-content: center; + display:flex; align-items:center; justify-content:center; + flex-shrink:0; + background: rgba(59,130,246,0.1); + color: var(--accent-light); +} +.drv-icon svg { width:20px; height:20px; } + +.drv-info { flex:1; min-width:0; } +.drv-name { + font-size: 13.5px; font-weight:600; + white-space:nowrap; overflow:hidden; text-overflow:ellipsis; +} +.drv-meta { font-size:11.5px; color:var(--text-dim); margin-top:2px; } + +/* Progress bar per driver */ +.drv-progress { + width: 120px; flex-shrink: 0; + display: flex; + flex-direction: column; + gap: 3px; +} +.drv-bar { + height:4px; + background: rgba(255,255,255,0.05); + border-radius:2px; + overflow:hidden; +} +.drv-bar-fill { + height:100%; + border-radius:2px; + transition: width .6s ease; +} +.drv-bar-fill.ok { background: var(--green); width:100%; } +.drv-bar-fill.warn { background: var(--yellow); width:60%; } +.drv-bar-fill.err { background: var(--red); width:30%; } +.drv-size { font-size:10px; color:var(--text-dim); text-align:right; } + +/* Action buttons per driver */ +.drv-actions { + display:flex; gap:6px; flex-shrink:0; } -.action-btn-icon svg { width: 20px; height: 20px; } +.drv-btn { + display:flex; align-items:center; gap:5px; + padding: 6px 14px; + border-radius: var(--r-sm); + border: 1px solid var(--border); + background: var(--surface-light); + color: var(--text-sec); + font-size: 11.5px; font-weight:600; + cursor:pointer; + transition: all .15s ease; + font-family:inherit; +} +.drv-btn svg { width:13px; height:13px; } +.drv-btn:hover { border-color: var(--accent); color: var(--accent-light); } -.action-btn-text { flex: 1; display: flex; flex-direction: column; } - -.action-btn-title { font-size: 13.5px; font-weight: 600; } - -.action-btn-desc { font-size: 11.5px; opacity: 0.6; font-weight: 400; margin-top: 1px; } - -.action-arrow { width: 16px; height: 16px; opacity: 0.4; transition: all var(--duration) var(--ease); } - -.action-btn:hover .action-arrow { opacity: 1; transform: translateX(3px); } - -.action-btn.primary { +.drv-btn.install-btn { background: linear-gradient(135deg, var(--accent-dark), var(--accent)); border-color: var(--accent); - box-shadow: 0 2px 8px var(--accent-glow); -} - -.action-btn.primary:hover { - box-shadow: 0 4px 20px var(--accent-glow-strong); - transform: translateY(-1px); -} - -.action-btn.primary .action-btn-icon { background: rgba(255,255,255,0.15); } - -.action-btn.blue { - background: rgba(59, 130, 246, 0.08); - border-color: rgba(59, 130, 246, 0.2); - color: var(--text-primary); -} - -.action-btn.blue .action-btn-icon { background: var(--info-bg); color: var(--info); } - -.action-btn.blue:hover { - background: rgba(59, 130, 246, 0.15); - border-color: rgba(59, 130, 246, 0.3); -} - -.action-btn.green { - background: rgba(16, 185, 129, 0.08); - border-color: rgba(16, 185, 129, 0.2); - color: var(--text-primary); -} - -.action-btn.green .action-btn-icon { background: var(--success-bg); color: var(--success); } - -.action-btn.green:hover { - background: rgba(16, 185, 129, 0.15); - border-color: rgba(16, 185, 129, 0.3); -} - -.action-btn:disabled { opacity: 0.4; cursor: not-allowed; transform: none !important; } - -/* ---- Resource Bars ---- */ -.resource-bars { padding: 22px; display: flex; flex-direction: column; gap: 20px; } - -.resource-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 8px; -} - -.resource-label-group { display: flex; align-items: center; gap: 8px; } -.resource-icon { width: 14px; height: 14px; color: var(--text-muted); } -.resource-name { font-size: 13px; font-weight: 600; color: var(--text-primary); } -.resource-value { font-size: 12px; color: var(--text-muted); font-weight: 500; } - -.progress-bar { - height: 6px; - background: rgba(255, 255, 255, 0.05); - border-radius: 3px; - overflow: hidden; - position: relative; -} - -.progress-fill { - height: 100%; - border-radius: 3px; - transition: width 1s var(--ease); - position: relative; - overflow: hidden; -} - -.progress-shine { - position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 100%; - background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent); - animation: shine 3s ease-in-out infinite; -} - -@keyframes shine { - 0% { left: -100%; } - 50% { left: 100%; } - 100% { left: 100%; } -} - -.ram-fill { background: linear-gradient(90deg, var(--success), #34d399); } -.disk-fill { background: linear-gradient(90deg, var(--info), #60a5fa); } - -/* ---- Driver List ---- */ -.driver-summary { - display: flex; - gap: 28px; - margin-bottom: 16px; - padding: 16px 22px; - align-items: center; -} - -.summary-item { - font-size: 13px; - color: var(--text-secondary); - display: flex; - align-items: center; - gap: 6px; -} - -.sum-icon { width: 14px; height: 14px; } - -.summary-item strong { - color: var(--text-primary); - font-size: 18px; - font-weight: 700; -} - -.summary-item.warning .sum-icon { color: var(--warning); } -.summary-item.warning strong { color: var(--warning); } -.summary-item.error .sum-icon { color: var(--danger); } -.summary-item.error strong { color: var(--danger); } -.summary-item.muted .sum-icon { color: var(--text-dim); } - -.filter-bar { display: flex; gap: 6px; margin-bottom: 16px; } - -.filter-btn { - display: flex; - align-items: center; - gap: 6px; - padding: 7px 14px; - border: 1px solid var(--border); - border-radius: 20px; - background: transparent; - color: var(--text-muted); - font-size: 12.5px; - font-weight: 500; - cursor: pointer; - transition: all var(--duration) var(--ease); - font-family: inherit; -} - -.filter-btn svg { width: 13px; height: 13px; } - -.filter-btn:hover { - border-color: var(--border-accent); - color: var(--accent-light); - background: var(--bg-active); -} - -.filter-btn.active { - background: var(--accent); - border-color: var(--accent); color: white; - box-shadow: 0 2px 8px var(--accent-glow); +} +.drv-btn.install-btn:hover { box-shadow: 0 2px 8px var(--accent-glow); } + +.drv-btn.installed-btn { + background: var(--green-bg); + border-color: rgba(16,185,129,0.3); + color: var(--green); + cursor: default; } -.driver-list, .update-list { display: flex; flex-direction: column; gap: 6px; } +/* Status badges */ +.drv-badge { + font-size:10px; font-weight:600; + padding: 2px 8px; border-radius:12px; + display:inline-flex; align-items:center; gap:3px; +} +.drv-badge svg { width:10px; height:10px; } +.drv-badge.signed { background:var(--green-bg); color:var(--green); } +.drv-badge.unsigned { background:var(--red-bg); color:var(--red); } -.driver-card { - background: var(--bg-card); - border: 1px solid var(--border); - border-radius: var(--radius-sm); - padding: 16px 20px; +/* ---- CTA Bar (Download All) ---- */ +.cta-bar { + padding: 16px 24px; + border-top: 1px solid var(--border); + background: rgba(10,22,40,0.8); display: flex; - align-items: center; - gap: 14px; - transition: all var(--duration) var(--ease); -} - -.driver-card:hover { - border-color: var(--border-light); - background: var(--bg-elevated); -} - -.driver-card.outdated { border-left: 3px solid var(--warning); } -.driver-card.error { border-left: 3px solid var(--danger); } - -.driver-class-icon { - width: 42px; - height: 42px; - border-radius: 10px; - display: flex; - align-items: center; justify-content: center; flex-shrink: 0; - background: var(--accent-glow); - color: var(--accent-light); } -.driver-class-icon svg { width: 20px; height: 20px; } +.cta-btn { + display:flex; align-items:center; gap:10px; + padding: 14px 48px; + background: linear-gradient(135deg, #059669, var(--green)); + border:none; border-radius:12px; + color:white; font-size:15px; font-weight:700; + letter-spacing:1px; + cursor:pointer; + transition: all .25s ease; + font-family:inherit; + box-shadow: 0 4px 20px rgba(16,185,129,0.3); +} +.cta-btn svg { width:20px; height:20px; } +.cta-btn:hover { transform:translateY(-2px); box-shadow:0 6px 30px rgba(16,185,129,0.4); } -.driver-info { flex: 1; min-width: 0; } - -.driver-name { - font-weight: 600; - font-size: 13.5px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - color: var(--text-primary); +.cta-count { + background: rgba(255,255,255,0.2); + padding: 2px 10px; border-radius:10px; + font-size:12px; } -.driver-meta { - font-size: 12px; - color: var(--text-muted); - margin-top: 3px; - display: flex; - align-items: center; - gap: 4px; -} - -.driver-meta-sep { color: var(--text-dim); } - -.driver-badges { display: flex; gap: 6px; flex-shrink: 0; } - -.badge { - font-size: 11px; - padding: 3px 10px; - border-radius: 20px; - font-weight: 600; - display: flex; - align-items: center; - gap: 4px; - border: 1px solid transparent; -} - -.badge svg { width: 11px; height: 11px; } - -.badge-signed { background: var(--success-bg); color: var(--success); border-color: rgba(16,185,129,0.2); } -.badge-unsigned { background: var(--danger-bg); color: var(--danger); border-color: rgba(239,68,68,0.2); } -.badge-outdated { background: var(--warning-bg); color: var(--warning); border-color: rgba(245,158,11,0.2); } -.badge-ok { background: var(--success-bg); color: var(--success); border-color: rgba(16,185,129,0.2); } - -/* ---- Update Cards ---- */ -.update-section-title { - font-size: 13px; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.5px; - margin: 20px 0 10px; - display: flex; - align-items: center; - gap: 8px; -} - -.update-section-title svg { width: 16px; height: 16px; } -.update-section-title.pending { color: var(--info); } -.update-section-title.installed { color: var(--success); } - -.update-card { - background: var(--bg-card); +/* ---- Update Card ---- */ +.upd-row { + padding: 14px 18px; + background: var(--surface); border: 1px solid var(--border); - border-radius: var(--radius-sm); - padding: 16px 20px; - transition: all var(--duration) var(--ease); + border-radius: var(--r); + margin-bottom: 8px; + transition: all .2s ease; } +.upd-row:hover { border-color: rgba(59,130,246,0.2); } +.upd-row.pending { border-left: 3px solid var(--accent); } +.upd-row.done { opacity:.5; } -.update-card:hover { border-color: var(--border-light); } -.update-card.installed { opacity: 0.5; } -.update-card.pending { border-left: 3px solid var(--info); } - -.update-title { - font-weight: 600; - font-size: 13.5px; - margin-bottom: 6px; - color: var(--text-primary); +.upd-title { font-size:13.5px; font-weight:600; margin-bottom:5px; } +.upd-meta { + display:flex; gap:14px; flex-wrap:wrap; + font-size:11.5px; color:var(--text-dim); } +.upd-tag { display:flex; align-items:center; gap:4px; } +.upd-tag svg { width:12px; height:12px; } +.upd-section { + font-size:12px; font-weight:700; text-transform:uppercase; + letter-spacing:.5px; margin:16px 0 8px; + display:flex; align-items:center; gap:6px; +} +.upd-section svg { width:14px; height:14px; } +.upd-section.pend { color:var(--accent-light); } +.upd-section.inst { color:var(--green); } -.update-meta { - font-size: 12px; - color: var(--text-muted); - display: flex; +/* ---- System Grid ---- */ +.sys-grid { + display:grid; + grid-template-columns: 1fr 1fr; gap: 14px; - flex-wrap: wrap; - align-items: center; + padding: 20px 24px; + overflow-y: auto; + flex: 1; } -.update-tag { - display: flex; - align-items: center; - gap: 4px; -} - -.update-tag svg { width: 12px; height: 12px; } - -/* ---- System Info ---- */ -.sysinfo-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; } - -.info-card { padding: 0; overflow: hidden; } - -.info-card-header { - padding: 16px 22px; - border-bottom: 1px solid var(--border); - display: flex; - align-items: center; - gap: 10px; - background: rgba(255, 255, 255, 0.02); -} - -.info-card-header svg { width: 18px; height: 18px; color: var(--accent-light); } - -.info-card-header h4 { - font-size: 13px; - text-transform: uppercase; - letter-spacing: 0.5px; - color: var(--text-secondary); - font-weight: 600; -} - -.info-row { - display: flex; - justify-content: space-between; - align-items: center; - padding: 12px 22px; - font-size: 13px; - border-bottom: 1px solid rgba(255, 255, 255, 0.03); - transition: background var(--duration) var(--ease); -} - -.info-row:last-child { border-bottom: none; } - -.info-row:hover { background: rgba(255, 255, 255, 0.02); } - -.info-key { - color: var(--text-muted); - display: flex; - align-items: center; - gap: 8px; -} - -.info-key svg { width: 14px; height: 14px; opacity: 0.5; } - -.info-val { - font-weight: 600; - text-align: right; - max-width: 55%; +.sys-card { + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--r); overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - color: var(--text-primary); } -/* ---- Empty State ---- */ -.empty-state { - text-align: center; - padding: 80px 20px; - color: var(--text-muted); +.sys-card-head { + display:flex; align-items:center; gap:8px; + padding: 12px 18px; + background: rgba(255,255,255,0.02); + border-bottom: 1px solid var(--border); + font-size: 12px; font-weight:700; + text-transform:uppercase; letter-spacing:.5px; + color: var(--text-sec); } +.sys-card-head svg { width:16px; height:16px; color:var(--accent-light); } -.empty-icon { - width: 80px; - height: 80px; - border-radius: 20px; - background: var(--bg-elevated); - border: 1px solid var(--border); - display: flex; - align-items: center; - justify-content: center; - margin: 0 auto 20px; +.sys-row { + display:flex; justify-content:space-between; + padding: 10px 18px; + font-size: 12.5px; + border-bottom: 1px solid rgba(255,255,255,0.03); } +.sys-row:last-of-type { border-bottom:none; } +.sys-row span:first-child { color:var(--text-dim); } +.sys-row span:last-child { font-weight:600; max-width:55%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } -.empty-icon svg { width: 36px; height: 36px; color: var(--text-dim); } +.sys-bar-wrap { padding:8px 18px 14px; } +.sys-bar { height:6px; background:rgba(255,255,255,0.05); border-radius:3px; overflow:hidden; } +.sys-bar-fill { height:100%; border-radius:3px; transition:width .8s ease; } +.ram-bar { background: linear-gradient(90deg, var(--green), #34d399); } +.disk-bar { background: linear-gradient(90deg, var(--accent), var(--accent-light)); } -.empty-state h3 { - font-size: 16px; - color: var(--text-secondary); - margin-bottom: 6px; +/* ---- Footer ---- */ +.app-footer { + display:flex; justify-content:space-between; align-items:center; + padding: 8px 24px; + border-top: 1px solid var(--border); + background: rgba(10,22,40,0.6); + font-size: 11px; color: var(--text-dim); + flex-shrink: 0; } +.footer-right { display:flex; gap:4px; } +.footer-btn { + display:flex; align-items:center; gap:4px; + padding:4px 12px; + background:transparent; border:1px solid transparent; + border-radius:4px; + color:var(--text-dim); font-size:11px; + cursor:pointer; + font-family:inherit; + transition: all .15s ease; +} +.footer-btn svg { width:12px; height:12px; } +.footer-btn:hover { color:var(--text-sec); border-color:var(--border); } -.empty-state p { font-size: 13px; } - -/* ---- Loading Overlay ---- */ +/* ---- Loading ---- */ .loading-overlay { - position: fixed; - inset: 0; - background: rgba(8, 11, 20, 0.9); - backdrop-filter: blur(8px); - display: flex; - align-items: center; - justify-content: center; - z-index: 1000; + position:fixed; inset:0; + background: rgba(8,12,24,0.92); + backdrop-filter: blur(6px); + display:flex; align-items:center; justify-content:center; + z-index:1000; } - .loading-card { - text-align: center; - padding: 48px; - background: var(--bg-card-solid); - border: 1px solid var(--border); - border-radius: 20px; - box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5); + text-align:center; + padding: 44px 56px; + background: var(--surface); + border:1px solid var(--border); + border-radius:16px; + box-shadow: 0 16px 48px rgba(0,0,0,.5); } - .loading-spinner { - width: 72px; - height: 72px; - margin: 0 auto 24px; - position: relative; + width:64px; height:64px; + margin:0 auto 20px; + position:relative; } - .spinner-ring { - position: absolute; - inset: 0; - border: 3px solid var(--border); + position:absolute; inset:0; + border:3px solid var(--border); border-top-color: var(--accent); - border-radius: 50%; + border-radius:50%; animation: spin 1s linear infinite; } - -@keyframes spin { to { transform: rotate(360deg); } } - -.spinner-icon { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - color: var(--accent-light); -} - -.spinner-icon svg { width: 28px; height: 28px; } - -.loading-title { - font-size: 16px; - font-weight: 700; - color: var(--text-primary); - margin-bottom: 4px; -} - -.loading-sub { - font-size: 13px; - color: var(--text-muted); - margin-bottom: 20px; -} - -.loading-dots { - display: flex; - justify-content: center; - gap: 6px; -} - +@keyframes spin { to{transform:rotate(360deg)} } +.spinner-icon { position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); color:var(--accent-light); } +.spinner-icon svg { width:24px; height:24px; } +.loading-title { font-size:15px; font-weight:700; margin-bottom:14px; } +.loading-dots { display:flex; justify-content:center; gap:5px; } .loading-dots span { - width: 6px; - height: 6px; - border-radius: 50%; - background: var(--accent); + width:5px; height:5px; border-radius:50%; + background:var(--accent); animation: dotPulse 1.4s ease-in-out infinite; } - -.loading-dots span:nth-child(2) { animation-delay: 0.2s; } -.loading-dots span:nth-child(3) { animation-delay: 0.4s; } - +.loading-dots span:nth-child(2){animation-delay:.2s} +.loading-dots span:nth-child(3){animation-delay:.4s} @keyframes dotPulse { - 0%, 80%, 100% { opacity: 0.3; transform: scale(0.8); } - 40% { opacity: 1; transform: scale(1); } -} - -/* ---- Responsive ---- */ -@media (max-width: 1000px) { - .stats-grid { grid-template-columns: repeat(2, 1fr); } - .dashboard-panels { grid-template-columns: 1fr; } - .sysinfo-grid { grid-template-columns: 1fr; } + 0%,80%,100%{opacity:.3;transform:scale(.8)} + 40%{opacity:1;transform:scale(1)} }