diff --git a/crates/desktop/src/App.tsx b/crates/desktop/src/App.tsx
index 999303e8..f46cca19 100644
--- a/crates/desktop/src/App.tsx
+++ b/crates/desktop/src/App.tsx
@@ -14,21 +14,22 @@ import { DeepLinkImportModal } from "./components/deep-link-import-modal";
import { OnboardingController } from "./components/onboarding-controller";
import { Redirect } from "./components/redirect";
import { ErrorBoundary } from "./components/ui/error-boundary";
-import { useSidebarNavigation } from "./hooks/use-sidebar-navigation";
import { MainLayout } from "./layouts/main-layout";
import type { DeepLinkImportIntent } from "./lib/deep-link";
import { parseDeepLink } from "./lib/deep-link";
import { setupAppMenu } from "./lib/menu";
import { initStore } from "./lib/store";
+import HomePage from "./pages/home";
import InferenceProvidersPage from "./pages/inference-providers";
+import MarketPage from "./pages/market";
import PluginsPage from "./pages/plugins";
import ProjectDetailPage from "./pages/project/detail";
+import SearchResultsPage from "./pages/search";
import SettingsPage from "./pages/settings";
import CustomAgentsPage from "./pages/settings/custom-agents";
import MCPServersPage from "./pages/settings/mcp-servers";
import SkillsPage from "./pages/settings/skills";
import SubAgentsPage from "./pages/settings/sub-agents";
-import SkillsShPage from "./pages/skills-sh";
import SkillsSearchPage from "./pages/skills-sh/search";
import { AgentAvailabilityProvider } from "./providers/agent-availability";
import { ServerProvider } from "./providers/server";
@@ -44,31 +45,14 @@ const queryClient = new QueryClient({
},
});
-function SkillsPageSkeleton() {
+function PageSkeleton() {
return (
-
-
-
-
-
+
+
);
}
-function DefaultSidebarRoute() {
- const { defaultHref, isLoading } = useSidebarNavigation();
-
- if (isLoading) {
- return null;
- }
-
- return
;
-}
-
function App() {
const [isStoreReady, setIsStoreReady] = useState(false);
const [pendingIntents, setPendingIntents] = useState<
@@ -178,110 +162,152 @@ function App() {
-
-
-
- }
+ fallback={}
>
-
+
-
+
+
- }
+ fallback={}
>
-
+
-
+
+
+ {(params) => (
+
+ )}
+
+
+
+ {(params) => (
+
+ )}
+
+
+
-
+ }
+ >
+
+
-
+
+
- }
+ fallback={}
>
-
+
+
+
+
+
+
+
+
+
- }
+ fallback={}
>
-
+
-
+
- }
+ fallback={}
>
-
+
-
+
-
+
+ }
+ >
+
+
+
-
+
-
+
+
+
-
+
+
+
+
+
+
+
+
- }
+ fallback={}
>
-
+
+
+
+
+
+
+
+
+
+
+
+
-
+
;
+}
+
+function SectionLabel({ children }: { children: React.ReactNode }) {
+ return (
+
+ {children}
+
+ );
+}
+
+const RESOURCE_HREFS = ["/skills", "/mcp", "/sub-agents"] as const;
+
+function isOnResourcePage(pathname: string) {
+ return RESOURCE_HREFS.some(
+ (href) => pathname === href || pathname.startsWith(`${href}/`),
+ );
+}
+
+function withAgent(href: string, agent: string | null) {
+ if (!agent) return href;
+ return `${href}?agent=${encodeURIComponent(agent)}`;
+}
+
export function AppSidebar() {
const { t } = useTranslation();
const [pathname] = useLocation();
+ const search = useSearch();
const { visibleSidebarItems } = useSidebarNavigation();
+ const stickyAgent = useStickyAgentFilter();
+
+ // On a resource page, the URL is the source of truth for the active filter,
+ // so mirror it into the sticky store. On non-resource pages we leave the
+ // sticky value alone so it survives the round-trip.
+ const onResource = isOnResourcePage(pathname);
+ const urlAgent = new URLSearchParams(search).get("agent") || null;
+ useEffect(() => {
+ if (onResource) {
+ setStickyAgentFilter(urlAgent);
+ }
+ }, [onResource, urlAgent]);
+
+ // Sidebar resource links carry the sticky filter forward, even from
+ // non-resource pages (so going to /inference-providers and back to /skills
+ // restores ?agent=
). The chip's clear button on a resource page
+ // resets both the URL and the sticky store.
+ const carriedAgent = stickyAgent;
+
+ const homeItem = visibleSidebarItems.find((item) => item.id === "home");
+ const marketItem = visibleSidebarItems.find((item) => item.id === "market");
return (
-