feat(desktop): render rich release notes and notify users of updates#2337
feat(desktop): render rich release notes and notify users of updates#2337devcool20 wants to merge 19 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
@devcool20 is attempting to deploy a commit to the Different AI Team on Vercel. A member of the Team first needs to authorize it. |
There was a problem hiding this comment.
1 issue found across 9 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
…mise-resolving design
…ling, click targets, visual alignment, type cleanups)
|
@evanklem addressed all the issues raised and updated the pr description. |
There was a problem hiding this comment.
2 issues found across 8 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
…rkdown rendering in release notes
There was a problem hiding this comment.
1 issue found across 3 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
…ates, and guard window usage
…preserve existing link relationships
…ing link target security
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Pablosinyores
left a comment
There was a problem hiding this comment.
Nice to see the release notes sanitized through DOMPurify + the noopener/noreferrer hook.
One blocker: apps/app/src/components/ui/safe-html-renderer.tsx has a duplicated leftover fragment at lines 36-42, after the if (typeof window …) hook block already closes at line 35:
35 }
36 if (!relParts.includes("noreferrer")) {
37 relParts.push("noreferrer");
38 }
39 node.rel = relParts.join(" ");
40 }
41 });
42 }
At module top level relParts/node are out of scope and the }); has no matching call, so this will not type-check/compile — looks like a copy-paste of the hook tail. Deleting lines 36-42 fixes it.
Two smaller things while you are in there:
marked.parse(content) as string:marked.parseis typedstring | Promise<string>. The cast is only safe while no async marked extensions are registered; using an explicit sync parse (or handling the Promise) is more robust.ADD_ATTR: ["class"]lets the release-notes HTML carry arbitraryclassattributes that can pull in app styling. If the rendered notes do not need it, droppingclasstightens the surface.
…allowed class attribute
|
@Pablosinyores done! resolved the window target check (now securing all custom targets), forced marked.parse to run synchronously with { async: false }, and dropped the allowed class attribute from DOMPurify. also, the code fragment you saw was a copy-paste duplication error inside the bot's suggestion comment, so the actual code file is clean. thanks for the catch! |
|
Re-reviewed at The four bugs from last round are actually fixed: the toast has a working button and a pinned close, the checker isn't looping every 5 seconds, the modal wraps long URLs, and the emoji is gone. Notes show during download and ready now too. After that it falls apart. 1.
|
| Element | What the app uses | This modal |
|---|---|---|
| Corners | rounded-4xl |
rounded-2xl (session-page.tsx:292), squarer than every other modal |
| Title | bare <DialogTitle> |
text-xl font-bold + a custom border-b header |
| Footer | base DialogFooter w/ bg-muted/20 |
border-t pt-3, no muted bar |
| Version pill | <Badge> (badge.tsx) |
hand-rolled <span class="rounded-full bg-primary/10 ... font-mono"> (line 295) |
| Button | default <Button> |
rounded-xl px-5, "Awesome!" hardcoded (line 306) |
| Close X | one, from base | never sets showCloseButton={false}, so the default bg-secondary X renders too, over the custom header |
<DialogContent> + <DialogTitle> + <DialogFooter> + <Badge> would have matched the app for free.
Smaller stuff
electron-updater-store.ts:4importsisElectronRuntimeand never uses it.updates-view.tsx:217puts bothtext-foregroundandtext-muted-foregroundon one<h4>. Tailwind keeps the last, sotext-foregrounddoes nothing.- The new localStorage keys don't match the rest of the app:
openwork:pending-release-version(session-page.tsx:334) vs the existing dottedopenwork.react.settings.*. electron-updater-store.ts:390hangs the store offwindow.__useElectronUpdaterStorebehind only atypeof windowcheck, so it ships in production builds.- The toast says "Click to view release notes" (
en.ts:740) next to a "View Update" button (en.ts:741) on a card that's already clickable. Three ways to do one thing, and the text matches none of them. - The update goes through
notifyAlert, which is for failures and collapses bursts onto a shared toast id (notifications.ts). An "update available" can collapse with or get replaced by an unrelated error toast. Wrong channel for it.
How this got tested
The screenshots set state by hand through window.__useElectronUpdaterStore and localStorage instead of running an update. That's why none of this gets caught: nothing runs the check, the download, or the real release-notes HTML. The What's New shot even calls setAppVersion("0.18.0") manually, which is the exact trigger that doesn't fire on a real reboot with auto-checks off (#3). Run one real update on a packaged build and most of this shows up.
What's actually fine
The store internals are good. The checkId guards and the finally that clears activeCheckPromise handle channel switches and concurrent checks, and the earlier race comments are resolved. The "navigate" action is wired right (notification-center.tsx:97-98).
Overall
The concurrency work is solid and the four UI bugs are fixed. The blocker is the same as last time: the main feature ships in a file that doesn't compile, and that was already pointed out. Build it, run one real update, and square the "typecheck passed" claim with the branch. Then fix the two subscriptions, decouple the modal trigger, scope the DOMPurify hook, and use the existing Dialog and Badge.
…ions, startup appVersion initialization, modal styling alignment, and notifications channel
There was a problem hiding this comment.
1 issue found across 6 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
…revent ReferenceError
|
@evanklem, done! as per the previous build oversight (that leftover duplicate fragment got brought in during a rebase). all points are fully resolved in 714ae18: compilation: cleaned up the leftover fragment in safe-html-renderer.tsx. tested with a fresh pnpm typecheck and build. |
…ettings namespace
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
|
You're iterating quickly on this pull request. To help protect your rate limits, cubic has paused automatic reviews on new pushes for now—when you're ready for another review, comment |


Summary
This PR addresses issue #2191 by showing informative, beautifully styled release notes to users when shipping or launching a new release. It introduces background update checks, status bar notifications, and a post-update welcome screen.
Related Issues
Closes #2191
Background & Goal
Currently, release notes in the updates settings pane are rendered as unformatted raw markdown text inside a small text box and are hidden during download or ready states. Furthermore, update checks only occur when manually visiting the Settings page, leaving users unaware of available updates. This PR makes release notes rich and informative for IT/power users and non-technical users alike, ensuring they can see what is new before and after updating.
Detailed Decisions
SafeHtmlRendererutilizingDOMPurify(with allowed class/link attributes) to sanitize and render HTML notes natively.[&_a]:text-primary [&_a]:underline). Appliedbreak-words [word-break:break-word] whitespace-normalclasses to prevent long URL links (e.g. GitHub commit hash URLs) from overflow clipping in the What's New welcome screen.BackgroundUpdatereffect to re-run and reset the startup delay timer every time state changed (e.g., transitioning to "checking"), resulting in continuous 5-second check loops. We resolved this by extracting only the stablecheckForUpdatesfunction and using a ReactuseReffor local preferences, ensuring the timer is registered exactly once on mount./settings/updateson the updater toast. Redesigned the custom SonnerToastCardheader layout to top-align the dismiss close (X) button viaflex items-start justify-between gap-2 shrink-0, preventing it from rendering inline next to text.🎉from the What's New modal header title to align with the rest of the application's clean casing patterns.Before & After
Checklist / Completed Tasks
useElectronUpdaterStoreto share update status reactively across all UI components and sync with IPC events (e.g. download progress).SafeHtmlRendererto handle native HTML and Markdown notes securely viaDOMPurify.AppRootrunning update checks 5 seconds after startup and every 4 hours, adding a notification to the Notification Center if a new version is found."navigate"action type in the notification center (notification-store.tsandnotification-center.tsx), letting users click the View Update button in the notification card to route directly to Settings → Updates.bg-sky-9for available updates, pulsingbg-green-9when the update is ready to install).localStorage. Displays a welcome dialog with rich release notes immediately after an update installs.window.__useElectronUpdaterStoreglobally to simplify testing different updater states from the browser DevTools console.checkIdverification insidehandleCheckResultat invocation and after async version validation.Verification Performed
1. Automated Tests
Run unit test suite:
bun test tests/electron-updater-store.test.tsResult:
6 pass, 0 failRan compiler verification to ensure there were no TypeScript issues:
Result: Completed successfully.
2. Manual Verification
Screenshots
What's New Welcome Modal (Styled Link & Wrapping)
Updates Settings View & Aligned Toast with View Action
Original Escaped HTML Issue