Skip to content

feat(ui): one-time announcement dialog for Plannotator TUI and Herdr Annotate - #1529

Merged
backnotprop merged 11 commits into
mainfrom
feat/tui-herdr-announcement
Sep 14, 2026
Merged

backnotprop merged 11 commits into
mainfrom
feat/tui-herdr-announcement

Conversation

@backnotprop

@backnotprop backnotprop commented Sep 14, 2026

Copy link
Copy Markdown
Owner

A one-time, full-screen-ish panel announcing Plannotator's two terminal clients,
Plannotator TUI and Herdr Annotate. It shows once per browser across plan
review, annotate and code review, then never again.

The design

Video first. The real Herdr Annotate demo fills the top of the panel edge to edge,
muted, looping and autoplaying, and everything under it fits in three rows: one
headline, one sentence, one action row. No mock terminal, no feature bullets, no
install commands, no badges; the repo pages carry all of that.

Plannotator TUI and Herdr Annotate

Annotate Markdown and terminal text, then send the notes to your agent.

[Full | Lite]

[Star plannotator-tui] [Star herdr-annotate] [Watch on X] [Got it]

The panel is 1120px wide at most and follows the viewport height too
(min(1120px, 100%, (100dvh - 14rem) * 1280/806)), so the footage keeps its aspect
ratio and never scrolls out of view: at 1440x900 the video is 1072px wide. At 400px
the video spans the panel on top and the headline, switch and actions wrap under it.
Both themes come from theme.css tokens; the dark footage sits on the card surface
with a hairline under it, which reads as a hero image in light mode and as one
continuous panel in dark.

The corner block. An uppercase NEW · WATCH: block covers the top-left corner
of the footage on both demos: flush to the panel edge with no inset, square outer
edges (the panel's own radius clips them), a rounded bottom-right, 190x48 with 15px
tracked type over the desktop video and 132x32 with 11px at 400px, the way a "new"
corner tag sits on a product card. A slow diagonal sheen sweeps the whole block every
4s (dropped under prefers-reduced-motion, with the media query as a backstop). Its
colors are Herdr Annotate's own badge palette from the repo's SVGs: #312b52 purple
background, #c9c6f1 lavender text (8:1 on it), and a #B9C0FF periwinkle 1px edge
along the two sides that meet the video, plus a soft shadow. They are set as --announce-brand*
variables on the dialog element, not tokens in theme.css, so another product's brand
never enters the app palette. The badge purple was chosen over the hero image's darker
ground (#10101f to #2c2536), which vanishes into the footage; the poster corner it
covers holds only the sidebar's "spaces" label (Full) and a tab marker (Lite).

Full and Lite. One video is visible at a time; a two-segment Full | Lite switch
(role="tablist", arrow keys) on the headline row swaps the footage and the
"Watch on X" link together. A switch rather than a second tile because a second tile
would either be a tiny thumbnail nobody can read or would halve the hero, and a
control next to the media is where a viewer expects to change what is playing.

Video treatment. <video muted playsInline loop preload="auto"> with the mp4
source first and the webm second, autoPlay unless prefers-reduced-motion
matches, in which case the poster waits behind a centered play button. Hovering the
playing footage reveals a small pause control bottom-left; a paused frame shows the
centered play button, so it is never mistaken for a broken player. The player is
keyed per demo, so a switch resets playback and load-failure state. If neither source
can load (offline), the frame keeps its place and offers "Watch on X" over it.

Hosting

The six files are new under apps/marketing/public/assets/, the same precedent as
code-review-demo.mp4 and guided-review-demo.mp4, and the dialog references them at
https://plannotator.ai/assets/...:

file size
tui-herdr-full-demo.mp4 (1280x806 h264, 40s, silent) 5.5 MB
tui-herdr-full-demo.webm (vp9) 9.0 MB
tui-herdr-full-poster.jpg 106 KB
tui-herdr-lite-demo.mp4 (1280x808 h264, 39s, silent) 3.5 MB
tui-herdr-lite-demo.webm (vp9) 4.8 MB
tui-herdr-lite-poster.jpg 121 KB

The videos go live on merge, when the Deploy workflow syncs apps/marketing/dist/
to S3 on the push to main. Until then the dialog's poster and video URLs 404 and it
shows its offline state. The screenshots below were taken with the same files served
locally in place of that origin (a temporary constant change, reverted before commit).

Hosted rather than inlined like the Edit Mode recording because the two demos are
~23 MB together; GuideIntroDialog already streams its hero from this origin. There is
no CSP to widen: the app HTML ships no Content-Security-Policy, and the servers only
set one on sandboxed artifact responses.

Gating (unchanged from the first version)

One cookie, plannotator-announce-tui-herdr-seen (value 1), read and written by
needsTerminalToolsAnnouncement() / markTerminalToolsAnnouncementSeen() in
packages/ui/utils/terminalToolsAnnouncement.ts. Dismissing it on any surface retires
it everywhere. A plain storage key rather than a settings-registry entry for the same
reason lookAndFeelAnnouncement.ts is: configStore.ensureLoaded seeds every registry
default into a cookie on first access, so a registry-backed flag could never tell
"never seen" from "seeded default".

LAST in each app's first-run chain. Code review gates it through
terminalToolsAnnouncementCanShow on guide intro, look-and-feel, review setup, Edit
Mode and token hover cards; the plan editor on the look-and-feel chooser, goal setup
and permission-mode setup. None of those consumes this cookie, so a session busy
asking setup questions defers the announcement to the next load instead of burning it.
Suppressed, never consumed, on archive browsing, read-only shared plans (the share
portal), the compact touch shell, and while the initial payload loads.

The shell is the shared one (portal, z-[100], role="dialog", hand-rolled Escape,
Tab wrap with the roving-tabindex switch skipped, focus restore) with the two
deliberate departures kept: the backdrop dismisses, and the capture-phase keydown
listener swallows Mod+Enter so a keystroke aimed at the announcement cannot approve
a plan or post a review behind it. Entrance reuses the Vim announcement's keyframes;
under reduced motion the panel fades without travelling.

Test plan

  • packages/ui/components/TerminalToolsAnnouncementDialog.test.tsx (12): labelled modal
    with exactly one completion action and initial focus on it; the footage is
    muted + playsinline + loop + autoplay with mp4 before webm and a hosted
    poster; reducedMotion withholds autoplay, renders the play button and drops the
    tag's sheen (which is on by default); the switch
    swaps the demo and the X link together; every outbound link is _blank +
    noopener noreferrer + https:; button, Escape and backdrop dismiss while a press
    inside the panel does not; Mod+Enter never reaches a listener behind the dialog;
    Tab wraps at both ends; closed renders no nodes.
  • packages/ui/utils/terminalToolsAnnouncement.test.ts (4) and the App-level tests in
    packages/editor/App.terminalToolsAnnouncement.test.tsx and
    packages/review-editor/App.decisionControl.test.tsx are unchanged and still pass.

Results:

  • bun run typecheck clean.
  • DOM_TESTS=1 bun test packages/ui packages/editor packages/review-editor: 2158 pass,
    5 fail, all five the pre-existing order-dependent DocBadges / artifactDocument
    cases.
  • bun run --cwd packages/ui smoke:package: packs and installs externally against
    @plannotator/core@0.25.2.
  • bun run --cwd apps/review build && bun run build:hook && bun run build:opencode && bun run build:marketing: all succeed; the six media files land in
    apps/marketing/dist/assets/.

Verified with Playwright (Chromium) on the review dev server in light and dark at
1440x900 and 400px wide, plus the Lite switch and prefers-reduced-motion; in every
run the video reported readyState 4 and was playing (paused at 0 under reduced
motion), so the captures show real frames.

Deploy note

The theme.css additions change the guides.show viewer's bundled CSS, so
packages/core/guide-viewer-manifest.ts is repinned (viewer.Glgcqvv_.css, new
integrity; the JS hash is unchanged). The pinned viewer build must be deployed to
guides.show /v1/ on merge
, the same step as PR #1526.

Docs

AGENTS.md's "First-run terminal-tools announcement" section describes the hosted
media, the reduced-motion and offline behavior, the demo switch and the height-aware
width; tests/UI-TESTING.md's manual checklist covers the video, links and theme steps.

… gate

Announces Plannotator TUI and Herdr Annotate. One cookie,
plannotator-announce-tui-herdr-seen, covers every surface, read through
storage directly rather than the settings registry (configStore seeds
registry defaults into cookies on first access, so a registry flag could
not tell 'never seen' from 'seeded default').

The shell matches the other first-run announcements. Two departures: the
backdrop dismisses, because the panel collects no decision, and the
keydown listener runs on the capture phase and swallows Mod+Enter so a
keystroke aimed at the announcement cannot approve a plan or post a
review behind it.
… first-run chain

Both apps gate it through terminalToolsAnnouncementCanShow, behind every
dialog that asks the user to decide something, and the destination
spotlight and auto-viewed toast now defer behind it too. Last rather than
first because none of those dialogs consumes this cookie: a session busy
asking setup questions defers the announcement instead of burning it.

Archive, read-only shared plans (the share portal), the compact touch
shell and the initial load suppress it without consuming the cookie.
…dering

Existing App tests seed the new key so the announcement does not open over
the surface they exercise.
…ipping them

At the card width the Homebrew line was cut mid-path with no affordance.
Six files under apps/marketing/public/assets/, served from
https://plannotator.ai/assets/ once the marketing deploy syncs them: the
Herdr Annotate Full and Lite demos as 1280px h264 mp4 (5.5MB, 3.5MB) and vp9
webm (9MB, 4.8MB), each with a poster jpg. Same hosting precedent as
code-review-demo.mp4 and guided-review-demo.mp4; the in-app terminal-tools
announcement streams them.
…tage

Replaces the text-and-mock panel with a video-first one: the real Herdr
Annotate demo fills the top of the panel edge to edge, muted, looping and
autoplaying (poster plus a play button under prefers-reduced-motion), over one
headline, one sentence, and one action row (star the two repos, Watch on X,
Got it). A Full | Lite segmented switch swaps the footage and the X link
together. The mock terminal, install commands, feature paragraphs, the NEW
badge and the third star button are gone.

Media is hosted on plannotator.ai like GuideIntroDialog's hero image (23MB is
far too much to inline); if neither source loads, the frame keeps its place
and offers the X post instead. The panel's width follows the viewport height
as well as its width so the video never scrolls out of view. Cookie gate,
chain ordering, surface suppression, focus trap, Esc and backdrop dismiss are
unchanged; tests now also cover the muted/playsinline/autoplay attributes, the
reduced-motion branch and the demo switch.
AGENTS.md now documents the hosted media, the reduced-motion and offline
behavior, the demo switch and the height-aware panel width; the manual
checklist's content and theme steps match the new dialog.
…h label

A small uppercase tag in the top-left corner of the video, over the footage,
with a slow diagonal sheen. Colors are Herdr Annotate's own badge palette
(#312b52 purple, #c9c6f1 lavender text, #B9C0FF periwinkle border), set as
--announce-brand* variables on the dialog itself so another product's brand
never enters theme.css. The sheen class is dropped under
prefers-reduced-motion, with the media query as a backstop, and the tag sits
where the posters keep only a sidebar label and a tab marker.
The tag now covers the top-left corner of the footage instead of floating
inside it: anchored to the panel edge with no inset, square outer edges (the
panel's own radius clips them), a rounded bottom-right, and the periwinkle
edge only along the two sides that meet the video. 190x48 with 15px tracked
uppercase type over the desktop video, 132x32 with 11px at phone width. The
sheen now sweeps the whole block every 4s; reduced motion still drops it.
@backnotprop
backnotprop merged commit cd8642c into main Sep 14, 2026
28 checks passed
@backnotprop
backnotprop deleted the feat/tui-herdr-announcement branch September 14, 2026 23:04
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.

1 participant