mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-07-17 18:06:06 +00:00
feat: add dialog
This commit is contained in:
@@ -34,7 +34,7 @@ import {
|
|||||||
DialogFooter,
|
DialogFooter,
|
||||||
} from '@/components/ui/dialog';
|
} from '@/components/ui/dialog';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { useState, useRef, useCallback, useEffect } from 'react';
|
import React, { useState, useRef, useCallback, useEffect, use } from 'react';
|
||||||
import { httpClient } from '@/app/infra/http/HttpClient';
|
import { httpClient } from '@/app/infra/http/HttpClient';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@@ -60,11 +60,16 @@ export default function PluginConfigPage() {
|
|||||||
// const [mcpModalOpen, setMcpModalOpen] = useState(false);
|
// const [mcpModalOpen, setMcpModalOpen] = useState(false);
|
||||||
const [mcpMarketInstallModalOpen, setMcpMarketInstallModalOpen] =
|
const [mcpMarketInstallModalOpen, setMcpMarketInstallModalOpen] =
|
||||||
useState(false);
|
useState(false);
|
||||||
|
const [mcpSSEInstallModalOpen, setMcpSSEInstallModalOpen] = useState(false);
|
||||||
|
const [mcpDescription,setMcpDescription] = useState('');
|
||||||
const [pluginInstallStatus, setPluginInstallStatus] =
|
const [pluginInstallStatus, setPluginInstallStatus] =
|
||||||
useState<PluginInstallStatus>(PluginInstallStatus.WAIT_INPUT);
|
useState<PluginInstallStatus>(PluginInstallStatus.WAIT_INPUT);
|
||||||
const [mcpInstallStatus, setMcpInstallStatus] = useState<PluginInstallStatus>(
|
const [mcpInstallStatus, setMcpInstallStatus] = useState<PluginInstallStatus>(
|
||||||
PluginInstallStatus.WAIT_INPUT,
|
PluginInstallStatus.WAIT_INPUT,
|
||||||
);
|
);
|
||||||
|
const [mcpSSEHeaders,setMcpSSEHeaders] = useState('')
|
||||||
|
const [mcpName,setMcpName] = useState('')
|
||||||
|
const [mcpTimeout,setMcpTimeout] = useState('')
|
||||||
const [installError, setInstallError] = useState<string | null>(null);
|
const [installError, setInstallError] = useState<string | null>(null);
|
||||||
const [mcpInstallError, setMcpInstallError] = useState<string | null>(null);
|
const [mcpInstallError, setMcpInstallError] = useState<string | null>(null);
|
||||||
const [githubURL, setGithubURL] = useState('');
|
const [githubURL, setGithubURL] = useState('');
|
||||||
@@ -119,6 +124,8 @@ export default function PluginConfigPage() {
|
|||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
const [mcpGithubURL, setMcpGithubURL] = useState('');
|
const [mcpGithubURL, setMcpGithubURL] = useState('');
|
||||||
|
const [mcpSSEURL, setMcpSSEURL] = useState('');
|
||||||
|
const [mcpInstallConfig, setMcpInstallConfig] = useState<Record<string, any> | null>(null);
|
||||||
const pluginInstalledRef = useRef<PluginInstalledComponentRef>(null);
|
const pluginInstalledRef = useRef<PluginInstalledComponentRef>(null);
|
||||||
const mcpComponentRef = useRef<MCPComponentRef>(null);
|
const mcpComponentRef = useRef<MCPComponentRef>(null);
|
||||||
|
|
||||||
@@ -309,10 +316,12 @@ export default function PluginConfigPage() {
|
|||||||
return renderPluginConnectionErrorState();
|
return renderPluginConnectionErrorState();
|
||||||
}
|
}
|
||||||
|
|
||||||
function installMcpServer(url: string) {
|
function installMcpServer(url: string, config?: Record<string, any>) {
|
||||||
setMcpInstallStatus(PluginInstallStatus.INSTALLING);
|
setMcpInstallStatus(PluginInstallStatus.INSTALLING);
|
||||||
httpClient
|
// NOTE: backend currently only accepts url. If backend accepts config in future,
|
||||||
.installMCPServerFromGithub(url)
|
// replace this call with: httpClient.installMCPServerFromGithub(url, config)
|
||||||
|
console.log('installing mcp server with config:', config);
|
||||||
|
httpClient.installMCPServerFromGithub(url)
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
const taskId = resp.task_id;
|
const taskId = resp.task_id;
|
||||||
|
|
||||||
@@ -374,9 +383,9 @@ export default function PluginConfigPage() {
|
|||||||
{t('plugins.marketplace')}
|
{t('plugins.marketplace')}
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
)}
|
)}
|
||||||
<TabsTrigger value="mcp" className="px-6 py-4 cursor-pointer">
|
<TabsTrigger value="mcp-market" className="px-6 py-4 cursor-pointer">
|
||||||
MCP
|
{t('mcp.marketplace')}
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
||||||
<div className="flex flex-row justify-end items-center">
|
<div className="flex flex-row justify-end items-center">
|
||||||
@@ -393,24 +402,52 @@ export default function PluginConfigPage() {
|
|||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button variant="default" className="px-6 py-4 cursor-pointer">
|
<Button variant="default" className="px-6 py-4 cursor-pointer">
|
||||||
<PlusIcon className="w-4 h-4" />
|
<PlusIcon className="w-4 h-4" />
|
||||||
{t('plugins.install')}
|
{activeTab === 'mcp-market' ? t('mcp.add') : t('plugins.install')}
|
||||||
<ChevronDownIcon className="ml-2 w-4 h-4" />
|
<ChevronDownIcon className="ml-2 w-4 h-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuItem onClick={handleFileSelect}>
|
{activeTab === 'mcp-market' ? (
|
||||||
<UploadIcon className="w-4 h-4" />
|
<>
|
||||||
{t('plugins.uploadLocal')}
|
{/* <DropdownMenuItem
|
||||||
</DropdownMenuItem>
|
onClick={() => {
|
||||||
{systemInfo.enable_marketplace && (
|
setActiveTab('mcp-market');
|
||||||
<DropdownMenuItem
|
setMcpMarketInstallModalOpen(true);
|
||||||
onClick={() => {
|
setMcpInstallStatus(PluginInstallStatus.WAIT_INPUT);
|
||||||
setActiveTab('market');
|
setMcpInstallError(null);
|
||||||
}}
|
setMcpGithubURL('');
|
||||||
>
|
}}
|
||||||
<StoreIcon className="w-4 h-4" />
|
>
|
||||||
{t('plugins.marketplace')}
|
<PlusIcon className="w-4 h-4" />
|
||||||
</DropdownMenuItem>
|
{t('mcp.installFromGithub')}
|
||||||
|
</DropdownMenuItem> */}
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => {
|
||||||
|
setActiveTab('mcp-market');
|
||||||
|
// setMcpMarketInstallModalOpen(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PlusIcon className="w-4 h-4" />
|
||||||
|
{t('mcp.createServer')}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<DropdownMenuItem onClick={handleFileSelect}>
|
||||||
|
<UploadIcon className="w-4 h-4" />
|
||||||
|
{t('plugins.uploadLocal')}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
{systemInfo.enable_marketplace && (
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => {
|
||||||
|
setActiveTab('market');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<StoreIcon className="w-4 h-4" />
|
||||||
|
{t('plugins.marketplace')}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
@@ -530,30 +567,91 @@ export default function PluginConfigPage() {
|
|||||||
pluginInstalledRef.current?.refreshPluginList();
|
pluginInstalledRef.current?.refreshPluginList();
|
||||||
}}
|
}}
|
||||||
/> */}
|
/> */}
|
||||||
|
|
||||||
{/* MCP Server 安装对话框 */}
|
{/* 通过sse安装MCP服务器 */}
|
||||||
{/* <Dialog
|
<Dialog
|
||||||
open={mcpMarketInstallModalOpen}
|
open={mcpSSEInstallModalOpen}
|
||||||
onOpenChange={setMcpMarketInstallModalOpen}
|
onOpenChange={setMcpSSEInstallModalOpen}
|
||||||
>
|
>
|
||||||
<DialogContent className="w-[500px] p-6">
|
<DialogContent className="w-[520px] p-6 bg-white dark:bg-[#1a1a1e]">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="flex items-center gap-4">
|
<DialogTitle className="flex items-center gap-4">
|
||||||
<GithubIcon className="size-6" />
|
<Download className="size-6" />
|
||||||
<span>{t('mcp.installFromGithub')}</span>
|
<span>{t('mcp.installFromSSE')}</span>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{mcpInstallStatus === PluginInstallStatus.WAIT_INPUT && (
|
|
||||||
<div className="mt-4">
|
<div>
|
||||||
<p className="mb-2">{t('mcp.onlySupportGithub')}</p>
|
<div>
|
||||||
|
<label className='text-sm text-muted-foreground block mb-1'>
|
||||||
|
{t('mcp.name')}
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
placeholder='mcp.nameExplained'
|
||||||
|
value={mcpName}
|
||||||
|
onChange={(e) => setMcpName(e.target.value)}
|
||||||
|
className='mb-1'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<label className="text-sm text-muted-foreground block mb-1">
|
||||||
|
{t('mcp.description')}
|
||||||
|
</label>
|
||||||
<Input
|
<Input
|
||||||
placeholder={t('mcp.enterGithubLink')}
|
placeholder={t('mcp.descriptionExplain')}
|
||||||
value={mcpGithubURL}
|
value={mcpDescription}
|
||||||
onChange={(e) => setMcpGithubURL(e.target.value)}
|
onChange={(e) => setMcpDescription(e.target.value)}
|
||||||
className="mb-4"
|
className='mb-1'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
|
|
||||||
|
{/* form fields */}
|
||||||
|
<div className="mt-4 space-y-3">
|
||||||
|
<div>
|
||||||
|
<label className="text-sm text-muted-foreground block mb-1">
|
||||||
|
{t('mcp.sseUrl')}
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
placeholder={t('mcp.enterSSELink')}
|
||||||
|
value={mcpSSEURL}
|
||||||
|
onChange={(e) => setMcpSSEURL(e.target.value)}
|
||||||
|
className="mb-1"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-4">
|
||||||
|
<div>
|
||||||
|
<label className="text-sm text-muted-foreground block mb-1">
|
||||||
|
{t('mcp.headers')}
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
placeholder={t('mcp.headers')}
|
||||||
|
value={mcpSSEHeaders}
|
||||||
|
onChange={(e) => setMcpSSEHeaders(e.target.value)}
|
||||||
|
className="mb-1"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='mt-4'>
|
||||||
|
<div>
|
||||||
|
<label className='text-sm text-muted-foreground block mb-1'>
|
||||||
|
{t('mcp.timeout')}
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
placeholder={t('mcp.enterTimeout')}
|
||||||
|
value={mcpTimeout || '60'}
|
||||||
|
onChange={(e) => setMcpTimeout(e.target.value)}
|
||||||
|
className="mb-1"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{mcpInstallStatus === PluginInstallStatus.INSTALLING && (
|
{mcpInstallStatus === PluginInstallStatus.INSTALLING && (
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<p className="mb-2">{t('mcp.installing')}</p>
|
<p className="mb-2">{t('mcp.installing')}</p>
|
||||||
@@ -565,31 +663,218 @@ export default function PluginConfigPage() {
|
|||||||
<p className="mb-2 text-red-500">{mcpInstallError}</p>
|
<p className="mb-2 text-red-500">{mcpInstallError}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
{mcpInstallStatus === PluginInstallStatus.WAIT_INPUT && (
|
{(mcpInstallStatus === PluginInstallStatus.WAIT_INPUT ||
|
||||||
|
mcpInstallStatus === PluginInstallStatus.ERROR) && (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => setMcpMarketInstallModalOpen(false)}
|
onClick={() => {
|
||||||
|
setMcpSSEInstallModalOpen(false)
|
||||||
|
setMcpInstallStatus(PluginInstallStatus.WAIT_INPUT);
|
||||||
|
setMcpInstallError(null);
|
||||||
|
setMcpInstallConfig(null);
|
||||||
|
setMcpSSEURL('')
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{t('common.cancel')}
|
{t('common.cancel')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={handleMcpModalConfirm}>
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
// basic validation
|
||||||
|
if (!mcpSSEURL) {
|
||||||
|
toast.error(t('mcp.urlRequired'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!mcpName) (
|
||||||
|
toast.error(t(''))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const configToSend = {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
handleMcpModalConfirm();
|
||||||
|
// call installer (for now installMcpServer will log config and call backend with url only)
|
||||||
|
installMcpServer(mcpGithubURL, configToSend);
|
||||||
|
}}
|
||||||
|
>
|
||||||
{t('common.confirm')}
|
{t('common.confirm')}
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{mcpInstallStatus === PluginInstallStatus.ERROR && (
|
</DialogFooter>
|
||||||
<Button
|
</DialogContent>
|
||||||
variant="default"
|
</Dialog>
|
||||||
onClick={() => setMcpMarketInstallModalOpen(false)}
|
|
||||||
>
|
{/* MCP Server 从github安装对话框(表单) */}
|
||||||
{t('common.close')}
|
<Dialog
|
||||||
</Button>
|
open={mcpMarketInstallModalOpen}
|
||||||
|
onOpenChange={setMcpMarketInstallModalOpen}
|
||||||
|
>
|
||||||
|
<DialogContent className="w-[520px] p-6 bg-white dark:bg-[#1a1a1e]">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="flex items-center gap-4">
|
||||||
|
<Download className="size-6" />
|
||||||
|
<span>{t('mcp.installFromGithub')}</span>
|
||||||
|
</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
{/* form fields */}
|
||||||
|
<div className="mt-4 space-y-3">
|
||||||
|
<div>
|
||||||
|
<label className="text-sm text-muted-foreground block mb-1">
|
||||||
|
{t('mcp.githubUrl')}
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
placeholder={t('mcp.enterGithubLink')}
|
||||||
|
value={mcpGithubURL}
|
||||||
|
onChange={(e) => setMcpGithubURL(e.target.value)}
|
||||||
|
className="mb-1"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="text-sm text-muted-foreground block mb-1">
|
||||||
|
{t('mcp.displayName', 'Display Name')}
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
placeholder={t('mcp.displayNamePlaceholder', 'My MCP Server')}
|
||||||
|
value={(mcpInstallConfig as any)?.displayName || ''}
|
||||||
|
onChange={(e) =>
|
||||||
|
setMcpInstallConfig((c) => ({ ...(c || {}), displayName: e.target.value }))
|
||||||
|
}
|
||||||
|
className="mb-1"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<div className="flex-1">
|
||||||
|
<label className="text-sm text-muted-foreground block mb-1">
|
||||||
|
{t('mcp.port', 'Port')}
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
placeholder="8080"
|
||||||
|
value={(mcpInstallConfig as any)?.port || ''}
|
||||||
|
onChange={(e) =>
|
||||||
|
setMcpInstallConfig((c) => ({ ...(c || {}), port: e.target.value }))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<label className="text-sm text-muted-foreground block mb-1">
|
||||||
|
{t('mcp.env', 'Environment')}
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
placeholder="production"
|
||||||
|
value={(mcpInstallConfig as any)?.env || ''}
|
||||||
|
onChange={(e) =>
|
||||||
|
setMcpInstallConfig((c) => ({ ...(c || {}), env: e.target.value }))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="text-sm text-muted-foreground block mb-1">
|
||||||
|
{t('mcp.adminToken', 'Admin Token')}
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
placeholder={t('mcp.adminTokenPlaceholder', 'secret-token')}
|
||||||
|
value={(mcpInstallConfig as any)?.adminToken || ''}
|
||||||
|
onChange={(e) =>
|
||||||
|
setMcpInstallConfig((c) => ({ ...(c || {}), adminToken: e.target.value }))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="text-sm text-muted-foreground block mb-1">
|
||||||
|
{t('mcp.extraConfig', 'Extra JSON Config')}
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
placeholder='{"key":"value"}'
|
||||||
|
value={(mcpInstallConfig as any)?.extraConfig || ''}
|
||||||
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
|
setMcpInstallConfig((c) => ({ ...(c || {}), extraConfig: e.target.value }))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<p className="text-xs text-muted-foreground mt-1">
|
||||||
|
{t('mcp.extraConfigHint', 'Optional JSON string for advanced config')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{mcpInstallStatus === PluginInstallStatus.INSTALLING && (
|
||||||
|
<div className="mt-4">
|
||||||
|
<p className="mb-2">{t('mcp.installing')}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{mcpInstallStatus === PluginInstallStatus.ERROR && (
|
||||||
|
<div className="mt-4">
|
||||||
|
<p className="mb-2">{t('mcp.installFailed')}</p>
|
||||||
|
<p className="mb-2 text-red-500">{mcpInstallError}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<DialogFooter>
|
||||||
|
{(mcpInstallStatus === PluginInstallStatus.WAIT_INPUT ||
|
||||||
|
mcpInstallStatus === PluginInstallStatus.ERROR) && (
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => {
|
||||||
|
setMcpMarketInstallModalOpen(false);
|
||||||
|
setMcpInstallStatus(PluginInstallStatus.WAIT_INPUT);
|
||||||
|
setMcpInstallError(null);
|
||||||
|
setMcpInstallConfig(null);
|
||||||
|
setMcpGithubURL('');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('common.cancel')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
// basic validation
|
||||||
|
if (!mcpGithubURL) {
|
||||||
|
toast.error(t('mcp.urlRequired'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// try parse extraConfig JSON
|
||||||
|
let parsedExtra: any = undefined;
|
||||||
|
try {
|
||||||
|
if (mcpInstallConfig?.extraConfig) {
|
||||||
|
parsedExtra = JSON.parse(mcpInstallConfig.extraConfig);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
toast.error(t('mcp.extraConfigInvalid'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const configToSend = {
|
||||||
|
displayName: mcpInstallConfig?.displayName,
|
||||||
|
port: mcpInstallConfig?.port,
|
||||||
|
env: mcpInstallConfig?.env,
|
||||||
|
adminToken: mcpInstallConfig?.adminToken,
|
||||||
|
extraConfig: parsedExtra,
|
||||||
|
};
|
||||||
|
|
||||||
|
handleMcpModalConfirm();
|
||||||
|
// call installer (for now installMcpServer will log config and call backend with url only)
|
||||||
|
installMcpServer(mcpGithubURL, configToSend);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('common.confirm')}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog> */}
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -333,6 +333,8 @@ const zhHans = {
|
|||||||
installFromGithub: '从Github安装MCP服务器',
|
installFromGithub: '从Github安装MCP服务器',
|
||||||
onlySupportGithub: '目前仅支持从Github安装MCP服务器',
|
onlySupportGithub: '目前仅支持从Github安装MCP服务器',
|
||||||
enterGithubLink: '输入Github仓库链接',
|
enterGithubLink: '输入Github仓库链接',
|
||||||
|
add: '添加',
|
||||||
|
name:'名称',
|
||||||
},
|
},
|
||||||
pipelines: {
|
pipelines: {
|
||||||
title: '流水线',
|
title: '流水线',
|
||||||
|
|||||||
Reference in New Issue
Block a user