Skip to content

Move explore tabs into home page sidebar#605

Merged
SupertigerDev merged 5 commits intoNerimity:mainfrom
Aeledfyr:explore-subitems
Mar 7, 2026
Merged

Move explore tabs into home page sidebar#605
SupertigerDev merged 5 commits intoNerimity:mainfrom
Aeledfyr:explore-subitems

Conversation

@Aeledfyr
Copy link
Copy Markdown
Contributor

@Aeledfyr Aeledfyr commented Mar 7, 2026

This PR moves the explore tabs from being a separate page with its own drawer into a sub-list in the home page drawer, and refactors the explore page header styles to be the same across all explore pages.

Refactors for the explore pages:

  • Remove the now-redundant back button
  • Move the notice in the themes page below the search bar, to match servers/bots
  • Share the search bar logic and styling between servers/bots
  • Use the title translation string for the page title (and fix the missing title in Explore Themes)

Screenshots

The animations for opening / interacting with the explore item list:
https://github.com/user-attachments/assets/761e626b-5cc7-42f8-8682-6c1c5140f632

Before:

After:

No mobile screenshots, but the changes are the same. The filter dropdown in for search is still wrapped to the next line on mobile.

Did you test your code?

Tested on Firefox on Desktop and Chrome on Android.

Checklist

  • Changes are clear, concise, and easy to review
  • Code has been tested and works as intended
  • Text/content changes support internationalization (i18n)
  • Any new user-facing strings are properly localized

Summary by CodeRabbit

  • New Features

    • Unified search and filter header added to Explore pages for consistent discovery.
    • Explore submenu now appears within the main navigation for quicker access to explore sections.
  • Refactor

    • Explore pages (Bots, Servers, Themes) redesigned to a shared, modern layout and composition.
    • Navigation items updated to show contextual sublists and improved selection behavior.
  • Style

    • Drawer and list styling refined, including subtle animations for submenu expansion.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 7, 2026

📝 Walkthrough

Walkthrough

Refactors Explore UI: removes ExploreDrawer, introduces ExplorePageContainer and ExploreSearch in ExplorePane, updates ExploreBots/ExploreServers/ExploreThemes to use the new layout, adds optional match to ExploreRoute and sets match: "/servers/*", integrates dynamic explore sublists into HomeDrawer, and adjusts related styles and selection logic.

Changes

Cohort / File(s) Summary
Route Configuration
src/common/exploreRoutes.ts
Added optional match?: string to ExploreRoute and set match: "/servers/*" on the servers route.
Explore Pane (new public UI pieces)
src/components/explore/ExplorePane.tsx
Added exports ExplorePageContainer, SearchHeader, Query type, and ExploreSearch component to encapsulate search/sort/filter UI.
Explore Pages
src/components/explore/ExploreBots.tsx, src/components/explore/ExploreServers.tsx, src/components/explore/ExploreThemes.tsx
Replaced inline header/search UI with ExplorePageContainer + ExploreSearch; removed local DropDown/Input usage and restructured page containers and MetaTitle usage.
Explore Drawer Removal
src/components/explore/ExploreDrawer.tsx
Deleted entire ExploreDrawer file and its exported SettingsDrawer component (list, items, footer, styles and routing logic removed).
Home Drawer & Styles
src/components/home-drawer/HomeDrawer.tsx, src/components/home-drawer/HomeDrawer.module.scss
Switched to UiItem API, added class?: string prop to ItemProps, render dynamic explore sublist from exploreRoutes when path matches /app/explore, added .exploreHeader and .exploreSublist styles and slide-down animation.
UI Item Styles
src/components/ui/Item.module.scss
Adjusted item layout: removed base justify-content, added align-items on .itemContent, and updated bottom-handle justification and ::after context.
Entry / Routing
src/index.tsx
Removed lazy import/usage of ExploreDrawer; switched to HomeDrawer for /explore route.
Small navigation tweaks
src/components/side-pane/SidePane.tsx, src/components/home-drawer/friend-item/HomeDrawerFriendItem.tsx
HomeItem selection now includes /app/explore paths; friend item uses handlePosition="left".

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Router
    participant HomeDrawer
    participant ExploreRoutes
    participant ExplorePage
    participant ExplorePane

    User->>Router: navigate to /app/explore/servers
    Router->>HomeDrawer: render (route context)
    HomeDrawer->>ExploreRoutes: read routes (uses optional match)
    ExploreRoutes-->>HomeDrawer: provide routes list
    HomeDrawer->>User: render Explore header + sublist (if inExplore)
    User->>HomeDrawer: click Servers subitem
    HomeDrawer->>Router: navigate to /app/explore/servers
    Router->>ExplorePage: mount page component
    ExplorePage->>ExplorePane: render inside ExplorePageContainer with ExploreSearch
    ExplorePane-->>User: show composed header + search UI
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 Hi! I hopped through code today, so spry,
Out went the drawer — in came the sky,
Pages now nest and sublists bloom,
Routes find matches, layouts room by room,
I twitch my nose — the UI's spry! 🥕✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately summarizes the main change: moving explore tabs from a separate page drawer into the home page sidebar, which aligns with the changeset's core objective.
Description check ✅ Passed The PR description includes all required template sections: what the PR does, screenshots with before/after comparisons, testing details, and a completed checklist confirming quality standards.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
src/components/explore/ExplorePane.tsx (1)

48-58: Keep ExploreSearch generic over the query IDs.

This widens sort and filter to plain string, while callers like ExploreBots keep them as narrower unions. With strict function checks that can make setQuery={setQuery} incompatible; otherwise it still erases the useful union typing.

🛠️ Suggested direction
-interface Query {
-  sort: string;
-  filter: string;
+interface Query<TSort extends string = string, TFilter extends string = string> {
+  sort: TSort;
+  filter: TFilter;
   search: string;
 }
 
-export function ExploreSearch(props: {
+export function ExploreSearch<TSort extends string, TFilter extends string>(props: {
   sortOpts: DropDownItem[];
   filterOpts: DropDownItem[];
-  query: () => Query,
-  setQuery: (q: Query) => void,
+  query: () => Query<TSort, TFilter>;
+  setQuery: (q: Query<TSort, TFilter>) => void;
 }) {
@@
-        onChange={(i) =>
-          props.setQuery({ ...props.query(), sort: i.id })
-        }
+        onChange={(i) =>
+          props.setQuery({ ...props.query(), sort: i.id as TSort })
+        }
@@
-        onChange={(i) =>
-          props.setQuery({ ...props.query(), filter: i.id })
-        }
+        onChange={(i) =>
+          props.setQuery({ ...props.query(), filter: i.id as TFilter })
+        }

Run this to verify whether strict function checks are enabled and to inspect the narrowed call sites:

#!/bin/bash
set -euo pipefail

python - <<'PY'
import json, pathlib
for path in pathlib.Path('.').rglob('tsconfig*.json'):
    try:
        data = json.loads(path.read_text())
    except Exception:
        continue
    co = data.get("compilerOptions", {})
    print(f"\n-- {path} --")
    print({"strict": co.get("strict"), "strictFunctionTypes": co.get("strictFunctionTypes")})
PY

echo
rg -n -C2 'interface Query|export function ExploreSearch|createSignal<\{|<ExploreSearch|setQuery=\{setQuery\}|query=\{query\}' src/components/explore
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/explore/ExplorePane.tsx` around lines 48 - 58, The
ExploreSearch component currently uses a concrete Query interface with
sort/filter as plain strings which widens caller types and can break
strictFunctionTypes; make ExploreSearch generic so it preserves callers'
narrower unions. Change the local Query to a generic type parameter (e.g.
ExploreSearch<S extends string, F extends string, Q extends { sort: S; filter:
F; search: string }>) and update props to use those generics: sortOpts and
filterOpts typed to the matching ID generics, and query: () => Q and setQuery:
(q: Q) => void. Update the ExploreSearch declaration and usages (e.g.
ExploreSearch<...>) so callers like ExploreBots keep their union types intact
while ExploreSearch remains flexible.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/home-drawer/HomeDrawer.module.scss`:
- Around line 33-35: The CSS selector :global .itemContent is invalid for CSS
Modules and triggers stylelint; change the rule to use the CSS Modules global
function form by converting ":global .itemContent { gap: 6px; }" to
":global(.itemContent) { gap: 6px; }" (update the rule in HomeDrawer.module.scss
where the .itemContent global rule is defined).
- Around line 59-87: Rename the keyframe and its usage to kebab-case to satisfy
stylelint: change the animation name "slideDown" to "slide-down" in the rule
that sets animation/animation-duration/animation-fill-mode and rename the
corresponding `@keyframes` declaration from "@keyframes slideDown" to "@keyframes
slide-down"; also remove the extra blank line within the block (the one before
`@keyframes`) to clear the stylelint blank-line warning. Ensure all references
inside HomeDrawer.module.scss use the new "slide-down" name.

In `@src/components/home-drawer/HomeDrawer.tsx`:
- Around line 120-124: The explore Item generation in HomeDrawer.tsx wrongly
concatenates setting.path/setting.match causing double slashes and invalid
patterns; update the Item to build both href and match from setting.routePath
(e.g. href = "/app" + setting.routePath and match = setting.routePath ? "/app" +
setting.routePath : undefined) instead of using setting.path or setting.match,
and remove the old concatenation that references setting.path/setting.match so
nested explore routes match correctly.

In `@src/components/ui/Item.module.scss`:
- Around line 78-82: The shared .itemContent CSS removed left gutter causing
items with Item.Root's default handlePosition="left" (in
src/components/ui/Item.tsx) to overlap the handle; restore a base left padding
in .itemContent to reserve the left-handle space and move the padding-left: 0px
rule into the specific explore-list variant selector (or the class used by the
new list) so only that variant removes the left gutter.

---

Nitpick comments:
In `@src/components/explore/ExplorePane.tsx`:
- Around line 48-58: The ExploreSearch component currently uses a concrete Query
interface with sort/filter as plain strings which widens caller types and can
break strictFunctionTypes; make ExploreSearch generic so it preserves callers'
narrower unions. Change the local Query to a generic type parameter (e.g.
ExploreSearch<S extends string, F extends string, Q extends { sort: S; filter:
F; search: string }>) and update props to use those generics: sortOpts and
filterOpts typed to the matching ID generics, and query: () => Q and setQuery:
(q: Q) => void. Update the ExploreSearch declaration and usages (e.g.
ExploreSearch<...>) so callers like ExploreBots keep their union types intact
while ExploreSearch remains flexible.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e612a3a9-678c-4400-8a0b-59210aa05f60

📥 Commits

Reviewing files that changed from the base of the PR and between 14c0d5a and 1df318e.

📒 Files selected for processing (11)
  • src/common/exploreRoutes.ts
  • src/components/explore/ExploreBots.tsx
  • src/components/explore/ExploreDrawer.tsx
  • src/components/explore/ExplorePane.tsx
  • src/components/explore/ExploreServers.tsx
  • src/components/explore/ExploreThemes.tsx
  • src/components/home-drawer/HomeDrawer.module.scss
  • src/components/home-drawer/HomeDrawer.tsx
  • src/components/side-pane/SidePane.tsx
  • src/components/ui/Item.module.scss
  • src/index.tsx
💤 Files with no reviewable changes (1)
  • src/components/explore/ExploreDrawer.tsx

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/components/home-drawer/HomeDrawer.tsx (1)

109-115: Drive the Explore header state from inExplore() only.

The arrow icon and submenu visibility already depend on inExplore(), so keeping selection on a separate match="/app/explore" path adds avoidable state to keep in sync.

♻️ Suggested cleanup
       <Item
         label={t("explore.drawer.title")}
         icon={inExplore() ? "keyboard_arrow_down" : "explore"}
         href="/app/explore/servers"
-        match="/app/explore"
+        selected={!!inExplore()}
         class={style.exploreHeader}
       />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/home-drawer/HomeDrawer.tsx` around lines 109 - 115, The Item
component's selection state should be driven solely by the inExplore() helper:
remove the redundant match="/app/explore" prop from the Item instance (the one
rendering the Explore header) and any code that relies on that prop for
selection, and ensure any visual selection/submenu visibility logic only reads
inExplore() (preserve label, icon, href and class={style.exploreHeader}); update
any tests or usages expecting the match prop to be present for this Item.
src/index.tsx (1)

311-314: Drop the redundant leftDrawer override.

The parent /app route already provides leftDrawer: HomeDrawer, so this branch only needs to swap mainPane.

♻️ Suggested cleanup
             <Route
               path="/explore"
-              components={{ mainPane: ExplorePane, leftDrawer: HomeDrawer }}
+              components={{ mainPane: ExplorePane }}
             >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/index.tsx` around lines 311 - 314, Remove the redundant leftDrawer
override from the Route that renders ExplorePane: locate the Route with
path="/explore" (the JSX Route element that currently sets components={{
mainPane: ExplorePane, leftDrawer: HomeDrawer }} ) and change its components
prop to only provide mainPane (e.g., components={{ mainPane: ExplorePane }}),
relying on the parent /app route to supply leftDrawer: HomeDrawer.
src/components/home-drawer/HomeDrawer.module.scss (1)

52-85: Consider a reduced-motion escape hatch for the new nav animation.

This runs on a primary navigation surface. Disabling it under prefers-reduced-motion would make the expanded Explore list friendlier for users who opt out of motion.

♿ Suggested tweak
 .exploreSublist {
   display: flex;
   flex-direction: column;
   position: relative;
   padding-left: 28px;
   gap: 2px;
   animation: slide-down 0.2s forwards;
+
+  `@media` (prefers-reduced-motion: reduce) {
+    animation: none;
+  }
 
   &::before {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/home-drawer/HomeDrawer.module.scss` around lines 52 - 85, The
nav animation on .exploreSublist uses `@keyframes` slide-down and should respect
users' reduced-motion preference; add a prefers-reduced-motion media query that
overrides the animation and ensures the element is rendered in its final visible
state (e.g., set animation: none; opacity: 1; transform: none;) for
.exploreSublist so the expand occurs instantly with no motion for users who opt
out.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/explore/ExploreBots.tsx`:
- Around line 120-127: The filter IDs in defaultQuery and filterOpts
("online_bots"/"offline_bots") are not part of the PublicServerFilter type used
by query/setQuery/getExploreItems; update either the type or the filter options
so they align: either extend PublicServerFilter to include "online_bots" and
"offline_bots" (so query, setQuery and getExploreItems accept those values) or
change defaultQuery and filterOpts to use only the existing PublicServerFilter
values ("pinned" | "all" | "verified"); ensure ExploreSearch is passed a query
value that matches PublicServerFilter and update getExploreItems usage
accordingly.

---

Nitpick comments:
In `@src/components/home-drawer/HomeDrawer.module.scss`:
- Around line 52-85: The nav animation on .exploreSublist uses `@keyframes`
slide-down and should respect users' reduced-motion preference; add a
prefers-reduced-motion media query that overrides the animation and ensures the
element is rendered in its final visible state (e.g., set animation: none;
opacity: 1; transform: none;) for .exploreSublist so the expand occurs instantly
with no motion for users who opt out.

In `@src/components/home-drawer/HomeDrawer.tsx`:
- Around line 109-115: The Item component's selection state should be driven
solely by the inExplore() helper: remove the redundant match="/app/explore" prop
from the Item instance (the one rendering the Explore header) and any code that
relies on that prop for selection, and ensure any visual selection/submenu
visibility logic only reads inExplore() (preserve label, icon, href and
class={style.exploreHeader}); update any tests or usages expecting the match
prop to be present for this Item.

In `@src/index.tsx`:
- Around line 311-314: Remove the redundant leftDrawer override from the Route
that renders ExplorePane: locate the Route with path="/explore" (the JSX Route
element that currently sets components={{ mainPane: ExplorePane, leftDrawer:
HomeDrawer }} ) and change its components prop to only provide mainPane (e.g.,
components={{ mainPane: ExplorePane }}), relying on the parent /app route to
supply leftDrawer: HomeDrawer.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 85be2694-f36b-4395-8e14-a0e0c0b09d71

📥 Commits

Reviewing files that changed from the base of the PR and between 1df318e and 6a7c1c7.

📒 Files selected for processing (12)
  • src/common/exploreRoutes.ts
  • src/components/explore/ExploreBots.tsx
  • src/components/explore/ExploreDrawer.tsx
  • src/components/explore/ExplorePane.tsx
  • src/components/explore/ExploreServers.tsx
  • src/components/explore/ExploreThemes.tsx
  • src/components/home-drawer/HomeDrawer.module.scss
  • src/components/home-drawer/HomeDrawer.tsx
  • src/components/home-drawer/friend-item/HomeDrawerFriendItem.tsx
  • src/components/side-pane/SidePane.tsx
  • src/components/ui/Item.module.scss
  • src/index.tsx
💤 Files with no reviewable changes (1)
  • src/components/explore/ExploreDrawer.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/components/explore/ExplorePane.tsx
  • src/components/explore/ExploreThemes.tsx
  • src/components/side-pane/SidePane.tsx
  • src/common/exploreRoutes.ts
  • src/components/ui/Item.module.scss

Comment on lines +120 to +127
<ExplorePageContainer>
<MetaTitle>{t("explore.bots.title")}</MetaTitle>
<ExploreSearch
sortOpts={sortOpts}
filterOpts={filterOpts}
query={query}
setQuery={setQuery}
/>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how PublicServerSort and PublicServerFilter are defined
ast-grep --pattern 'type PublicServerSort = $_'
ast-grep --pattern 'type PublicServerFilter = $_'
rg -n 'PublicServerSort|PublicServerFilter' --type ts -A 2 -B 1

Repository: Nerimity/nerimity-web

Length of output: 2907


🏁 Script executed:

fd -e tsx -e ts | xargs rg -l "ExploreSearch" | head -5

Repository: Nerimity/nerimity-web

Length of output: 184


🏁 Script executed:

ast-grep --pattern 'function ExploreSearch($_) {
  $$$
}'

Repository: Nerimity/nerimity-web

Length of output: 2895


🏁 Script executed:

rg -n "interface.*ExploreSearch|type.*ExploreSearch" --type ts

Repository: Nerimity/nerimity-web

Length of output: 47


🏁 Script executed:

rg -n "export.*ExploreSearch" --type tsx -A 10

Repository: Nerimity/nerimity-web

Length of output: 92


🏁 Script executed:

rg -n "type Query|interface Query" src/components/explore/ --type ts

Repository: Nerimity/nerimity-web

Length of output: 124


🏁 Script executed:

rg -n "type DropDownItem|interface DropDownItem" --type ts -A 3

Repository: Nerimity/nerimity-web

Length of output: 332


🏁 Script executed:

sed -n '48,53p' src/components/explore/ExplorePane.tsx

Repository: Nerimity/nerimity-web

Length of output: 137


🏁 Script executed:

sed -n '60,100p' src/components/explore/ExploreBots.tsx

Repository: Nerimity/nerimity-web

Length of output: 1144


🏁 Script executed:

sed -n '100,130p' src/components/explore/ExploreBots.tsx

Repository: Nerimity/nerimity-web

Length of output: 1097


🏁 Script executed:

sed -n '44,50p' src/chat-api/services/ExploreService.ts

Repository: Nerimity/nerimity-web

Length of output: 236


🏁 Script executed:

sed -n '107,115p' src/components/explore/ExploreBots.tsx

Repository: Nerimity/nerimity-web

Length of output: 453


🏁 Script executed:

rg -n "online_bots|offline_bots" src/components/explore/ --type ts

Repository: Nerimity/nerimity-web

Length of output: 327


🏁 Script executed:

sed -n '40,52p' src/components/explore/ExploreBots.tsx

Repository: Nerimity/nerimity-web

Length of output: 389


🏁 Script executed:

rg -n "getExploreItems" src/components/explore/ExploreBots.tsx -B 5 -A 5

Repository: Nerimity/nerimity-web

Length of output: 940


Fix type mismatch: filter values don't match PublicServerFilter type definition.

The defaultQuery and filterOpts use filter IDs "online_bots" and "offline_bots", but the local query signal is typed with PublicServerFilter which only includes "pinned" | "all" | "verified". These invalid values will cause type errors when passed through setQuery and getExploreItems.

Either update the PublicServerFilter type to include the bot-specific filters, or align the filter options with the actual type definition.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/explore/ExploreBots.tsx` around lines 120 - 127, The filter
IDs in defaultQuery and filterOpts ("online_bots"/"offline_bots") are not part
of the PublicServerFilter type used by query/setQuery/getExploreItems; update
either the type or the filter options so they align: either extend
PublicServerFilter to include "online_bots" and "offline_bots" (so query,
setQuery and getExploreItems accept those values) or change defaultQuery and
filterOpts to use only the existing PublicServerFilter values ("pinned" | "all"
| "verified"); ensure ExploreSearch is passed a query value that matches
PublicServerFilter and update getExploreItems usage accordingly.

@SupertigerDev SupertigerDev merged commit 5a23f6f into Nerimity:main Mar 7, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants