From 7c8819cf348490b30598ebf197eb190de1ce7eae Mon Sep 17 00:00:00 2001 From: Haileyesus Date: Wed, 8 Apr 2026 11:31:20 +0300 Subject: [PATCH] refactor: plugin routes --- src/App.tsx | 7 +++++- .../refactored/shared/layout/MainHeading.tsx | 23 +++++++++++++++---- .../refactored/shared/layout/MobileNav.tsx | 23 +++++++++++++++---- 3 files changed, 42 insertions(+), 11 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 6101ae9b..35edd914 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,6 +4,7 @@ import { Outlet, RouterProvider, createBrowserRouter, + useLocation, useParams, } from 'react-router-dom'; import { AuthProvider, ProtectedRoute } from './components/auth'; @@ -28,6 +29,7 @@ const isValidRouteTab = (value: string | undefined): boolean => { normalizedValue === 'files' || normalizedValue === 'git' || normalizedValue === 'tasks' || + normalizedValue === 'plugins' || normalizedValue === 'preview' || normalizedValue.startsWith('plugin:') ); @@ -60,6 +62,7 @@ function WorkspaceLayout() { } function WorkspaceTabRoute() { + const location = useLocation(); const { workspaceId, sessionId, tab } = useParams<{ workspaceId: string; sessionId?: string; @@ -77,11 +80,13 @@ function WorkspaceTabRoute() { const decodedWorkspaceId = workspaceId ? decodeURIComponent(workspaceId) : null; const decodedSessionId = sessionId ? decodeURIComponent(sessionId) : null; const decodedTab = tab ? decodeURIComponent(tab) : 'chat'; + const pluginName = decodeURIComponent(new URLSearchParams(location.search).get('name') || ''); + const tabLabel = decodedTab === 'plugins' && pluginName ? `plugin:${pluginName}` : decodedTab; return (
-

{decodedTab} view

+

{tabLabel} view

Workspace:{' '} diff --git a/src/components/refactored/shared/layout/MainHeading.tsx b/src/components/refactored/shared/layout/MainHeading.tsx index e0670931..2cdce9c6 100644 --- a/src/components/refactored/shared/layout/MainHeading.tsx +++ b/src/components/refactored/shared/layout/MainHeading.tsx @@ -1,5 +1,5 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { useNavigate, useParams } from 'react-router-dom'; +import { useLocation, useNavigate, useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import type { AppTab } from '@/types/app'; import { usePlugins } from '@/contexts/PluginsContext'; @@ -56,6 +56,7 @@ export function MainHeading() { const { isMobile } = useDeviceSettings({ trackPWA: false }); const { sidebarIsCollapsed, setSidebarIsCollapsed } = useSystemUI(); const { workspaceId, sessionId, tab } = useParams(); + const location = useLocation(); const scrollRef = useRef(null); const [canScrollLeft, setCanScrollLeft] = useState(false); @@ -63,10 +64,18 @@ export function MainHeading() { const decodedWorkspaceId = useMemo(() => decodeValue(workspaceId), [workspaceId]); const decodedSessionId = useMemo(() => decodeValue(sessionId), [sessionId]); + const pluginName = useMemo(() => { + const params = new URLSearchParams(location.search); + return decodeValue(params.get('name') ?? undefined); + }, [location.search]); const activeTab = useMemo(() => { const routeTab = decodeValue(tab); + if (routeTab === 'plugins' && pluginName) { + return `plugin:${pluginName}` as AppTab; + } + return (routeTab || 'chat') as AppTab; - }, [tab]); + }, [pluginName, tab]); const pluginDisplayName = useMemo( () => @@ -117,15 +126,19 @@ export function MainHeading() { const handleTabSelect = (nextTab: AppTab) => { // Preserve route context while switching only the active tab path segment. - const encodedTab = encodeURIComponent(nextTab); + const isPluginTab = nextTab.startsWith('plugin:'); + const pluginTabName = isPluginTab ? nextTab.replace('plugin:', '') : ''; + const targetTab = isPluginTab ? 'plugins' : nextTab; + const encodedTargetTab = encodeURIComponent(targetTab); + const pluginQuery = isPluginTab ? `?name=${encodeURIComponent(pluginTabName)}` : ''; if (decodedSessionId) { - navigate(`/sessions/${encodeURIComponent(decodedSessionId)}/${encodedTab}`); + navigate(`/sessions/${encodeURIComponent(decodedSessionId)}/${encodedTargetTab}${pluginQuery}`); return; } const encodedWorkspaceId = encodeURIComponent(decodedWorkspaceId); - navigate(`/workspaces/${encodedWorkspaceId}/${encodedTab}`); + navigate(`/workspaces/${encodedWorkspaceId}/${encodedTargetTab}${pluginQuery}`); }; return ( diff --git a/src/components/refactored/shared/layout/MobileNav.tsx b/src/components/refactored/shared/layout/MobileNav.tsx index 381989a8..1e34a99d 100644 --- a/src/components/refactored/shared/layout/MobileNav.tsx +++ b/src/components/refactored/shared/layout/MobileNav.tsx @@ -1,5 +1,5 @@ import { useState, useRef, useEffect, useMemo } from 'react'; -import { useNavigate, useParams } from 'react-router-dom'; +import { useLocation, useNavigate, useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { MessageSquare, @@ -58,6 +58,7 @@ export function MobileNav() { const { isMobile } = useDeviceSettings({ trackPWA: false }); const { isChatInputFocused } = useSystemUI(); const { workspaceId, sessionId, tab } = useParams(); + const location = useLocation(); const { tasksEnabled, isTaskMasterInstalled } = useTasksSettings(); const shouldShowTasksTab = Boolean(tasksEnabled && isTaskMasterInstalled); const { plugins } = usePlugins(); @@ -66,10 +67,18 @@ export function MobileNav() { const enabledPlugins = plugins.filter((plugin) => plugin.enabled); const hasPlugins = enabledPlugins.length > 0; + const pluginName = useMemo(() => { + const params = new URLSearchParams(location.search); + return decodeValue(params.get('name') ?? undefined); + }, [location.search]); const activeTab = useMemo(() => { const routeTab = decodeValue(tab); + if (routeTab === 'plugins' && pluginName) { + return `plugin:${pluginName}` as AppTab; + } + return (routeTab || 'chat') as AppTab; - }, [tab]); + }, [pluginName, tab]); const isPluginActive = activeTab.startsWith('plugin:'); useEffect(() => { @@ -93,16 +102,20 @@ export function MobileNav() { return; } - const encodedTab = encodeURIComponent(nextTab); + const isPluginTab = nextTab.startsWith('plugin:'); + const pluginTabName = isPluginTab ? nextTab.replace('plugin:', '') : ''; + const targetTab = isPluginTab ? 'plugins' : nextTab; + const encodedTargetTab = encodeURIComponent(targetTab); + const pluginQuery = isPluginTab ? `?name=${encodeURIComponent(pluginTabName)}` : ''; const decodedSessionId = decodeValue(sessionId); if (decodedSessionId) { - navigate(`/sessions/${encodeURIComponent(decodedSessionId)}/${encodedTab}`); + navigate(`/sessions/${encodeURIComponent(decodedSessionId)}/${encodedTargetTab}${pluginQuery}`); return; } const encodedWorkspaceId = encodeURIComponent(decodeValue(workspaceId)); - navigate(`/workspaces/${encodedWorkspaceId}/${encodedTab}`); + navigate(`/workspaces/${encodedWorkspaceId}/${encodedTargetTab}${pluginQuery}`); }; const selectPlugin = (name: string) => {