Files
airship/packages
Nayan b0c4e38d37 feat(overlay): give long menus collapsible groups and one shared device list
The device menu had outgrown a flat list, and two menus were improvising a
delete glyph the icon set did not have.

`MenuGroup` is a single-open accordion entry; `MenuNode` takes an arbitrary row.
`items()` filters rows inside collapsed groups, so keyboard navigation does not
walk through entries nobody can see, and `isMenuItem` is exported —
`font-menu.test.ts` was hand-rolling a `"label" in e` predicate that never
tested `run`.

Two placement fixes, found once the menus were tall enough to show them.
`onReflow` ignores scroll events originating inside the popover, which had been
snapping a tall menu back to the top on every wheel tick. `placePopover` resets
`left` to `0px` before measuring, because a stale value from the previous open
shrank the width available to a content-sized box.

`frame-chrome.ts` is mostly extraction. The frame verbs move to
`frame-verbs.ts` and the custom W×H form to `device-menu.ts`, carrying their
toasts and the delete-undo with them, so the frame bar, the frame list and the
keyboard are the same act rather than three implementations of it.
`deviceGroups()` seeds open on the frame's own family, and the zoom menu is
rebuilt per open.

Delete's glyph moves from `minus` to `trash`. The imported set has no delete
mark, so the two call sites had improvised `−` and `✕` — one of which reads as
half of an add. Eleventh hand-drawn local mark; ICONS.md carries the rationale.
2026-08-16 13:05:37 +05:30
..