mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-05-16 01:12:46 +00:00
refactor: plugin routes
This commit is contained in:
@@ -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 (
|
||||
<div className="h-full p-6">
|
||||
<div className="rounded-xl border border-border/70 bg-card/30 p-5">
|
||||
<h2 className="text-lg font-semibold">{decodedTab} view</h2>
|
||||
<h2 className="text-lg font-semibold">{tabLabel} view</h2>
|
||||
<p className="mt-2 text-sm text-muted-foreground">
|
||||
Workspace:{' '}
|
||||
<span className="font-medium text-foreground">
|
||||
|
||||
@@ -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<MainHeadingRouteParams>();
|
||||
const location = useLocation();
|
||||
|
||||
const scrollRef = useRef<HTMLDivElement>(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<AppTab>(() => {
|
||||
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 (
|
||||
|
||||
@@ -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<MobileNavRouteParams>();
|
||||
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<AppTab>(() => {
|
||||
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) => {
|
||||
|
||||
Reference in New Issue
Block a user