Skip to content

Retire the alert bell; the alarm treatment is the whole indicator - #716

Draft
nedtwigg wants to merge 2 commits into
alert-ring-flashfrom
alert-retire-bell
Draft

nedtwigg wants to merge 2 commits into
alert-ring-flashfrom
alert-retire-bell

Conversation

@nedtwigg

Copy link
Copy Markdown
Member

Stacked on #709.

The swinging bell was a second, worse indicator for a fact the whole-Pane alarm treatment already shows, and its tilt tiers (MIGHT_BE_BUSY / BUSY / … ) were status trivia. It is gone from every surface: pane header, Door, hidden Workspace tab, mobile session list and header. WATCHING state lives in the terminal context and Settings.

  • Deleted AlertBell, bell-icon-class, WorkspaceRingCues, the bell-ring keyframes, the four README GIFs, the tutorial's "bell tilts" step, and the header's alert button (its HeaderActionButton props went with it).
  • One <AlertRingInset ground=…> component (from alert-ring.tsx) draws the 2px alarm edge on the Door, the hidden Workspace tab, and the mobile rows/header, with the shadow literals in one table in design.tsx.
  • The Workspace union carries ringingSince (one uninterrupted ringing interval at Workspace scope, carried forward while any member still rings) so the hidden tab bursts once and never re-bursts when the oldest member is attended.
  • Mobile dismisses by attention: a tap on the terminal attends, like the desktop. Pocket's adapter has no attention verb yet (it could not dismiss before either); remote-api.md → Future names terminal.attend.
  • scripts/pairing-walkthrough's ringing probe counted <svg> elements and would have gone silent; it now matches the alarm class.
  • Specs: alert.md Pane Header / Door / Workspace union rewritten; the four-cycle bell paragraphs and their rationale deleted, the Chrome-150 measurements folded into the per-episode pulse rationale. alert.md 7200 → 7050; mobile-terminal-ui.md 1950 → 2000.

ringSeq is still published by the manager; the next PR deletes it end to end.

🤖 Generated with Claude Code

nedtwigg and others added 2 commits September 20, 2026 01:01
The swinging bell was a second, worse indicator for a fact the alarm wash
and perimeter ring already carry, and its tilt tiers were status trivia.
Every surface now shows a ring the same way:

- Pane header: no bell and no tint — the Pane overlay outlines it. The
  header's right-click and command-mode `a` remain the context's entry
  points.
- Door: the inset ring alone; the SPOKEN speaker glyph now sits beside the
  TODO pill rather than beside a bell.
- Hidden Workspace tab: a 2px inset in `alarm-vs-header-inactive`, its
  arrival burst keyed on the union's earliest-started ringing member's
  episode, so a later member or a Workspace switch cannot replay it.
  `WorkspaceRingCues` and its per-Workspace generation are gone.
- Mobile: the ringing session-list row and the header bar wear the inset,
  and a plain `Dismiss alert` button appears while ringing — the mobile
  composition has no terminal context to reach.

The bell's `--animate-bell-ring` keyframes go with it; its four-cycle
measurements move to the alarm pulse's rationale, which they now back.

The vscode-ext guide loses its four bell GIFs for two sentences about the
outlined pane, and the playground tutorial loses `al-busy` ("the bell tilts
while the command works"), which had nothing left to show.

`AlertState.ringSeq` survives unread for now: the manager still counts
latches and `alertStatesEqual` still compares it. Remaining readers are
`MobileWall.tsx`, `MobileTerminalSessionItem`, and `wall-model.ts`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Follow-up cleanups on the bell retirement, all behavior-preserving except
the mobile dismissal:

- `AlertRingInset` in `alert-ring.tsx` (the module gains JSX, so `.ts` ->
  `.tsx`) renders the keyed overlay the Door, the Workspace tab, the mobile
  row and the mobile header each hand-assembled. The 2px shadow literals
  live in one `Record<AlertRingGround, string>` in `design.tsx`, replacing
  `DOOR_ALARM_INSET_CLASS` / `WORKSPACE_TAB_ALARM_INSET_CLASS`; the caller
  passes only its own corner radius. `data-alert-ring-inset` now carries the
  ground, which is what the tests assert.
- Mobile rows and the mobile header carry `episode` in place of the unread
  `ringSeq`, and derive their row through `alertRingRow` + `useAlertRingBurst`
  like every other surface instead of testing `status` by hand. A remote row
  passes `episode: null`: `DirectoryEntry.ringing` is edgeless, so there is
  no start to clock a burst from.
- The mobile "Dismiss alert" button is gone. A tap on the terminal attends
  the Session, which is what puts the ring out — the same verb desktop uses,
  and keystrokes already attended. Pocket is unchanged either way, its
  `alertAttend` being inert; `remote-api.md` -> Future now names the missing
  `terminal.attend`.
- The Workspace union carries `ringingSince` rather than a borrowed member
  episode, and the strip holds it for the whole ringing interval, so
  attending the member that started the ring cannot advance it to a
  survivor's later start and re-burst the tab.
- `HeaderActionButton` loses `tooltipDetail`, `tooltipAlign`,
  `onMouseDownCapture` and `onContextMenu`, none of which has a caller left;
  `alertRingBurstProps` and `animationClockStyle` lose their last outside
  caller with the bell and go module-private.
- `Ringing` / `TodoAndRinging` header stories rendered identically to
  `Default` / `TodoOnly`; the untinted ringing header is pinned in
  `TerminalPaneHeader.test.tsx` instead.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 20, 2026

Copy link
Copy Markdown

Deploying mouseterm with  Cloudflare Pages  Cloudflare Pages

Latest commit: f4f760a
Status: ✅  Deploy successful!
Preview URL: https://6459659f.mouseterm.pages.dev
Branch Preview URL: https://alert-retire-bell.mouseterm.pages.dev

View logs

@dormouse-bot dormouse-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Feedback on work in progress, not a merge verdict — mark the PR ready when you want the full pass. Two findings, both with inline suggestions.

The walkthrough's ringing probe reads the wrong element. sessionRowsExpr now tests row.className for alarm-vs, but AlertRingInset paints that token onto an overlay <span> inside the row button; the button itself only ever carries bg-app-bg / bg-header-active-bg plus the focus-ring shadow. ringing is therefore always false, and step 8's waitUntil on r.todo && r.ringing && r.text.includes('777;notify') burns its full 60s and fails — the silent-probe failure the change was meant to prevent, traded for a loud one. The sibling todo probe on the line above already queries descendants; data-alert-ring-inset is the stable hook for this one.

A Workspace that starts ringing while it is visible never bursts its tab. unionFor returns EMPTY_WORKSPACE_UNION for the active Workspace without touching unionsRef, so the cached entry goes on describing whatever that Workspace looked like the last time it was hidden. Sequence: Workspace B is hidden and ringing, so the cache holds ringing: true, ringingSince: T1; the user switches to B and attends it; a member rings again at T2; the user switches away. The carry-forward branch sees previous.ringing && projected.ringing and holds T1, so the burst's animationDelay is -(now - T1) — already past the animation's end — and the tab wears a static edge for a summons that should have flashed. The retired WorkspaceRingCues covered this deliberately ("Cues observe the active Workspace too, so switching tabs cannot create one"); the replacement drops it. Dropping the cache entry while active restores the behavior without reintroducing a switch-replay: a genuinely uninterrupted ring still resolves to its member's original startedAt, so its clock is still expired on return, and the projection stays skipped for the visible tab.

text: row.innerText.trim(),
todo: [...row.querySelectorAll('span')].some((el) => el.textContent.trim() === 'TODO'),
ringing: row.querySelectorAll('svg').length > 1,
ringing: /alarm-vs/.test(row.className),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The alarm token lands on the AlertRingInset overlay span, not on the row button, so this is always false. Match the todo probe above and query the descendant by its data hook.

Suggested change
ringing: /alarm-vs/.test(row.className),
ringing: row.querySelector('[data-alert-ring-inset]') !== null,

@@ -157,12 +153,19 @@ export function WorkspaceStrip({
}
const unionFor = (id: WorkspaceId, active: boolean): WorkspaceUnion => {
if (active) return EMPTY_WORKSPACE_UNION;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Returning early without touching unionsRef leaves the active Workspace's cached union describing a ring that may already have ended, which the carry-forward below then holds onto. Dropping the entry keeps the projection skipped for the visible tab and lets the next hidden render start from the truth.

Suggested change
if (active) return EMPTY_WORKSPACE_UNION;
// A visible Workspace shows no indicators, and its cached union must not
// outlive the ring it described: a ring that ends while the Workspace is
// active would otherwise carry its `ringingSince` into the next one.
if (active) {
unionsRef.current.delete(id);
return EMPTY_WORKSPACE_UNION;
}

Comment thread TUTORIAL_MOCKUP.md
Comment on lines +99 to 102
Changes from today's nine: `al-busy` (the pane's in-progress feedback)
is **cut** as a step — it is feedback, not a concept, and it is what the user is
already staring at during Beat 2. `al-ring` is **split into three** so each
attention mechanism gets its own line. Everything else keeps its id.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This PR cuts al-busy, so today's list is eight and the cut is no longer a pending change.

Suggested change
Changes from today's nine: `al-busy` (the pane's in-progress feedback)
is **cut** as a step — it is feedback, not a concept, and it is what the user is
already staring at during Beat 2. `al-ring` is **split into three** so each
attention mechanism gets its own line. Everything else keeps its id.
Changes from today's eight: `al-ring` is **split into three** so each
attention mechanism gets its own line. Everything else keeps its id.

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.

2 participants