perf: frontend

This commit is contained in:
Junyan Qin
2025-08-16 23:23:24 +08:00
parent 28d4b1dd61
commit 0ea7609ff1
8 changed files with 88 additions and 65 deletions
+29 -22
View File
@@ -3,6 +3,7 @@ from __future__ import annotations
import typing import typing
from typing import Any from typing import Any
import base64 import base64
import traceback
import sqlalchemy import sqlalchemy
@@ -48,33 +49,39 @@ class RuntimeConnectionHandler(handler.Handler):
install_source = data['install_source'] install_source = data['install_source']
install_info = data['install_info'] install_info = data['install_info']
result = await self.ap.persistence_mgr.execute_async( try:
sqlalchemy.select(persistence_plugin.PluginSetting) result = await self.ap.persistence_mgr.execute_async(
.where(persistence_plugin.PluginSetting.plugin_author == plugin_author) sqlalchemy.select(persistence_plugin.PluginSetting)
.where(persistence_plugin.PluginSetting.plugin_name == plugin_name)
)
if result.first() is not None:
# delete plugin setting
await self.ap.persistence_mgr.execute_async(
sqlalchemy.delete(persistence_plugin.PluginSetting)
.where(persistence_plugin.PluginSetting.plugin_author == plugin_author) .where(persistence_plugin.PluginSetting.plugin_author == plugin_author)
.where(persistence_plugin.PluginSetting.plugin_name == plugin_name) .where(persistence_plugin.PluginSetting.plugin_name == plugin_name)
) )
# create plugin setting if result.first() is not None:
await self.ap.persistence_mgr.execute_async( # delete plugin setting
sqlalchemy.insert(persistence_plugin.PluginSetting).values( await self.ap.persistence_mgr.execute_async(
plugin_author=plugin_author, sqlalchemy.delete(persistence_plugin.PluginSetting)
plugin_name=plugin_name, .where(persistence_plugin.PluginSetting.plugin_author == plugin_author)
install_source=install_source, .where(persistence_plugin.PluginSetting.plugin_name == plugin_name)
install_info=install_info, )
)
)
return handler.ActionResponse.success( # create plugin setting
data={}, await self.ap.persistence_mgr.execute_async(
) sqlalchemy.insert(persistence_plugin.PluginSetting).values(
plugin_author=plugin_author,
plugin_name=plugin_name,
install_source=install_source,
install_info=install_info,
)
)
return handler.ActionResponse.success(
data={},
)
except Exception as e:
traceback.print_exc()
return handler.ActionResponse.error(
message=f'Failed to initialize plugin settings: {e}',
)
@self.action(RuntimeToLangBotAction.GET_PLUGIN_SETTINGS) @self.action(RuntimeToLangBotAction.GET_PLUGIN_SETTINGS)
async def get_plugin_settings(data: dict[str, Any]) -> handler.ActionResponse: async def get_plugin_settings(data: dict[str, Any]) -> handler.ActionResponse:
+6 -1
View File
@@ -293,7 +293,12 @@ export default function PluginConfigPage() {
)} )}
{pluginInstallStatus === PluginInstallStatus.ASK_CONFIRM && ( {pluginInstallStatus === PluginInstallStatus.ASK_CONFIRM && (
<div className="mt-4"> <div className="mt-4">
<p className="mb-2">{t('plugins.askConfirm')}</p> <p className="mb-2">
{t('plugins.askConfirm', {
name: installInfo.plugin_name,
version: installInfo.plugin_version,
})}
</p>
</div> </div>
)} )}
{pluginInstallStatus === PluginInstallStatus.INSTALLING && ( {pluginInstallStatus === PluginInstallStatus.INSTALLING && (
@@ -5,7 +5,7 @@ import { Badge } from '@/components/ui/badge';
import { Switch } from '@/components/ui/switch'; import { Switch } from '@/components/ui/switch';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { ExternalLink } from 'lucide-react'; import { BugIcon, ExternalLink } from 'lucide-react';
import { getCloudServiceClientSync } from '@/app/infra/http'; import { getCloudServiceClientSync } from '@/app/infra/http';
export default function PluginCardComponent({ export default function PluginCardComponent({
@@ -65,48 +65,53 @@ export default function PluginCardComponent({
variant="outline" variant="outline"
className="text-[0.7rem] border-orange-400 text-orange-400" className="text-[0.7rem] border-orange-400 text-orange-400"
> >
<BugIcon className="w-4 h-4" />
{t('plugins.debugging')} {t('plugins.debugging')}
</Badge> </Badge>
)} )}
{cardVO.install_source === 'github' && ( {!cardVO.debug && (
<Badge <>
variant="outline" {cardVO.install_source === 'github' && (
className="text-[0.7rem] border-blue-400 text-blue-400" <Badge
onClick={(e) => { variant="outline"
e.stopPropagation(); className="text-[0.7rem] border-blue-400 text-blue-400"
window.open(cardVO.install_info.github_url, '_blank'); onClick={(e) => {
}} e.stopPropagation();
> window.open(cardVO.install_info.github_url, '_blank');
{t('plugins.fromGithub')} }}
<ExternalLink className="w-4 h-4" /> >
</Badge> {t('plugins.fromGithub')}
)} <ExternalLink className="w-4 h-4" />
{cardVO.install_source === 'local' && ( </Badge>
<Badge )}
variant="outline" {cardVO.install_source === 'local' && (
className="text-[0.7rem] border-green-400 text-green-400" <Badge
> variant="outline"
{t('plugins.fromLocal')} className="text-[0.7rem] border-green-400 text-green-400"
</Badge> >
)} {t('plugins.fromLocal')}
{cardVO.install_source === 'marketplace' && ( </Badge>
<Badge )}
variant="outline" {cardVO.install_source === 'marketplace' && (
className="text-[0.7rem] border-purple-400 text-purple-400" <Badge
onClick={(e) => { variant="outline"
e.stopPropagation(); className="text-[0.7rem] border-purple-400 text-purple-400"
window.open( onClick={(e) => {
getCloudServiceClientSync().getPluginMarketplaceURL( e.stopPropagation();
cardVO.author, window.open(
cardVO.name, getCloudServiceClientSync().getPluginMarketplaceURL(
), cardVO.author,
'_blank', cardVO.name,
); ),
}} '_blank',
> );
{t('plugins.fromMarketplace')} }}
<ExternalLink className="w-4 h-4" /> >
</Badge> {t('plugins.fromMarketplace')}
<ExternalLink className="w-4 h-4" />
</Badge>
)}
</>
)} )}
</div> </div>
</div> </div>
+1 -1
View File
@@ -13,7 +13,7 @@
padding-right: 0.8rem; padding-right: 0.8rem;
padding-top: 2rem; padding-top: 2rem;
display: grid; display: grid;
grid-template-columns: repeat(auto-fill, minmax(24rem, 1fr)); grid-template-columns: repeat(auto-fill, minmax(30rem, 1fr));
gap: 2rem; gap: 2rem;
justify-items: stretch; justify-items: stretch;
align-items: start; align-items: start;
+4 -1
View File
@@ -21,7 +21,10 @@ export class CloudServiceClient extends BaseHttpClient {
sort_order?: string, sort_order?: string,
): Promise<ApiRespMarketplacePlugins> { ): Promise<ApiRespMarketplacePlugins> {
return this.get<ApiRespMarketplacePlugins>('/api/v1/marketplace/plugins', { return this.get<ApiRespMarketplacePlugins>('/api/v1/marketplace/plugins', {
params: { page, page_size, sort_by, sort_order }, page,
page_size,
sort_by,
sort_order,
}); });
} }
+1
View File
@@ -189,6 +189,7 @@ const enUS = {
uploadSuccess: 'Upload successful', uploadSuccess: 'Upload successful',
uploadFailed: 'Upload failed', uploadFailed: 'Upload failed',
selectFileToUpload: 'Select plugin file to upload', selectFileToUpload: 'Select plugin file to upload',
askConfirm: 'Are you sure to install plugin "{{name}}" ({{version}})?',
fromGithub: 'From GitHub', fromGithub: 'From GitHub',
fromLocal: 'From Local', fromLocal: 'From Local',
fromMarketplace: 'From Marketplace', fromMarketplace: 'From Marketplace',
+1
View File
@@ -189,6 +189,7 @@ const jaJP = {
uploadSuccess: 'アップロード成功', uploadSuccess: 'アップロード成功',
uploadFailed: 'アップロード失敗', uploadFailed: 'アップロード失敗',
selectFileToUpload: 'アップロードするプラグインファイルを選択', selectFileToUpload: 'アップロードするプラグインファイルを選択',
askConfirm: 'プラグイン "{{name}}" ({{version}}) をインストールしますか?',
fromGithub: 'GitHubから', fromGithub: 'GitHubから',
fromLocal: 'ローカルから', fromLocal: 'ローカルから',
fromMarketplace: 'プラグインマーケットから', fromMarketplace: 'プラグインマーケットから',
+1
View File
@@ -184,6 +184,7 @@ const zhHans = {
uploadSuccess: '上传成功', uploadSuccess: '上传成功',
uploadFailed: '上传失败', uploadFailed: '上传失败',
selectFileToUpload: '选择要上传的插件文件', selectFileToUpload: '选择要上传的插件文件',
askConfirm: '确定要安装插件 "{{name}}" ({{version}}) 吗?',
fromGithub: '来自 GitHub', fromGithub: '来自 GitHub',
fromLocal: '来自本地', fromLocal: '来自本地',
fromMarketplace: '来自市场', fromMarketplace: '来自市场',