@grimoire-rs/indexer 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -21,6 +21,120 @@ props `{ pkg }` → `{ pkg, compact }`.
21
21
 
22
22
  ## [Unreleased]
23
23
 
24
+ ## [0.5.2] - 2026-08-31
25
+
26
+ ### Fixed
27
+
28
+ - **The sort control drew two borders when clicked.** Hover tints a control's
29
+ border to the accent and focus drew a 2px accent outline standing 1px clear
30
+ of it, so anyone who reached the control with a mouse had both at once — one
31
+ state reading as two lines with a gap between them. The ring merges onto the
32
+ border now, the way the card's focus state already did, and the offset comes
33
+ off every bordered control in the toolbar together rather than off the one
34
+ that showed it: the search field, the keyword overflow menu's search, both
35
+ sort halves and both view picks. Keyword chips gain the same ring, having
36
+ taken the browser's default until now.
37
+ - **A long address no longer wraps the card's head onto a second line.** A
38
+ namespace breaks at its own slashes and dots, so a wide one took two rows and
39
+ left the card taller than its neighbours with the tail of the address against
40
+ the padding edge. It stays on one line and the overflow is trimmed from the
41
+ *front* — the registry host is on every address in an index and the
42
+ repository at the tail is what tells two apart — with the whole of it in the
43
+ element's `title`.
44
+ - **Keyword chips could stutter, or stop part-way through the rail's slide.**
45
+ The rail's FLIP pass inverted each chip with an inline `transition: none` and
46
+ a `translate`, then dropped both on the next animation frame; any commit
47
+ landing inside that window left the chip carrying the offset with its
48
+ transition disabled, and nothing else took those off. It also measured seats
49
+ with `getBoundingClientRect`, which reports where a chip is *drawn*, so a
50
+ chip caught mid-slide recorded its animated box and the next inversion
51
+ compounded the error instead of correcting it. Neither window was rare: the
52
+ rail's own fit measurement re-commits whenever a rescore changes how many
53
+ chips fit. Seats now come from `offsetLeft`/`offsetTop`, which ignore
54
+ transforms and scroll, and the slide is a Web Animation — it starts without a
55
+ frame, writes nothing to `style`, and clears itself when it finishes or is
56
+ cancelled. `prefers-reduced-motion` is honoured by the effect rather than by
57
+ the stylesheet, and `--grim-duration-slow` is still its length.
58
+
59
+ ## [0.5.1] - 2026-08-31
60
+
61
+ ### Added
62
+
63
+ - **`grim-indexer dev --host [addr]`.** Bare, it binds every interface; with a
64
+ value, that address. Without it the server binds loopback only — Astro's own
65
+ default — which is unreachable from a dev container, a VM or a WSL guest,
66
+ where the port forwards and the connection then hangs against a socket that
67
+ is not listening for it. `npm run dev -- --host` takes the same two forms.
68
+ Bare `--host` puts the preview on your network; the CLI reference says so.
69
+ - **`CodeBlock.astro`**, a reusable code block: the site's own frame, the same
70
+ Shiki theme pair every rendered README uses, the same copy button and toast,
71
+ and an optional VS Code button. Props
72
+ `{ code, lang?, name?, vscodeHref?, vscodeLabel? }`; `vscodeHref` is a plain
73
+ URL, so `vscodeUrl`, `vscodeVoteUrl`, `addRegistryUrl` and a hand-written
74
+ deep link all work. Worked example in
75
+ [Reuse shipped components](docs/how-to/reuse-components.md#code-blocks).
76
+ - **`data-slot="code-block"`**, for it. Contract tier, like every other slot.
77
+ - **A second argument on `registryAddCommand` and `registryScopeChoices`**,
78
+ the registry to build the command for — defaulting to the one in your config,
79
+ so nothing existing changes. It is what lets a setup page draw the "add
80
+ registry" bar for an index *other* than the site's own: a corporate index in
81
+ the hero and the public one further down.
82
+ - **A clear button on the keyword filters**, in the catalog toolbar. Rendered
83
+ only while a keyword is picked, and it lifts the keywords alone — Escape is
84
+ still what clears the search and the kinds with them.
85
+
86
+ ### Fixed
87
+
88
+ - **The keyword overflow menu opened invisibly.** Its panel was an absolutely
89
+ positioned child of the toolbar's filter row, which is a scroll container, so
90
+ the panel was cropped to the row and stretched the row's scroll extent — a
91
+ menu nobody could see, with a stray horizontal and vertical scrollbar to show
92
+ for it. It is a popover now, which puts it in the top layer, outside every
93
+ ancestor's `overflow`, and brings Escape and light dismiss with it. The
94
+ trigger is a `<button>` rather than a `<summary>`, so the toolbar's arrow-key
95
+ navigation now reaches it.
96
+ - **The detail page's logo drifted down under a long description.** The header
97
+ centred the tile against the text beside it, so the same package's mark sat
98
+ at a different height on every page. It holds against the title now.
99
+ - **Dropped the tinted square behind a package logo**, on the cards, the list
100
+ rows and the detail header alike. A logo is a designed mark already sitting
101
+ on its own ground, so the slot framed it twice — most visibly for the many
102
+ logos that are themselves a rounded square, which then sat inside a slightly
103
+ larger one. The slot still reserves the same box, so nothing shifts. The
104
+ dashed frame the broken-logo state drew goes with it — it read as a fault in
105
+ the layout rather than in the image; the slashed glyph is what says the logo
106
+ failed, and `role="img"` with a label is what announces it. The
107
+ initial-letter tile a package with no logo gets is unchanged: there the
108
+ coloured ground is the mark.
109
+ - **The dev server's URL is a legal URL when the server binds an IPv6
110
+ address.** An unbracketed literal reads its own colons as a port, so
111
+ `http://::1:4321/` threw `ERR_INVALID_URL` at the first `new URL` against it
112
+ and took `dev:smoke` down with a message naming neither the address nor the
113
+ host. Wildcard binds still become `localhost`; anything carrying a colon is
114
+ bracketed.
115
+ - **The detail page's logo tile is a fixed square.** `aspect-ratio: 1` derived
116
+ its width from the height the box had *before* `align-self: stretch` grew it,
117
+ so it rendered 56x93 beside a logo and 56x70 beside a letter — portrait,
118
+ never square, and narrow enough to read as bounding the image rather than
119
+ framing it.
120
+ - **The list row's glyphs sit on the row's centre.** `vertical-align: middle`
121
+ aligns to half the parent's x-height, about 2px under the line's true middle
122
+ at this step, and the upvote arrow's optical nudge added a third pixel the
123
+ same way. The kind and rating cells are flex boxes now and centre their
124
+ contents as boxes.
125
+ - **Keyword chips could freeze part-way through the rail's slide.** The rail
126
+ animates a rescore by inverting each chip with an inline `translate` and
127
+ dropping it on the next animation frame. A rescore that also changes how many
128
+ chips fit commits a second time, and that commit cancelled the frame before
129
+ it ran — leaving every moved chip parked at its offset with transitions
130
+ disabled. Deselecting the last keyword hit it most often, because that
131
+ rescore is the largest. The offsets are now cleared before each measurement
132
+ and on cleanup, so an interrupted slide resolves instead of sticking.
133
+ - **Switching between the cards and list views repainted the whole catalog.**
134
+ Off-screen cards are skipped until they are scrolled to. The two views share
135
+ no element types, so a switch still rebuilds every item — past a few hundred
136
+ packages the answer is windowing the list, and this is not that.
137
+
24
138
  ## [0.5.0] - 2026-08-30
25
139
 
26
140
  ### Added
@@ -186,5 +300,7 @@ props `{ pkg }` → `{ pkg, compact }`.
186
300
  boot can leave a `.index-*` holding Vite's `deps_temp_<hash>`, which the
187
301
  dependency optimizer recreates after the removal has already returned.
188
302
 
189
- [Unreleased]: https://github.com/grimoire-rs/indexer/compare/v0.5.0...HEAD
303
+ [Unreleased]: https://github.com/grimoire-rs/indexer/compare/v0.5.2...HEAD
304
+ [0.5.2]: https://github.com/grimoire-rs/indexer/compare/v0.5.1...v0.5.2
305
+ [0.5.1]: https://github.com/grimoire-rs/indexer/compare/v0.5.0...v0.5.1
190
306
  [0.5.0]: https://github.com/grimoire-rs/indexer/compare/v0.4.4...v0.5.0
package/dist/cli/dev.d.ts CHANGED
@@ -2,6 +2,20 @@ import { type ExitCode } from "./exit.js";
2
2
  export interface DevFlags {
3
3
  outDir?: string;
4
4
  port?: string;
5
+ /** `true` for a bare `--host` (every interface), or the address to bind. */
6
+ host?: string | boolean;
5
7
  }
8
+ /**
9
+ * What `--host` binds to, in the shape Astro's `server.host` takes: `true` for
10
+ * every interface, a string for one address, `undefined` to leave the default
11
+ * loopback bind alone.
12
+ *
13
+ * Only the shapes that cannot be an address are rejected here — an empty value,
14
+ * and anything carrying whitespace or a `/`, which is what `--host
15
+ * http://0.0.0.0` and a pasted URL look like. Whether the address exists on
16
+ * this machine is the kernel's answer, not this function's, and it arrives as a
17
+ * bind error naming the address.
18
+ */
19
+ export declare function resolveHost(host: string | boolean | undefined): string | boolean | undefined;
6
20
  export declare function dev(root: string, flags: DevFlags): Promise<ExitCode>;
7
21
  //# sourceMappingURL=dev.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/cli/dev.ts"],"names":[],"mappings":"AASA,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAC;AAG1D,MAAM,WAAW,QAAQ;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAgBD,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAoC1E"}
1
+ {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/cli/dev.ts"],"names":[],"mappings":"AASA,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAC;AAG1D,MAAM,WAAW,QAAQ;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACzB;AAgBD;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAU5F;AAED,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAqC1E"}
package/dist/cli/dev.js CHANGED
@@ -21,10 +21,31 @@ function resolvePort(port) {
21
21
  }
22
22
  return value;
23
23
  }
24
+ /**
25
+ * What `--host` binds to, in the shape Astro's `server.host` takes: `true` for
26
+ * every interface, a string for one address, `undefined` to leave the default
27
+ * loopback bind alone.
28
+ *
29
+ * Only the shapes that cannot be an address are rejected here — an empty value,
30
+ * and anything carrying whitespace or a `/`, which is what `--host
31
+ * http://0.0.0.0` and a pasted URL look like. Whether the address exists on
32
+ * this machine is the kernel's answer, not this function's, and it arrives as a
33
+ * bind error naming the address.
34
+ */
35
+ export function resolveHost(host) {
36
+ if (host === undefined || typeof host === "boolean")
37
+ return host;
38
+ const value = host.trim();
39
+ if (value === "" || /[\s/]/.test(value)) {
40
+ throw new CliError(`--host ${JSON.stringify(host)}: must be a hostname or IP address, or bare for all interfaces`, EXIT.usage);
41
+ }
42
+ return value;
43
+ }
24
44
  export async function dev(root, flags) {
25
45
  const rootDir = path.resolve(root);
26
46
  const outDir = resolveOutDir(rootDir, flags.outDir);
27
47
  const port = resolvePort(flags.port);
48
+ const host = resolveHost(flags.host);
28
49
  const [{ loadConfig }, { compileIndex }, { devSite }] = await Promise.all([
29
50
  import("../config.js"),
30
51
  import("../data/index.js"),
@@ -32,7 +53,7 @@ export async function dev(root, flags) {
32
53
  ]);
33
54
  const config = await loadConfig(rootDir);
34
55
  const { count, namespaces } = await compileIndex({ root: rootDir, outDir });
35
- const server = await devSite({ root: rootDir, outDir, config, port });
56
+ const server = await devSite({ root: rootDir, outDir, config, port, host });
36
57
  console.log(`\n ${server.url}\n`);
37
58
  console.log(` ${count} package(s) across ${namespaces.length} namespace(s)`);
38
59
  // `index.config.json` is read once, at boot, and baked into the bundle — the
@@ -1 +1 @@
1
- {"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/cli/dev.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,sCAAsC;AAEtC,8EAA8E;AAC9E,+EAA+E;AAC/E,4EAA4E;AAC5E,uBAAuB;AACvB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAiB,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAO7C;;;;GAIG;AACH,SAAS,WAAW,CAAC,IAAwB;IAC3C,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACzC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3B,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,KAAK,EAAE,CAAC;QAC3D,MAAM,IAAI,QAAQ,CAAC,UAAU,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,mCAAmC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IACpG,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,GAAG,CAAC,IAAY,EAAE,KAAe;IACrD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAErC,MAAM,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACxE,MAAM,CAAC,cAAc,CAAC;QACtB,MAAM,CAAC,kBAAkB,CAAC;QAC1B,MAAM,CAAC,sBAAsB,CAAC;KAC/B,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,CAAC;IACzC,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,YAAY,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IAC5E,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IAEtE,OAAO,CAAC,GAAG,CAAC,OAAO,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;IACnC,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,sBAAsB,UAAU,CAAC,MAAM,eAAe,CAAC,CAAC;IAC9E,6EAA6E;IAC7E,yEAAyE;IACzE,kCAAkC;IAClC,OAAO,CAAC,GAAG,CAAC,gEAAgE,CAAC,CAAC;IAE9E,4EAA4E;IAC5E,4EAA4E;IAC5E,0DAA0D;IAC1D,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAClC,KAAK,MAAM,MAAM,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAU,EAAE,CAAC;YACpD,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;gBACtB,KAAK,MAAM;qBACR,IAAI,EAAE;qBACN,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC;qBACf,OAAO,CAAC,OAAO,CAAC,CAAC;YACtB,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,IAAI,CAAC,EAAE,CAAC;AACjB,CAAC"}
1
+ {"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/cli/dev.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AA+FA,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAkJzD"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AA+FA,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAyJzD"}
package/dist/cli/main.js CHANGED
@@ -143,6 +143,11 @@ export async function run(argv) {
143
143
  .description("serve this index locally with a live preview")
144
144
  .option("--out-dir <dir>", "output directory", "dist")
145
145
  .option("--port <n>", "port to listen on")
146
+ // `[addr]` and not `<addr>`: bare `--host` means every interface, the same
147
+ // thing it means to `astro dev`, whose own banner is what tells a reader
148
+ // stuck behind a dev container or a WSL port forward to reach for it.
149
+ // Commander hands back `true` for the bare form and the string otherwise.
150
+ .option("--host [addr]", "expose the server on a network address (bare: all interfaces)")
146
151
  .action(async (root, opts) => {
147
152
  code = await dev(root, opts);
148
153
  });
@@ -1 +1 @@
1
- {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "preact/jsx-ru
2
2
  import { useEffect, useLayoutEffect, useMemo, useRef, useState, } from "preact/hooks";
3
3
  // Lucide (ISC) draws the toolbar. The brand marks and kind glyphs moved out
4
4
  // with the card and the row that wear them.
5
- import { ArrowDownWideNarrow, ArrowUpNarrowWide, LayoutGrid, List, } from "lucide-preact";
5
+ import { ArrowDownWideNarrow, ArrowUpNarrowWide, LayoutGrid, List, X, } from "lucide-preact";
6
6
  import { PackageCard } from "./PackageCard.js";
7
7
  import { PackageRow } from "./PackageRow.js";
8
8
  import { keywordFrequency, selectRailKeywords } from "../lib/keywordRail.js";
@@ -22,6 +22,12 @@ function kindOrder(kind) {
22
22
  * all of it is a wall of chips nobody reads.
23
23
  */
24
24
  const KEYWORD_CHIP_LIMIT = 8;
25
+ /**
26
+ * `popovertarget` needs an id, and the catalog is a singleton on its page —
27
+ * one island, one toolbar, one overflow menu — so this is a constant rather
28
+ * than something generated per mount.
29
+ */
30
+ const KEYWORD_MENU_ID = "grim-keyword-overflow";
25
31
  /**
26
32
  * The direction each field is *worth* reading first in — A→Z for a name,
27
33
  * newest and best-liked first for the two ranked keys.
@@ -252,8 +258,69 @@ export default function Catalog({ packages, vscodeExtension, }) {
252
258
  ...(controlsRef.current?.querySelectorAll('button.chip:not([aria-hidden="true"])') ?? []),
253
259
  ];
254
260
  const railRefs = useRef(new Map());
255
- const railRects = useRef(new Map());
261
+ /**
262
+ * Where each chip sat at the last commit, as `offsetLeft`/`offsetTop`.
263
+ *
264
+ * Layout positions, deliberately, not `getBoundingClientRect`: the offsets
265
+ * ignore transforms and page scroll, so a chip measured mid-slide reports
266
+ * the seat it is animating towards rather than the box it is drawn in this
267
+ * frame. See the FLIP effect below for why that distinction is the whole
268
+ * fix.
269
+ */
270
+ const railSeats = useRef(new Map());
271
+ /** The slide each chip is currently running, so a new one can replace it. */
272
+ const railSlides = useRef(new WeakMap());
256
273
  const railRef = useRef(null);
274
+ const kwMenuRef = useRef(null);
275
+ const kwTriggerRef = useRef(null);
276
+ // Drives the trigger's own `aria-expanded` and its open styling. The panel's
277
+ // visibility is the popover's business, not this flag's.
278
+ const [kwMenuOpen, setKwMenuOpen] = useState(false);
279
+ /**
280
+ * Seat the overflow menu under its trigger.
281
+ *
282
+ * The panel is a popover, so it lives in the top layer and is positioned
283
+ * against the viewport rather than against any ancestor — which is the whole
284
+ * point (`.filter-row` is a scroll container and used to crop it). That
285
+ * leaves the seat to us.
286
+ *
287
+ * It always opens downward: the toolbar sits at the top of the page, and a
288
+ * flip would only ever fire on a viewport short enough that the panel has
289
+ * nowhere to go either way. What does not fit becomes `max-height` and
290
+ * scrolls inside the list.
291
+ *
292
+ * Called twice per open, from `beforetoggle` and again from `toggle`. The
293
+ * first runs while the panel is still `display: none`, so its measured width
294
+ * is 0 and the horizontal clamp is a no-op — but the vertical seat is right,
295
+ * which is what stops it appearing in the wrong place for a frame. The
296
+ * second has a real width and finishes the clamp.
297
+ */
298
+ const placeKwMenu = () => {
299
+ const panel = kwMenuRef.current;
300
+ const trigger = kwTriggerRef.current;
301
+ if (!panel || !trigger)
302
+ return;
303
+ const seat = trigger.getBoundingClientRect();
304
+ const gap = 8;
305
+ const width = panel.getBoundingClientRect().width;
306
+ panel.style.left = `${Math.max(gap, Math.min(seat.left, window.innerWidth - width - gap))}px`;
307
+ panel.style.top = `${seat.bottom + gap}px`;
308
+ panel.style.maxHeight = `${Math.max(120, window.innerHeight - seat.bottom - gap * 3)}px`;
309
+ };
310
+ // A fixed panel does not travel with the trigger, so it is re-seated rather
311
+ // than left behind. Only while open — there is nothing to follow otherwise.
312
+ useEffect(() => {
313
+ if (!kwMenuOpen)
314
+ return;
315
+ const reseat = () => placeKwMenu();
316
+ // Capturing: the scroll may be any ancestor's, including `.filter-row`'s.
317
+ window.addEventListener("scroll", reseat, { capture: true, passive: true });
318
+ window.addEventListener("resize", reseat);
319
+ return () => {
320
+ window.removeEventListener("scroll", reseat, { capture: true });
321
+ window.removeEventListener("resize", reseat);
322
+ };
323
+ }, [kwMenuOpen]);
257
324
  /**
258
325
  * How many keyword chips actually fit on the row, measured.
259
326
  *
@@ -317,42 +384,76 @@ export default function Catalog({ packages, vscodeExtension, }) {
317
384
  * between two frames reads as having been *replaced*, and a reader who
318
385
  * cannot see that a chip moved has no reason to believe it is the same one.
319
386
  *
320
- * First (the map of rects kept from the last commit), Last (measured now),
321
- * Invert (an inline translate back to where the chip was), Play (dropped on
322
- * the next frame, so the stylesheet's transition carries it home). Measure
323
- * every chip before transforming any: `translate` composites and does not
324
- * reflow, but reading a rect after writing a style on a sibling is the
325
- * shape that makes a layout thrash, and this runs per keystroke.
387
+ * Two choices carry the whole thing, and both are here because the shape
388
+ * they replace — an inline `translate` under the stylesheet's transition,
389
+ * dropped again on a `requestAnimationFrame` could leave a chip stopped
390
+ * off its seat with no path back:
391
+ *
392
+ * - **Seats come from `offsetLeft`/`offsetTop`, never from
393
+ * `getBoundingClientRect`.** The offsets are layout positions and ignore
394
+ * both transforms and scroll; a rect is where the chip is *drawn*, so a
395
+ * chip measured mid-slide recorded its animated box and the next
396
+ * inversion compounded that error — the stutter. Mid-slide is the common
397
+ * case, not a rare one: the fit measurement above commits a second time
398
+ * whenever a rescore changes how many chips fit, and that commit lands
399
+ * inside the previous slide. Because the offsets are transform-blind,
400
+ * that second commit now measures the same seats and starts nothing.
401
+ * - **The slide is a Web Animation, not an inline style.** It needs no
402
+ * frame to start, so no commit can land between an invert and its play
403
+ * and freeze a chip at the offset. It writes nothing to `style`, so
404
+ * there is nothing left to strip when a chip unmounts or a pass is
405
+ * superseded. And it clears itself the instant it finishes or is
406
+ * cancelled, which makes "the rail always ends up in its real state" a
407
+ * property of the mechanism rather than of a cleanup remembering to run.
326
408
  */
327
409
  useLayoutEffect(() => {
328
- const previous = railRects.current;
410
+ const previous = railSeats.current;
329
411
  const current = new Map();
330
412
  const moved = [];
331
413
  for (const [keyword, el] of railRefs.current) {
332
- const rect = el.getBoundingClientRect();
333
- current.set(keyword, rect);
414
+ const x = el.offsetLeft;
415
+ const y = el.offsetTop;
416
+ current.set(keyword, { x, y });
334
417
  const was = previous.get(keyword);
335
418
  if (!was)
336
419
  continue;
337
- const dx = was.left - rect.left;
338
- const dy = was.top - rect.top;
420
+ const dx = was.x - x;
421
+ const dy = was.y - y;
339
422
  if (dx !== 0 || dy !== 0)
340
423
  moved.push({ el, dx, dy });
341
424
  }
342
- railRects.current = current;
425
+ // Rebuilt from `railRefs` every pass, so a chip that left the rail leaves
426
+ // this map with it and cannot seed a slide if it comes back elsewhere.
427
+ railSeats.current = current;
343
428
  if (moved.length === 0)
344
429
  return;
430
+ const rail = railRef.current;
431
+ // Guarded rather than assumed, like the fit observer above: the test
432
+ // renderer's DOM has no Web Animations API, and a rail that does not
433
+ // slide is not worth throwing during a render over.
434
+ if (!rail || typeof rail.animate !== "function")
435
+ return;
436
+ // The motion is the ornament here — the filtering works identically
437
+ // without it — so a reader who asked for less of it gets none.
438
+ if (window.matchMedia?.("(prefers-reduced-motion: reduce)").matches)
439
+ return;
440
+ // The stylesheet stays the source of truth for how long a slide runs:
441
+ // `--grim-duration-slow` is written in milliseconds and that is the unit
442
+ // the Web Animations API takes, so the token survives the move out of CSS.
443
+ // Read once, after every seat above, so the reads and the writes below
444
+ // stay in two passes rather than interleaving per chip.
445
+ const ms = Number.parseFloat(getComputedStyle(rail).getPropertyValue("--grim-duration-slow"));
345
446
  for (const { el, dx, dy } of moved) {
346
- el.style.transition = "none";
347
- el.style.translate = `${dx}px ${dy}px`;
447
+ // One slide per chip. A chip that moves again mid-slide takes the new
448
+ // delta from its layout seat, which starts the second slide a step off
449
+ // where the first had drawn it — a jump measured in the pixels one
450
+ // frame of easing covers, and it is bounded, unlike two animations
451
+ // compositing the same property against each other.
452
+ // ponytail: not blended with the in-flight offset, which would cost a
453
+ // computed-style read per chip in the middle of the write pass.
454
+ railSlides.current.get(el)?.cancel();
455
+ railSlides.current.set(el, el.animate({ translate: [`${dx}px ${dy}px`, "none"] }, { duration: Number.isFinite(ms) ? ms : 200, easing: "ease-out" }));
348
456
  }
349
- const frame = requestAnimationFrame(() => {
350
- for (const { el } of moved) {
351
- el.style.transition = "";
352
- el.style.translate = "";
353
- }
354
- });
355
- return () => cancelAnimationFrame(frame);
356
457
  });
357
458
  /** Move focus `delta` cards along, clamping at both ends rather than wrapping. */
358
459
  const focusCard = (from, delta) => {
@@ -768,7 +869,28 @@ export default function Catalog({ packages, vscodeExtension, }) {
768
869
  ]
769
870
  .filter(Boolean)
770
871
  .join(" "), "data-slot": "filter-chip", "aria-pressed": keywords.includes(keyword), "aria-hidden": clipped ? "true" : undefined, tabIndex: clipped ? -1 : undefined, onKeyDown: onChipKeyDown, onClick: () => toggleKeyword(keyword), children: keyword }, keyword));
771
- }) })] })), menuKeywords.length > 0 && (_jsxs("details", { class: "kw-menu", children: [_jsxs("summary", { class: "chip", "data-slot": "filter-chip", children: ["+", menuKeywords.length, " more"] }), _jsxs("div", { class: "kw-menu-panel", children: [_jsx("input", { type: "text", class: "kw-menu-search", placeholder: "Filter keywords\u2026", "aria-label": "Filter keywords", value: keywordFilter, onInput: (e) => setKeywordFilter(e.target.value) }), _jsxs("div", { class: "kw-menu-list", children: [menuShown.map(({ keyword, count }) => (_jsxs("button", { type: "button", class: "kw-menu-item", onClick: () => toggleKeyword(keyword), children: [_jsx("span", { children: keyword }), _jsx("small", { children: count })] }, keyword))), menuShown.length === 0 && (_jsx("p", { class: "kw-menu-empty", children: "No keyword matches." }))] })] })] })), hasDeprecated && (_jsx("button", { type: "button", class: showDeprecated
872
+ }) })] })), menuKeywords.length > 0 && (_jsxs("div", { class: "kw-menu", children: [_jsxs("button", { type: "button", class: "chip", "data-slot": "filter-chip", ref: kwTriggerRef, popovertarget: KEYWORD_MENU_ID, "aria-expanded": kwMenuOpen, children: ["+", menuKeywords.length, " more"] }), _jsxs("div", { class: "kw-menu-panel", id: KEYWORD_MENU_ID, popover: "auto", ref: kwMenuRef,
873
+ // Both, and in this order: `beforetoggle` runs synchronously
874
+ // inside the show steps, so the panel is seated before it is
875
+ // ever painted; `toggle` runs after, when its width can
876
+ // actually be measured for the clamp.
877
+ onBeforeToggle: (e) => {
878
+ setKwMenuOpen(e.newState === "open");
879
+ placeKwMenu();
880
+ }, onToggle: (e) => {
881
+ setKwMenuOpen(e.newState === "open");
882
+ placeKwMenu();
883
+ }, children: [_jsx("input", { type: "text", class: "kw-menu-search", placeholder: "Filter keywords\u2026", "aria-label": "Filter keywords", value: keywordFilter, onInput: (e) => setKeywordFilter(e.target.value) }), _jsxs("div", { class: "kw-menu-list", children: [menuShown.map(({ keyword, count }) => (_jsxs("button", { type: "button", class: "kw-menu-item", onClick: () => toggleKeyword(keyword), children: [_jsx("span", { children: keyword }), _jsx("small", { children: count })] }, keyword))), menuShown.length === 0 && (_jsx("p", { class: "kw-menu-empty", children: "No keyword matches." }))] })] })] })), keywords.length > 0 && (_jsxs("button", { type: "button", class: "chip kw-clear", "data-slot": "filter-chip", title: "Clear the keyword filters", onKeyDown: onChipKeyDown, onClick: (e) => {
884
+ setKeywords([]);
885
+ // This button is the last thing standing when it is pressed:
886
+ // clearing the facets unmounts it, and focus would land on
887
+ // `<body>`, sending a keyboard reader back to the top of the
888
+ // document. `detail === 0` is a click synthesized by Enter or
889
+ // Space, so a pointer user is left alone and a keyboard one
890
+ // gets the toolbar's own anchor instead of nothing.
891
+ if (e.detail === 0)
892
+ searchRef.current?.focus();
893
+ }, children: [_jsx(X, { size: 13, "aria-hidden": "true" }), "clear ", keywords.length] })), hasDeprecated && (_jsx("button", { type: "button", class: showDeprecated
772
894
  ? "chip deprecated-toggle active"
773
895
  : "chip deprecated-toggle", "aria-pressed": showDeprecated, title: showDeprecated
774
896
  ? "Hide deprecated packages"