Classify a star for the rows that list it, not every star each time the index is built

The search index and the route index gave every one of the 455 571 stars a subtitle up front
through spectralClassification, which filtered the dwarf table afresh on each call; the star
field's tints read the same table once per BP−RP star. Both indices now carry the star itself
and classify it only for the rows shown (entrySubtitle): eight search results, a few route
options. The two filtered columns of the table are built once.

Node, over the shipped catalogue, five runs: classifying every star 121-167 ms before, 56-62
after hoisting the table alone; reading the sequence at every BP−RP colour 187-216 ms, now
111-127. In the app on :4302, two cold loads each, the long task when the Search tab opens was
264-380 ms (median 280) before and 164-276 ms (median 171) after; the longest boot task 863 and
875 ms before, 654 and 741 after.

Tests: the search spec checks its index holds no classification and the row still reads
"Star · ~M8"; the scene spec now reads the route options, and checks its index holds none either.
Controls: classifying every star in the search index, doing so in the route index, and route
options printing the index's subtitle each fail the named test.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-29 23:30:53 +02:00
co-authored by Claude Opus 5.5
parent ff744a00de
commit 18faa6d0b2
6 changed files with 46 additions and 12 deletions
@@ -964,9 +964,20 @@ describe('GalaxySystemSceneComponent camera-flight transitions', () => {
});
it('offers a star for a route by its classification, and a star with none by its name alone', () => {
const component = fixture.componentInstance as unknown as { starSearchIndex(): { entry: { starId?: number; subtitle: string } }[]; currentStarOption(): { subtitle: string } | null };
expect(component.starSearchIndex().find(({ entry }) => entry.starId === LENS.id)?.entry.subtitle).toBe('');
expect(component.starSearchIndex().find(({ entry }) => entry.starId === PROXIMA.id)?.entry.subtitle).toBe('M5V');
const component = fixture.componentInstance as unknown as {
starSearchIndex(): { entry: { starId?: number; subtitle: string } }[];
routeOptions(): { id: number; subtitle: string }[];
onRouteQuery(query: string): void;
currentStarOption(): { subtitle: string } | null;
};
const optionFor = (query: string, id: number): string | undefined => {
component.onRouteQuery(query);
return component.routeOptions().find((option) => option.id === id)?.subtitle;
};
expect(optionFor('KMT-2016', LENS.id)).toBe('');
expect(optionFor('Proxima', PROXIMA.id)).toBe('M5V');
// Classified for the options shown only: the index holds none, where all 455 571 stars cost 140-230 ms at boot.
expect(component.starSearchIndex().every(({ entry }) => entry.subtitle === '')).toBe(true);
navigationStore.selectStar(LENS.id);
expect(component.currentStarOption()?.subtitle).toBe('');
navigationStore.selectStar(PROXIMA.id);