The + menu was one long list in two groups, placeable types and
library ones. That split is no longer wanted. The menu now lists the
categories of the asset panel, and each opens its types beside it, so
a new asset is filed where the panel shows it.
- Menu learns submenus: an entry with submenu instead of onSelect
opens a second Menu right of the first, or left of it where the
canvas ends. Its first entry lines up with the row that opened it.
- A submenu opens on hover or click. Resting on another entry swaps
or closes it after 150 ms, so a pointer cutting across the rows on
its way into a submenu does not lose it.
- The row of an open submenu stays lit, and a triangle pointing right
marks every such row.
- A submenu has no backdrop and no Escape of its own. Popup takes a
backdrop option for that. The first menu's backdrop and Escape
close the whole stack, and picking an entry does the same.
- assetMenuCategories replaces assetMenuGroups and takes categories
and their order from ASSET_GROUPS. The types are sorted by label.
- New test hooks __gm.menuSubmenus() and __gm.addMenuCategory().
pickAssetType opens the category first, so the modules that pick a
type through it need no change of their own. Modules 04, 10, 47,
48, 53 and 54 open the category themselves where they read the menu.
Tests:
- Unit asset-menu: 4 green
- e2e 04: ALL PASSED, 63 checks. New: categories in panel order, each
type once and sorted, submenu placement, hover swaps the submenu,
picking closes menu and submenu
- Gate: CHECK EXIT 0, 261 files, 4040 tests
- The other modules that use the + menu did not run here
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Typing "variable" into the node palette landed on `Run behavior`, because the
filter weighed label, type and description alike and then sorted by category.
`searchRank` in `src/ui/search.ts` now ranks a match: exact label or type, then
prefix, then label, then type, and only last a hit that lives in the
description alone. Category and alphabet order within a rank. The searchable
`Menu` uses the same function, so searching means the same thing everywhere.
The `+` menu grew to fifteen entries in declaration order. It is built in two
alphabetical groups now -- the types that go into a scene, then the ones that
stay in the library -- with a separator between them, the split `isPlaceableType`
already draws. `ASSET_TYPES` itself stays untouched: it is also the
serialisation and check order. A search flattens the groups and drops the lines.
Popups were capped at fixed heights (320 menu, 260 palette, 340 pickers) while
the canvas had room to spare. `Popup.roomFor()` measures its own chrome and
takes the window height minus a tenth at each end. Placement clamps
asymmetrically: the bottom margin is enforced, the top is only a budget, so a
menu anchored near the top stays at its button instead of being pushed off it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>