diff --git a/README.md b/README.md index 7cd5cb24e..bab21ce69 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,12 @@ Open-source vector design tool with a **Design-as-Code** philosophy. An alternat Design visually on an infinite canvas, generate code from designs, and let AI build entire screens from a single prompt — all in one tool that runs in your browser or as a native desktop app.

- + + OpenPencil demo video (click to play) + +

+

+ ▶ Watch demo video

## Highlights diff --git a/src/components/editor/update-ready-banner.tsx b/src/components/editor/update-ready-banner.tsx index cfe01f764..7f3397ba5 100644 --- a/src/components/editor/update-ready-banner.tsx +++ b/src/components/editor/update-ready-banner.tsx @@ -1,10 +1,12 @@ import { useEffect, useState } from 'react' -import { Loader2 } from 'lucide-react' +import { AlertCircle, CheckCircle2, Download, Loader2, RefreshCw, Sparkles } from 'lucide-react' import { Button } from '@/components/ui/button' export default function UpdateReadyBanner() { const [updateState, setUpdateState] = useState(null) const [isInstalling, setIsInstalling] = useState(false) + const [isChecking, setIsChecking] = useState(false) + const [dismissed, setDismissed] = useState(false) useEffect(() => { const updater = window.electronAPI?.updater @@ -29,6 +31,13 @@ export default function UpdateReadyBanner() { } }, []) + useEffect(() => { + if (!updateState) return + if (updateState.status === 'available' || updateState.status === 'downloading' || updateState.status === 'downloaded' || updateState.status === 'error') { + setDismissed(false) + } + }, [updateState]) + const handleInstall = async () => { if (!window.electronAPI?.updater) return @@ -39,39 +48,173 @@ export default function UpdateReadyBanner() { } } - if (!updateState || updateState.status !== 'downloaded') { + const handleCheckUpdates = async () => { + if (!window.electronAPI?.updater) return + setIsChecking(true) + try { + const next = await window.electronAPI.updater.checkForUpdates() + setUpdateState(next) + } finally { + setIsChecking(false) + } + } + + if (!updateState) { return null } + const visible = + !dismissed + && (updateState.status === 'checking' + || updateState.status === 'available' + || updateState.status === 'downloading' + || updateState.status === 'downloaded' + || updateState.status === 'error') + + if (!visible) { + return null + } + + const progress = Math.max(0, Math.min(100, Math.round(updateState.downloadProgress ?? 0))) + const releaseDate = updateState.releaseDate + ? new Date(updateState.releaseDate).toLocaleDateString() + : null + + const titleByStatus: Partial> = { + checking: 'Checking for updates', + available: 'Update found', + downloading: 'Downloading update', + downloaded: 'Ready to install', + error: 'Update failed', + } + + const subtitleByStatus: Partial> = { + checking: 'Looking for the latest release...', + available: updateState.latestVersion ? `Version ${updateState.latestVersion} is available.` : 'A new version is available.', + downloading: updateState.latestVersion + ? `Version ${updateState.latestVersion} is downloading in the background.` + : 'Downloading update package in the background.', + downloaded: updateState.latestVersion + ? `Version ${updateState.latestVersion} has been downloaded.` + : 'The update has been downloaded.', + error: updateState.error || 'Unable to check or download the update.', + } + return ( -
-
-
-

- Update Ready To Install -

-

- {updateState.latestVersion ? `Version ${updateState.latestVersion} has been downloaded.` : 'A new version has been downloaded.'} -

-

- Can take 10-15 seconds for the app to relaunch automatically. -

+
+
+
+
+
+
+ + Software Update +
+

+ {titleByStatus[updateState.status] || 'Software Update'} +

+

+ {subtitleByStatus[updateState.status]} +

+
+ +
- +
+
+
+ Current + {updateState.currentVersion || 'Unknown'} +
+
+ Latest + {updateState.latestVersion || 'Checking...'} +
+
+ + {(updateState.status === 'downloading' || updateState.status === 'available') && ( +
+
+ + + Download Progress + + {progress}% +
+
+
+
+
+ )} + + {updateState.status === 'error' && ( +
+ + {updateState.error || 'Unknown updater error.'} +
+ )} + + {updateState.status === 'downloaded' && ( +
+ + Restart to apply update. Relaunch usually takes 10-15 seconds. +
+ )} + + {releaseDate && ( +

+ Release date: {releaseDate} +

+ )} + +
+ + + +
+
)