mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-03-16 19:37:24 +00:00
feat: new plugin system
This commit is contained in:
@@ -1,8 +1,17 @@
|
||||
import { MessageSquare, Terminal, Folder, GitBranch, ClipboardCheck, type LucideIcon } from 'lucide-react';
|
||||
import {
|
||||
MessageSquare,
|
||||
Terminal,
|
||||
Folder,
|
||||
GitBranch,
|
||||
ClipboardCheck,
|
||||
type LucideIcon,
|
||||
} from 'lucide-react';
|
||||
import Tooltip from '../../../Tooltip';
|
||||
import type { AppTab } from '../../../../types/app';
|
||||
import type { Dispatch, SetStateAction } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { usePlugins } from '../../../../contexts/PluginsContext';
|
||||
import PluginIcon from '../../../plugins/PluginIcon';
|
||||
|
||||
type MainContentTabSwitcherProps = {
|
||||
activeTab: AppTab;
|
||||
@@ -10,20 +19,32 @@ type MainContentTabSwitcherProps = {
|
||||
shouldShowTasksTab: boolean;
|
||||
};
|
||||
|
||||
type TabDefinition = {
|
||||
type BuiltInTab = {
|
||||
kind: 'builtin';
|
||||
id: AppTab;
|
||||
labelKey: string;
|
||||
icon: LucideIcon;
|
||||
};
|
||||
|
||||
const BASE_TABS: TabDefinition[] = [
|
||||
{ id: 'chat', labelKey: 'tabs.chat', icon: MessageSquare },
|
||||
{ id: 'shell', labelKey: 'tabs.shell', icon: Terminal },
|
||||
{ id: 'files', labelKey: 'tabs.files', icon: Folder },
|
||||
{ id: 'git', labelKey: 'tabs.git', icon: GitBranch },
|
||||
type PluginTab = {
|
||||
kind: 'plugin';
|
||||
id: AppTab;
|
||||
label: string;
|
||||
pluginName: string;
|
||||
iconFile: string;
|
||||
};
|
||||
|
||||
type TabDefinition = BuiltInTab | PluginTab;
|
||||
|
||||
const BASE_TABS: BuiltInTab[] = [
|
||||
{ kind: 'builtin', id: 'chat', labelKey: 'tabs.chat', icon: MessageSquare },
|
||||
{ kind: 'builtin', id: 'shell', labelKey: 'tabs.shell', icon: Terminal },
|
||||
{ kind: 'builtin', id: 'files', labelKey: 'tabs.files', icon: Folder },
|
||||
{ kind: 'builtin', id: 'git', labelKey: 'tabs.git', icon: GitBranch },
|
||||
];
|
||||
|
||||
const TASKS_TAB: TabDefinition = {
|
||||
const TASKS_TAB: BuiltInTab = {
|
||||
kind: 'builtin',
|
||||
id: 'tasks',
|
||||
labelKey: 'tabs.tasks',
|
||||
icon: ClipboardCheck,
|
||||
@@ -35,17 +56,30 @@ export default function MainContentTabSwitcher({
|
||||
shouldShowTasksTab,
|
||||
}: MainContentTabSwitcherProps) {
|
||||
const { t } = useTranslation();
|
||||
const { plugins } = usePlugins();
|
||||
|
||||
const tabs = shouldShowTasksTab ? [...BASE_TABS, TASKS_TAB] : BASE_TABS;
|
||||
const builtInTabs: BuiltInTab[] = shouldShowTasksTab ? [...BASE_TABS, TASKS_TAB] : BASE_TABS;
|
||||
|
||||
const pluginTabs: PluginTab[] = plugins
|
||||
.filter((p) => p.enabled)
|
||||
.map((p) => ({
|
||||
kind: 'plugin',
|
||||
id: `plugin:${p.name}` as AppTab,
|
||||
label: p.displayName,
|
||||
pluginName: p.name,
|
||||
iconFile: p.icon,
|
||||
}));
|
||||
|
||||
const tabs: TabDefinition[] = [...builtInTabs, ...pluginTabs];
|
||||
|
||||
return (
|
||||
<div className="inline-flex items-center bg-muted/60 rounded-lg p-[3px] gap-[2px]">
|
||||
{tabs.map((tab) => {
|
||||
const Icon = tab.icon;
|
||||
const isActive = tab.id === activeTab;
|
||||
const displayLabel = tab.kind === 'builtin' ? t(tab.labelKey) : tab.label;
|
||||
|
||||
return (
|
||||
<Tooltip key={tab.id} content={t(tab.labelKey)} position="bottom">
|
||||
<Tooltip key={tab.id} content={displayLabel} position="bottom">
|
||||
<button
|
||||
onClick={() => setActiveTab(tab.id)}
|
||||
className={`relative flex items-center gap-1.5 px-2.5 py-[5px] text-sm font-medium rounded-md transition-all duration-150 ${
|
||||
@@ -54,8 +88,16 @@ export default function MainContentTabSwitcher({
|
||||
: 'text-muted-foreground hover:text-foreground'
|
||||
}`}
|
||||
>
|
||||
<Icon className="w-3.5 h-3.5" strokeWidth={isActive ? 2.2 : 1.8} />
|
||||
<span className="hidden lg:inline">{t(tab.labelKey)}</span>
|
||||
{tab.kind === 'builtin' ? (
|
||||
<tab.icon className="w-3.5 h-3.5" strokeWidth={isActive ? 2.2 : 1.8} />
|
||||
) : (
|
||||
<PluginIcon
|
||||
pluginName={tab.pluginName}
|
||||
iconFile={tab.iconFile}
|
||||
className="w-3.5 h-3.5 flex items-center justify-center [&>svg]:w-full [&>svg]:h-full"
|
||||
/>
|
||||
)}
|
||||
<span className="hidden lg:inline">{displayLabel}</span>
|
||||
</button>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import SessionProviderLogo from '../../../llm-logo-provider/SessionProviderLogo';
|
||||
import type { AppTab, Project, ProjectSession } from '../../../../types/app';
|
||||
import { usePlugins } from '../../../../contexts/PluginsContext';
|
||||
|
||||
type MainContentTitleProps = {
|
||||
activeTab: AppTab;
|
||||
@@ -9,7 +10,11 @@ type MainContentTitleProps = {
|
||||
shouldShowTasksTab: boolean;
|
||||
};
|
||||
|
||||
function getTabTitle(activeTab: AppTab, shouldShowTasksTab: boolean, t: (key: string) => string) {
|
||||
function getTabTitle(activeTab: AppTab, shouldShowTasksTab: boolean, t: (key: string) => string, pluginDisplayName?: string) {
|
||||
if (activeTab.startsWith('plugin:') && pluginDisplayName) {
|
||||
return pluginDisplayName;
|
||||
}
|
||||
|
||||
if (activeTab === 'files') {
|
||||
return t('mainContent.projectFiles');
|
||||
}
|
||||
@@ -40,6 +45,11 @@ export default function MainContentTitle({
|
||||
shouldShowTasksTab,
|
||||
}: MainContentTitleProps) {
|
||||
const { t } = useTranslation();
|
||||
const { plugins } = usePlugins();
|
||||
|
||||
const pluginDisplayName = activeTab.startsWith('plugin:')
|
||||
? plugins.find((p) => p.name === activeTab.replace('plugin:', ''))?.displayName
|
||||
: undefined;
|
||||
|
||||
const showSessionIcon = activeTab === 'chat' && Boolean(selectedSession);
|
||||
const showChatNewSession = activeTab === 'chat' && !selectedSession;
|
||||
@@ -68,7 +78,7 @@ export default function MainContentTitle({
|
||||
) : (
|
||||
<div className="min-w-0">
|
||||
<h2 className="text-sm font-semibold text-foreground leading-tight">
|
||||
{getTabTitle(activeTab, shouldShowTasksTab, t)}
|
||||
{getTabTitle(activeTab, shouldShowTasksTab, t, pluginDisplayName)}
|
||||
</h2>
|
||||
<div className="text-[11px] text-muted-foreground truncate leading-tight">{selectedProject.displayName}</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user