From 61f38498c91082ac1c5cdf3d97e1e18841ff8515 Mon Sep 17 00:00:00 2001
From: "caco.eth" <49823133+FrancoAguzzi@users.noreply.github.com>
Date: Wed, 15 Jan 2025 19:48:48 -0300
Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Improve=20namegraph.dev=20(#549)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat: [SC-26100] :bug: Improve Ui and add Exact Match button

* feat: [SC-26100] ✨ Wrap new exact match feature in collections search | Add changeset for fetchCollectionsByMember inclusion in namegraph-sdk

* feat: [SC-26100] :bug: Update lock file to fix builds

* feat: [SC-26100] ✨ Integrate Scramble method picker in collection details page

---------

Co-authored-by: lightwalker.eth <126201998+lightwalker-eth@users.noreply.github.com>
---
 .changeset/many-ways-smell.md                 |   5 +
 .../[id]/explore-collection-page.tsx          | 407 ++++++++-------
 apps/namegraph.dev/app/collections/page.tsx   | 492 ++++++++++--------
 .../collections/collection-card.tsx           |  51 ++
 .../collections/collections-grid-skeleton.tsx |  18 +-
 apps/namegraph.dev/components/ui/toggle.tsx   |  45 ++
 apps/namegraph.dev/lib/utils.ts               |  18 +
 apps/namegraph.dev/package.json               |   1 +
 packages/namegraph-sdk/src/index.ts           |  25 +-
 packages/namegraph-sdk/src/utils.ts           |  11 +-
 pnpm-lock.yaml                                |  27 +
 11 files changed, 665 insertions(+), 435 deletions(-)
 create mode 100644 .changeset/many-ways-smell.md
 create mode 100644 apps/namegraph.dev/components/collections/collection-card.tsx
 create mode 100644 apps/namegraph.dev/components/ui/toggle.tsx

diff --git a/.changeset/many-ways-smell.md b/.changeset/many-ways-smell.md
new file mode 100644
index 000000000..3d266c1ce
--- /dev/null
+++ b/.changeset/many-ways-smell.md
@@ -0,0 +1,5 @@
+---
+"@namehash/namegraph-sdk": minor
+---
+
+Add fetchCollectionsByMember method in NameGraph SDK package
diff --git a/apps/namegraph.dev/app/collections/[id]/explore-collection-page.tsx b/apps/namegraph.dev/app/collections/[id]/explore-collection-page.tsx
index ae29f7ca3..1f33ffe8e 100644
--- a/apps/namegraph.dev/app/collections/[id]/explore-collection-page.tsx
+++ b/apps/namegraph.dev/app/collections/[id]/explore-collection-page.tsx
@@ -1,4 +1,5 @@
 "use client";
+/* eslint-disable react-hooks/exhaustive-deps */
 
 import {
   fetchCollectionMembers,
@@ -11,6 +12,7 @@ import {
   NameGraphCollection,
   NameGraphFindCollectionsResponse,
   NameGraphSuggestion,
+  ScrambleMethod,
 } from "@namehash/namegraph-sdk/utils";
 import { useEffect, useState } from "react";
 import { Noto_Emoji } from "next/font/google";
@@ -18,6 +20,15 @@ import { useQueryParams } from "@/components/use-query-params";
 import { Button } from "@/components/ui/button";
 import { ChevronLeft, ChevronRight, Loader } from "lucide-react";
 import Skeleton from "@/components/skeleton";
+import { CollectionCard } from "@/components/collections/collection-card";
+import { Tooltip } from "@namehash/namekit-react/client";
+import {
+  Select,
+  SelectContent,
+  SelectItem,
+  SelectTrigger,
+  SelectValue,
+} from "@/components/ui/select";
 
 const notoBlack = Noto_Emoji({ preload: false });
 
@@ -30,6 +41,16 @@ interface SuggestionsData {
   suggestions: NameGraphSuggestion[];
 }
 
+const FromNameGraphScrambleMethodToDropdownTextContent: Record<
+  ScrambleMethod,
+  string
+> = {
+  [ScrambleMethod["full-shuffle"]]: "Full Shuffle",
+  [ScrambleMethod["left-right-shuffle"]]: "Left - Right Shuffle",
+  [ScrambleMethod["left-right-shuffle-with-unigrams"]]:
+    "Left - Right Shuffle with Unigrams",
+};
+
 export const ExploreCollectionPage = ({ id }: { id: string }) => {
   const [loadingCollection, setLoadingCollection] = useState(true);
   const [loadingCollectionMembers, setLoadingCollectionMembers] =
@@ -84,9 +105,6 @@ export const ExploreCollectionPage = ({ id }: { id: string }) => {
     loadCollectionMembers();
     loadCollectionRelatedCollections();
   }, [id]);
-  useEffect(() => {
-    console.log(collectionMembers);
-  }, [collectionMembers]);
 
   const loadCollectionMembers = () => {
     if (!!collectionMembers?.[params.page]) {
@@ -95,7 +113,7 @@ export const ExploreCollectionPage = ({ id }: { id: string }) => {
 
     setLoadingCollectionMembers(true);
     fetchCollectionMembers(id, {
-      offset: params.page - 1,
+      offset: (params.page - 1) * navigationConfig.itemsPerPage,
     })
       .then((res) => {
         if (res) {
@@ -155,12 +173,19 @@ export const ExploreCollectionPage = ({ id }: { id: string }) => {
     }
   };
 
+  const [scrambleMethod, setScrambleMethod] = useState<ScrambleMethod>(
+    ScrambleMethod["left-right-shuffle-with-unigrams"],
+  );
+
   const scramble = () => {
     if (id) {
       const randomSeed = Number(Number(Math.random() * 10).toFixed(0));
 
       setLoadingScramble(true);
-      scrambleNamesByCollectionId(id, { seed: randomSeed })
+      scrambleNamesByCollectionId(id, {
+        seed: randomSeed,
+        method: scrambleMethod,
+      })
         .then((res) => {
           setScrambledNameIdeas(res);
         })
@@ -221,9 +246,8 @@ export const ExploreCollectionPage = ({ id }: { id: string }) => {
   const isLastCollectionsPageForCurrentQuery = () => {
     if (navigationConfig.totalItems) {
       return (
-        Number(params.page) !== 1 &&
-        Number(params.page) * navigationConfig.itemsPerPage >
-          navigationConfig.totalItems
+        Number(params.page) * navigationConfig.itemsPerPage >=
+        navigationConfig.totalItems
       );
     } else return false;
   };
@@ -280,7 +304,7 @@ export const ExploreCollectionPage = ({ id }: { id: string }) => {
             {/* Collections List */}
             <div className="w-full space-y-4">
               {loadingCollection ? (
-                <div className="flex flex-col space-y-7 my-8">
+                <div className="w-full h-full flex flex-col justify-center items-center my-8 animate-spin">
                   <Loader />
                 </div>
               ) : collection ? (
@@ -289,17 +313,74 @@ export const ExploreCollectionPage = ({ id }: { id: string }) => {
                     <div className="w-full">
                       <div className="w-full mb-8">
                         <div className="w-full flex flex-col space-y-4 p-3 rounded-xl border border-gray-200">
-                          <div className="w-full">
-                            {/* Collection Count and Sort */}
-                            <div className="max-w-[756px] w-full flex flex-col md:flex-row md:items-center md:justify-between space-y-4 md:space-y-0 mb-5">
-                              <div className="flex items-center">
-                                <div className="text-lg font-semibold mr-2.5">
-                                  {getNavigationPageTextGuide()}
+                          <div className="w-full h-[517px] flex flex-col justify-start">
+                            <div className="h-full">
+                              {/* Collection Count and Sort */}
+                              <div className="max-w-[756px] w-full flex flex-col md:flex-row md:items-center md:justify-between space-y-4 md:space-y-0 mb-5">
+                                <div className="flex items-center">
+                                  <div className="text-lg font-semibold mr-2.5">
+                                    {getNavigationPageTextGuide()}
+                                  </div>
+                                  {navigationConfig.totalItems ? (
+                                    <div className="flex">
+                                      <Button
+                                        className="cursor-pointer p-[9px] bg-white shadow-none hover:bg-gray-50 rounded-lg disabled:opacity-50 disabled:hover:bg-white"
+                                        disabled={isFirstCollectionsPageForCurrentQuery()}
+                                        onClick={() =>
+                                          handlePageChange(
+                                            Number(params.page) - 1,
+                                          )
+                                        }
+                                      >
+                                        <ChevronLeft className="w-6 h-6 text-black" />
+                                      </Button>
+                                      <Button
+                                        className="cursor-pointer p-[9px] bg-white shadow-none hover:bg-gray-50 rounded-lg disabled:opacity-50"
+                                        disabled={isLastCollectionsPageForCurrentQuery()}
+                                        onClick={() =>
+                                          handlePageChange(
+                                            Number(params.page) + 1,
+                                          )
+                                        }
+                                      >
+                                        <ChevronRight className="w-6 h-6 text-black" />
+                                      </Button>
+                                    </div>
+                                  ) : null}
                                 </div>
-                                {navigationConfig.totalItems ? (
-                                  <div className="flex">
+                              </div>
+                              {/* Collections List */}
+                              <div className="w-full h-full max-w-[756px] space-y-4">
+                                {loadingCollectionMembers ? (
+                                  <div className="flex flex-col w-full mt-auto justify-center items-center animate-spin h-[370px]">
+                                    <Loader />
+                                  </div>
+                                ) : collectionMembers ? (
+                                  collectionMembers[
+                                    params.page
+                                  ]?.suggestions.map((suggestion) => (
+                                    <div
+                                      key={suggestion.name}
+                                      className="bg-gray-100 rounded-full groupp-2 px-4 flex items-start"
+                                    >
+                                      <div className="max-h-[20px] relative flex items-center justify-center overflow-hidden">
+                                        {suggestion.name}
+                                      </div>
+                                    </div>
+                                  ))
+                                ) : null}
+                              </div>
+                            </div>
+                            <div className="mt-auto">
+                              {/* Pagination */}
+                              {navigationConfig.totalItems ? (
+                                <div className="flex items-center justify-between border border-gray-200 border-l-0 border-r-0 border-b-0 mt-3 p-3">
+                                  <div className="text-sm text-gray-500 mr-2.5">
+                                    {getNavigationPageTextGuide()}
+                                  </div>
+                                  <div className="flex items-center gap-2">
                                     <Button
-                                      className="cursor-pointer p-[9px] bg-white shadow-none hover:bg-gray-50 rounded-lg disabled:opacity-50 disabled:hover:bg-white"
+                                      className="bg-white text-black shadow-none hover:bg-gray-50 text-sm p-2.5"
                                       disabled={isFirstCollectionsPageForCurrentQuery()}
                                       onClick={() =>
                                         handlePageChange(
@@ -307,10 +388,11 @@ export const ExploreCollectionPage = ({ id }: { id: string }) => {
                                         )
                                       }
                                     >
-                                      <ChevronLeft className="w-6 h-6 text-black" />
+                                      <ChevronLeft />
+                                      Prev
                                     </Button>
                                     <Button
-                                      className="cursor-pointer p-[9px] bg-white shadow-none hover:bg-gray-50 rounded-lg disabled:opacity-50"
+                                      className="bg-white text-black shadow-none hover:bg-gray-50 text-sm p-2.5"
                                       disabled={isLastCollectionsPageForCurrentQuery()}
                                       onClick={() =>
                                         handlePageChange(
@@ -318,90 +400,40 @@ export const ExploreCollectionPage = ({ id }: { id: string }) => {
                                         )
                                       }
                                     >
-                                      <ChevronRight className="w-6 h-6 text-black" />
+                                      Next
+                                      <ChevronRight />
                                     </Button>
                                   </div>
-                                ) : null}
-                              </div>
-                            </div>
-                            {/* Collections List */}
-                            <div className="w-full max-w-[756px] space-y-4">
-                              {loadingCollectionMembers ? (
-                                <div className="flex flex-col space-y-7 my-8">
-                                  <Loader />
                                 </div>
-                              ) : collectionMembers ? (
-                                collectionMembers[params.page]?.suggestions.map(
-                                  (suggestion) => (
-                                    <div
-                                      key={suggestion.name}
-                                      className="bg-gray-100 rounded-full groupp-2 px-4 flex items-start"
-                                    >
-                                      <div className="relative flex items-center justify-center overflow-hidden">
-                                        {suggestion.name}
-                                      </div>
-                                    </div>
-                                  ),
-                                )
                               ) : null}
                             </div>
-                            {/* Pagination */}
-                            {navigationConfig.totalItems ? (
-                              <div className="flex items-center justify-between border border-gray-200 border-l-0 border-r-0 border-b-0 mt-3 pt-3">
-                                <div className="text-sm text-gray-500 mr-2.5">
-                                  {getNavigationPageTextGuide()}
-                                </div>
-                                <div className="flex items-center gap-2">
-                                  <Button
-                                    className="bg-white text-black shadow-none hover:bg-gray-50 text-sm p-2.5"
-                                    disabled={isFirstCollectionsPageForCurrentQuery()}
-                                    onClick={() =>
-                                      handlePageChange(Number(params.page) - 1)
-                                    }
-                                  >
-                                    <ChevronLeft />
-                                    Prev
-                                  </Button>
-                                  <Button
-                                    className="bg-white text-black shadow-none hover:bg-gray-50 text-sm p-2.5"
-                                    disabled={isLastCollectionsPageForCurrentQuery()}
-                                    onClick={() =>
-                                      handlePageChange(Number(params.page) + 1)
-                                    }
-                                  >
-                                    Next
-                                    <ChevronRight />
-                                  </Button>
-                                </div>
-                              </div>
-                            ) : null}
                           </div>
                         </div>
                       </div>
-                      <div className="border border-gray-200 rounded-lg p-3">
-                        <div className="flex flex-col space-y-4">
-                          <p className="font-semibold text-lg">
-                            Interact with this collection
-                          </p>
-                          <div className="flex space-x-4">
-                            <Button onClick={scramble}>
-                              Scramble name ideas
-                            </Button>
+                      <div className="w-full border border-gray-200 rounded-lg">
+                        <div className="w-full flex border border-l-0 border-t-0 border-r-0 px-3 py-3 justify-between items-center">
+                          <Tooltip
+                            trigger={
+                              <p className="font-semibold text-lg w-full">
+                                Sample names
+                              </p>
+                            }
+                          >
+                            <p>Sampling names means creating </p>
+                          </Tooltip>
 
-                            <Button onClick={ideate}>Sample name ideas</Button>
-                          </div>
+                          <Button onClick={ideate}>Sample name ideas</Button>
                         </div>
                         <div className="flex flex-col space-y-8 mt-4">
-                          {scrambledNameIdeas ? (
-                            <div className="flex flex-col border border-gray-200 rounded-xl p-3">
-                              <div className="text-lg underline font-semibold mb-4 pl-2">
-                                Scrambled name ideas
-                              </div>
-                              {loadingScramble ? (
-                                <Loader />
+                          {sampledNameIdeas ? (
+                            <div className="flex flex-col rounded-xl py-3 h-[200px]">
+                              {loadingIdeate ? (
+                                <div className="flex flex-col w-full h-full justify-center items-center animate-spin">
+                                  <Loader />
+                                </div>
                               ) : (
-                                <div className="flex flex-wrap gap-3">
-                                  {scrambledNameIdeas?.map((suggestion) => {
+                                <div className="flex flex-wrap gap-3 pl-3">
+                                  {sampledNameIdeas?.map((suggestion) => {
                                     return (
                                       <div
                                         key={suggestion.name}
@@ -417,17 +449,59 @@ export const ExploreCollectionPage = ({ id }: { id: string }) => {
                               )}
                             </div>
                           ) : null}
+                        </div>
+                      </div>
 
-                          {sampledNameIdeas ? (
-                            <div className="flex flex-col border border-gray-200 rounded-xl p-3">
-                              <div className="text-lg underline font-semibold mb-4 pl-2">
-                                Sampled name ideas
-                              </div>
-                              {loadingIdeate ? (
-                                <Loader />
+                      <div className="w-full border border-gray-200 rounded-lg mt-8">
+                        <div className="w-full flex border border-l-0 border-t-0 border-r-0 px-3 py-3 justify-between items-center">
+                          <Tooltip
+                            trigger={
+                              <p className="font-semibold text-lg w-full">
+                                Scramble names
+                              </p>
+                            }
+                          >
+                            <p>Scrambling names means creating </p>
+                          </Tooltip>
+                          <Select
+                            defaultValue={scrambleMethod}
+                            onValueChange={(newValue) =>
+                              setScrambleMethod(newValue as ScrambleMethod)
+                            }
+                          >
+                            <SelectTrigger className="w-[180px]">
+                              <SelectValue placeholder="Sort by" />
+                            </SelectTrigger>
+                            <SelectContent>
+                              {Object.entries(
+                                FromNameGraphScrambleMethodToDropdownTextContent,
+                              ).map(([key]) => {
+                                return (
+                                  <SelectItem key={key} value={key}>
+                                    {
+                                      FromNameGraphScrambleMethodToDropdownTextContent[
+                                        key as ScrambleMethod
+                                      ]
+                                    }
+                                  </SelectItem>
+                                );
+                              })}
+                            </SelectContent>
+                          </Select>
+                          <Button onClick={scramble}>
+                            Scramble name ideas
+                          </Button>
+                        </div>
+                        <div className="flex flex-col space-y-8 mt-4">
+                          {scrambledNameIdeas ? (
+                            <div className="flex flex-col rounded-xl py-3 h-[200px]">
+                              {loadingScramble ? (
+                                <div className="flex flex-col w-full h-full justify-center items-center animate-spin">
+                                  <Loader />
+                                </div>
                               ) : (
-                                <div className="flex flex-wrap gap-3">
-                                  {sampledNameIdeas?.map((suggestion) => {
+                                <div className="flex flex-wrap gap-3 pl-3">
+                                  {scrambledNameIdeas?.map((suggestion) => {
                                     return (
                                       <div
                                         key={suggestion.name}
@@ -452,111 +526,42 @@ export const ExploreCollectionPage = ({ id }: { id: string }) => {
                         <h2 className="flex items-center text-lg font-semibold h-[47px] px-5 border border-t-0 border-r-0 border-l-0 border-gray-200">
                           Related collections
                         </h2>
-                        {relatedCollections ? (
-                          relatedCollections.related_collections.map(
-                            (collection) => (
-                              <div
-                                key={collection.collection_id}
-                                className="!no-underline group rounded-lg cursor-pointer px-5 py-3 flex items-start gap-[18px]"
-                              >
-                                <div
-                                  style={{
-                                    border: "1px solid rgba(0, 0, 0, 0.05)",
-                                  }}
-                                  className="group-hover:bg-gray-300 group-hover:transition flex justify-center items-center rounded-md bg-background h-[72px] w-[72px] bg-gray-100"
-                                >
-                                  <div className="relative flex items-center justify-center overflow-hidden">
-                                    <p
-                                      className={`text-3xl ${notoBlack.className}`}
-                                    >
-                                      {collection.avatar_emoji}
-                                    </p>
-                                  </div>
-                                </div>
-                                <div className="flex-1 overflow-hidden">
-                                  <h3 className="text-sm font-semibold">
-                                    {collection.title}
-                                  </h3>
-                                  <p className="text-xs text-gray-500 mb-2 truncate">
-                                    by {collection.owner}
-                                  </p>
-                                  <div className="relative">
-                                    <div className="flex gap-2">
-                                      {collection.top_names.map((tag) => (
-                                        <span
-                                          key={tag.namehash}
-                                          className="bg-gray-100 text-sm px-2 py-1 bg-muted rounded-full"
-                                        >
-                                          {tag.name}
-                                        </span>
-                                      ))}
-                                    </div>
-                                    <div className="bg-gradient-white-to-transparent absolute right-0 top-0 w-40 h-full"></div>
-                                  </div>
-                                </div>
-                              </div>
-                            ),
-                          )
-                        ) : (
-                          <div className="p-3 px-5">
-                            No name suggestions found
-                          </div>
-                        )}
+
+                        <div className="px-5">
+                          {relatedCollections ? (
+                            relatedCollections.related_collections.map(
+                              (collection) => (
+                                <CollectionCard
+                                  key={collection.collection_id}
+                                  collection={collection}
+                                />
+                              ),
+                            )
+                          ) : (
+                            <div className="p-3 px-5">
+                              No name suggestions found
+                            </div>
+                          )}
+                        </div>
                       </div>
                       <div className="z-40 border rounded-md border-gray-200 w-full h-fit">
                         <h2 className="flex items-center text-lg font-semibold h-[47px] px-5 border border-t-0 border-r-0 border-l-0 border-gray-200">
                           Other collections
                         </h2>
-                        {relatedCollections ? (
-                          relatedCollections.other_collections.map(
-                            (collection) => (
-                              <div
-                                key={collection.collection_id}
-                                className="!no-underline group rounded-lg cursor-pointer px-5 py-3 flex items-start gap-[18px]"
-                              >
-                                <div
-                                  style={{
-                                    border: "1px solid rgba(0, 0, 0, 0.05)",
-                                  }}
-                                  className="group-hover:bg-gray-300 group-hover:transition flex justify-center items-center rounded-md bg-background h-[72px] w-[72px] bg-gray-100"
-                                >
-                                  <div className="relative flex items-center justify-center overflow-hidden">
-                                    <p
-                                      className={`text-3xl ${notoBlack.className}`}
-                                    >
-                                      {collection.avatar_emoji}
-                                    </p>
-                                  </div>
-                                </div>
-                                <div className="flex-1 overflow-hidden">
-                                  <h3 className="text-sm font-semibold">
-                                    {collection.title}
-                                  </h3>
-                                  <p className="text-xs text-gray-500 mb-2 truncate">
-                                    by {collection.owner}
-                                  </p>
-                                  <div className="relative">
-                                    <div className="flex gap-2">
-                                      {collection.top_names.map((tag) => (
-                                        <span
-                                          key={tag.namehash}
-                                          className="bg-gray-100 text-sm px-2 py-1 bg-muted rounded-full"
-                                        >
-                                          {tag.name}
-                                        </span>
-                                      ))}
-                                    </div>
-                                    <div className="bg-gradient-white-to-transparent absolute right-0 top-0 w-40 h-full"></div>
-                                  </div>
-                                </div>
-                              </div>
-                            ),
-                          )
-                        ) : (
-                          <div className="p-3 px-5">
-                            No name suggestions found
-                          </div>
-                        )}
+                        <div className="px-5">
+                          {relatedCollections ? (
+                            relatedCollections.other_collections.map(
+                              (collection) => (
+                                <CollectionCard
+                                  key={collection.collection_id}
+                                  collection={collection}
+                                />
+                              ),
+                            )
+                          ) : (
+                            <div className="p-3">No name suggestions found</div>
+                          )}
+                        </div>
                       </div>
                     </div>
                   </div>
diff --git a/apps/namegraph.dev/app/collections/page.tsx b/apps/namegraph.dev/app/collections/page.tsx
index 5d3c9e0ec..5b2802fbe 100644
--- a/apps/namegraph.dev/app/collections/page.tsx
+++ b/apps/namegraph.dev/app/collections/page.tsx
@@ -5,9 +5,10 @@ import {
   NameGraphCollection,
   NameGraphSortOrderOptions,
 } from "@namehash/namegraph-sdk/utils";
-import { findCollectionsByString } from "@/lib/utils";
+import { findCollectionsByMember, findCollectionsByString } from "@/lib/utils";
 import { DebounceInput } from "react-debounce-input";
 import { Suspense, useEffect, useState } from "react";
+import { Toggle } from "@/components/ui/toggle";
 import { Button } from "@/components/ui/button";
 import {
   Select,
@@ -22,10 +23,8 @@ import {
   CollectionsGridSkeleton,
 } from "@/components/collections/collections-grid-skeleton";
 import { useQueryParams } from "@/components/use-query-params";
-import { Noto_Emoji } from "next/font/google";
-import { Link } from "@namehash/namekit-react";
+import { CollectionCard } from "@/components/collections/collection-card";
 
-const notoBlack = Noto_Emoji({ preload: false });
 interface NavigationConfig {
   itemsPerPage: number;
   totalItems?: number;
@@ -33,7 +32,7 @@ interface NavigationConfig {
 
 interface CollectionsData {
   sort_order: NameGraphSortOrderOptions;
-  other_collections: NameGraphCollection[];
+  other_collections: NameGraphCollection[] | null;
   related_collections: NameGraphCollection[];
 }
 
@@ -56,6 +55,7 @@ export default function ExploreCollectionsPage() {
     search: "",
     page: DEFAULT_PAGE_NUMBER,
     orderBy: NameGraphSortOrderOptions.AI,
+    exactMatch: false,
   };
   type DefaultDomainFiltersType = typeof DEFAULT_COLLECTIONS_PARAMS;
   const { params, setParams } = useQueryParams<DefaultDomainFiltersType>(
@@ -67,14 +67,45 @@ export default function ExploreCollectionsPage() {
       search: searchTerm,
       page: DEFAULT_PAGE_NUMBER, // Resets page when search changes
     });
+
+    setNavigationConfig({
+      ...navigationConfig,
+      totalItems: undefined,
+    });
+
+    if (!params.search) {
+      return;
+    }
+
+    setCollections(null);
+    setLoadingCollections(true);
+
+    queryCollections({
+      search: params.search || "",
+      orderBy: params.orderBy || NameGraphSortOrderOptions.AI,
+      page: params.page || 1,
+      exactMatch: params.exactMatch,
+    });
   };
 
   const handleOrderBy = (orderBy: NameGraphSortOrderOptions) => {
     setParams({ orderBy });
+    queryCollections({
+      search: params.search || "",
+      orderBy: orderBy || NameGraphSortOrderOptions.AI,
+      page: params.page || 1,
+      exactMatch: params.exactMatch,
+    });
   };
 
   const handlePageChange = (page: number) => {
     setParams({ page });
+    queryCollections({
+      search: params.search || "",
+      orderBy: params.orderBy || NameGraphSortOrderOptions.AI,
+      page: page || 1,
+      exactMatch: params.exactMatch,
+    });
   };
 
   /**
@@ -87,8 +118,15 @@ export default function ExploreCollectionsPage() {
   const [collections, setCollections] = useState<
     undefined | null | Record<number, CollectionsData | null | undefined>
   >(undefined);
-  const [collectionsQueriedStandFor, setCollectionsQueriedStandFor] =
-    useState("");
+  const [lastQueryDone, setLastQueryDone] = useState<{
+    search: string;
+    exactMatch: boolean;
+  }>({ search: params.search || "", exactMatch: params.exactMatch || false });
+
+  useEffect(() => {
+    console.log("New values for collections results:");
+    console.log(collections, params);
+  }, [collections]);
 
   const [loadingCollections, setLoadingCollections] = useState(true);
 
@@ -98,13 +136,21 @@ export default function ExploreCollectionsPage() {
     totalItems: undefined,
   });
 
-  const loadCollections = () => {
+  interface QueryCollectionsParam {
+    exactMatch: boolean;
+    orderBy: NameGraphSortOrderOptions;
+    search: string;
+    page: number;
+  }
+
+  const queryCollections = (params: QueryCollectionsParam) => {
     if (params.search) {
       let query = params.search;
       if (params.search.includes(".")) {
         query = params.search.split(".")[0];
       }
 
+      const MAX_COLLECTIONS_FOR_EXACT_MATCH = 10;
       const MAX_RELATED_COLLECTIONS = 20;
       const OTHER_COLLECTIONS_NUMBER = 5;
 
@@ -117,103 +163,152 @@ export default function ExploreCollectionsPage() {
        *
        * Of course this is only true if both the query and the sorting
        * algorithm lastly used are the same. If any of these have changes,
-       * we do the loadCollections query once again and update the page's results.
+       * we do the queryCollections query once again and update the page's results.
        */
       if (
-        !!collectionsQueriedStandFor &&
-        collectionsQueriedStandFor === params.search &&
+        !!lastQueryDone &&
+        lastQueryDone.search === params.search &&
         !!collections?.[params.page] &&
-        params.orderBy == collections?.[params.page]?.sort_order
+        params.orderBy == collections?.[params.page]?.sort_order &&
+        params.exactMatch === lastQueryDone.exactMatch
       ) {
         return;
       }
 
       setLoadingCollections(true);
-      setCollectionsQueriedStandFor(query);
-      findCollectionsByString(query, {
-        offset: params.page - 1,
-        sort_order: params.orderBy,
-        max_total_collections:
-          MAX_RELATED_COLLECTIONS + OTHER_COLLECTIONS_NUMBER,
-        /**
-         * Please note how the number of collections one page show is
-         * strategically aligned with ITEMS_PER_PAGE_OPTIONS.
-         */
-        max_related_collections: MAX_RELATED_COLLECTIONS,
-        max_other_collections: OTHER_COLLECTIONS_NUMBER,
-        min_other_collections: OTHER_COLLECTIONS_NUMBER,
-      })
-        .then((res) => {
-          if (res) {
-            const MAX_COLLECTIONS_NUMBER_NAME_API_CAN_DOCUMENT = 1000;
-
-            setNavigationConfig({
-              ...navigationConfig,
-              totalItems:
-                typeof res.metadata.total_number_of_matched_collections ===
-                "number"
-                  ? res.metadata.total_number_of_matched_collections
-                  : /**
-                     * NameAPI makes usage of a stringified "+1000" for
-                     * res.metadata.total_number_of_matched_collections
-                     * if there are more than 1000 collections this query
-                     * is contained in. Since this is a different data type
-                     * and we are handling number operations with totalItems,
-                     * we are here normalizing this use case to use the number 1000
-                     */
-                    MAX_COLLECTIONS_NUMBER_NAME_API_CAN_DOCUMENT,
-            });
 
-            const moreCollections = res.other_collections;
-            const relatedCollections = res.related_collections;
+      if (params.exactMatch) {
+        findCollectionsByMember(query, {
+          offset: (params.page - 1) * navigationConfig.itemsPerPage,
+          sort_order: params.orderBy,
+          limit_names: MAX_COLLECTIONS_FOR_EXACT_MATCH,
+          /**
+           * Please note how the number of collections one page show is
+           * strategically aligned with ITEMS_PER_PAGE_OPTIONS.
+           */
+          max_results: MAX_COLLECTIONS_FOR_EXACT_MATCH,
+        })
+          .then((res) => {
+            if (res) {
+              const MAX_COLLECTIONS_NUMBER_NAME_API_CAN_DOCUMENT = 1000;
+
+              setNavigationConfig({
+                ...navigationConfig,
+                totalItems:
+                  typeof res.metadata.total_number_of_matched_collections ===
+                  "number"
+                    ? res.metadata.total_number_of_matched_collections
+                    : /**
+                       * NameAPI makes usage of a stringified "+1000" for
+                       * res.metadata.total_number_of_matched_collections
+                       * if there are more than 1000 collections this query
+                       * is contained in. Since this is a different data type
+                       * and we are handling number operations with totalItems,
+                       * we are here normalizing this use case to use the number 1000
+                       */
+                      MAX_COLLECTIONS_NUMBER_NAME_API_CAN_DOCUMENT,
+              });
+
+              const relatedCollections = res.collections;
 
+              setCollections({
+                ...collections,
+                [params.page]: {
+                  sort_order: params.orderBy,
+                  related_collections: relatedCollections,
+                  other_collections: null,
+                },
+              });
+            } else {
+              setCollections({
+                ...collections,
+                [params.page]: null,
+              });
+            }
+          })
+          .catch(() => {
             setCollections({
               ...collections,
-              [params.page]: {
-                sort_order: params.orderBy,
-                related_collections: relatedCollections,
-                other_collections: moreCollections,
-              },
+              [params.page]: null,
             });
+          })
+          .finally(() => {
             setLoadingCollections(false);
-          } else {
+          });
+      } else {
+        findCollectionsByString(query, {
+          offset: (params.page - 1) * navigationConfig.itemsPerPage,
+          sort_order: params.orderBy,
+          max_total_collections:
+            MAX_RELATED_COLLECTIONS + OTHER_COLLECTIONS_NUMBER,
+          /**
+           * Please note how the number of collections one page show is
+           * strategically aligned with ITEMS_PER_PAGE_OPTIONS.
+           */
+          max_related_collections: MAX_RELATED_COLLECTIONS,
+          max_other_collections: OTHER_COLLECTIONS_NUMBER,
+          min_other_collections: OTHER_COLLECTIONS_NUMBER,
+        })
+          .then((res) => {
+            if (res) {
+              const MAX_COLLECTIONS_NUMBER_NAME_API_CAN_DOCUMENT = 1000;
+
+              setNavigationConfig({
+                ...navigationConfig,
+                totalItems:
+                  typeof res.metadata.total_number_of_matched_collections ===
+                  "number"
+                    ? res.metadata.total_number_of_matched_collections
+                    : /**
+                       * NameAPI makes usage of a stringified "+1000" for
+                       * res.metadata.total_number_of_matched_collections
+                       * if there are more than 1000 collections this query
+                       * is contained in. Since this is a different data type
+                       * and we are handling number operations with totalItems,
+                       * we are here normalizing this use case to use the number 1000
+                       */
+                      MAX_COLLECTIONS_NUMBER_NAME_API_CAN_DOCUMENT,
+              });
+
+              const moreCollections = res.other_collections;
+              const relatedCollections = res.related_collections;
+
+              setCollections({
+                ...collections,
+                [params.page]: {
+                  sort_order: params.orderBy,
+                  related_collections: relatedCollections,
+                  other_collections: moreCollections,
+                },
+              });
+            } else {
+              setCollections({
+                ...collections,
+                [params.page]: null,
+              });
+            }
+          })
+          .catch(() => {
             setCollections({
               ...collections,
               [params.page]: null,
             });
-          }
-        })
-        .catch(() => {
-          setCollections({
-            ...collections,
-            [params.page]: null,
+          })
+          .finally(() => {
+            setLoadingCollections(false);
           });
-        });
+      }
+
+      setLastQueryDone({
+        search: params.search,
+        exactMatch: params.exactMatch,
+      });
     } else {
       setCollections(null);
       setLoadingCollections(false);
     }
   };
 
-  useEffect(() => {
-    setNavigationConfig({
-      ...navigationConfig,
-      totalItems: undefined,
-    });
-
-    if (!params.search) {
-      return;
-    }
-
-    setCollections(null);
-    setLoadingCollections(true);
-    loadCollections();
-  }, [params.search]);
-
-  useEffect(() => {
-    loadCollections();
-  }, [params.page, params.orderBy]);
-
   /**
    * Navigation helper functions
    */
@@ -223,9 +318,8 @@ export default function ExploreCollectionsPage() {
   const isLastCollectionsPageForCurrentQuery = () => {
     if (navigationConfig.totalItems) {
       return (
-        Number(params.page) !== 1 &&
-        Number(params.page) * navigationConfig.itemsPerPage >
-          navigationConfig.totalItems
+        Number(params.page) * navigationConfig.itemsPerPage >=
+        navigationConfig.totalItems
       );
     } else return false;
   };
@@ -236,9 +330,15 @@ export default function ExploreCollectionsPage() {
           Number(params.page) * navigationConfig.itemsPerPage,
           navigationConfig.totalItems,
         )} of ${navigationConfig.totalItems} collections`
-      : "No collections found";
+      : !loadingCollections
+        ? "No collections found"
+        : "";
   };
 
+  useEffect(() => {
+    handleSearch(params.search);
+  }, [params.search]);
+
   return (
     <Suspense fallback={<div>Loading...</div>}>
       <div className="mx-auto py-8 w-full">
@@ -284,7 +384,9 @@ export default function ExploreCollectionsPage() {
                     <div className="w-full flex flex-col xl:flex-row">
                       <div className="w-full">
                         {/* Collection Count and Sort */}
-                        <div className="max-w-[756px] w-full flex flex-col md:flex-row md:items-center md:justify-between space-y-4 md:space-y-0 mb-5">
+                        <div
+                          className={`w-full flex flex-col md:flex-row md:items-center md:justify-between space-y-4 md:space-y-0 mb-5 ${!collections[params.page]?.other_collections ? "" : "max-w-[756px]"}`}
+                        >
                           <div className="flex items-center">
                             <div className="text-lg font-semibold mr-2.5">
                               {getNavigationPageTextGuide()}
@@ -312,93 +414,86 @@ export default function ExploreCollectionsPage() {
                               </div>
                             ) : null}
                           </div>
-                          {collections[params.page] ? (
-                            <div className="flex space-x-3 items-center">
-                              <div className="text-sm text-gray-500">
-                                Sort by
+
+                          <div className="flex space-x-4">
+                            {collections[params.page] ? (
+                              <div className="flex text-gray-200 border rounded-lg">
+                                <Toggle
+                                  pressed={params.exactMatch}
+                                  onPressedChange={(pressed) => {
+                                    setNavigationConfig({
+                                      ...navigationConfig,
+                                      totalItems: undefined,
+                                    });
+                                    setParams({ exactMatch: pressed });
+                                    queryCollections({
+                                      search: params.search || "",
+                                      orderBy:
+                                        params.orderBy ||
+                                        NameGraphSortOrderOptions.AI,
+                                      page: params.page || 1,
+                                      exactMatch: pressed,
+                                    });
+                                  }}
+                                >
+                                  Exact Match
+                                </Toggle>
                               </div>
-                              <Select
-                                defaultValue={
-                                  params.orderBy || NameGraphSortOrderOptions.AI
-                                }
-                                onValueChange={(newValue) =>
-                                  handleOrderBy(
-                                    newValue as NameGraphSortOrderOptions,
-                                  )
-                                }
-                              >
-                                <SelectTrigger className="w-[180px]">
-                                  <SelectValue placeholder="Sort by" />
-                                </SelectTrigger>
-                                <SelectContent>
-                                  {Object.entries(
-                                    FromNameGraphSortOrderToDropdownTextContent,
-                                  ).map(([key]) => {
-                                    return (
-                                      <SelectItem key={key} value={key}>
-                                        {
-                                          FromNameGraphSortOrderToDropdownTextContent[
-                                            key as NameGraphSortOrderOptions
-                                          ]
-                                        }
-                                      </SelectItem>
-                                    );
-                                  })}
-                                </SelectContent>
-                              </Select>
-                            </div>
-                          ) : null}
+                            ) : null}
+                            {collections[params.page] ? (
+                              <div className="flex space-x-3 items-center">
+                                <div className="text-sm text-gray-500">
+                                  Sort by
+                                </div>
+                                <Select
+                                  defaultValue={
+                                    params.orderBy ||
+                                    NameGraphSortOrderOptions.AI
+                                  }
+                                  onValueChange={(newValue) =>
+                                    handleOrderBy(
+                                      newValue as NameGraphSortOrderOptions,
+                                    )
+                                  }
+                                >
+                                  <SelectTrigger className="w-[180px]">
+                                    <SelectValue placeholder="Sort by" />
+                                  </SelectTrigger>
+                                  <SelectContent>
+                                    {Object.entries(
+                                      FromNameGraphSortOrderToDropdownTextContent,
+                                    ).map(([key]) => {
+                                      return (
+                                        <SelectItem key={key} value={key}>
+                                          {
+                                            FromNameGraphSortOrderToDropdownTextContent[
+                                              key as NameGraphSortOrderOptions
+                                            ]
+                                          }
+                                        </SelectItem>
+                                      );
+                                    })}
+                                  </SelectContent>
+                                </Select>
+                              </div>
+                            ) : null}
+                          </div>
                         </div>
                         {/* Collections List */}
-                        <div className="w-full max-w-[756px] space-y-4">
+                        <div
+                          className={`w-full ${!collections[params.page]?.other_collections ? "" : "max-w-[756px]"}`}
+                        >
                           {loadingCollections ? (
-                            <div className="flex flex-col space-y-7 my-8">
+                            <div className="flex flex-col">
                               <CollectionsCardsSkeleton />
                             </div>
                           ) : collections[params.page] ? (
                             collections[params.page]?.related_collections.map(
                               (collection) => (
-                                <Link
+                                <CollectionCard
                                   key={collection.collection_id}
-                                  href={`/collections/${collection.collection_id}`}
-                                  className="!no-underline group cursor-pointer border border-l-0 border-r-0 border-b-0 pt-3 border-gray-200 flex items-start gap-[18px]"
-                                >
-                                  <div
-                                    style={{
-                                      border: "1px solid rgba(0, 0, 0, 0.05)",
-                                    }}
-                                    className="group-hover:bg-gray-300 group-hover:transition flex justify-center items-center rounded-md bg-background h-[72px] w-[72px] bg-gray-100"
-                                  >
-                                    <div className="relative flex items-center justify-center overflow-hidden">
-                                      <p
-                                        className={`text-3xl ${notoBlack.className}`}
-                                      >
-                                        {collection.avatar_emoji}
-                                      </p>
-                                    </div>
-                                  </div>
-                                  <div className="flex-1 overflow-hidden">
-                                    <h3 className="text-sm font-semibold">
-                                      {collection.title}
-                                    </h3>
-                                    <p className="text-xs text-gray-500 mb-2 truncate">
-                                      by {collection.owner}
-                                    </p>
-                                    <div className="relative">
-                                      <div className="flex gap-2">
-                                        {collection.top_names.map((tag) => (
-                                          <span
-                                            key={tag.namehash}
-                                            className="bg-gray-100 text-sm px-2 py-1 bg-muted rounded-full"
-                                          >
-                                            {tag.name}
-                                          </span>
-                                        ))}
-                                      </div>
-                                      <div className="bg-gradient-white-to-transparent absolute right-0 top-0 w-40 h-full"></div>
-                                    </div>
-                                  </div>
-                                </Link>
+                                  collection={collection}
+                                />
                               ),
                             )
                           ) : null}
@@ -435,63 +530,26 @@ export default function ExploreCollectionsPage() {
                         ) : null}
                       </div>
 
-                      <div className="z-40 xl:max-w-[400px] mt-10 xl:mt-0 xl:ml-[68px] border rounded-md border-gray-200 w-full h-fit">
-                        <h2 className="flex items-center text-lg font-semibold h-[47px] px-5 border border-t-0 border-r-0 border-l-0 border-gray-200">
-                          Other collections
-                        </h2>
-                        {collections[params.page] ? (
-                          collections[params.page]?.other_collections.map(
-                            (collection) => (
-                              <Link
-                                key={collection.collection_id}
-                                href={`/collections/${collection.collection_id}`}
-                                className="!no-underline group rounded-lg cursor-pointer px-5 py-3 flex items-start gap-[18px]"
-                              >
-                                <div
-                                  style={{
-                                    border: "1px solid rgba(0, 0, 0, 0.05)",
-                                  }}
-                                  className="group-hover:bg-gray-300 group-hover:transition flex justify-center items-center rounded-md bg-background h-[72px] w-[72px] bg-gray-100"
-                                >
-                                  <div className="relative flex items-center justify-center overflow-hidden">
-                                    <p
-                                      className={`text-3xl ${notoBlack.className}`}
-                                    >
-                                      {collection.avatar_emoji}
-                                    </p>
-                                  </div>
-                                </div>
-                                <div className="flex-1 overflow-hidden">
-                                  <h3 className="text-sm font-semibold">
-                                    {collection.title}
-                                  </h3>
-                                  <p className="text-xs text-gray-500 mb-2 truncate">
-                                    by {collection.owner}
-                                  </p>
-                                  <div className="relative">
-                                    <div className="flex gap-2">
-                                      {collection.top_names.map((tag) => (
-                                        <span
-                                          key={tag.namehash}
-                                          className="bg-gray-100 text-sm px-2 py-1 bg-muted rounded-full"
-                                        >
-                                          {tag.name}
-                                        </span>
-                                      ))}
-                                    </div>
-                                    <div className="bg-gradient-white-to-transparent absolute right-0 top-0 w-40 h-full"></div>
-                                  </div>
-                                </div>
-                              </Link>
-                            ),
-                          )
-                        ) : (
-                          <div className="p-3 px-5">No collections found</div>
-                        )}
-                      </div>
+                      {collections[params.page]?.other_collections && (
+                        <div className="z-40 xl:max-w-[400px] mt-10 xl:mt-0 xl:ml-[68px] border rounded-md border-gray-200 w-full h-fit">
+                          <h2 className="flex items-center text-lg font-semibold h-[47px] px-5 border border-t-0 border-r-0 border-l-0 border-gray-200">
+                            Other collections
+                          </h2>
+                          <div className="px-5">
+                            {collections[params.page]?.other_collections?.map(
+                              (collection) => (
+                                <CollectionCard
+                                  key={collection.collection_id}
+                                  collection={collection}
+                                />
+                              ),
+                            )}
+                          </div>
+                        </div>
+                      )}
                     </div>
                   </>
-                ) : !loadCollections && !params.search && !collections ? (
+                ) : !params.search && !collections ? (
                   <>Error</>
                 ) : null}
               </>
diff --git a/apps/namegraph.dev/components/collections/collection-card.tsx b/apps/namegraph.dev/components/collections/collection-card.tsx
new file mode 100644
index 000000000..684c2236a
--- /dev/null
+++ b/apps/namegraph.dev/components/collections/collection-card.tsx
@@ -0,0 +1,51 @@
+import { NameGraphCollection } from "@namehash/namegraph-sdk/utils";
+import { Link } from "@namehash/namekit-react";
+import { Noto_Emoji } from "next/font/google";
+
+const notoBlack = Noto_Emoji({ preload: false });
+
+export const CollectionCard = ({
+  collection,
+}: {
+  collection: NameGraphCollection;
+}) => {
+  return (
+    <Link
+      key={collection.collection_id}
+      href={`/collections/${collection.collection_id}`}
+      className="!no-underline group rounded-lg py-3 cursor-pointer flex items-start space-x-[18px]"
+    >
+      <div
+        style={{
+          border: "1px solid rgba(0, 0, 0, 0.05)",
+        }}
+        className="group-hover:bg-gray-300 group-hover:transition flex justify-center items-center rounded-md bg-background h-[72px] w-[72px] bg-gray-100"
+      >
+        <div className="relative flex items-center justify-center overflow-hidden">
+          <p className={`text-3xl ${notoBlack.className}`}>
+            {collection.avatar_emoji}
+          </p>
+        </div>
+      </div>
+      <div className="flex-1 overflow-hidden">
+        <h3 className="text-sm font-semibold truncate">{collection.title}</h3>
+        <p className="text-xs text-gray-500 mb-2 truncate">
+          by {collection.owner}
+        </p>
+        <div className="relative">
+          <div className="flex gap-2">
+            {collection.top_names.map((tag) => (
+              <span
+                key={tag.namehash}
+                className="max-h-[28px] w-max bg-gray-100 text-sm px-2 py-1 bg-muted rounded-full"
+              >
+                {tag.name}
+              </span>
+            ))}
+          </div>
+          <div className="bg-gradient-white-to-transparent absolute right-0 top-0 w-40 h-full"></div>
+        </div>
+      </div>
+    </Link>
+  );
+};
diff --git a/apps/namegraph.dev/components/collections/collections-grid-skeleton.tsx b/apps/namegraph.dev/components/collections/collections-grid-skeleton.tsx
index 104468af3..18330f6ce 100644
--- a/apps/namegraph.dev/components/collections/collections-grid-skeleton.tsx
+++ b/apps/namegraph.dev/components/collections/collections-grid-skeleton.tsx
@@ -3,7 +3,7 @@ import Skeleton from "../skeleton";
 export const CollectionsGridSkeleton = () => {
   return (
     <div className="w-full flex flex-col xl:flex-row max-w-7xl xl:space-x-0">
-      <div className="w-full flex flex-col space-y-6 items-start">
+      <div className="w-full flex flex-col space-y-2.5 items-start">
         <div className="mt-2 md:mt-0 flex flex-col md:flex-row justify-between w-full mb-2">
           <div className="flex">
             <div className="flex mr-2.5">
@@ -23,10 +23,10 @@ export const CollectionsGridSkeleton = () => {
         </div>
       </div>
       <div className="relative z-50 xl:max-w-[400px] mt-10 xl:mt-0 border rounded-md border-gray-200 w-full h-fit">
-        <div className="flex items-center h-[47px] border border-gray-200 border-t-0 border-r-0 border-l-0">
-          <Skeleton className="w-[260px] h-7 px-5" />
+        <div className="px-5 py-[13px] border border-gray-200 border-r-0 border-l-0 border-t-0">
+          <Skeleton className="w-[80px] h-5" />
         </div>
-        <div className="px-5 py-3 gap-[18px] space-y-6 overflow-hidden">
+        <div className="px-5 overflow-hidden pb-[10px]">
           <CollectionsCardsSkeleton />
         </div>
         <div className="bg-gradient-white-to-transparent absolute right-0 top-[47px] w-20 h-full"></div>
@@ -39,7 +39,7 @@ const NUMBER_OF_SKELETONS_OF_COLLECTION_CARDS = 5;
 
 export const CollectionsCardsSkeleton = () => {
   return (
-    <>
+    <div className="flex flex-col space-y-2.5">
       {[...Array(NUMBER_OF_SKELETONS_OF_COLLECTION_CARDS).keys()].map(
         (collection) => {
           return (
@@ -49,17 +49,17 @@ export const CollectionsCardsSkeleton = () => {
           );
         },
       )}
-    </>
+    </div>
   );
 };
 
 const CollectionCardSkeleton = () => {
   return (
-    <div className="flex">
+    <div className="flex h-[86px] py-3">
       <div>
-        <Skeleton className="w-[72px] h-[72px]" />
+        <Skeleton className="w-[73px] h-[73px]" />
       </div>
-      <div className="w-full flex flex-col justify-start space-y-2 ml-4 items-start">
+      <div className="w-full flex flex-col justify-start space-y-2 ml-[18px] items-start">
         <Skeleton className="w-[100px] md:w-[200px] h-4 mr-auto" />
         <Skeleton className="w-[180px] md:w-[320px] h-3 mr-auto" />
         <div className="flex gap-2 relative">
diff --git a/apps/namegraph.dev/components/ui/toggle.tsx b/apps/namegraph.dev/components/ui/toggle.tsx
new file mode 100644
index 000000000..9c930a48d
--- /dev/null
+++ b/apps/namegraph.dev/components/ui/toggle.tsx
@@ -0,0 +1,45 @@
+"use client"
+
+import * as React from "react"
+import * as TogglePrimitive from "@radix-ui/react-toggle"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const toggleVariants = cva(
+  "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium transition-colors hover:bg-neutral-100 hover:text-neutral-500 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-neutral-950 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-neutral-100 data-[state=on]:text-neutral-900 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 dark:hover:bg-neutral-800 dark:hover:text-neutral-400 dark:focus-visible:ring-neutral-300 dark:data-[state=on]:bg-neutral-800 dark:data-[state=on]:text-neutral-50",
+  {
+    variants: {
+      variant: {
+        default: "bg-transparent",
+        outline:
+          "border border-neutral-200 bg-transparent shadow-sm hover:bg-neutral-100 hover:text-neutral-900 dark:border-neutral-800 dark:hover:bg-neutral-800 dark:hover:text-neutral-50",
+      },
+      size: {
+        default: "h-9 px-2 min-w-9",
+        sm: "h-8 px-1.5 min-w-8",
+        lg: "h-10 px-2.5 min-w-10",
+      },
+    },
+    defaultVariants: {
+      variant: "default",
+      size: "default",
+    },
+  }
+)
+
+const Toggle = React.forwardRef<
+  React.ElementRef<typeof TogglePrimitive.Root>,
+  React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> &
+    VariantProps<typeof toggleVariants>
+>(({ className, variant, size, ...props }, ref) => (
+  <TogglePrimitive.Root
+    ref={ref}
+    className={cn(toggleVariants({ variant, size, className }))}
+    {...props}
+  />
+))
+
+Toggle.displayName = TogglePrimitive.Root.displayName
+
+export { Toggle, toggleVariants }
diff --git a/apps/namegraph.dev/lib/utils.ts b/apps/namegraph.dev/lib/utils.ts
index f06d2775b..db52b3839 100644
--- a/apps/namegraph.dev/lib/utils.ts
+++ b/apps/namegraph.dev/lib/utils.ts
@@ -3,12 +3,14 @@ import { twMerge } from "tailwind-merge";
 import {
   DEFAULT_FULL_MODE,
   NameGraphCollection,
+  NameGraphCollectionByMemberResponse,
   NameGraphFetchTopCollectionMembersResponse,
   NameGraphFindCollectionsResponse,
   NameGraphGroupedByCategoryResponse,
   NameGraphGroupingCategory,
   NameGraphSortOrderOptions,
   NameGraphSuggestion,
+  ScrambleMethod,
 } from "@namehash/namegraph-sdk/utils";
 import { createNameGraphClient } from "@namehash/namegraph-sdk";
 
@@ -159,6 +161,21 @@ export const fetchCollectionMembers = async (
   return nameGeneratorSuggestions;
 };
 
+export const findCollectionsByMember = async (
+  query: string,
+  options?: {
+    offset?: number;
+    max_results?: number;
+    limit_names?: number;
+    sort_order?: NameGraphSortOrderOptions;
+  },
+): Promise<NameGraphCollectionByMemberResponse> => {
+  const nameGeneratorSuggestions =
+    await NameGraphClient.findCollectionsByMember(query, options);
+
+  return nameGeneratorSuggestions;
+};
+
 export const getCollectionById = async (
   collection_id: string,
 ): Promise<NameGraphCollection> => {
@@ -183,6 +200,7 @@ export const scrambleNamesByCollectionId = async (
   collectionId: string,
   options?: {
     seed?: number;
+    method?: ScrambleMethod;
   },
 ): Promise<NameGraphSuggestion[]> => {
   const nameGeneratorSuggestions =
diff --git a/apps/namegraph.dev/package.json b/apps/namegraph.dev/package.json
index 1e63021dc..780e7e7b2 100644
--- a/apps/namegraph.dev/package.json
+++ b/apps/namegraph.dev/package.json
@@ -15,6 +15,7 @@
     "@radix-ui/react-scroll-area": "^1.2.1",
     "@radix-ui/react-select": "2.1.4",
     "@radix-ui/react-slot": "^1.1.1",
+    "@radix-ui/react-toggle": "^1.1.1",
     "class-variance-authority": "^0.7.1",
     "classcat": "5.0.5",
     "clsx": "^2.1.1",
diff --git a/packages/namegraph-sdk/src/index.ts b/packages/namegraph-sdk/src/index.ts
index 5e71dc520..69af50b1b 100644
--- a/packages/namegraph-sdk/src/index.ts
+++ b/packages/namegraph-sdk/src/index.ts
@@ -21,6 +21,7 @@ import {
   DEFAULT_INSTANT_MODE,
   NameGraphSortOrderOptions,
   NameGraphCollection,
+  ScrambleMethod,
 } from "./utils";
 
 export class NameGraph {
@@ -118,7 +119,8 @@ export class NameGraph {
     },
   ): Promise<NameGraphSuggestion[]> {
     const max_sample_size = 5;
-    const seed = options?.seed || 0;
+    const random_seed = Number(Number(Math.random() * 10).toFixed(0));
+    const seed = options?.seed || random_seed;
 
     const payload = {
       collection_id,
@@ -149,12 +151,15 @@ export class NameGraph {
     collection_id: string,
     options?: {
       seed?: number;
+      method?: ScrambleMethod;
     },
   ): Promise<NameGraphSuggestion[]> {
-    const method = "left-right-shuffle-with-unigrams";
+    const default_method = ScrambleMethod["left-right-shuffle-with-unigrams"];
+    const method = options?.method || default_method;
     const n_top_members = 25;
     const max_suggestions = 10;
-    const seed = options?.seed || 0;
+    const random_seed = Number(Number(Math.random() * 10).toFixed(0));
+    const seed = options?.seed || random_seed;
 
     const payload = {
       collection_id,
@@ -276,11 +281,17 @@ export class NameGraph {
 
   public findCollectionsByMember(
     label: string,
+    options?: {
+      offset?: number;
+      max_results?: number;
+      limit_names?: number;
+      sort_order?: NameGraphSortOrderOptions;
+    },
   ): Promise<NameGraphCollectionByMemberResponse> {
-    const limit_names = 10;
-    const offset = 0;
-    const sort_order = "AI";
-    const max_results = 3;
+    const limit_names = options?.limit_names || 10;
+    const offset = options?.offset || 0;
+    const sort_order = options?.sort_order || NameGraphSortOrderOptions.AI;
+    const max_results = options?.max_results || 3;
 
     const payload = {
       limit_names,
diff --git a/packages/namegraph-sdk/src/utils.ts b/packages/namegraph-sdk/src/utils.ts
index 251232da7..bfb641204 100644
--- a/packages/namegraph-sdk/src/utils.ts
+++ b/packages/namegraph-sdk/src/utils.ts
@@ -109,7 +109,7 @@ export type NameGraphCollectionByMemberResponse = {
     elasticsearch_processing_time_ms: number;
     elasticsearch_communication_time_ms: number;
   };
-  collections: NameGraphSuggestion[];
+  collections: NameGraphCollection[];
 };
 
 export type NameGraphCollection = {
@@ -245,3 +245,12 @@ export const sampleWritersBlockSuggestions = (
 
   return result;
 };
+
+export const ScrambleMethod = {
+  "left-right-shuffle": "left-right-shuffle",
+  "left-right-shuffle-with-unigrams": "left-right-shuffle-with-unigrams",
+  "full-shuffle": "full-shuffle",
+} as const;
+
+export type ScrambleMethod =
+  (typeof ScrambleMethod)[keyof typeof ScrambleMethod];
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ee729f1ef..ac51609c0 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -148,6 +148,9 @@ importers:
       '@radix-ui/react-slot':
         specifier: ^1.1.1
         version: 1.1.1(@types/react@18.3.1)(react@18.3.1)
+      '@radix-ui/react-toggle':
+        specifier: ^1.1.1
+        version: 1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       class-variance-authority:
         specifier: ^0.7.1
         version: 0.7.1
@@ -1986,6 +1989,19 @@ packages:
       '@types/react':
         optional: true
 
+  '@radix-ui/react-toggle@1.1.1':
+    resolution: {integrity: sha512-i77tcgObYr743IonC1hrsnnPmszDRn8p+EGUsUt+5a/JFn28fxaM88Py6V2mc8J5kELMWishI0rLnuGLFD/nnQ==}
+    peerDependencies:
+      '@types/react': '*'
+      '@types/react-dom': '*'
+      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+      react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+      '@types/react-dom':
+        optional: true
+
   '@radix-ui/react-use-callback-ref@1.1.0':
     resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==}
     peerDependencies:
@@ -6796,6 +6812,17 @@ snapshots:
     optionalDependencies:
       '@types/react': 18.3.1
 
+  '@radix-ui/react-toggle@1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+    dependencies:
+      '@radix-ui/primitive': 1.1.1
+      '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.1)(react@18.3.1)
+      react: 18.3.1
+      react-dom: 18.3.1(react@18.3.1)
+    optionalDependencies:
+      '@types/react': 18.3.1
+      '@types/react-dom': 18.3.1
+
   '@radix-ui/react-use-callback-ref@1.1.0(@types/react@18.3.1)(react@18.3.1)':
     dependencies:
       react: 18.3.1