Alerts: key WATCHING on the command name instead of the Session - #325
Merged
Conversation
WATCHING was a per-Session toggle, so the bell had to be re-enabled on every new pane running the same program. But the thing you want to be alerted about is a *program* -- `claude`, a test runner, a build -- not a particular tab. WATCHING is now a property of the command. The user keeps an app-global, persisted set of watched command names (`dormouse:watched-commands`); a Session runs the output/silence monitor exactly while its foreground command's `commandArgv0` is in that set. Enable it while `claude` runs and every `claude` pane watches, now and later; turn it off anywhere and the rule is gone everywhere. There is no per-Session enable and no per-Session mute, so `PersistedAlertState.watchingEnabled` is gone too. With nothing running there is no rule to key on, so the bell changes nothing and opens the alert dialog to say so. The dialog gained the per-command rule switch plus a list of every watched command with a remove control -- the only place a rule set on a since-closed pane can be found. Two behaviors this forced: - A ring must outlive its monitor. Watching switches off the instant the watched command exits, usually the same instant the ring was raised, so the WATCHING ring moved out of ActivityMonitor into the entry. Removing a rule still silences the ring; a command merely ending does not. - WATCHING now outranks COMMAND_EXIT_ARMED in the status projection. A watched command is by definition running, so the old precedence would have masked the monitor's busy/quiet states for the whole run. Simplification along the way: alert-manager.ts 719 -> 596 lines and alert.md 283 -> 201 while gaining the new model. Deleted the duplicated bell transition table (one copy now, in the renderer), toggleAlert / disableAlert / dismissOrToggleAlert, two platform methods net, and the legacy watching-status inference in seed(). startCommandExitWatch and the terminal-state reducer now share one resolveCommandStart. The playground tutorial's alert section is rebuilt around the new model: 6 steps -> 9, adding the rule spreading across panes plus the two tracks with no coverage before (program-sent notifications, command exit). The demos fake shell integration with OSC 633 through the real parser. Known limitation, stated in alert.md rather than worked around: WATCHING needs a shell that reports command boundaries, so cmd.exe, fish, and any shell where injection did not take no longer get it. Routing the renderer-side keystroke fallback into the manager would buy those shells a worse version of the feature at the cost of a second command-tracking path. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Clicking the bell on any playground pane gave "Nothing is running in this tab". WATCHING is keyed on the running command's name, and `TutorialShell` never reported command boundaries -- so no playground pane had a command to key on, including the one hosting the tutorial that teaches the feature. The fake shell now emits the same OSC 633 sequences a real integrated shell does: A/B around the prompt, E + C on launch, D on exit (127 for an unknown command). Panes report `tut`, `changelog`, `ascii-splash`, or whatever the user types at the prompt, so the bell reads `Alert on all "tut"` and works everywhere. This also retires the keystroke fallback for playground panes, which is the correct outcome -- real integration wins. The `s` demo still overrides both alert panes with a fake `longtask` so the rule visibly spreads across panes, but it now restores each pane's real command line afterwards via `reportRunningCommand()` instead of reporting a bare exit, so a pane whose TUI is still drawing never looks idle. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The alert section of the playground tutorial teaches the mechanics but not the idea. The idea is attention: Dormouse never interrupts you about the pane you are looking at, and "looking at it" ends either by switching panes or by simply sitting still for 15 seconds. Today that model appears once, as an aside in a hint, framed as a way to fail a step rather than as the point of the feature. This is a working draft to iterate on, not a spec -- it proposes a four-beat arc that gives attention its own beat (one task run three ways, with only the user's behavior changing between runs), and flags the two real design problems: how to reward a non-event, and that the demo needs two task lengths to show both the attended and the attention-expired cases. No code or spec changes. The rework is a later PR. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Deploying mouseterm with
|
| Latest commit: |
286ed80
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://eefeff60.mouseterm.pages.dev |
| Branch Preview URL: | https://per-process-alarms.mouseterm.pages.dev |
dormouse-bot
approved these changes
Jul 28, 2026
The dialog gained real UI in the WATCHING rework -- a per-command rule switch, a "nothing is running" explanation, and a removable rule list -- and none of it had stories. Worse, because no story primed a running command, every existing dialog story was silently rendering the new no-command variant rather than the case it was named for. Adds a `primedWatchedCommands` story parameter (cleared before and after every story, since the rule set is app-global and persists to localStorage) and a `primedRunningCommand` helper that reports a command through shell integration so the bell has an argv0 to name. New `TodoAlertDialog.stories.tsx` renders the dialog directly against a fixed rect, covering rule on/off, no-command with and without existing rules, a six-rule list, notification detail alongside a rule, and a pathological command name. New header stories cover the command-scoped bell tooltip in all three states plus the no-command dialog via the real right-click path. Two fixes fell out of writing them: - The rule-switch label was unbounded, so a long argv0 stretched the dialog to ~640px and pushed the close button into the TODO switch. The label now truncates (full name still in the list and the title attribute) and the switch grid reserves room for the close button. - The tooltip play helper hovers instead of focusing: a programmatic `.focus()` does not reliably drive React's `onFocus` here, while `mouseover` is what React synthesizes `onMouseEnter` from. It retries and throws, so a regression shows up in the Interactions panel rather than as an empty snapshot. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
dormouse-bot
approved these changes
Jul 28, 2026
dormouse-bot
approved these changes
Jul 28, 2026
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
WATCHING was a per-Session toggle, so the bell had to be re-enabled on every new pane running the same program. But the thing you want to be alerted about is a program —
claude, a test runner, a build — not a particular tab.What changed
WATCHING is now a property of the command. You keep an app-global, persisted set of watched command names; a Session runs the output/silence monitor exactly while its foreground command's first word is in that set. Enable it while
clauderuns and everyclaudepane watches, now and later; turn it off anywhere and the rule is gone everywhere. There is no per-Session enable and no per-Session mute, soPersistedAlertState.watchingEnabledis gone too.With nothing running there is no rule to key on, so the bell changes nothing and opens the alert dialog to say so. The dialog gained the per-command rule switch plus a list of every watched command with a remove control — the only place a rule set on a since-closed pane can be found.
Two behaviors this forced
ActivityMonitorinto the entry. Removing a rule still silences the ring; a command merely ending does not.COMMAND_EXIT_ARMEDin the status projection. A watched command is by definition running, so the old precedence would have masked the monitor's busy/quiet states for the whole run.Simplification
alert-manager.ts719 → 596 lines,alert.md283 → 201, while both gained the new model. Deleted: the duplicated bell transition table (one copy now, in the renderer),AlertManager.dismissOrToggleAlert,toggleAlert/disableAlert, two platform methods net, and the legacy watching-status inference inseed().startCommandExitWatchand the terminal-state reducer now share oneresolveCommandStart.Playground fake shell
TutorialShellnever reported command boundaries, so no playground pane had a command to key on — including the pane hosting the tutorial that teaches the feature. It now emits the sameOSC 633sequences a real integrated shell does (A/Baround the prompt,E+Con launch,Don exit). Every pane reports its program, so the bell works everywhere.Known limitation
WATCHING needs a shell that reports command boundaries, so
cmd.exe,fish, and any shell where integration injection did not take no longer get it. Routing the renderer-side keystroke fallback into the manager would buy those shells a worse version of the feature at the cost of a second command-tracking path. Stated plainly inalert.mdrather than left implicit.Tutorial
The desktop alert section was rebuilt around the new model (6 steps → 9, adding the rule spreading across panes plus the two tracks that had no coverage before). It is knowingly in a middling state — it teaches the mechanics but not the attention model, which is the actual idea.
TUTORIAL_MOCKUP.mdis a working draft for that rework; it lands in a follow-up PR.Verification
pnpm test(1280 tests),pnpm lint:specs, andbuild:vscodeall pass. Driven end-to-end in the desktop playground: one bell click lights every pane running that command; the dialog shows the rule switch and removable rule list; the ring fires on silence and leaves a TODO;OSC 777and command-exit both ring without a rule; the bell at a prompt opens the "nothing is running" dialog and creates nothing.Not exercised by hand: VS Code (the one host where
AlertManageris cross-process and the rule set arrives bypostMessage) and a real integrated shell in standalone.🤖 Generated with Claude Code