Skip to content

feat: show announcement banners#1018

Merged
EhabY merged 10 commits into
mainfrom
feat/show-announcement-banners
Jul 9, 2026
Merged

feat: show announcement banners#1018
EhabY merged 10 commits into
mainfrom
feat/show-announcement-banners

Conversation

@EhabY

@EhabY EhabY commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

Closes #101

Summary

  • Surface active Coder deployment announcements in a compact status bar item: $(megaphone) Coder or $(megaphone) Coder N.
  • Show popup notifications only for banners the user has not seen on that deployment, respecting coder.disableNotifications.
  • Add a Coder: View Announcements quick pick to read full messages.
  • Centralize Coder status bar item ids, names, and priorities in util/statusBar so their relative order lives in one place.

Testing

  • pnpm typecheck
  • pnpm lint
  • pnpm format:check
  • pnpm test:extension
  • pnpm build
Implementation notes
  • Modern deployments mirror announcement_banners[0] into the deprecated service_banner, so the latter is only read when announcement_banners is absent (older deployments); nothing is double-counted.
  • Banner fingerprints hash the message alone (the API exposes no stable banner ids): editing the text re-notifies, reordering or color-only changes do not.
  • Seen fingerprints are stored per deployment and unioned on write, and banners are marked seen only when the user could actually see them (popup shown, or the quick pick opening); announcements suppressed by coder.disableNotifications stay eligible for a popup after notifications are re-enabled.
  • Overlapping fetches are generation-guarded, so a delayed response never overwrites newer banners or the seen store.
  • Non-modal popups are never awaited (they may auto-dismiss without settling); the popup's View action opens the picker from the just-fetched banners without a second request.
  • Fetch cadence: session change, a 30-minute authenticated poll, and a manual refresh before the quick pick opens. Failures keep the last banners, log a warning, and only surface an error popup on the manual path; the "no announcements" message appears only after a successful refresh.
  • The status bar registry preserves pre-existing placement: agentMetadata keeps its id and effective priority 0, and items that never had an explicit id stay id-less so users' show/hide preferences survive.
  • Follow-ups: Split non-secret memento state out of SecretsManager (or rename it) #1023 (move memento-backed state out of SecretsManager), Set a default timeout for REST requests #1024 (default REST timeout; like the other pollers, a hung request currently stalls the announcement poll).

Generated by Coder Agents on behalf of @EhabY.

Visuals

image image

@EhabY EhabY self-assigned this Jun 30, 2026
@EhabY EhabY force-pushed the feat/show-announcement-banners branch 2 times, most recently from 723aafb to 9c063da Compare July 6, 2026 16:38
Comment thread src/announcements/banners.ts Outdated
Comment thread src/announcements/banners.ts
Comment thread src/announcements/banners.ts Outdated
Comment thread src/announcements/banners.ts
Comment thread src/announcements/manager.ts Outdated
Comment thread src/announcements/manager.ts
Comment thread src/announcements/banners.ts
Comment thread test/unit/announcements/banners.test.ts Outdated
Comment thread test/unit/announcements/manager.test.ts

@mtojek mtojek left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM!

@EhabY EhabY force-pushed the feat/show-announcement-banners branch from 7fd389c to 819daa9 Compare July 8, 2026 15:43

@mtojek mtojek left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I love the final effect!

@EhabY EhabY force-pushed the feat/show-announcement-banners branch from 819daa9 to 29de019 Compare July 9, 2026 07:59
EhabY and others added 10 commits July 9, 2026 11:27
- Store seen banners per deployment (coder.seenBanners.<host>) to match
  the per-deployment key convention and avoid cross-window races
- Skip the seen-banner write when keys are unchanged
- Reuse errToStr so API error messages surface in refresh failures
- Guard against appearance responses missing banner fields
- Drop the raw hex color from the announcements QuickPick
- Cover the periodic refresh timer with a fake-timers test
Centralize status bar item names and priorities in util/statusBar while
preserving each item's pre-registry id and placement. Read the
deprecated service_banner only when announcement_banners is absent so
modern deployments that mirror the first announcement into it show no
duplicates, and derive banner keys from the message alone. Union seen
keys instead of overwriting, mark banners seen only when the user could
see them, and guard overlapping fetches with a generation counter so
stale responses never apply. Open the picker from the popup without
refetching, show the empty-state message only after a successful
refresh, avoid awaiting non-modal notifications, truncate popup text by
code points, and align tests with the setup() factory pattern.
Fixes truncation to break on word boundaries, dedupes banners with
identical content, clarifies the empty vs. absent announcement_banners
case, guards against showing an empty picker after banners are cleared,
and renames "seen" banners to "surfaced" throughout to match what the
tracking actually represents.
…colors)

Replaces the QuickPick + per-item toast flow with a real Markdown Preview
tab (via a TextDocumentContentProvider on an extensionless virtual URI),
so the full list renders with working markdown instead of raw syntax.
Each banner is boxed in its own div using the deployment's configured
background_color when set (matching coder/coder's own dashboard, contrast
heuristic included), falling back to the VS Code blockquote theme
otherwise; links inside custom-colored boxes inherit the box's text color
instead of the theme's link-blue, which can clash.

Other changes bundled into this pass:
- Status bar hover renders as MarkdownString, capped at 5 banners with a
  "+N more" pointer to the full preview, lines hard-broken instead of a
  numbered/bulleted list (indent, implied order).
- Popups always show a generic count, never raw (possibly markdown-laden)
  banner text, since they can't render it and "View" already does.
- A warning-colored status bar background flags unsurfaced banners,
  clearing as soon as the list is opened rather than requiring a fetch.
- showAnnouncements() debounces concurrent calls and wraps the load in a
  window progress indicator.
- "seen" terminology renamed to "surfaced" throughout to match what's
  actually being tracked (shown, not necessarily read).
- Fixed a race where a session change mid-refresh could open a blank
  preview, and centralized error handling so preview/storage failures
  show a friendly message instead of an unhandled rejection.
Flatten fetch()'s notify branching into guard clauses and drop the
redundant manual-path marking (the preview already marks banners
surfaced). Merge tryMarkSurfaced into markSurfaced.

Follow the codebase pattern for popup action handling: bare .then()
without a catch. This surfaced that the message mocks returned
undefined instead of a Thenable, so make them resolve like the real
API.
@EhabY EhabY force-pushed the feat/show-announcement-banners branch from afba1e8 to c9646b9 Compare July 9, 2026 08:29
@EhabY EhabY linked an issue Jul 9, 2026 that may be closed by this pull request
@EhabY EhabY merged commit 3d2e5e1 into main Jul 9, 2026
13 checks passed
@EhabY EhabY deleted the feat/show-announcement-banners branch July 9, 2026 08:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Show announcement banners

2 participants