From b5cd354e6573d55d6d456c1322a0081c2c17b303 Mon Sep 17 00:00:00 2001 From: Phil Merrell Date: Sun, 6 Sep 2026 10:34:35 -0600 Subject: [PATCH 1/2] feat(announcements): move the banner above the chat composer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The banner sat at the top of the shell. What it announces — a new model, a new capability — is acted on in the composer, so the notice now lives where the decision is made rather than in a corner the eye has already left. It mounts from `chat-input` beside `quota-warning-banner`, which is where users already look for ambient notices. It still floats rather than stacking in flow. `bottom-full` against a `relative` chat-input host puts it clear of the quota tabs, which stay attached to the input, and keeps the property from the previous change: measured before and after a dismissal, the composer, greeting and scroll container all move by exactly 0px. Restyled to match the sibling it now sits beside — a compact shrink-to-fit pill rather than a bar spanning the composer, which also shrinks how much it overlays. **It is now a chat-view surface only.** That is the real consequence of the move and it is deliberate: What's New remains the everywhere-record, which is why `panel` is forced onto every announcement server-side. The spec's §D1 is updated to say so rather than leaving the doc describing a placement that no longer exists, and the two admin help strings that told authors "a strip below the top nav" now describe where a banner actually appears. `chat-input` is reused by the agent-preview and marketplace test-drive panes, where a platform-wide notice would read as a bug rather than an announcement. A `showAnnouncements` input gates it, following the same opt-out shape as the `show*` controls beside it: default true, explicitly false at those two call sites, and threaded through `chat-container` so its embedded mode is off too. The shell mount and its `isAuthenticated()` gate are gone with it — the composer only exists inside an authenticated chat route, so the 401-on-login hazard that gate existed for is now structural rather than guarded. Three real test failures found and fixed on the way: two `chat-container` specs stub `app-chat-input` and needed the new input added to the stub, and one of the banner's own assertions was stale after the restyle. Full frontend suite 2486 passed, only the known `submission-review` flake outstanding. Co-Authored-By: Claude Opus 5 --- docs/specs/feature-announcements.md | 18 +++++++++-- .../announcement-form.page.ts | 2 +- .../manage-announcements.page.ts | 3 +- .../components/review-test-drive.component.ts | 1 + .../components/agent-preview.component.ts | 1 + frontend/ai.client/src/app/app.html | 10 +------ frontend/ai.client/src/app/app.ts | 18 ----------- .../announcement-banner.component.spec.ts | 27 ++++++++++------- .../announcement-banner.component.ts | 30 +++++++++++-------- .../chat-container.component.branding.spec.ts | 1 + .../chat-container.component.html | 6 ++++ .../chat-container.component.ts | 11 +++++++ .../chat-container.error-handling.spec.ts | 1 + .../chat-input/chat-input.component.html | 8 +++++ .../chat-input/chat-input.component.ts | 17 ++++++++++- 15 files changed, 98 insertions(+), 56 deletions(-) diff --git a/docs/specs/feature-announcements.md b/docs/specs/feature-announcements.md index 635b5306..bd199615 100644 --- a/docs/specs/feature-announcements.md +++ b/docs/specs/feature-announcements.md @@ -84,8 +84,20 @@ An announcement carries `surfaces: list[Literal["panel", "banner", "modal"]]`. never interrupts, browsable forever. **Implied on every announcement**: the service adds `"panel"` if the admin omits it, so dismissing a loud surface can never destroy the information. -- **`banner`** — a strip below the top nav, rendered by a sibling of - `quota-warning-banner`. One line plus an optional CTA and a ✕. +- **`banner`** — a compact pill floating just above the chat composer, + rendered by a sibling of `quota-warning-banner`. One line plus an optional + CTA and a ✕. + + *Revised after PR-4 shipped.* It was first built as a full-bleed strip below + the top nav. Two things moved it. Dismissing a strip that occupied layout + reflowed the whole view, so it became an overlay; and what a banner + announces — a new model, a new capability — is acted on **in the composer**, + so the notice belongs where the decision is made rather than in a corner the + eye has already left. The consequence to keep in mind: the banner is now a + **chat-view surface only**, and it is deliberately suppressed in the + embedded preview panes (agent preview, marketplace test-drive). The panel + remains the everywhere-record, which is why `panel` is forced onto every + announcement server-side. - **`modal`** — a dialog on next load. This is the only surface that can demand a real acknowledgement (`requiresAck`). @@ -474,7 +486,7 @@ from §D7. | Component | Location | Notes | |---|---|---| | Whats-new panel | `components/topnav/components/whats-new-panel/` | Dialog listing panel items newest-first, relative dates, **New** / **Updated** pills, markdown body. Opens from the user dropdown; unread dot on the avatar and the menu row. Mirrors `user-menu-link-modal`. | -| Announcement banner | `components/announcement-banner/` | Sibling of `quota-warning-banner`. `role="status"`, `aria-live="polite"`, severity colours from the `state-*` token scale that banner already uses, ✕ + optional CTA. | +| Announcement banner | `components/announcement-banner/` | Mounted by `chat-input` beside `quota-warning-banner`, floated `bottom-full` so dismissing it never moves the composer. `role="status"`, `aria-live="polite"`, severity colours from the `state-*` token scale, ✕ + optional CTA. Gated off in embedded panes via `[showAnnouncements]="false"`. | | Announcement modal | `components/announcement-modal/` | `user-menu-link-modal` plus a primary ack button. When `requiresAck`, `appDialogDismiss` and the escape handler are disabled so the only exit is the button. | | Admin list | `admin/manage-announcements/manage-announcements.page.ts` | Mirrors `manage-user-menu-links`. State chips, surface icons, ack counts, "Show again" action. | | Admin form | `admin/manage-announcements/announcement-form.page.ts` | Title, markdown body with live preview, surface checkboxes, severity, schedule, role picker, `showToNewUsers` (with the §D6 warning text), `requiresAck`, CTA. | diff --git a/frontend/ai.client/src/app/admin/manage-announcements/announcement-form.page.ts b/frontend/ai.client/src/app/admin/manage-announcements/announcement-form.page.ts index 054812ac..66ac5a7a 100644 --- a/frontend/ai.client/src/app/admin/manage-announcements/announcement-form.page.ts +++ b/frontend/ai.client/src/app/admin/manage-announcements/announcement-form.page.ts @@ -178,7 +178,7 @@ function byteLength(value: string): number { class="mt-1 size-4 rounded border-gray-300 text-primary-600 focus:ring-primary-500" />