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>
53 lines
2.4 KiB
TypeScript
53 lines
2.4 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { CreateAssetCommand } from '../../src/core/commands/assetCommands';
|
|
import { findAssetOf } from '../../src/core/model/Project';
|
|
import { paletteEntries, rankedEntries } from '../../src/editor/graph/paletteEntries';
|
|
import { makeProject } from '../helpers';
|
|
|
|
/** The palette of a fresh behavior: the real catalog, not a hand-made list. */
|
|
function behaviorPalette() {
|
|
const f = makeProject();
|
|
const create = new CreateAssetCommand('behavior', 'B');
|
|
f.stack.execute(create);
|
|
const asset = findAssetOf(f.project, create.asset.id, 'behavior')!;
|
|
return paletteEntries(f.project, asset);
|
|
}
|
|
|
|
const labels = (query: string) => rankedEntries(behaviorPalette(), query).map((e) => e.def.label);
|
|
|
|
describe('rankedEntries', () => {
|
|
it('falls back to category and alphabet while nothing is typed', () => {
|
|
const entries = rankedEntries(behaviorPalette(), '');
|
|
expect(entries.length).toBe(behaviorPalette().length);
|
|
const events = entries.filter((e) => e.def.category === 'event');
|
|
expect(entries.slice(0, events.length)).toEqual(events);
|
|
expect(events.map((e) => e.def.label)).toEqual([...events.map((e) => e.def.label)].sort());
|
|
});
|
|
|
|
it('puts the node named after the query first (#43)', () => {
|
|
// What the issue reported: "variable" + Enter took the first row, which was `Run behavior`
|
|
// because its description mentions variables. The entry that is *called* it comes first now.
|
|
const first = labels('variable')[0]!;
|
|
expect(first.toLowerCase()).toContain('variable');
|
|
expect(first).not.toBe('Run behavior');
|
|
});
|
|
|
|
it('ranks every label hit ahead of every description-only hit', () => {
|
|
const ranked = rankedEntries(behaviorPalette(), 'variable');
|
|
const named = ranked.filter((e) => e.def.label.toLowerCase().includes('variable'));
|
|
const mentioned = ranked.filter((e) => !e.def.label.toLowerCase().includes('variable'));
|
|
expect(named.length).toBeGreaterThan(0);
|
|
expect(mentioned.length).toBeGreaterThan(0);
|
|
expect(ranked.indexOf(named[named.length - 1]!)).toBeLessThan(ranked.indexOf(mentioned[0]!));
|
|
});
|
|
|
|
it('finds an exact label before anything that merely starts with it', () => {
|
|
expect(labels('log')[0]).toBe('Log');
|
|
});
|
|
|
|
it('still matches type and description, so nothing becomes unfindable', () => {
|
|
expect(labels('Vsprchtnich')).toEqual([]);
|
|
expect(labels('onstart')).toContain('On start');
|
|
});
|
|
});
|