feat(web): Add centered empty state messages to pipeline extension dialogs (#1784)

* Initial plan

* feat: add empty state messages in pipeline extension dialogs

Co-authored-by: RockChinQ <45992437+RockChinQ@users.noreply.github.com>

* fix: center empty state messages in dialog content area

Co-authored-by: RockChinQ <45992437+RockChinQ@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: RockChinQ <45992437+RockChinQ@users.noreply.github.com>
This commit is contained in:
Copilot
2025-11-16 23:37:40 +08:00
committed by GitHub
parent abb2f7ae05
commit c27ccb8475
5 changed files with 104 additions and 78 deletions

View File

@@ -331,48 +331,56 @@ export default function PipelineExtension({
<DialogTitle>{t('pipelines.extensions.selectPlugins')}</DialogTitle> <DialogTitle>{t('pipelines.extensions.selectPlugins')}</DialogTitle>
</DialogHeader> </DialogHeader>
<div className="flex-1 overflow-y-auto space-y-2 pr-2"> <div className="flex-1 overflow-y-auto space-y-2 pr-2">
{allPlugins.map((plugin) => { {allPlugins.length === 0 ? (
const pluginId = getPluginId(plugin); <div className="flex h-full items-center justify-center">
const metadata = plugin.manifest.manifest.metadata; <p className="text-sm text-muted-foreground">
const isSelected = tempSelectedPluginIds.includes(pluginId); {t('pipelines.extensions.noPluginsInstalled')}
return ( </p>
<div </div>
key={pluginId} ) : (
className="flex items-center gap-3 rounded-lg border p-3 hover:bg-accent cursor-pointer" allPlugins.map((plugin) => {
onClick={() => handleTogglePlugin(pluginId)} const pluginId = getPluginId(plugin);
> const metadata = plugin.manifest.manifest.metadata;
<Checkbox checked={isSelected} /> const isSelected = tempSelectedPluginIds.includes(pluginId);
<img return (
src={backendClient.getPluginIconURL( <div
metadata.author || '', key={pluginId}
metadata.name, className="flex items-center gap-3 rounded-lg border p-3 hover:bg-accent cursor-pointer"
onClick={() => handleTogglePlugin(pluginId)}
>
<Checkbox checked={isSelected} />
<img
src={backendClient.getPluginIconURL(
metadata.author || '',
metadata.name,
)}
alt={metadata.name}
className="w-10 h-10 rounded-lg border bg-muted object-cover flex-shrink-0"
/>
<div className="flex-1">
<div className="font-medium">{metadata.name}</div>
<div className="text-sm text-muted-foreground">
{metadata.author} v{metadata.version}
</div>
<div className="flex gap-1 mt-1">
<PluginComponentList
components={plugin.components}
showComponentName={true}
showTitle={false}
useBadge={true}
t={t}
/>
</div>
</div>
{!plugin.enabled && (
<Badge variant="secondary">
{t('pipelines.extensions.disabled')}
</Badge>
)} )}
alt={metadata.name}
className="w-10 h-10 rounded-lg border bg-muted object-cover flex-shrink-0"
/>
<div className="flex-1">
<div className="font-medium">{metadata.name}</div>
<div className="text-sm text-muted-foreground">
{metadata.author} v{metadata.version}
</div>
<div className="flex gap-1 mt-1">
<PluginComponentList
components={plugin.components}
showComponentName={true}
showTitle={false}
useBadge={true}
t={t}
/>
</div>
</div> </div>
{!plugin.enabled && ( );
<Badge variant="secondary"> })
{t('pipelines.extensions.disabled')} )}
</Badge>
)}
</div>
);
})}
</div> </div>
<DialogFooter> <DialogFooter>
<Button <Button
@@ -397,46 +405,56 @@ export default function PipelineExtension({
</DialogTitle> </DialogTitle>
</DialogHeader> </DialogHeader>
<div className="flex-1 overflow-y-auto space-y-2 pr-2"> <div className="flex-1 overflow-y-auto space-y-2 pr-2">
{allMCPServers.map((server) => { {allMCPServers.length === 0 ? (
const isSelected = tempSelectedMCPIds.includes(server.uuid || ''); <div className="flex h-full items-center justify-center">
return ( <p className="text-sm text-muted-foreground">
<div {t('pipelines.extensions.noMCPServersConfigured')}
key={server.uuid} </p>
className="flex items-center gap-3 rounded-lg border p-3 hover:bg-accent cursor-pointer" </div>
onClick={() => handleToggleMCPServer(server.uuid || '')} ) : (
> allMCPServers.map((server) => {
<Checkbox checked={isSelected} /> const isSelected = tempSelectedMCPIds.includes(
<div className="w-10 h-10 rounded-lg border bg-muted flex items-center justify-center flex-shrink-0"> server.uuid || '',
<Server className="h-5 w-5 text-muted-foreground" /> );
</div> return (
<div className="flex-1"> <div
<div className="font-medium">{server.name}</div> key={server.uuid}
<div className="text-sm text-muted-foreground"> className="flex items-center gap-3 rounded-lg border p-3 hover:bg-accent cursor-pointer"
{server.mode} onClick={() => handleToggleMCPServer(server.uuid || '')}
>
<Checkbox checked={isSelected} />
<div className="w-10 h-10 rounded-lg border bg-muted flex items-center justify-center flex-shrink-0">
<Server className="h-5 w-5 text-muted-foreground" />
</div> </div>
{server.runtime_info && <div className="flex-1">
server.runtime_info.status === 'connected' && ( <div className="font-medium">{server.name}</div>
<Badge <div className="text-sm text-muted-foreground">
variant="outline" {server.mode}
className="flex items-center gap-1 mt-1" </div>
> {server.runtime_info &&
<Wrench className="h-3 w-3 text-black dark:text-white" /> server.runtime_info.status === 'connected' && (
<span className="text-xs text-black dark:text-white"> <Badge
{t('pipelines.extensions.toolCount', { variant="outline"
count: server.runtime_info.tool_count || 0, className="flex items-center gap-1 mt-1"
})} >
</span> <Wrench className="h-3 w-3 text-black dark:text-white" />
</Badge> <span className="text-xs text-black dark:text-white">
)} {t('pipelines.extensions.toolCount', {
count: server.runtime_info.tool_count || 0,
})}
</span>
</Badge>
)}
</div>
{!server.enable && (
<Badge variant="secondary">
{t('pipelines.extensions.disabled')}
</Badge>
)}
</div> </div>
{!server.enable && ( );
<Badge variant="secondary"> })
{t('pipelines.extensions.disabled')} )}
</Badge>
)}
</div>
);
})}
</div> </div>
<DialogFooter> <DialogFooter>
<Button variant="outline" onClick={() => setMcpDialogOpen(false)}> <Button variant="outline" onClick={() => setMcpDialogOpen(false)}>

View File

@@ -482,6 +482,8 @@ const enUS = {
addMCPServer: 'Add MCP Server', addMCPServer: 'Add MCP Server',
selectMCPServers: 'Select MCP Servers', selectMCPServers: 'Select MCP Servers',
toolCount: '{{count}} tools', toolCount: '{{count}} tools',
noPluginsInstalled: 'No installed plugins',
noMCPServersConfigured: 'No configured MCP servers',
}, },
debugDialog: { debugDialog: {
title: 'Pipeline Chat', title: 'Pipeline Chat',

View File

@@ -485,6 +485,8 @@ const jaJP = {
addMCPServer: 'MCPサーバーを追加', addMCPServer: 'MCPサーバーを追加',
selectMCPServers: 'MCPサーバーを選択', selectMCPServers: 'MCPサーバーを選択',
toolCount: '{{count}}個のツール', toolCount: '{{count}}個のツール',
noPluginsInstalled: 'インストールされているプラグインがありません',
noMCPServersConfigured: '設定されているMCPサーバーがありません',
}, },
debugDialog: { debugDialog: {
title: 'パイプラインのチャット', title: 'パイプラインのチャット',

View File

@@ -464,6 +464,8 @@ const zhHans = {
addMCPServer: '添加 MCP 服务器', addMCPServer: '添加 MCP 服务器',
selectMCPServers: '选择 MCP 服务器', selectMCPServers: '选择 MCP 服务器',
toolCount: '{{count}} 个工具', toolCount: '{{count}} 个工具',
noPluginsInstalled: '无已安装的插件',
noMCPServersConfigured: '无已配置的 MCP 服务器',
}, },
debugDialog: { debugDialog: {
title: '流水线对话', title: '流水线对话',

View File

@@ -462,6 +462,8 @@ const zhHant = {
addMCPServer: '新增 MCP 伺服器', addMCPServer: '新增 MCP 伺服器',
selectMCPServers: '選擇 MCP 伺服器', selectMCPServers: '選擇 MCP 伺服器',
toolCount: '{{count}} 個工具', toolCount: '{{count}} 個工具',
noPluginsInstalled: '無已安裝的插件',
noMCPServersConfigured: '無已配置的 MCP 伺服器',
}, },
debugDialog: { debugDialog: {
title: '流程線對話', title: '流程線對話',