From 0aa491978a5e851684aeb464335d2130f0cd9af2 Mon Sep 17 00:00:00 2001 From: vndangkhoa Date: Wed, 11 Mar 2026 08:13:24 +0700 Subject: [PATCH] feat: add web app support, update disk usage UI, hide empty categories --- README.md | 7 +- backend/main.go | 20 ++- src/components/Dashboard.tsx | 273 ++++++++++++++++++++--------------- src/main.tsx | 8 + 4 files changed, 184 insertions(+), 124 deletions(-) diff --git a/README.md b/README.md index 9b81f1b..2af94f6 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,8 @@ A modern, high-performance system optimizer for macOS, built with **Electron**, - **Flash Clean**: Instantly remove system caches, logs, Xcode cache, Homebrew cache, and manage Trash with a detailed inspection view. - **App Uninstaller**: View installed applications, their sizes, and thoroughly remove them along with their associated preference files and caches. - **Deep Clean**: Scan for large files and heavy folders. -- **Real-time Monitoring**: Track disk usage and category sizes. +- **Real-time Monitoring**: Track disk usage and category sizes with accurate categorical percentages. +- **Web App Support**: Access the fully functional dashboard remotely or locally via any standard web browser at `http://localhost:36969` (or your NAS host IP). - **Native Menubar Integration**: Includes a responsive, monochrome template icon that adapts to macOS light/dark modes perfectly. - **Cross-Platform**: Runs natively with compiled Go backends on Apple Silicon (M1/M2/M3), Intel Macs, and Windows. @@ -39,10 +40,10 @@ To create distributable release binaries (Universal `.dmg` for macOS, Portable ` ### 1. Build the App ```bash # macOS Universal DMG -pnpm run build && pnpm run electron:build && npx electron-builder --mac --universal +pnpm run build:mac # Windows Portable EXE -pnpm run build && pnpm run electron:build && npx electron-builder --win portable --x64 +pnpm run build:win ``` ### 2. Locate the Installer diff --git a/backend/main.go b/backend/main.go index f0f1d22..127cffe 100644 --- a/backend/main.go +++ b/backend/main.go @@ -47,8 +47,24 @@ func main() { http.HandleFunc("/api/apps/action", handleAppAction) http.HandleFunc("/api/apps/uninstall", handleAppUninstall) - // Static File Serving is handled directly by Electron. - // Backend only needs to provide API routes. + // Serve Static Files for Web Mode + // This serves the built Vite frontend from the `dist` folder. + distPath := filepath.Join("..", "dist") + if _, err := os.Stat("dist"); err == nil { + distPath = "dist" // If running directly from the project root + } + + fs := http.FileServer(http.Dir(distPath)) + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + // If the requested file exists, serve it + path := filepath.Join(distPath, r.URL.Path) + if _, err := os.Stat(path); err == nil { + fs.ServeHTTP(w, r) + return + } + // Otherwise, fallback to index.html for SPA routing + http.ServeFile(w, r, filepath.Join(distPath, "index.html")) + }) fmt.Printf("🚀 Antigravity Backend running on http://localhost%s\n", Port) diff --git a/src/components/Dashboard.tsx b/src/components/Dashboard.tsx index 53bfefd..5e4e051 100644 --- a/src/components/Dashboard.tsx +++ b/src/components/Dashboard.tsx @@ -414,13 +414,14 @@ export function Dashboard() { {isSystemDrive && segments.length > 0 ? ( segments.map((seg, i) => { const width = total > 0 ? (seg.size / total) * 100 : 0; + const segUsedPercent = used > 0 ? (seg.size / used) * 100 : 0; if (width < 0.5) return null; // Hide tiny segments return (
); }) @@ -436,12 +437,18 @@ export function Dashboard() { {/* Legend for System Drive */} {isSystemDrive && segments.length > 0 && (
- {segments.map((seg, i) => seg.size > 0.1 && ( -
-
- {seg.label} -
- ))} + {segments.map((seg, i) => { + if (seg.size <= 0.1) return null; + const width = total > 0 ? (seg.size / total) * 100 : 0; + return ( +
+
+ + {seg.label} {width > 0 && `(${width.toFixed(1)}%)`} + +
+ ); + })}
)}
@@ -481,118 +488,146 @@ export function Dashboard() {
- } - label="Applications" - size={categorySizes.apps >= 0 ? formatBytes(categorySizes.apps) : "Not Scanned"} - onClick={() => runScan('apps', 'Applications')} - actionIcon - description="Installed apps & support files" - /> - } - label="Documents" - size={categorySizes.documents >= 0 ? formatBytes(categorySizes.documents) : "Not Scanned"} - onClick={() => runScan('docs', 'Documents')} - actionIcon - description="Personal documents folder" - /> - } - label="Archives" - size={categorySizes.archives !== undefined && categorySizes.archives >= 0 ? formatBytes(categorySizes.archives) : "Not Scanned"} - onClick={() => runScan('archives', 'Archives')} - actionIcon - description="Compressed files (.zip, .rar, .7z)" - /> - } - label="Downloads" - size={categorySizes.downloads >= 0 ? formatBytes(categorySizes.downloads) : "Not Scanned"} - onClick={() => runScan('downloads', 'Downloads')} - actionIcon - description="Downloads folder items" - /> - } - label="Desktop" - size={categorySizes.desktop >= 0 ? formatBytes(categorySizes.desktop) : "Not Scanned"} - onClick={() => runScan('desktop', 'Desktop Items')} - actionIcon - description="Files on your desktop" - /> - } - label="Music" - size={categorySizes.music >= 0 ? formatBytes(categorySizes.music) : "Not Scanned"} - onClick={() => runScan('music', 'Music Library')} - actionIcon - description="iTunes/Music library & files" - /> - } - label="Movies" - size={categorySizes.movies >= 0 ? formatBytes(categorySizes.movies) : "Not Scanned"} - onClick={() => runScan('movies', 'Movies & Videos')} - actionIcon - description="Movies folder & video files" - /> - } - label="Disk Images" - size={categorySizes.virtual_machines !== undefined && categorySizes.virtual_machines >= 0 ? formatBytes(categorySizes.virtual_machines) : "Not Scanned"} - onClick={() => runScan('vms', 'Disk Images')} - actionIcon - description="ISOs, VM disks, & installers" - /> - } - label="Games" - size={categorySizes.games !== undefined && categorySizes.games >= 0 ? formatBytes(categorySizes.games) : "Not Scanned"} - onClick={() => runScan('games', 'Games Libraries')} - actionIcon - description="Steam, Epic, EA, Ubisoft" - /> - } - label="AI Models" - size={categorySizes.ai !== undefined && categorySizes.ai >= 0 ? formatBytes(categorySizes.ai) : "Not Scanned"} - onClick={() => runScan('ai', 'AI Tools')} - actionIcon - description="ComfyUI, WebUI, Checkpoints" - /> - } - label="Docker" - size={categorySizes.docker !== undefined && categorySizes.docker >= 0 ? formatBytes(categorySizes.docker) : "Not Scanned"} - onClick={() => runScan('docker', 'Docker Data')} - actionIcon - description="Docker Desktop disk usage" - /> - } - label="System Cache" - size={categorySizes.cache !== undefined && categorySizes.cache >= 0 ? formatBytes(categorySizes.cache) : "Not Scanned"} - onClick={() => runScan('cache', 'Cache & Temp')} - actionIcon - description="Browser & System temporary files" - /> - } - label="iCloud Drive" - size={categorySizes.icloud >= 0 ? formatBytes(categorySizes.icloud) : "Not Scanned"} - onClick={() => runScan('icloud', 'iCloud Drive')} - actionIcon - description="Local iCloud file copies" - /> - } - label="Photos" - size={categorySizes.photos >= 0 ? formatBytes(categorySizes.photos) : "Not Scanned"} - onClick={() => runScan('photos', 'Photos')} - actionIcon - description="Photos library caches" - /> + {categorySizes.apps !== 0 && ( + } + label="Applications" + size={categorySizes.apps >= 0 ? formatBytes(categorySizes.apps) : "Not Scanned"} + onClick={() => runScan('apps', 'Applications')} + actionIcon + description="Installed apps & support files" + /> + )} + {categorySizes.documents !== 0 && ( + } + label="Documents" + size={categorySizes.documents >= 0 ? formatBytes(categorySizes.documents) : "Not Scanned"} + onClick={() => runScan('docs', 'Documents')} + actionIcon + description="Personal documents folder" + /> + )} + {categorySizes.archives !== 0 && ( + } + label="Archives" + size={categorySizes.archives !== undefined && categorySizes.archives >= 0 ? formatBytes(categorySizes.archives) : "Not Scanned"} + onClick={() => runScan('archives', 'Archives')} + actionIcon + description="Compressed files (.zip, .rar, .7z)" + /> + )} + {categorySizes.downloads !== 0 && ( + } + label="Downloads" + size={categorySizes.downloads >= 0 ? formatBytes(categorySizes.downloads) : "Not Scanned"} + onClick={() => runScan('downloads', 'Downloads')} + actionIcon + description="Downloads folder items" + /> + )} + {categorySizes.desktop !== 0 && ( + } + label="Desktop" + size={categorySizes.desktop >= 0 ? formatBytes(categorySizes.desktop) : "Not Scanned"} + onClick={() => runScan('desktop', 'Desktop Items')} + actionIcon + description="Files on your desktop" + /> + )} + {categorySizes.music !== 0 && ( + } + label="Music" + size={categorySizes.music >= 0 ? formatBytes(categorySizes.music) : "Not Scanned"} + onClick={() => runScan('music', 'Music Library')} + actionIcon + description="iTunes/Music library & files" + /> + )} + {categorySizes.movies !== 0 && ( + } + label="Movies" + size={categorySizes.movies >= 0 ? formatBytes(categorySizes.movies) : "Not Scanned"} + onClick={() => runScan('movies', 'Movies & Videos')} + actionIcon + description="Movies folder & video files" + /> + )} + {categorySizes.virtual_machines !== 0 && ( + } + label="Disk Images" + size={categorySizes.virtual_machines !== undefined && categorySizes.virtual_machines >= 0 ? formatBytes(categorySizes.virtual_machines) : "Not Scanned"} + onClick={() => runScan('vms', 'Disk Images')} + actionIcon + description="ISOs, VM disks, & installers" + /> + )} + {categorySizes.games !== 0 && ( + } + label="Games" + size={categorySizes.games !== undefined && categorySizes.games >= 0 ? formatBytes(categorySizes.games) : "Not Scanned"} + onClick={() => runScan('games', 'Games Libraries')} + actionIcon + description="Steam, Epic, EA, Ubisoft" + /> + )} + {categorySizes.ai !== 0 && ( + } + label="AI Models" + size={categorySizes.ai !== undefined && categorySizes.ai >= 0 ? formatBytes(categorySizes.ai) : "Not Scanned"} + onClick={() => runScan('ai', 'AI Tools')} + actionIcon + description="ComfyUI, WebUI, Checkpoints" + /> + )} + {categorySizes.docker !== 0 && ( + } + label="Docker" + size={categorySizes.docker !== undefined && categorySizes.docker >= 0 ? formatBytes(categorySizes.docker) : "Not Scanned"} + onClick={() => runScan('docker', 'Docker Data')} + actionIcon + description="Docker Desktop disk usage" + /> + )} + {categorySizes.cache !== 0 && ( + } + label="System Cache" + size={categorySizes.cache !== undefined && categorySizes.cache >= 0 ? formatBytes(categorySizes.cache) : "Not Scanned"} + onClick={() => runScan('cache', 'Cache & Temp')} + actionIcon + description="Browser & System temporary files" + /> + )} + {categorySizes.icloud !== 0 && ( + } + label="iCloud Drive" + size={categorySizes.icloud >= 0 ? formatBytes(categorySizes.icloud) : "Not Scanned"} + onClick={() => runScan('icloud', 'iCloud Drive')} + actionIcon + description="Local iCloud file copies" + /> + )} + {categorySizes.photos !== 0 && ( + } + label="Photos" + size={categorySizes.photos >= 0 ? formatBytes(categorySizes.photos) : "Not Scanned"} + onClick={() => runScan('photos', 'Photos')} + actionIcon + description="Photos library caches" + /> + )} } label="Trash" diff --git a/src/main.tsx b/src/main.tsx index bef5202..c6a08df 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -3,6 +3,14 @@ import { createRoot } from 'react-dom/client' import './index.css' import App from './App.tsx' +if ('serviceWorker' in navigator) { + navigator.serviceWorker.getRegistrations().then((registrations) => { + for (let registration of registrations) { + registration.unregister(); + } + }); +} + createRoot(document.getElementById('root')!).render(