feat(notices): notices button in the header - #2668
Open
camielvs wants to merge 4 commits into
Open
Conversation
This was referenced Aug 26, 2026
Collaborator
Author
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
🎩 PreviewA preview build has been created at: |
camielvs
commented
Aug 26, 2026
camielvs
commented
Aug 26, 2026
camielvs
commented
Aug 26, 2026
camielvs
commented
Aug 26, 2026
camielvs
commented
Aug 26, 2026
camielvs
commented
Aug 26, 2026
camielvs
force-pushed
the
banners-04-strip
branch
from
August 26, 2026 23:45
eee6c2b to
91b5f62
Compare
camielvs
force-pushed
the
banners-05-inbox
branch
2 times, most recently
from
August 27, 2026 00:05
9e7df61 to
0769ace
Compare
camielvs
force-pushed
the
banners-04-strip
branch
from
August 27, 2026 00:44
91b5f62 to
45113d5
Compare
camielvs
force-pushed
the
banners-05-inbox
branch
2 times, most recently
from
August 27, 2026 01:18
af61eb7 to
b02bd10
Compare
camielvs
force-pushed
the
banners-04-strip
branch
from
August 27, 2026 01:18
45113d5 to
69c580a
Compare
camielvs
marked this pull request as ready for review
August 28, 2026 00:02
Collaborator
Collaborator
camielvs
force-pushed
the
banners-05-inbox
branch
from
August 28, 2026 17:03
b02bd10 to
abcdac8
Compare
camielvs
force-pushed
the
banners-04-strip
branch
from
August 28, 2026 17:04
8464f5c to
ec2d04d
Compare
camielvs
force-pushed
the
banners-05-inbox
branch
from
August 28, 2026 17:04
abcdac8 to
e5b71e0
Compare
camielvs
force-pushed
the
banners-04-strip
branch
from
August 28, 2026 23:30
ec2d04d to
8d1683b
Compare
camielvs
force-pushed
the
banners-05-inbox
branch
2 times, most recently
from
August 28, 2026 23:51
1d161de to
f563d37
Compare
camielvs
force-pushed
the
banners-04-strip
branch
from
August 28, 2026 23:51
8d1683b to
e2ef400
Compare
Adds a Megaphone button to the top bar that opens the full list of notices, with an unread count badge. Opening the list marks everything in it as read; notices the host marked dismissible can be removed one at a time from here. The list is also the way back once the strip has been hidden, which until now was a one-way door for the rest of the session. Its toggle mirrors the strip's, and the strip's own controls stay out of the way while the list is open.
…oggle The notices button now stays in the header whether or not there is anything to read, and opening it with nothing to show gives a "No notices" empty state. Previously the whole affordance unmounted when the list emptied, which also meant dismissing the last notice pulled the open popover out from under the reader. Cap the unread badge at 9+ so a host-supplied count cannot clip inside the badge; the trigger's label still carries the exact number. The banner toggle follows hasHiddenNotices, so it offers to bring the banners back as soon as one notice is hidden rather than only when all of them are. Move the viewport-fit sizing onto the PopoverContent primitive so no caller has to name Radix custom properties, and let the header row use the fill prop rather than a width class. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
camielvs
force-pushed
the
banners-05-inbox
branch
from
August 29, 2026 00:13
f563d37 to
c5ba846
Compare
Collaborator
Author
|
Weird. I definitely pushed the rework, as a three-column view. The question of 1 v 2 v 3 columns is tricky - 1 column is bad for short notices, whilst the others are bad for long notices. It's easy to change though, so once we dream up a solution we can iterate. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.






Why
Banners live on the dashboard home only — same as the announcements they replace. Someone who opens a run page from the CLI and stays there never sees them. And there was no way to look at a notice again after clearing it off the page.
This adds a megaphone to the header: notices are reachable from any route, with an unread badge.
What you get
A
Popoveron aTooltipButtonin both top bars (v1AppMenu, v2AppMenuActions), listing every notice in full — unclamped, so a long body isn't cut off the way it is in a banner. Dismissible notices can be retired from here too. Empty state is "No notices"; the button stays in the header either way so it doesn't shift its neighbours around.Unread, and only unread, is what this PR adds to the state model: a notice is unread until the reader opens the popover, tracked by id in
localStorageunderread-notices. Badge caps at9+visually while the trigger'saria-labelkeeps the exact count ("Notices, 12 unread"). Reading is separate from dismissal, so opening the list doesn't clear anything from the banners.Reviewer notes
useNoticeInboxwrapsuseNoticesrather than duplicating it — it adds read-tracking and open state and forwardsnotices/dismissthrough. Same module-store +useSyncExternalStoreshape as #2667, for the same reason: the badge and the list are in different subtrees.Open state is deliberately in the store rather than
useState, because it has to survive the trigger unmounting when the route swaps between the v1 and v2 top bars. TheuseEffect(() => closeNoticeInbox, [])closes it on unmount so it can't come back open on a different page.One shared primitive changes:
popover.tsx, one line.PopoverContenthad a fixedw-72and no height ceiling, so a tall popover ran off the bottom of the viewport with no way to reach the rest. It now caps to Radix's available width/height and scrolls. This affects every popover in the app — worth confirming, though the change only ever removes overflow.Accessibility: the trigger's name carries the count, dismissal is a labelled button, and the popover keeps focus and stays reachable after its last notice goes (tested).
How to test
Install a source (see #2667 or
src/config/NOTICES.md), then:Where this sits
Additive on top of the #2667 merge point — the banners work without this.