diff --git a/web/src/app/home/components/home-sidebar/HomeSidebar.tsx b/web/src/app/home/components/home-sidebar/HomeSidebar.tsx
index 7ea5b9ab..39a4754f 100644
--- a/web/src/app/home/components/home-sidebar/HomeSidebar.tsx
+++ b/web/src/app/home/components/home-sidebar/HomeSidebar.tsx
@@ -29,6 +29,8 @@ import {
FilePlus2,
Sparkles,
HardDrive,
+ Server,
+ Puzzle,
} from 'lucide-react';
import { useTheme } from '@/components/providers/theme-provider';
@@ -370,7 +372,23 @@ function NavItems({
// Entity categories: collapsible with sub-items
const entityKey = ENTITY_KEY_MAP[config.id];
- const items: SidebarEntityItem[] = sidebarData[entityKey];
+ const isExtensionsCategory = config.id === 'plugins';
+ const items: SidebarEntityItem[] = isExtensionsCategory
+ ? [
+ ...sidebarData.plugins.map((p) => ({
+ ...p,
+ extensionType: 'plugin' as const,
+ })),
+ ...sidebarData.mcpServers.map((m) => ({
+ ...m,
+ extensionType: 'mcp' as const,
+ })),
+ ...sidebarData.skills.map((s) => ({
+ ...s,
+ extensionType: 'skill' as const,
+ })),
+ ]
+ : sidebarData[entityKey];
const routePrefix = ENTITY_ROUTE_MAP[config.id];
const hasDetailPages = DETAIL_PAGE_CATEGORIES.includes(config.id);
const canCreate = CREATABLE_CATEGORIES.includes(config.id);
@@ -379,6 +397,18 @@ function NavItems({
const isSkill = config.id === 'skills';
const isBot = config.id === 'bots';
const isMCP = config.id === 'mcp';
+
+ const resolveItemRoute = (item: SidebarEntityItem): string => {
+ if (item.extensionType === 'mcp') {
+ return `/home/mcp?id=${encodeURIComponent(item.id)}`;
+ }
+ if (item.extensionType === 'skill') {
+ return `/home/skills?id=${encodeURIComponent(item.id)}`;
+ }
+ return hasDetailPages
+ ? `${routePrefix}?id=${encodeURIComponent(item.id)}`
+ : routePrefix;
+ };
const isActive =
selectedChild?.id === config.id ||
pathname === routePrefix ||
@@ -394,7 +424,13 @@ function NavItems({
// Shared entity list renderer used by both popover and collapsible
const renderEntityList = (inPopover: boolean) => {
- const sortedItems = sortByRecent(items);
+ const sortedItems = isExtensionsCategory
+ ? [...items].sort((a, b) =>
+ a.name.localeCompare(b.name, undefined, {
+ sensitivity: 'base',
+ }),
+ )
+ : sortByRecent(items);
const isExpanded = expandedLists[config.id] ?? false;
const maxItems = inPopover ? 10 : MAX_VISIBLE_ITEMS;
const visibleItems =
@@ -416,152 +452,212 @@ function NavItems({
);
}
- return (
- <>
- {visibleItems.map((item) => {
- const itemRoute = hasDetailPages
- ? `${routePrefix}?id=${encodeURIComponent(item.id)}`
- : routePrefix;
- const isItemActive =
- hasDetailPages &&
- pathname === routePrefix &&
- searchParams.get('id') === item.id;
+ const itemActiveCheck = (item: SidebarEntityItem): boolean => {
+ if (item.extensionType === 'mcp') {
+ return (
+ pathname === '/home/mcp' && searchParams.get('id') === item.id
+ );
+ }
+ if (item.extensionType === 'skill') {
+ return (
+ pathname === '/home/skills' &&
+ searchParams.get('id') === item.id
+ );
+ }
+ return (
+ hasDetailPages &&
+ pathname === routePrefix &&
+ searchParams.get('id') === item.id
+ );
+ };
- if (inPopover) {
- return (
-
+ );
+ }
- // Normal sidebar sub-item rendering
- return (
-
- {(isBot || isMCP) && (
-
- )}
-
- ) : isMCP ? (
+ // Normal sidebar sub-item rendering
+ return (
+
+ {(isBot || isMCP) && (
- ) : null}
- {item.name}
- {item.debug && (
-