# Files changed · PR \#57 · screen/col

[View on GitCafe](https://git.cafe/screen/col/pull/57/diffs)

Repository: [screen/col](https://git.cafe/screen/col)

Visibility: public

[Complete React Bits component coverage and fix overlapping search matches](https://git.cafe/screen/col/pull/57.md)

Version: 3

Head commit: 8d8f8ae5ae448b35c2d85a3e8e4554dd2a246c61

Base commit: abcfb31011f8661b4114d777bafcae92d43183cb

Comparison base: abcfb31011f8661b4114d777bafcae92d43183cb

Showing up to 20 entries from this revision. If the pull request changes, reopen the first page before continuing.

## data/components.ts

Status: modified; +7; −3

```diff
@@ -130,7 +130,7 @@
     { name: "Select", url: "https://webawesome.com/docs/components/select/" },
     { name: "Tab Group", url: "https://webawesome.com/docs/components/tab-group/" },
   ],
-  // 209 components
+  // 213 components verified against https://reactbits.dev/llms.txt on 2026-10-07.
   "react-bits": [
       { name: "ASCII Text", url: "https://www.reactbits.dev/text-animations/ascii-text" },
       { name: "Blur Text", aliases: ["soft focus"], url: "https://www.reactbits.dev/text-animations/blur-text" },
@@ -214,6 +214,7 @@
       { name: "Card Swap", url: "https://www.reactbits.dev/components/card-swap" },
       { name: "Carousel", aliases: ["slider"], url: "https://www.reactbits.dev/components/carousel" },
       { name: "Chroma Grid", url: "https://www.reactbits.dev/components/chroma-grid" },
+      { name: "Circular Carousel", url: "https://www.reactbits.dev/components/circular-carousel" },
       { name: "Circular Gallery", url: "https://www.reactbits.dev/components/circular-gallery" },
       { name: "Counter", url: "https://www.reactbits.dev/components/counter" },
       { name: "Curved Input", url: "https://www.reactbits.dev/components/curved-input" },
@@ -223,6 +224,7 @@
       { name: "Dome Gallery", url: "https://www.reactbits.dev/components/dome-gallery" },
       { name: "Drift Wall", url: "https://www.reactbits.dev/components/drift-wall" },
       { name: "Elastic Slider", url: "https://www.reactbits.dev/components/elastic-slider" },
+      { name: "Flex Carousel", url: "https://www.reactbits.dev/components/flex-carousel" },
       { name: "Flowing Menu", url: "https://www.reactbits.dev/components/flowing-menu" },
       { name: "Fluid Glass", url: "https://www.reactbits.dev/components/fluid-glass" },
       { name: "Flying Posters", url: "https://www.reactbits.dev/components/flying-posters" },
@@ -319,9 +321,11 @@
       { name: "Line Waves", url: "https://www.reactbits.dev/backgrounds/line-waves" },
       { name: "Liquid Chrome", url: "https://www.reactbits.dev/backgrounds/liquid-chrome" },
       { name: "Liquid Ether", url: "https://www.reactbits.dev/backgrounds/liquid-ether" },
+      { name: "Micro Slats", url: "https://www.reactbits.dev/backgrounds/micro-slats" },
       { name: "Molten Metal", url: "https://www.reactbits.dev/backgrounds/molten-metal" },
       { name: "Orb", url: "https://www.reactbits.dev/backgrounds/orb" },
       { name: "Particles", aliases: ["particle system"], url: "https://www.reactbits.dev/backgrounds/particles" },
+      { name: "Pattern Waves", url: "https://www.reactbits.dev/backgrounds/pattern-waves" },
       { name: "Pixel Blast", url: "https://www.reactbits.dev/backgrounds/pixel-blast" },
       { name: "Pixel Snow", url: "https://www.reactbits.dev/backgrounds/pixel-snow" },
       { name: "Plasma", url: "https://www.reactbits.dev/backgrounds/plasma" },
@@ -571,7 +575,7 @@
       { name: "Context Menu", url: "https://ui.shadcn.com/docs/components/context-menu" },
       { name: "Dropdown Menu", url: "https://ui.shadcn.com/docs/components/dropdown-menu" },
       { name: "Menubar", url: "https://ui.shadcn.com/docs/components/menubar" },
+      { name: "Command", aliases: ["cmdk", "command palette", "command menu", "spotlight"], url: "https://ui.shadcn.com/docs/components/command" },
-      { name: "Command", aliases: ["cmdk", "command palette", "spotlight"], url: "https://ui.shadcn.com/docs/components/command" },
       { name: "Alert", url: "https://ui.shadcn.com/docs/components/alert" },
       { name: "Toast", url: "https://ui.shadcn.com/docs/components/toast" },
       { name: "Sonner", aliases: ["toast", "notification"], url: "https://ui.shadcn.com/docs/components/sonner" },
@@ -625,7 +629,7 @@
       { name: "Sidebar", aliases: ["app nav", "side panel"], url: "https://shadcn-svelte.com/docs/components/sidebar" },
       { name: "Tabs", url: "https://shadcn-svelte.com/docs/components/tabs" },
       { name: "Alert Dialog", url: "https://shadcn-svelte.com/docs/components/alert-dialog" },
-      { name: "Command", aliases: ["cmdk", "command palette", "spotlight"], url: "https://shadcn-svelte.com/docs/components/command" },
+      { name: "Command", aliases: ["cmdk", "command palette", "command menu", "spotlight"], url: "https://shadcn-svelte.com/docs/components/command" },
       { name: "Context Menu", url: "https://shadcn-svelte.com/docs/components/context-menu" },
       { name: "Dialog", url: "https://shadcn-svelte.com/docs/components/dialog" },
       { name: "Drawer", url: "https://shadcn-svelte.com/docs/components/drawer" },
```

## lib/directory.test.mjs

Status: modified; +23; −0

```diff
@@ -66,6 +66,29 @@
   );
 });
 
+test("new React Bits components match their own documentation pages without overlapping names", () => {
+  for (const [name, path] of [
+    ["Circular Carousel", "components/circular-carousel"],
+    ["Flex Carousel", "components/flex-carousel"],
+    ["Micro Slats", "backgrounds/micro-slats"],
+    ["Pattern Waves", "backgrounds/pattern-waves"],
+  ]) {
+    const match = search(`react ${name}`).find(({ library }) => library.slug === "react-bits");
+    assert.ok(match, `expected React Bits to match ${name}`);
+    assert.deepEqual(match.components.map(({ name, url }) => ({ name, url })), [
+      { name, url: `https://www.reactbits.dev/${path}` },
+    ]);
+  }
+});
+
+test("component words cannot be combined across unrelated React Bits entries", () => {
+  const query = "circular counter";
+  assert.equal(search(query).some(({ library }) => library.slug === "react-bits"), false);
+  assert.deepEqual(facetCounts({ query }).total, { category: 0, stack: 0, useCase: 0 });
+  const counter = search("react counter").find(({ library }) => library.slug === "react-bits");
+  assert.deepEqual(counter.components.map(({ name }) => name), ["Counter", "Count Up"]);
+});
+
 test("reports no component match when a library has no verified component for the query", () => {
   // "accessibility" is not a component name anywhere, so no library may claim
   // a component for it even though several match on their own metadata.
```

## lib/directory.ts

Status: modified; +24; −24

```diff
@@ -75,10 +75,6 @@
       ...library.stacks,
       ...library.useCases,
       ...(library.tags ?? []),
-      ...(components[library.slug] ?? []).flatMap((component) => [
-        component.name,
-        ...(component.aliases ?? []),
-      ]),
     ]
       .join(" ")
       .toLowerCase(),
@@ -107,10 +103,8 @@
 /**
  * Ranks one matched component against the query, best first.
  *
- * Substring matching means "stroke text" also touches every other "… Text"
- * component, so without this the card would fill with weak matches and push the
- * component the user actually asked for off the end. Exported because the MCP
+ * Exact names lead before prefixes, aliases and loose matches. Exported because
- * tool layer orders component search results by the same rule.
+ * the MCP tool layer orders component search results by the same rule.
  */
 export function componentRank(
   component: LibraryComponent,
@@ -151,35 +145,41 @@
     const tokens = normalizedQuery.split(/\s+/).filter(Boolean);
 
     const results = index
+      .filter(({ library }) => {
-      .filter(({ library, haystack }) => {
         return (
-          tokens.every((token) => haystack.includes(token)) &&
           (category === null || library.category === category) &&
           stacks.every((stack) => library.stacks.includes(stack)) &&
           (useCases.length === 0 || useCases.some((useCase) => library.useCases.includes(useCase)))
         );
       })
-      .map((entry) => ({
-        library: entry.library,
+      .flatMap((entry) => {
+        const componentTokens = tokens.filter((token) => !entry.haystack.includes(token));
         // With no tokens every component would vacuously match, so browsing the
         // directory must not present a seeded library's components as hits.
-        // Otherwise any single token can identify a component: the filter above
-        // already guaranteed every token matches somewhere on this library, so
-        // "any" is what keeps a mixed query like "radix accordion" pointing at
-        // the Accordion component instead of dropping the attribution.
+        // Library metadata can supply context such as "radix" in "radix accordion",
+        // but the remaining tokens must match the same component.
-        components:
+        const components =
           tokens.length === 0
             ? []
             : entry.components
-                .filter(({ haystack }) => tokens.some((token) => haystack.includes(token)))
-                .map(({ component }) => component)
+                .filter(({ haystack }) =>
+                  tokens.some((token) => haystack.includes(token)) &&
+                  componentTokens.every((token) => haystack.includes(token)),
+                )
+                .map(({ component, haystack }) => {
+                  const matchingTokens = tokens.filter((token) => haystack.includes(token));
+                  return {
+                    component,
+                    rank: componentRank(component, matchingTokens, matchingTokens.join(" ")),
+                  };
+                })
                 // Stable sort, so components of equal strength keep the order
                 // the registry lists them in.
-                .sort((a, b) =>
-                  componentRank(a, tokens, normalizedQuery) -
-                  componentRank(b, tokens, normalizedQuery),
-                ),
+                .sort((a, b) => a.rank - b.rank)
+                .map(({ component }) => component);
-      }));
+        if (componentTokens.length > 0 && components.length === 0) return [];
+        return [{ library: entry.library, components }];
+      });
 
     // Array.prototype.sort is stable, so returning 0 keeps curated order.
     return results.sort((a, b) => {
```

## lib/mcp-tools.test.mjs

Status: modified; +14; −0

```diff
@@ -50,6 +50,20 @@
   for (const hit of result.results) assert.ok(hit.stacks.includes("Svelte"));
 });
 
+test("React Bits component names resolve consistently through library and component tools", () => {
+  for (const { name, url } of componentIndex["react-bits"]) {
+    const libraries = call("search_libraries", { query: `react ${name}` });
+    const library = libraries.results.find(({ slug }) => slug === "react-bits");
+    assert.ok(library, `expected React Bits for ${name}`);
+    assert.equal(library.matchedComponents[0].name, name);
+    const components = call("search_components", { query: name, library: "react-bits" });
+    assert.equal(components.results[0].id, `react-bits/${name}`);
+    assert.equal(components.results[0].url, url);
+  }
+  const overlap = call("search_libraries", { query: "circular counter" });
+  assert.equal(overlap.results.some(({ slug }) => slug === "react-bits"), false);
+});
+
 test("natural-language filler words do not block a keyword search", () => {
   const prose = call("search_libraries", { query: "a library for react with tailwind" });
   const keywords = call("search_libraries", { query: "react tailwind" });
```
