game-maker / suite (push) Failing after 10m23s
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>
22 lines
973 B
TypeScript
22 lines
973 B
TypeScript
/**
|
||
* How well a thing answers a search query, smaller is better, -1 for "not at all".
|
||
*
|
||
* `fields` are ordered by how much they count: a node's label first, its type next, its description
|
||
* last. Inside one field an exact hit beats a prefix beats a substring, and an exact hit counts as
|
||
* the best possible answer whichever field it came from – someone typing a whole word means that
|
||
* word. Without this, a flat "contains" filter lets a long description outrank the node that is
|
||
* literally named after the query.
|
||
*/
|
||
export function searchRank(query: string, fields: readonly string[]): number {
|
||
if (!query) return 0;
|
||
let best = -1;
|
||
fields.forEach((field, index) => {
|
||
const value = field.toLowerCase();
|
||
const kind = value === query ? 0 : value.startsWith(query) ? 1 : value.includes(query) ? 2 : -1;
|
||
if (kind < 0) return;
|
||
const rank = kind === 0 ? 0 : index * 3 + kind;
|
||
if (best < 0 || rank < best) best = rank;
|
||
});
|
||
return best;
|
||
}
|