From f3f3bf19b3f4e3af287ab43cd4b6a41b0452999d Mon Sep 17 00:00:00 2001 From: Kayshen Xu Date: Thu, 26 Feb 2026 21:47:42 +0800 Subject: [PATCH] V0.0.3 (#12) * feat(editor): add github releases auto update flow Enable desktop auto-update checks and install prompt so packaged apps can update from published GitHub Releases. * fix(canvas): keep frame labels aligned during zoom Draw labels in viewport space with zoom-aware positioning so frame titles stay anchored while panning and zooming. * fix(ai): disable chat interactions when no models connected Prevent sending and shortcut actions when provider models are unavailable, and show a concise model picker hint to avoid false-available UI states. * docs: refresh readme community and demo section Embed the product demo video inline, add a styled Discord community entry, and update media assets for clearer README presentation. * chore: update nitro external dependency settings Align SSR bundling with current runtime usage by removing the unnecessary @opencode-ai external entries. * feat(electron): enhance macOS build process and update auto-updater - Added a new build script for macOS that allows building for both arm64 and x64 architectures. - Updated GitHub Actions workflow to support separate build arguments for macOS arm64 and x64. - Implemented a custom GitHub update provider for macOS to handle versioning based on architecture. - Introduced a new icon for the electron app and removed the old logo image. * feat(editor): enhance update ready banner with additional states and UI improvements - Added new state management for checking updates and dismissed status. - Improved visibility logic for the update banner based on update states. - Enhanced UI to display current and latest version information, download progress, and error messages. - Updated layout and styling for better user experience and accessibility. * docs: replace stripped video embed with clickable cover link GitHub sanitizes README HTML video tags, so the embedded demo player does not render on repository pages. Use a clickable poster image and explicit watch link to preserve the demo entry point across GitHub README rendering. --------- Co-authored-by: Fini --- README.md | 9 +- src/components/editor/update-ready-banner.tsx | 199 +++++++++++++++--- 2 files changed, 177 insertions(+), 31 deletions(-) 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} +

+ )} + +
+ + + +
+
)