The name of a thing beats its description, so search says so (#43)

Architecture: the popup section now describes the ranked search, the two
groups in the `+` menu and the tenth-of-the-canvas budget, including why the
top margin is only a budget and not a floor.
Behaviors, Concepts: what the palette and the `+` menu order looks like.
Development: the two new debug hooks that make an order testable at all.
admin committed 2026-09-15 10:21:25 +02:00
1 parent 452b31df54
commit 779c850f4d
4 files changed
+43 -12

No files matched your search

+22 -6
@@ -13,11 +13,27 @@
on the `click` it synthesises and leaves `detail` at 0 on `pointerdown`, so every press handler
that trusted `e.detail` never saw a second click. Presses are counted per key rather than per
widget, because a list rebuilds its rows when the first click changes the selection.
Menus read as lists: entries are left-aligned and scroll once they pass 320 px instead of
running off the screen. A menu that grows with the project — the parent picker lists every
Menus read as lists: entries are left-aligned and scroll once they are taller than there is room
for, instead of running off the screen. **The room is the canvas**, not a constant (#43):
`Popup.roomFor()` takes the window height, less a tenth of it at each end, less the chrome around
the scrolling part — and it *measures* that chrome (a search box, a header, a row of buttons)
rather than repeating it as a number, so no popup can forget one. A fixed cap would waste a tall
screen or overflow a short one, and a constant gap that frames a popup on a laptop is a hairline
on a large screen. It is what holds the whole `+` menu of fifteen asset types on screen, and what
lets the image, font and sound pickers show six rows of what the server has instead of two.
A popup that does not fit below its anchor is still *moved* up rather than squeezed, and
`clampPopupY()` enforces the tenth while moving it — otherwise the gap ends up entirely above it
and the popup sits flush on the bottom edge, which is exactly what it used to do.
Upwards that tenth is a budget the height cap spends, not a distance to keep: a menu hanging off
a button near the top of the screen belongs under its button, so nothing pushes it down to reach
the margin. Below is the rule, above is what is left over.
A menu that grows with the project — the parent picker lists every
object of the scene — is opened with `{ searchable: true }`, which puts a filter box above
the entries (Enter takes the first match). A field that opens a menu is a `Dropdown`: value
flush left, chevron drawn flush right rather than written into the label.
the entries; that search ranks by `ui/search.ts` and Enter takes the best match, the same rule
the node palette uses. Entries can be grouped with `{ separator: true }` — a line, dropped when
a search flattens the list or when it would open, close or double up the menu. A field that
opens a menu is a `Dropdown`: value flush left, chevron drawn flush right rather than written
into the label.
Every label that can be wider than its control is cut with an ellipsis (`truncate` in
`Label.ts`, shared by `Label`, `ListRow` and `Button`). A `Button` keeps the full text and
measures from it — only what is drawn is shortened — and a subclass that puts something else
@@ -74,9 +90,9 @@ src/editor/inspector/ Inspector panel, host (what a section builds into), field
(where a value lives — shared with the List tab),
one section per selection kind (asset/object/node/frame), variableRows (one row shape for
every variable), portSection, aspectAction, objectQueries/vectorRows/parentRow
src/editor/panels/ AssetPanel, HierarchyPanel, Toolbar
src/editor/panels/ AssetPanel, assetMenu (the two groups of the + menu), HierarchyPanel, Toolbar
src/editor/graph/ GraphEditor (header + picker), GraphCanvas, EdgeLayer (curves and fold marks), graphMenus,
NodeView, NodePalette + paletteEntries, folding (hidden links, collapsed nodes), Breadcrumb + trail (the path into nested behaviors), beads (the run's pulses)
NodeView, NodePalette + paletteEntries (which also ranks a search), folding (hidden links, collapsed nodes), Breadcrumb + trail (the path into nested behaviors), beads (the run's pulses)
src/editor/container/ ContainerEditor (header + picker), ContainerCanvas (a SceneViewport on a template)
src/editor/texture/ TextureEditor (header + picker), TextureCanvas (picture + frames), SliceDialog, sheetView (the grid maths),
ImagePicker + imageGrid (the "+ → Texture" dialog), checker (the transparency board)
+5
@@ -210,6 +210,11 @@ shown on the node and editable in the inspector.
`vy` variable and moves the owner by `(axis.x · speed, vy)` with *Collide with solids* on;
`blocked y` while falling means standing, so `vy` goes back to 0 and `onGround` becomes true;
`On key down (Space)` sets `vy = -jumpSpeed` when `onGround`.
- **The palette answers by how well it matches**, not alphabetically: an exact hit on a node's name
or type first, then a name that begins with what was typed, then one that contains it, then the
type, and only last a node whose *description* mentions it. Category and alphabet order what is
equally good. That is what `Enter` takes, so typing `variable` adds the node called that and not
`Run behavior`, whose description happens to mention variables (#43).
- **Editing**: right-click the canvas for the searchable palette, drag port → port to connect
(types are checked; a data input holds one link and is replaced), drag nodes, right-click a
node or port to delete/disconnect, `Delete` removes the selection, wheel zooms. Everything is
+4 -1
@@ -36,7 +36,10 @@ no movement): everything that happens is defined by behavior graphs.
**Toolbar and panels.** The toolbar holds the **Project** menu (new project, switch project,
*Delete this project…*), the project name (type to rename), the **Scene** picker for the active
scene, Undo/Redo, Play/Pause, and on the right the notice line, the save status and the frame
rate. The **Assets** panel's `+` creates an asset and opens its tab. A click on a row only
rate. The **Assets** panel's `+` creates an asset and opens its tab; its menu lists the types in
two alphabetical groups, first what can be placed into a scene, then what only lives in the library
(#43) — the line between them answers the question that follows right after "which type?".
A click on a row only
selects (the inspector shows it, no tab moves); a **double-click** opens the tab the asset lives
in — Scene, Behavior, State machine, Shader, Particles, Texture, Animation or Container — and on a scene
also makes it the active one. `Enter` does the same once the list has keyboard focus (`Tab`).
+12 -5
@@ -74,11 +74,18 @@ not by clicking its name: since the tenth tab the row hides some of them at the
and the helper goes through the `»` menu when it has to. Clicking the label directly took the
whole suite down the day the Particles tab was added.
The same holds for the **`+` menu**: it has one entry per asset type and scrolls past 320 px, so the
last of them sit under the mask, where `locate` still finds an entry but a click on it lands on
nothing. A step picks a type with `pickAssetType(t, 'Font')` from the same file, which scrolls it in
first. The fourteenth type (Variables) pushed `Font` under that edge and took the font step down,
because only the particles step had carried the workaround of its own. Sound is the fifteenth.
The same holds for the **`+` menu**: it has one entry per asset type, and an entry below the mask
edge is still found by `locate` while a click on it lands on nothing. The fourteenth type
(Variables) pushed `Font` under that edge and took the font step down, because only the particles
step had carried a workaround of its own; sound is the fifteenth. Since #43 a menu may grow to the
height of the canvas, so on the test window all fifteen stand on screen at once — the step still
picks a type with `pickAssetType(t, 'Font')` from the same file, which keeps the scroll as insurance
for a shorter window. The menu's own order is checked in the assets step through `__gm.menuRows()`:
two alphabetical groups, placeable types then library ones, one separator between them. Order is
the whole answer there, and no colour check can see an order.
The node palette has the same property, and `__gm.graph.paletteRows()` is its witness: the rows all
look alike, only their sequence says whether the search ranked them (#43).
**What no colour check can see: whether anything was drawn at all.** `__gm.pixelAt` re-renders the
stage on demand, so it answers correctly on a screen that has been frozen for a minute — which is