mirror of
https://github.com/langbot-app/LangBot.git
synced 2025-11-25 03:15:06 +08:00
perf: add component list in plugin detail dialog
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
import { PluginComponent } from '@/app/infra/entities/plugin';
|
||||
import { TFunction } from 'i18next';
|
||||
import { Wrench, AudioWaveform, Hash } from 'lucide-react';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
|
||||
export default function PluginComponentList({
|
||||
components,
|
||||
showComponentName,
|
||||
showTitle,
|
||||
useBadge,
|
||||
t,
|
||||
}: {
|
||||
components: PluginComponent[];
|
||||
showComponentName: boolean;
|
||||
showTitle: boolean;
|
||||
useBadge: boolean;
|
||||
t: TFunction;
|
||||
}) {
|
||||
const componentKindCount: Record<string, number> = {};
|
||||
|
||||
for (const component of components) {
|
||||
const kind = component.manifest.manifest.kind;
|
||||
if (componentKindCount[kind]) {
|
||||
componentKindCount[kind]++;
|
||||
} else {
|
||||
componentKindCount[kind] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
const kindIconMap: Record<string, React.ReactNode> = {
|
||||
Tool: <Wrench className="w-5 h-5" />,
|
||||
EventListener: <AudioWaveform className="w-5 h-5" />,
|
||||
Command: <Hash className="w-5 h-5" />,
|
||||
};
|
||||
|
||||
const componentKindList = Object.keys(componentKindCount);
|
||||
|
||||
return (
|
||||
<>
|
||||
{showTitle && <div>{t('plugins.componentsList')}</div>}
|
||||
{componentKindList.length > 0 && (
|
||||
<>
|
||||
{componentKindList.map((kind) => {
|
||||
return (
|
||||
<>
|
||||
{useBadge && (
|
||||
<Badge variant="outline">
|
||||
{kindIconMap[kind]}
|
||||
{showComponentName &&
|
||||
t('plugins.componentName.' + kind) + ' '}
|
||||
{componentKindCount[kind]}
|
||||
</Badge>
|
||||
)}
|
||||
|
||||
{!useBadge && (
|
||||
<div
|
||||
key={kind}
|
||||
className="flex flex-row items-center justify-start gap-[0.2rem]"
|
||||
>
|
||||
{kindIconMap[kind]}
|
||||
{showComponentName &&
|
||||
t('plugins.componentName.' + kind) + ' '}
|
||||
{componentKindCount[kind]}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
|
||||
{componentKindList.length === 0 && <div>{t('plugins.noComponents')}</div>}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect, forwardRef, useImperativeHandle } from 'react';
|
||||
import { PluginCardVO } from '@/app/home/plugins/plugin-installed/PluginCardVO';
|
||||
import PluginCardComponent from '@/app/home/plugins/plugin-installed/plugin-card/PluginCardComponent';
|
||||
import PluginForm from '@/app/home/plugins/plugin-installed/plugin-form/PluginForm';
|
||||
import { PluginCardVO } from '@/app/home/plugins/components/plugin-installed/PluginCardVO';
|
||||
import PluginCardComponent from '@/app/home/plugins/components/plugin-installed/plugin-card/PluginCardComponent';
|
||||
import PluginForm from '@/app/home/plugins/components/plugin-installed/plugin-form/PluginForm';
|
||||
import styles from '@/app/home/plugins/plugins.module.css';
|
||||
import { httpClient } from '@/app/infra/http/HttpClient';
|
||||
import {
|
||||
@@ -1,21 +1,10 @@
|
||||
import { PluginCardVO } from '@/app/home/plugins/plugin-installed/PluginCardVO';
|
||||
import { PluginCardVO } from '@/app/home/plugins/components/plugin-installed/PluginCardVO';
|
||||
import { useState } from 'react';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TFunction } from 'i18next';
|
||||
import {
|
||||
AudioWaveform,
|
||||
Wrench,
|
||||
Hash,
|
||||
BugIcon,
|
||||
ExternalLink,
|
||||
Ellipsis,
|
||||
Trash,
|
||||
ArrowUp,
|
||||
} from 'lucide-react';
|
||||
import { BugIcon, ExternalLink, Ellipsis, Trash, ArrowUp } from 'lucide-react';
|
||||
import { getCloudServiceClientSync } from '@/app/infra/http';
|
||||
import { httpClient } from '@/app/infra/http/HttpClient';
|
||||
import { PluginComponent } from '@/app/infra/entities/plugin';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
DropdownMenu,
|
||||
@@ -23,49 +12,7 @@ import {
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
|
||||
function getComponentList(components: PluginComponent[], t: TFunction) {
|
||||
const componentKindCount: Record<string, number> = {};
|
||||
|
||||
for (const component of components) {
|
||||
const kind = component.manifest.manifest.kind;
|
||||
if (componentKindCount[kind]) {
|
||||
componentKindCount[kind]++;
|
||||
} else {
|
||||
componentKindCount[kind] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
const kindIconMap: Record<string, React.ReactNode> = {
|
||||
Tool: <Wrench className="w-5 h-5" />,
|
||||
EventListener: <AudioWaveform className="w-5 h-5" />,
|
||||
Command: <Hash className="w-5 h-5" />,
|
||||
};
|
||||
|
||||
const componentKindList = Object.keys(componentKindCount);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>{t('plugins.componentsList')}</div>
|
||||
{componentKindList.length > 0 && (
|
||||
<>
|
||||
{componentKindList.map((kind) => {
|
||||
return (
|
||||
<div
|
||||
key={kind}
|
||||
className="flex flex-row items-center justify-start gap-[0.4rem]"
|
||||
>
|
||||
{kindIconMap[kind]} {componentKindCount[kind]}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
|
||||
{componentKindList.length === 0 && <div>{t('plugins.noComponents')}</div>}
|
||||
</>
|
||||
);
|
||||
}
|
||||
import PluginComponentList from '@/app/home/plugins/components/plugin-installed/PluginComponentList';
|
||||
|
||||
export default function PluginCardComponent({
|
||||
cardVO,
|
||||
@@ -180,7 +127,13 @@ export default function PluginCardComponent({
|
||||
</div>
|
||||
|
||||
<div className="w-full flex flex-row items-start justify-start gap-[0.6rem]">
|
||||
{getComponentList(cardVO.components, t)}
|
||||
<PluginComponentList
|
||||
components={cardVO.components}
|
||||
showComponentName={false}
|
||||
showTitle={true}
|
||||
useBadge={false}
|
||||
t={t}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Button } from '@/components/ui/button';
|
||||
import { toast } from 'sonner';
|
||||
import { extractI18nObject } from '@/i18n/I18nProvider';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import PluginComponentList from '@/app/home/plugins/components/plugin-installed/PluginComponentList';
|
||||
|
||||
export default function PluginForm({
|
||||
pluginAuthor,
|
||||
@@ -78,6 +79,17 @@ export default function PluginForm({
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mb-4 flex flex-row items-center justify-start gap-[0.4rem]">
|
||||
<PluginComponentList
|
||||
components={pluginInfo.components}
|
||||
showComponentName={true}
|
||||
showTitle={false}
|
||||
useBadge={true}
|
||||
t={t}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{pluginInfo.manifest.manifest.spec.config.length > 0 && (
|
||||
<DynamicFormComponent
|
||||
itemConfigList={pluginInfo.manifest.manifest.spec.config}
|
||||
@@ -1,9 +1,8 @@
|
||||
'use client';
|
||||
import PluginInstalledComponent, {
|
||||
PluginInstalledComponentRef,
|
||||
} from '@/app/home/plugins/plugin-installed/PluginInstalledComponent';
|
||||
import MarketPage from '@/app/home/plugins/plugin-market/PluginMarketComponent';
|
||||
|
||||
} from '@/app/home/plugins/components/plugin-installed/PluginInstalledComponent';
|
||||
import MarketPage from '@/app/home/plugins/components/plugin-market/PluginMarketComponent';
|
||||
import MCPServerComponent from '@/app/home/plugins/mcp-server/MCPServerComponent';
|
||||
import MCPFormDialog from '@/app/home/plugins/mcp-server/mcp-form/MCPFormDialog';
|
||||
import MCPDeleteConfirmDialog from '@/app/home/plugins/mcp-server/mcp-form/MCPDeleteConfirmDialog';
|
||||
|
||||
@@ -206,9 +206,11 @@ const enUS = {
|
||||
deleteConfirm: 'Delete Confirmation',
|
||||
deleteSuccess: 'Delete successful',
|
||||
modifyFailed: 'Modify failed: ',
|
||||
eventCount: 'Events: {{count}}',
|
||||
toolCount: 'Tools: {{count}}',
|
||||
starCount: 'Stars: {{count}}',
|
||||
componentName: {
|
||||
Tool: 'Tool',
|
||||
EventListener: 'Event Listener',
|
||||
Command: 'Command',
|
||||
},
|
||||
uploadLocal: 'Upload Local',
|
||||
debugging: 'Debugging',
|
||||
uploadLocalPlugin: 'Upload Local Plugin',
|
||||
|
||||
@@ -207,9 +207,11 @@ const jaJP = {
|
||||
deleteConfirm: '削除の確認',
|
||||
deleteSuccess: '削除に成功しました',
|
||||
modifyFailed: '変更に失敗しました:',
|
||||
eventCount: 'イベント:{{count}}',
|
||||
toolCount: 'ツール:{{count}}',
|
||||
starCount: 'スター:{{count}}',
|
||||
componentName: {
|
||||
Tool: 'ツール',
|
||||
EventListener: 'イベント監視器',
|
||||
Command: 'コマンド',
|
||||
},
|
||||
uploadLocal: 'ローカルアップロード',
|
||||
debugging: 'デバッグ中',
|
||||
uploadLocalPlugin: 'ローカルプラグインのアップロード',
|
||||
|
||||
@@ -199,9 +199,11 @@ const zhHans = {
|
||||
deleteConfirm: '删除确认',
|
||||
deleteSuccess: '删除成功',
|
||||
modifyFailed: '修改失败:',
|
||||
eventCount: '事件:{{count}}',
|
||||
toolCount: '工具:{{count}}',
|
||||
starCount: '星标:{{count}}',
|
||||
componentName: {
|
||||
Tool: '工具',
|
||||
EventListener: '事件监听器',
|
||||
Command: '命令',
|
||||
},
|
||||
uploadLocal: '本地上传',
|
||||
debugging: '调试中',
|
||||
uploadLocalPlugin: '上传本地插件',
|
||||
|
||||
@@ -197,9 +197,11 @@ const zhHant = {
|
||||
close: '關閉',
|
||||
deleteConfirm: '刪除確認',
|
||||
modifyFailed: '修改失敗:',
|
||||
eventCount: '事件:{{count}}',
|
||||
toolCount: '工具:{{count}}',
|
||||
starCount: '星標:{{count}}',
|
||||
componentName: {
|
||||
Tool: '工具',
|
||||
EventListener: '事件監聽器',
|
||||
Command: '命令',
|
||||
},
|
||||
uploadLocal: '本地上傳',
|
||||
debugging: '調試中',
|
||||
uploadLocalPlugin: '上傳本地插件',
|
||||
|
||||
Reference in New Issue
Block a user