Skip to content

Agentic UI: add persistent notifications for updates - #4204

Merged
bcotrim merged 15 commits into
trunkfrom
add/persistent-cards-update-notification
Jul 23, 2026
Merged

Agentic UI: add persistent notifications for updates#4204
bcotrim merged 15 commits into
trunkfrom
add/persistent-cards-update-notification

Conversation

@katinthehatsite

@katinthehatsite katinthehatsite commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Related issues

Related to STU-1984

Builds on #4203 (toast notifications), which landed separately.

How AI was used in this PR

The initial implementation is @katinthehatsite's work from the original branch.

The follow-up work — addressing review findings, simplifying the dismissal plumbing, and merging trunk — was done with Claude Code and reviewed before pushing.

Proposed Changes

Adds persistent message cards to the Agentic UI sidebar. Unlike the toasts from #4203, which fade on their own, these stay until the user acts on them or dismisses them — for things the user needs to see even if they looked away.

The first (and currently only) card tells the user when an app update has been downloaded and is ready to install:

  • The card appears in the sidebar footer with a Restart now button that installs the update immediately, instead of waiting for the user to happen to quit.
  • With the sidebar collapsed, a small dot on the sidebar toggle signals that a message is waiting.
  • Dismissing hides the card for the current session only. Persisting the dismissal isn't needed: restarting Studio installs the pending update, so the card's reason to exist is gone after any restart. If Studio stays open long enough for a newer version to download, the card comes back — dismissals are version-scoped.
  • Card content is derived at render time from the current update status, so nothing user-facing is stored and a locale change re-renders it translated.

The card layer is deliberately generic (a list of messages filtered by dismissed ids), so future messages — onboarding checklists, announcements — can plug into it. If one of those ever needs a dismissal that outlives a restart, that's the point to add persisted storage for it.

Testing Instructions

The update card only appears when the auto-updater has actually downloaded an update, so fake it locally:

  1. Run with the Agentic UI enabled: ENABLE_AGENTIC_UI=true npm start
  2. At the top of setupUpdates() in apps/studio/src/updates.ts, add:
    updaterState = 'waiting-for-restart';
    downloadedVersion = '99.9.9';
    setTimeout( () => {
    	downloadedVersion = '99.9.10';
    	void sendIpcEventToRenderer( 'app-update-status', buildAppUpdateStatus() );
    }, 20_000 );
    return;
    Restart the app fully — this is main-process code, so hot reload won't pick it up.
  3. Confirm the card "Studio 99.9.9 is ready to install" appears in the sidebar footer.
  4. Dismiss it → it animates out and stays hidden.
  5. Wait for the 20s mark → the 99.9.10 card appears, confirming both the live IPC push and that dismissals are version-scoped.
  6. Collapse the sidebar while a card is active → a dot appears on the sidebar toggle button.
  7. Click Restart now → the app quits (with a real update it relaunches into the new version).
  8. Check the card in both light and dark mode.
  9. Remove the fake before merging.

Pre-merge Checklist

  • Have you checked for TypeScript, React or other console errors?
  • Verified in both light and dark mode
@katinthehatsite katinthehatsite self-assigned this Jul 15, 2026
@katinthehatsite
katinthehatsite marked this pull request as draft July 15, 2026 14:13
@katinthehatsite
katinthehatsite changed the base branch from trunk to add/toast-notifications-agentic-ui July 15, 2026 14:13
@wpmobilebot

wpmobilebot commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

📊 Performance Test Results

Comparing f403302 vs trunk

app-size

Metric trunk f403302 Diff Change
App Size (Mac) 1362.04 MB 1362.05 MB +0.01 MB ⚪ 0.0%

site-editor

Metric trunk f403302 Diff Change
load 1107 ms 1076 ms 31 ms ⚪ 0.0%

site-startup

Metric trunk f403302 Diff Change
siteCreation 6492 ms 6472 ms 20 ms ⚪ 0.0%
siteStartup 2388 ms 2388 ms 0 ms ⚪ 0.0%

Results are median values from multiple test runs.

Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff)

Base automatically changed from add/toast-notifications-agentic-ui to trunk July 16, 2026 10:11
bcotrim added 5 commits July 20, 2026 12:11
…-update-notification

# Conflicts:
#	apps/studio/src/ipc-handlers.ts
#	apps/studio/src/preload.ts
#	apps/studio/src/storage/storage-types.ts
#	apps/studio/src/storage/user-data.ts
#	apps/ui/src/components/app-toasts/index.tsx
#	apps/ui/src/components/sidebar-layout/index.tsx
#	apps/ui/src/components/sidebar-layout/style.module.css
#	apps/ui/src/data/app-messages.test.ts
#	apps/ui/src/data/app-messages.ts
#	apps/ui/src/data/core/connectors/hosted/index.ts
#	apps/ui/src/data/core/connectors/ipc/index.ts
#	apps/ui/src/data/core/connectors/local/index.ts
#	apps/ui/src/data/core/types.ts
#	apps/ui/src/data/queries/use-import-site.ts
…-update-notification

# Conflicts:
#	apps/ui/src/app/app-providers.tsx
@bcotrim
bcotrim requested review from nightnei and sejas July 22, 2026 08:47
@bcotrim
bcotrim marked this pull request as ready for review July 22, 2026 08:47

@nightnei nightnei left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The animation is not smoth, feels like it gets stuck before disappearing
https://github.com/user-attachments/assets/6c10caf4-f730-41c1-b6bb-6a1071a5ffbd

Comment thread apps/studio/src/updates.ts Outdated
autoUpdater.on( 'update-downloaded', async ( _event, releaseNotes, releaseName ) => {
updaterState = 'waiting-for-restart';
console.log( 'Update has been downloaded' );
downloadedVersion = typeof releaseName === 'string' && releaseName ? releaseName : null;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's string, then I don't think we need to check for existance:

Suggested change
downloadedVersion = typeof releaseName === 'string' && releaseName ? releaseName : null;
downloadedVersion = typeof releaseName === 'string' ? releaseName : null;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moreover, we use it just for console.log, then probably tehre is no reason to use extra dewnloadedVersion variable at all. Also, do we need this console.log?

},

onAppUpdateStatusChanged( listener ) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any

@nightnei nightnei Jul 23, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Propose creating a Linear issue (or addressing it here) to declare it globally, similar to what we have for Studio Classic - https://github.com/Automattic/studio/blob/trunk/apps/studio/src/preload.ts#L252-L258

@nightnei nightnei left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, works great 👍

@bcotrim
bcotrim enabled auto-merge (squash) July 23, 2026 16:39
@bcotrim
bcotrim merged commit c385a48 into trunk Jul 23, 2026
13 checks passed
@bcotrim
bcotrim deleted the add/persistent-cards-update-notification branch July 23, 2026 19:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

4 participants