Skip to content

feat(automations): the terminal menu's automations open on hover, like snippets - #72

Merged
rockyway merged 1 commit into
developfrom
feature/automation-menu-flyout
Sep 4, 2026
Merged

feat(automations): the terminal menu's automations open on hover, like snippets#72
rockyway merged 1 commit into
developfrom
feature/automation-menu-flyout

Conversation

@rockyway

@rockyway rockyway commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

"We have merged the other feature called snippet. It has the right context menu on the terminal
and has the submenu on hover. Now we need to do the same submenu on hover on the automations item
on the right context menu."

The armed automations were spread into the terminal's right-click menu as one flat row per rule —
⚡ Automation: Watch the build, ⚡ Automation: Answer the prompt — sitting in among Copy, Paste,
Clear and Mute, and growing the menu by a row for every rule.

They nest now, in the shape Snippets and Command History already use:

Before After
⚡ Automation: <name> × N, inline ⚡ Automations (N) ▸ → hover → the rules
the rule's state only in the row's tooltip the state in the flyout's dimmed right-hand column
menu grows by a row per rule one row, always

Each flyout row still opens that rule's editor and closes the menu, exactly as the flat rows did.

Why this is a change rather than a fix

The flat shape was not an oversight — it was the documented decision at the time, and the comment
said so: "a menu with no submenu machinery cannot nest, and an Automations ▸ row that opened
nothing would be the disabled-looking item this repo keeps refusing to ship."
plan/029 (Snippets,
#70) built that machinery, and the reason expired the day it merged. That paragraph is rewritten in
place rather than appended to, so the file does not assert both.

The shared half

armedEntryViews already decided which rules are offered, in what order, and in whose words — the
tab badge, the pane title, the two accordion menus and this flyout all read it. What was still
duplicated was the row that opens the list: views.length === 1 ? 'Automation' : 'Automations (N)' was a literal inside the accordion, and the flyout parent needed the same text. It is
armedMenuLabel now, lifted rather than copied, and a test compares the flyout parent's label with
the accordion header's own text node for equalitytoContain would have passed a parent
hard-coded to Automations against a header reading Automations (2), which is the exact drift the
extraction exists to stop.

Two menu systems, two shapes, one vocabulary: PaneContextMenu and CanvasNodeMenu have no
positioning machinery and keep the accordion (TabContextMenu.css settles that shape for the Color
Schema panel in as many words); Terminal/ContextMenu has an edge-aware flyout, so it opens one.

Details worth naming

  • Zero or one item. The builder still returns an array, and that is how "nothing is armed" is
    said to a host that spreads its items. A parent row over an empty panel is the item that looks
    live and does nothing.
  • The array form of rows, so ContextMenu's own case-insensitive filter over label +
    detail does the searching. Snippets takes the function form because filterSnippets also owns
    #tag matching and the flatten-on-search rule; there is no equivalent for a handful of rules
    named by their author.
  • closeMenuOnSelect: true, stated per row. The flyout's default is to keep the menu up, and
    the flat rows it replaces always closed it — an editor opening behind a menu that stayed is two
    surfaces both believing they have the keyboard.
  • emptyRow is reachable only by typing. The parent does not exist when nothing is armed, so
    "no automations at all" never renders inside the panel.
  • The type import from Terminal/ContextMenu is import type, so no part of that module — least of
    all its stylesheet — reaches PaneContextMenu or CanvasNodeMenu, which mount the component from
    the same file.

Gates

bun run typecheck clean · bunx jest 3531 passed / 206 suites · CI green on a4405a1
(Rust Tests Linux/Windows/macOS, e2e) · Tam's GUI pass — signed off ("sign off for GUI, it
works"
), run from the wt-watchdog worktree at this head.

Both new guards were mutation-checked: hard-coding the parent's label to Automations, and dropping
closeMenuOnSelect, each fail exactly one assertion — the shared-label test and the hover test
respectively, and neither killed the other's mutant.

The hover test drives a real ContextMenu: it asserts the panel is absent, delivers a mouseover
with relatedTarget: null (React synthesises mouseenter from that; a mouseover carrying an
in-tree relatedTarget is a silent no-op that looks exactly like a missing handler), then asserts
the two rows, their state column, that clicking the second opens r2 and that onClose fired.

Not tested here

Tam tests the UI himself. The flyout's own behaviour — placement, edge flipping, the hover-close
grace period, keyboard navigation — is contextMenuFlyout.test.tsx's, unchanged by this PR.

…e snippets

plan/029 gave `Terminal/ContextMenu` a hover flyout, and the automations armed on a
terminal were still spread into it as one flat `Automation: <name>` row per rule — the
shape chosen back when this host had no submenu machinery at all, which put a terminal's
rules in among Copy/Paste/Clear and grew the menu by a row each. They nest now: one
`Automations (N)` row that opens the rules on hover, each carrying this terminal's state
in the dimmed column and opening that rule's editor.

The row that OPENS the list is `armedMenuLabel`, shared with the accordion the pane and
canvas menus render, so the two cannot be renamed apart. `armedEntryViews` already
decided which rules, in what order, and in whose words — only the shape follows the host.

Still zero-or-one item, so nothing is drawn when nothing is armed: a parent over an empty
panel is the item that looks live and does nothing.

Gates: `bun run typecheck` clean, `bunx jest` 3531 passed / 206 suites. Both new guards
mutation-checked — hard-coding the parent's label, and dropping `closeMenuOnSelect`, each
fail exactly one assertion.
@rockyway
rockyway merged commit 8e3e666 into develop Sep 4, 2026
5 checks passed
@rockyway
rockyway deleted the feature/automation-menu-flyout branch September 4, 2026 22:21
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