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 driver information was returned.
' + - 'No drivers found
' + this.esc(result.error) + '
' + - '' + this.esc(result.error) + '
Your system is fully updated.
' + - 'Your system is fully up to date!