From 7a8102430f358250e814c9ac8d6a0d880dcf7d82 Mon Sep 17 00:00:00 2001 From: HYana Date: Sat, 10 May 2025 01:19:30 +0800 Subject: [PATCH] fix: lint code to build success --- web/.lintstagedrc.json | 2 +- .../home/bots/components/bot-card/BotCard.tsx | 32 +- .../home/bots/components/bot-form/BotForm.tsx | 244 ++++++++------- .../bots/components/bot-form/ChooseEntity.ts | 2 +- web/src/app/home/bots/page.tsx | 61 ++-- .../dynamic-form/DynamicFormComponent.tsx | 137 +++++---- .../dynamic-form/DynamicFormItemComponent.tsx | 187 ++++++------ .../dynamic-form/DynamicFormItemConfig.ts | 24 +- .../components/home-sidebar/HomeSidebar.tsx | 55 ++-- .../home-sidebar/HomeSidebarChild.tsx | 8 +- .../home-sidebar/sidbarConfigList.tsx | 44 ++- web/src/app/home/layout.tsx | 10 +- .../models/component/llm-card/LLMCard.tsx | 71 +++-- .../models/component/llm-form/LLMForm.tsx | 280 +++++++++++------- web/src/app/home/models/page.tsx | 17 +- .../components/pipeline-card/PipelineCard.tsx | 37 +-- .../pipeline-form/PipelineFormComponent.tsx | 190 +++++++----- web/src/app/home/pipelines/page.tsx | 26 +- web/src/app/home/plugins/page.tsx | 102 ++++--- .../PluginInstalledComponent.tsx | 165 ++++++----- .../plugin-card/PluginCardComponent.tsx | 61 +++- .../plugin-form/PluginForm.tsx | 132 +++++---- .../plugin-market/PluginMarketComponent.tsx | 138 +++++---- .../PluginMarketCardComponent.tsx | 50 +++- web/src/app/infra/entities/api/index.ts | 5 +- web/src/app/infra/entities/form/dynamic.ts | 44 +-- web/src/app/infra/entities/pipeline/index.ts | 2 +- web/src/app/infra/http/HttpClient.ts | 18 +- web/src/app/login/page.tsx | 58 ++-- web/src/app/not-found.tsx | 6 +- web/src/app/register/page.tsx | 52 ++-- web/src/components/ui/alert.tsx | 40 +-- web/src/components/ui/badge.tsx | 32 +- web/src/components/ui/button.tsx | 46 +-- web/src/components/ui/card.tsx | 54 ++-- web/src/components/ui/checkbox.tsx | 18 +- web/src/components/ui/dialog.tsx | 54 ++-- web/src/components/ui/form.tsx | 95 +++--- web/src/components/ui/input.tsx | 18 +- web/src/components/ui/label.tsx | 16 +- web/src/components/ui/pagination.tsx | 56 ++-- web/src/components/ui/select.tsx | 68 ++--- web/src/components/ui/sonner.tsx | 22 +- web/src/components/ui/switch.tsx | 18 +- web/src/components/ui/tabs.tsx | 28 +- web/src/components/ui/toggle-group.tsx | 34 +-- web/src/components/ui/toggle.tsx | 32 +- web/src/lib/utils.ts | 6 +- 48 files changed, 1657 insertions(+), 1240 deletions(-) diff --git a/web/.lintstagedrc.json b/web/.lintstagedrc.json index 253287a0..d9209d1f 100644 --- a/web/.lintstagedrc.json +++ b/web/.lintstagedrc.json @@ -1,3 +1,3 @@ { - "*.{js,jsx,ts,tsx}": ["eslint --fix", "eslint"] + "*.{js,jsx,ts,tsx}": ["pnpm lint --fix", "pnpm lint"] } diff --git a/web/src/app/home/bots/components/bot-card/BotCard.tsx b/web/src/app/home/bots/components/bot-card/BotCard.tsx index 6a2382f4..924032c9 100644 --- a/web/src/app/home/bots/components/bot-card/BotCard.tsx +++ b/web/src/app/home/bots/components/bot-card/BotCard.tsx @@ -4,38 +4,50 @@ import styles from './botCard.module.css'; export default function BotCard({ botCardVO }: { botCardVO: BotCardVO }) { return (
-
- icon + icon
-
-
- {botCardVO.name} -
+
{botCardVO.name}
{botCardVO.description}
- + + + {botCardVO.adapterLabel}
- + + + {botCardVO.usePipelineName}
-
-
); } diff --git a/web/src/app/home/bots/components/bot-form/BotForm.tsx b/web/src/app/home/bots/components/bot-form/BotForm.tsx index 3cb65d88..f78ea24f 100644 --- a/web/src/app/home/bots/components/bot-form/BotForm.tsx +++ b/web/src/app/home/bots/components/bot-form/BotForm.tsx @@ -1,5 +1,8 @@ import { useEffect, useState } from 'react'; -import { IChooseAdapterEntity, IPipelineEntity } from '@/app/home/bots/components/bot-form/ChooseEntity'; +import { + IChooseAdapterEntity, + IPipelineEntity, +} from '@/app/home/bots/components/bot-form/ChooseEntity'; import { DynamicFormItemConfig, getDefaultValues, @@ -11,10 +14,10 @@ import DynamicFormComponent from '@/app/home/components/dynamic-form/DynamicForm import { httpClient } from '@/app/infra/http/HttpClient'; import { Bot } from '@/app/infra/entities/api'; -import { zodResolver } from "@hookform/resolvers/zod" -import { useForm } from "react-hook-form" -import { z } from "zod" -import { toast } from "sonner" +import { zodResolver } from '@hookform/resolvers/zod'; +import { useForm } from 'react-hook-form'; +import { z } from 'zod'; +import { toast } from 'sonner'; import { Dialog, @@ -22,23 +25,27 @@ import { DialogDescription, DialogHeader, DialogTitle, - DialogTrigger, DialogFooter, -} from "@/components/ui/dialog" -import { Button } from "@/components/ui/button" +} from '@/components/ui/dialog'; +import { Button } from '@/components/ui/button'; import { Form, FormControl, - FormDescription, FormField, FormItem, FormLabel, FormMessage, -} from "@/components/ui/form" -import { Input } from "@/components/ui/input" -import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select" -import { Checkbox } from "@/components/ui/checkbox" -import { Switch } from "@/components/ui/switch" +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; +import { Switch } from '@/components/ui/switch'; const formSchema = z.object({ name: z.string().min(1, { message: '机器人名称不能为空' }), @@ -60,7 +67,6 @@ export default function BotForm({ onFormCancel: () => void; onBotDeleted: () => void; }) { - const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { @@ -73,7 +79,6 @@ export default function BotForm({ }, }); - const [showDeleteConfirmModal, setShowDeleteConfirmModal] = useState(false); const [adapterNameToDynamicConfigMap, setAdapterNameToDynamicConfigMap] = @@ -91,38 +96,38 @@ export default function BotForm({ Record >({}); - const [pipelineNameList, setPipelineNameList] = useState< - IPipelineEntity[] - >([]); + const [pipelineNameList, setPipelineNameList] = useState( + [], + ); const [dynamicFormConfigList, setDynamicFormConfigList] = useState< IDynamicFormItemSchema[] >([]); - const [isLoading, setIsLoading] = useState(false); + const [, setIsLoading] = useState(false); useEffect(() => { initBotFormComponent().then(() => { - - // 拉取初始化表单信息 - if (initBotId) { - getBotConfig(initBotId).then((val) => { - form.setValue('name', val.name); - form.setValue('description', val.description); - form.setValue('adapter', val.adapter); - form.setValue('adapter_config', val.adapter_config); - form.setValue('enable', val.enable); - form.setValue('use_pipeline_uuid', val.use_pipeline_uuid || ''); - console.log('form', form.getValues()); - handleAdapterSelect(val.adapter); - // dynamicForm.setFieldsValue(val.adapter_config); - }).catch((err) => { - toast.error("获取机器人配置失败:" + err.message); - }); - - } else { - form.reset(); - } - }) + // 拉取初始化表单信息 + if (initBotId) { + getBotConfig(initBotId) + .then((val) => { + form.setValue('name', val.name); + form.setValue('description', val.description); + form.setValue('adapter', val.adapter); + form.setValue('adapter_config', val.adapter_config); + form.setValue('enable', val.enable); + form.setValue('use_pipeline_uuid', val.use_pipeline_uuid || ''); + console.log('form', form.getValues()); + handleAdapterSelect(val.adapter); + // dynamicForm.setFieldsValue(val.adapter_config); + }) + .catch((err) => { + toast.error('获取机器人配置失败:' + err.message); + }); + } else { + form.reset(); + } + }); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); async function initBotFormComponent() { @@ -152,18 +157,24 @@ export default function BotForm({ // 初始化适配器图标列表 setAdapterIconList( - adaptersRes.adapters.reduce((acc, item) => { - acc[item.name] = httpClient.getAdapterIconURL(item.name); - return acc; - }, {} as Record), + adaptersRes.adapters.reduce( + (acc, item) => { + acc[item.name] = httpClient.getAdapterIconURL(item.name); + return acc; + }, + {} as Record, + ), ); // 初始化适配器描述列表 setAdapterDescriptionList( - adaptersRes.adapters.reduce((acc, item) => { - acc[item.name] = item.description.zh_CN; - return acc; - }, {} as Record), + adaptersRes.adapters.reduce( + (acc, item) => { + acc[item.name] = item.description.zh_CN; + return acc; + }, + {} as Record, + ), ); // 初始化适配器表单map @@ -185,21 +196,24 @@ export default function BotForm({ }); setAdapterNameToDynamicConfigMap(adapterNameToDynamicConfigMap); } - async function getBotConfig(botId: string): Promise> { + async function getBotConfig( + botId: string, + ): Promise> { return new Promise((resolve, reject) => { - httpClient.getBot(botId) - .then(res => { + httpClient + .getBot(botId) + .then((res) => { const bot = res.bot; resolve({ adapter: bot.adapter, - description: bot.description, + description: bot.description, name: bot.name, adapter_config: bot.adapter_config, enable: bot.enable ?? true, use_pipeline_uuid: bot.use_pipeline_uuid ?? '', }); }) - .catch(err => { + .catch((err) => { reject(err); }); }); @@ -212,7 +226,10 @@ export default function BotForm({ if (dynamicFormConfigList) { setDynamicFormConfigList(dynamicFormConfigList); if (!initBotId) { - form.setValue('adapter_config', getDefaultValues(dynamicFormConfigList)); + form.setValue( + 'adapter_config', + getDefaultValues(dynamicFormConfigList), + ); } } setShowDynamicForm(true); @@ -221,14 +238,6 @@ export default function BotForm({ } } - function handleSubmitButton() { - // form.submit(); - } - - function handleFormFinish() { - // dynamicForm.submit(); - } - // 只有通过外层固定表单验证才会走到这里,真正的提交逻辑在这里 function onDynamicFormSubmit(value: object) { setIsLoading(true); @@ -250,10 +259,10 @@ export default function BotForm({ .then((res) => { console.log('update bot success', res); onFormSubmit(form.getValues()); - toast.success("保存成功"); + toast.success('保存成功'); }) .catch((err) => { - toast.error("保存失败:" + err.message); + toast.error('保存失败:' + err.message); }) .finally(() => { setIsLoading(false); @@ -274,10 +283,10 @@ export default function BotForm({ .then((res) => { console.log(res); onFormSubmit(form.getValues()); - toast.success("创建成功"); + toast.success('创建成功'); }) .catch((err) => { - toast.error("创建失败:" + err.message); + toast.error('创建失败:' + err.message); }) .finally(() => { setIsLoading(false); @@ -289,38 +298,44 @@ export default function BotForm({ console.log('set loading', false); } - function handleSaveButton() { - form.handleSubmit(onDynamicFormSubmit)(); - } - function deleteBot() { if (initBotId) { - httpClient.deleteBot(initBotId).then(() => { - onBotDeleted(); - }).catch((err) => { - toast.error("删除失败:" + err.message); - }); + httpClient + .deleteBot(initBotId) + .then(() => { + onBotDeleted(); + }) + .catch((err) => { + toast.error('删除失败:' + err.message); + }); } } return (
- + 删除确认 - - 你确定要删除这个机器人吗? - + 你确定要删除这个机器人吗? - - @@ -328,7 +343,10 @@ export default function BotForm({
- +
{/* 是否启用 & 绑定流水线 仅在编辑模式 */} {initBotId && ( @@ -340,7 +358,10 @@ export default function BotForm({ 是否启用 - + )} @@ -353,10 +374,7 @@ export default function BotForm({ 绑定流水线 - @@ -375,7 +393,6 @@ export default function BotForm({ )} />
- )} ( - 机器人名称* + + 机器人名称* + @@ -396,7 +415,9 @@ export default function BotForm({ name="description" render={({ field }) => ( - 机器人描述* + + 机器人描述* + @@ -410,7 +431,9 @@ export default function BotForm({ name="adapter" render={({ field }) => ( - 平台/适配器选择* + + 平台/适配器选择* +
; case DynamicFormItemType.BOOLEAN: - return ( - - ); + return ; case DynamicFormItemType.STRING_ARRAY: return ( @@ -67,15 +73,22 @@ export default function DynamicFormItemComponent({ field.onChange(newValue); }} /> - @@ -95,10 +108,7 @@ export default function DynamicFormItemComponent({ case DynamicFormItemType.SELECT: return ( - @@ -116,10 +126,7 @@ export default function DynamicFormItemComponent({ case DynamicFormItemType.LLM_MODEL_SELECTOR: return ( - @@ -138,66 +145,78 @@ export default function DynamicFormItemComponent({ case DynamicFormItemType.PROMPT_EDITOR: return (
- {field.value.map((item: { role: string; content: string }, index: number) => ( -
- {/* 角色选择 */} - {index === 0 ? ( -
system
- ) : ( - { + const newValue = [...field.value]; + newValue[index] = { ...newValue[index], role: value }; + field.onChange(newValue); + }} + > + + + + + + user + assistant + + + + )} + {/* 内容输入 */} + { const newValue = [...field.value]; - newValue[index] = { ...newValue[index], role: value }; + newValue[index] = { + ...newValue[index], + content: e.target.value, + }; field.onChange(newValue); }} - > - - - - - - user - assistant - - - - )} - {/* 内容输入 */} - { - const newValue = [...field.value]; - newValue[index] = { ...newValue[index], content: e.target.value }; - field.onChange(newValue); - }} - /> - {/* 删除按钮,第一轮不显示 */} - {index !== 0 && ( - - )} -
- ))} + /> + {/* 删除按钮,第一轮不显示 */} + {index !== 0 && ( + + )} +
+ ), + )}
); } diff --git a/web/src/app/home/components/home-sidebar/sidbarConfigList.tsx b/web/src/app/home/components/home-sidebar/sidbarConfigList.tsx index 86af4f47..a0429539 100644 --- a/web/src/app/home/components/home-sidebar/sidbarConfigList.tsx +++ b/web/src/app/home/components/home-sidebar/sidbarConfigList.tsx @@ -5,25 +5,61 @@ export const sidebarConfigList = [ new SidebarChildVO({ id: 'models', name: '模型配置', - icon: , + icon: ( + + + + ), route: '/home/models', }), new SidebarChildVO({ id: 'bots', name: '机器人', - icon: , + icon: ( + + + + ), route: '/home/bots', }), new SidebarChildVO({ id: 'pipelines', name: '流水线', - icon: , + icon: ( + + + + ), route: '/home/pipelines', }), new SidebarChildVO({ id: 'plugins', name: '插件管理', - icon: , + icon: ( + + + + ), route: '/home/plugins', }), ]; diff --git a/web/src/app/home/layout.tsx b/web/src/app/home/layout.tsx index f12f2973..ae4bc723 100644 --- a/web/src/app/home/layout.tsx +++ b/web/src/app/home/layout.tsx @@ -17,20 +17,16 @@ export default function HomeLayout({ }; return ( -
- +
- -
- {children} -
-
+
{children}
+
); } diff --git a/web/src/app/home/models/component/llm-card/LLMCard.tsx b/web/src/app/home/models/component/llm-card/LLMCard.tsx index f0dffcf9..bd7c2f5e 100644 --- a/web/src/app/home/models/component/llm-card/LLMCard.tsx +++ b/web/src/app/home/models/component/llm-card/LLMCard.tsx @@ -1,19 +1,35 @@ import styles from './LLMCard.module.css'; import { LLMCardVO } from '@/app/home/models/component/llm-card/LLMCardVO'; -import { Button } from '@/components/ui/button'; function checkAbilityBadges(abilities: string[]) { - const abilityBadges = { - 'vision':
- - 视觉能力 -
, - 'func_call':
- - 函数调用 -
, - } + vision: ( +
+ + + + 视觉能力 +
+ ), + func_call: ( +
+ + + + 函数调用 +
+ ), + }; return abilities.map((ability) => { return abilityBadges[ability as keyof typeof abilityBadges]; @@ -24,7 +40,11 @@ export default function LLMCard({ cardVO }: { cardVO: LLMCardVO }) { return (
- icon + icon
{/* 名称 */} @@ -33,24 +53,39 @@ export default function LLMCard({ cardVO }: { cardVO: LLMCardVO }) {
{/* 厂商 */}
- + + + {cardVO.providerLabel}
{/* baseURL */}
- - - {cardVO.baseURL} - + + + + {cardVO.baseURL}
{/* 能力 */}
{checkAbilityBadges(cardVO.abilities)}
-
); diff --git a/web/src/app/home/models/component/llm-form/LLMForm.tsx b/web/src/app/home/models/component/llm-form/LLMForm.tsx index de46e739..fd6bd998 100644 --- a/web/src/app/home/models/component/llm-form/LLMForm.tsx +++ b/web/src/app/home/models/component/llm-form/LLMForm.tsx @@ -5,9 +5,9 @@ import { httpClient } from '@/app/infra/http/HttpClient'; import { LLMModel } from '@/app/infra/entities/api'; import { UUID } from 'uuidjs'; -import { zodResolver } from "@hookform/resolvers/zod" -import { useForm } from "react-hook-form" -import { z } from "zod" +import { zodResolver } from '@hookform/resolvers/zod'; +import { useForm } from 'react-hook-form'; +import { z } from 'zod'; import { Dialog, @@ -15,10 +15,9 @@ import { DialogDescription, DialogHeader, DialogTitle, - DialogTrigger, DialogFooter, -} from "@/components/ui/dialog" -import { Button } from "@/components/ui/button" +} from '@/components/ui/dialog'; +import { Button } from '@/components/ui/button'; import { Form, FormControl, @@ -27,31 +26,44 @@ import { FormItem, FormLabel, FormMessage, -} from "@/components/ui/form" -import { Input } from "@/components/ui/input" -import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select" -import { Checkbox } from "@/components/ui/checkbox" -import { toast } from "sonner" -const extraArgSchema = z.object({ - key: z.string().min(1, { message: '键名不能为空' }), - type: z.enum(['string', 'number', 'boolean']), - value: z.string(), -}).superRefine((data, ctx) => { - if (data.type === 'number' && isNaN(Number(data.value))) { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: "必须是有效的数字", - path: ['value'], - }); - } - if (data.type === 'boolean' && data.value !== 'true' && data.value !== 'false') { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: "必须是 true 或 false", - path: ['value'], - }); - } -}); +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; +import { Checkbox } from '@/components/ui/checkbox'; +import { toast } from 'sonner'; +const extraArgSchema = z + .object({ + key: z.string().min(1, { message: '键名不能为空' }), + type: z.enum(['string', 'number', 'boolean']), + value: z.string(), + }) + .superRefine((data, ctx) => { + if (data.type === 'number' && isNaN(Number(data.value))) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: '必须是有效的数字', + path: ['value'], + }); + } + if ( + data.type === 'boolean' && + data.value !== 'true' && + data.value !== 'false' + ) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: '必须是 true 或 false', + path: ['value'], + }); + } + }); const formSchema = z.object({ name: z.string().min(1, { message: '模型名称不能为空' }), @@ -60,7 +72,7 @@ const formSchema = z.object({ api_key: z.string().min(1, { message: 'API Key不能为空' }), abilities: z.array(z.string()), extra_args: z.array(extraArgSchema).optional(), -}) +}); export default function LLMForm({ editMode, @@ -87,10 +99,12 @@ export default function LLMForm({ }, }); - const [extraArgs, setExtraArgs] = useState<{key: string, type: 'string' | 'number' | 'boolean', value: string}[]>([]); + const [extraArgs, setExtraArgs] = useState< + { key: string; type: 'string' | 'number' | 'boolean'; value: string }[] + >([]); const [showDeleteConfirmModal, setShowDeleteConfirmModal] = useState(false); - const abilityOptions: { label: string, value: string }[] = [ + const abilityOptions: { label: string; value: string }[] = [ { label: '视觉能力', value: 'vision', @@ -115,21 +129,21 @@ export default function LLMForm({ form.setValue('model_provider', val.model_provider); form.setValue('url', val.url); form.setValue('api_key', val.api_key); - form.setValue('abilities', val.abilities as ("vision" | "func_call")[]); + form.setValue('abilities', val.abilities as ('vision' | 'func_call')[]); // 转换extra_args为新格式 - if(val.extra_args) { - const args = val.extra_args.map(arg => { + if (val.extra_args) { + const args = val.extra_args.map((arg) => { const [key, value] = arg.split(':'); let type: 'string' | 'number' | 'boolean' = 'string'; - if(!isNaN(Number(value))) { + if (!isNaN(Number(value))) { type = 'number'; - } else if(value === 'true' || value === 'false') { + } else if (value === 'true' || value === 'false') { type = 'boolean'; } return { key, type, - value + value, }; }); setExtraArgs(args); @@ -143,14 +157,18 @@ export default function LLMForm({ }, []); const addExtraArg = () => { - setExtraArgs([...extraArgs, {key: '', type: 'string', value: ''}]); + setExtraArgs([...extraArgs, { key: '', type: 'string', value: '' }]); }; - const updateExtraArg = (index: number, field: 'key' | 'type' | 'value', value: string) => { + const updateExtraArg = ( + index: number, + field: 'key' | 'type' | 'value', + value: string, + ) => { const newArgs = [...extraArgs]; newArgs[index] = { ...newArgs[index], - [field]: value + [field]: value, }; setExtraArgs(newArgs); form.setValue('extra_args', newArgs); @@ -216,11 +234,12 @@ export default function LLMForm({ function onCreateLLM(value: z.infer) { console.log('create llm', value); // 转换extra_args为对象格式 + // eslint-disable-next-line @typescript-eslint/no-explicit-any const extraArgsObj: Record = {}; - value.extra_args?.forEach(arg => { - if(arg.type === 'number') { + value.extra_args?.forEach((arg) => { + if (arg.type === 'number') { extraArgsObj[arg.key] = Number(arg.value); - } else if(arg.type === 'boolean') { + } else if (arg.type === 'boolean') { extraArgsObj[arg.key] = arg.value === 'true'; } else { extraArgsObj[arg.key] = arg.value; @@ -240,46 +259,56 @@ export default function LLMForm({ api_keys: [value.api_key], abilities: value.abilities, }; - httpClient.createProviderLLMModel(requestParam).then(() => { - onFormSubmit(value); - toast.success("创建成功"); - }).catch((err) => { - toast.error("创建失败:" + err.message); - }); + httpClient + .createProviderLLMModel(requestParam) + .then(() => { + onFormSubmit(value); + toast.success('创建成功'); + }) + .catch((err) => { + toast.error('创建失败:' + err.message); + }); } - function handleAbilitiesChange() { } - function deleteModel() { if (initLLMId) { - httpClient.deleteProviderLLMModel(initLLMId).then(() => { - onLLMDeleted(); - toast.success("删除成功"); - }).catch((err) => { - toast.error("删除失败:" + err.message); - }); + httpClient + .deleteProviderLLMModel(initLLMId) + .then(() => { + onLLMDeleted(); + toast.success('删除成功'); + }) + .catch((err) => { + toast.error('删除失败:' + err.message); + }); } } return (
- - + 删除确认 - - 你确定要删除这个模型吗? - + 你确定要删除这个模型吗? - - @@ -287,14 +316,19 @@ export default function LLMForm({ - +
( - 模型名称* + + 模型名称* + @@ -311,15 +345,22 @@ export default function LLMForm({ name="model_provider" render={({ field }) => ( - 模型供应商* + + 模型供应商* + - { + field.onChange(value); + const index = requesterNameList.findIndex( + (item) => item.value === value, + ); + if (index !== -1) { + form.setValue('url', requesterDefaultURLList[index]); + } + }} + value={field.value} + > @@ -338,13 +379,15 @@ export default function LLMForm({ )} /> - + ( - 请求URL* + + 请求URL* + @@ -357,7 +400,9 @@ export default function LLMForm({ name="api_key" render={({ field }) => ( - API Key* + + API Key* + @@ -368,13 +413,11 @@ export default function LLMForm({ ( + render={() => ( 能力
- - 选择模型能力 - + 选择模型能力
{abilityOptions.map((item) => ( { return checked - ? field.onChange([...(field.value || []), item.value]) + ? field.onChange([ + ...(field.value || []), + item.value, + ]) : field.onChange( field.value?.filter( - (value) => value !== item.value - ) - ) + (value) => value !== item.value, + ), + ); }} /> @@ -405,7 +454,7 @@ export default function LLMForm({ {item.label}
- ) + ); }} /> ))} @@ -419,14 +468,18 @@ export default function LLMForm({
{extraArgs.map((arg, index) => (
- updateExtraArg(index, 'key', e.target.value)} + onChange={(e) => + updateExtraArg(index, 'key', e.target.value) + } /> - - updateExtraArg(index, 'value', e.target.value)} + onChange={(e) => + updateExtraArg(index, 'value', e.target.value) + } /> - @@ -462,24 +522,28 @@ export default function LLMForm({ -
- {!editMode && ( - - )} + {!editMode && } {editMode && ( - )} - -
); } diff --git a/web/src/app/home/models/page.tsx b/web/src/app/home/models/page.tsx index bc158d53..2b1cd835 100644 --- a/web/src/app/home/models/page.tsx +++ b/web/src/app/home/models/page.tsx @@ -3,7 +3,6 @@ import { useState, useEffect } from 'react'; import { LLMCardVO } from '@/app/home/models/component/llm-card/LLMCardVO'; import styles from './LLMConfig.module.css'; -import EmptyAndCreateComponent from '@/app/home/components/empty-and-create-component/EmptyAndCreateComponent'; import LLMCard from '@/app/home/models/component/llm-card/LLMCard'; import LLMForm from '@/app/home/models/component/llm-form/LLMForm'; import CreateCardComponent from '@/app/infra/basic-component/create-card-component/CreateCardComponent'; @@ -12,12 +11,10 @@ import { LLMModel } from '@/app/infra/entities/api'; import { Dialog, DialogContent, - DialogDescription, DialogHeader, DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog" -import { toast } from "sonner"; +} from '@/components/ui/dialog'; +import { toast } from 'sonner'; export default function LLMConfigPage() { const [cardList, setCardList] = useState([]); @@ -47,7 +44,9 @@ export default function LLMConfigPage() { id: model.uuid, iconURL: httpClient.getProviderRequesterIconURL(model.requester), name: model.name, - providerLabel: requesterNameList.find((item) => item.value === model.requester)?.label || model.requester.substring(0, 10), + providerLabel: + requesterNameList.find((item) => item.value === model.requester) + ?.label || model.requester.substring(0, 10), baseURL: model.requester_config?.base_url, abilities: model.abilities || [], }); @@ -57,7 +56,7 @@ export default function LLMConfigPage() { }) .catch((err) => { console.error('get LLM model list error', err); - toast.error("获取模型列表失败:" + err.message); + toast.error('获取模型列表失败:' + err.message); }); } @@ -74,8 +73,7 @@ export default function LLMConfigPage() { } return ( -
- +
@@ -99,7 +97,6 @@ export default function LLMConfigPage() {
- -
-
{cardVO.name} @@ -22,27 +15,35 @@ export default function PipelineCard({
- + + +
更新于{cardVO.lastUpdatedTimeAgo}
-
- {cardVO.isDefault && ( - -
- -
- 默认 +
+ + + +
默认
-
)}
-
); } diff --git a/web/src/app/home/pipelines/components/pipeline-form/PipelineFormComponent.tsx b/web/src/app/home/pipelines/components/pipeline-form/PipelineFormComponent.tsx index b1f391ba..99169da3 100644 --- a/web/src/app/home/pipelines/components/pipeline-form/PipelineFormComponent.tsx +++ b/web/src/app/home/pipelines/components/pipeline-form/PipelineFormComponent.tsx @@ -1,15 +1,18 @@ - import { useEffect, useState } from 'react'; import { httpClient } from '@/app/infra/http/HttpClient'; import { Pipeline } from '@/app/infra/entities/api'; -import { PipelineFormEntity, PipelineConfigTab, PipelineConfigStage } from '@/app/infra/entities/pipeline'; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" +import { + PipelineFormEntity, + PipelineConfigTab, + PipelineConfigStage, +} from '@/app/infra/entities/pipeline'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import DynamicFormComponent from '@/app/home/components/dynamic-form/DynamicFormComponent'; import { Button } from '@/components/ui/button'; -import { useForm } from "react-hook-form" -import { zodResolver } from "@hookform/resolvers/zod" -import { z } from "zod" -import { Input } from "@/components/ui/input" +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { z } from 'zod'; +import { Input } from '@/components/ui/input'; import { Form, FormControl, @@ -17,8 +20,8 @@ import { FormItem, FormLabel, FormMessage, -} from "@/components/ui/form" -import { toast } from "sonner" +} from '@/components/ui/form'; +import { toast } from 'sonner'; export default function PipelineFormComponent({ initValues, @@ -26,7 +29,6 @@ export default function PipelineFormComponent({ onNewPipelineCreated, isEditMode, pipelineId, - disableForm, }: { pipelineId?: string; isEditMode: boolean; @@ -36,48 +38,52 @@ export default function PipelineFormComponent({ onFinish: () => void; onNewPipelineCreated: (pipelineId: string) => void; }) { - - const formSchema = isEditMode ? z.object({ - basic: z.object({ - name: z.string().min(1, { message: '名称不能为空' }), - description: z.string().min(1, { message: '描述不能为空' }), - }), - ai: z.record(z.string(), z.any()), - trigger: z.record(z.string(), z.any()), - safety: z.record(z.string(), z.any()), - output: z.record(z.string(), z.any()), - }) + const formSchema = isEditMode + ? z.object({ + basic: z.object({ + name: z.string().min(1, { message: '名称不能为空' }), + description: z.string().min(1, { message: '描述不能为空' }), + }), + ai: z.record(z.string(), z.any()), + trigger: z.record(z.string(), z.any()), + safety: z.record(z.string(), z.any()), + output: z.record(z.string(), z.any()), + }) : z.object({ - basic: z.object({ - name: z.string().min(1, { message: '名称不能为空' }), - description: z.string().min(1, { message: '描述不能为空' }), - }), - ai: z.record(z.string(), z.any()).optional(), - trigger: z.record(z.string(), z.any()).optional(), - safety: z.record(z.string(), z.any()).optional(), - output: z.record(z.string(), z.any()).optional(), - }); + basic: z.object({ + name: z.string().min(1, { message: '名称不能为空' }), + description: z.string().min(1, { message: '描述不能为空' }), + }), + ai: z.record(z.string(), z.any()).optional(), + trigger: z.record(z.string(), z.any()).optional(), + safety: z.record(z.string(), z.any()).optional(), + output: z.record(z.string(), z.any()).optional(), + }); type FormValues = z.infer; // 这里不好,可以改成enum等 - const formLabelList: FormLabel[] = isEditMode ? [ - { label: '基础信息', name: 'basic' }, - { label: 'AI能力', name: 'ai' }, - { label: '触发条件', name: 'trigger' }, - { label: '安全能力', name: 'safety' }, - { label: '输出处理', name: 'output' }, - ] : [ - { label: '基础信息', name: 'basic' }, - ]; + const formLabelList: FormLabel[] = isEditMode + ? [ + { label: '基础信息', name: 'basic' }, + { label: 'AI能力', name: 'ai' }, + { label: '触发条件', name: 'trigger' }, + { label: '安全能力', name: 'safety' }, + { label: '输出处理', name: 'output' }, + ] + : [{ label: '基础信息', name: 'basic' }]; // const [basicForm] = Form.useForm(); // const [aiForm] = Form.useForm(); // const [triggerForm] = Form.useForm(); // const [safetyForm] = Form.useForm(); // const [outputForm] = Form.useForm(); - const [aiConfigTabSchema, setAIConfigTabSchema] = useState(); - const [triggerConfigTabSchema, setTriggerConfigTabSchema] = useState(); - const [safetyConfigTabSchema, setSafetyConfigTabSchema] = useState(); - const [outputConfigTabSchema, setOutputConfigTabSchema] = useState(); + const [aiConfigTabSchema, setAIConfigTabSchema] = + useState(); + const [triggerConfigTabSchema, setTriggerConfigTabSchema] = + useState(); + const [safetyConfigTabSchema, setSafetyConfigTabSchema] = + useState(); + const [outputConfigTabSchema, setOutputConfigTabSchema] = + useState(); const form = useForm({ resolver: zodResolver(formSchema), @@ -91,7 +97,6 @@ export default function PipelineFormComponent({ }); useEffect(() => { - // get config schema from metadata httpClient.getGeneralPipelineMetadata().then((resp) => { for (const config of resp.configs) { @@ -121,7 +126,7 @@ export default function PipelineFormComponent({ }, }); } - }, [initValues, form]); + }, [initValues, form, isEditMode]); function handleFormSubmit(values: FormValues) { console.log('handleFormSubmit', values); @@ -139,17 +144,19 @@ export default function PipelineFormComponent({ description: values.basic.description, name: values.basic.name, }; - httpClient.createPipeline(pipeline).then((resp) => { - onFinish(); - onNewPipelineCreated(resp.uuid); - toast.success("创建成功 请编辑流水线详细参数"); - }).catch((err) => { - toast.error("创建失败:" + err.message); - }); + httpClient + .createPipeline(pipeline) + .then((resp) => { + onFinish(); + onNewPipelineCreated(resp.uuid); + toast.success('创建成功 请编辑流水线详细参数'); + }) + .catch((err) => { + toast.error('创建失败:' + err.message); + }); } function handleModify(values: FormValues) { - const realConfig = { ai: values.ai, trigger: values.trigger, @@ -168,15 +175,21 @@ export default function PipelineFormComponent({ // uuid: pipelineId || '', // is_default: false, }; - httpClient.updatePipeline(pipelineId || '', pipeline).then(() => { - onFinish(); - toast.success("保存成功"); - }).catch((err) => { - toast.error("保存失败:" + err.message); - }); + httpClient + .updatePipeline(pipelineId || '', pipeline) + .then(() => { + onFinish(); + toast.success('保存成功'); + }) + .catch((err) => { + toast.error('保存失败:' + err.message); + }); } - function renderDynamicForms(stage: PipelineConfigStage, formName: keyof FormValues) { + function renderDynamicForms( + stage: PipelineConfigStage, + formName: keyof FormValues, + ) { // 如果是 AI 配置,需要特殊处理 if (formName === 'ai') { // 获取当前选择的 runner @@ -188,13 +201,21 @@ export default function PipelineFormComponent({
{stage.label.zh_CN}
{stage.description && ( -
{stage.description.zh_CN}
+
+ {stage.description.zh_CN} +
)} )?.[stage.name] || {}} + initialValues={ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (form.watch(formName) as Record)?.[stage.name] || + {} + } onSubmit={(values) => { - const currentValues = form.getValues(formName) as Record || {}; + const currentValues = + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (form.getValues(formName) as Record) || {}; form.setValue(formName, { ...currentValues, [stage.name]: values, @@ -219,9 +240,14 @@ export default function PipelineFormComponent({ )} )?.[stage.name] || {}} + initialValues={ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (form.watch(formName) as Record)?.[stage.name] || {} + } onSubmit={(values) => { - const currentValues = form.getValues(formName) as Record || {}; + const currentValues = + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (form.getValues(formName) as Record) || {}; form.setValue(formName, { ...currentValues, [stage.name]: values, @@ -246,7 +272,11 @@ export default function PipelineFormComponent({ {formLabelList.map((formLabel) => ( - +

{formLabel.label}

{formLabel.name === 'basic' && ( @@ -256,7 +286,9 @@ export default function PipelineFormComponent({ name="basic.name" render={({ field }) => ( - 名称* + + 名称* + @@ -270,7 +302,9 @@ export default function PipelineFormComponent({ name="basic.description" render={({ field }) => ( - 描述* + + 描述* + @@ -285,25 +319,33 @@ export default function PipelineFormComponent({ <> {formLabel.name === 'ai' && aiConfigTabSchema && (
- {aiConfigTabSchema.stages.map((stage) => renderDynamicForms(stage, 'ai'))} + {aiConfigTabSchema.stages.map((stage) => + renderDynamicForms(stage, 'ai'), + )}
)} {formLabel.name === 'trigger' && triggerConfigTabSchema && (
- {triggerConfigTabSchema.stages.map((stage) => renderDynamicForms(stage, 'trigger'))} + {triggerConfigTabSchema.stages.map((stage) => + renderDynamicForms(stage, 'trigger'), + )}
)} {formLabel.name === 'safety' && safetyConfigTabSchema && (
- {safetyConfigTabSchema.stages.map((stage) => renderDynamicForms(stage, 'safety'))} + {safetyConfigTabSchema.stages.map((stage) => + renderDynamicForms(stage, 'safety'), + )}
)} {formLabel.name === 'output' && outputConfigTabSchema && (
- {outputConfigTabSchema.stages.map((stage) => renderDynamicForms(stage, 'output'))} + {outputConfigTabSchema.stages.map((stage) => + renderDynamicForms(stage, 'output'), + )}
)} @@ -314,9 +356,7 @@ export default function PipelineFormComponent({
- + diff --git a/web/src/app/home/pipelines/page.tsx b/web/src/app/home/pipelines/page.tsx index fc1fddfe..cd339f49 100644 --- a/web/src/app/home/pipelines/page.tsx +++ b/web/src/app/home/pipelines/page.tsx @@ -10,13 +10,10 @@ import styles from './pipelineConfig.module.css'; import { Dialog, DialogContent, - DialogDescription, DialogHeader, DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog" -import { Button } from '@/components/ui/button'; -import { toast } from "sonner" +} from '@/components/ui/dialog'; +import { toast } from 'sonner'; export default function PluginConfigPage() { const [modalOpen, setModalOpen] = useState(false); const [isEditForm, setIsEditForm] = useState(false); @@ -40,11 +37,21 @@ export default function PluginConfigPage() { httpClient .getPipelines() .then((value) => { - let currentTime = new Date(); + const currentTime = new Date(); const pipelineList = value.pipelines.map((pipeline) => { - let lastUpdatedTimeAgo = Math.floor((currentTime.getTime() - new Date(pipeline.updated_at ?? currentTime.getTime()).getTime()) / 1000 / 60 / 60 / 24); + const lastUpdatedTimeAgo = Math.floor( + (currentTime.getTime() - + new Date( + pipeline.updated_at ?? currentTime.getTime(), + ).getTime()) / + 1000 / + 60 / + 60 / + 24, + ); - let lastUpdatedTimeAgoText = lastUpdatedTimeAgo > 0 ? ` ${lastUpdatedTimeAgo} 天前` : '今天'; + const lastUpdatedTimeAgoText = + lastUpdatedTimeAgo > 0 ? ` ${lastUpdatedTimeAgo} 天前` : '今天'; return new PipelineCardVO({ lastUpdatedTimeAgo: lastUpdatedTimeAgoText, @@ -58,7 +65,7 @@ export default function PluginConfigPage() { }) .catch((error) => { console.log(error); - toast.error("获取流水线列表失败:" + error.message); + toast.error('获取流水线列表失败:' + error.message); }); } @@ -80,7 +87,6 @@ export default function PluginConfigPage() { return (
- diff --git a/web/src/app/home/plugins/page.tsx b/web/src/app/home/plugins/page.tsx index 20b64901..a207c3dd 100644 --- a/web/src/app/home/plugins/page.tsx +++ b/web/src/app/home/plugins/page.tsx @@ -1,14 +1,22 @@ 'use client'; -import PluginInstalledComponent, { PluginInstalledComponentRef } from '@/app/home/plugins/plugin-installed/PluginInstalledComponent'; +import PluginInstalledComponent, { + PluginInstalledComponentRef, +} from '@/app/home/plugins/plugin-installed/PluginInstalledComponent'; import PluginMarketComponent from '@/app/home/plugins/plugin-market/PluginMarketComponent'; import styles from './plugins.module.css'; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { Button } from "@/components/ui/button"; -import { PlusIcon } from "lucide-react"; -import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"; -import { Input } from "@/components/ui/input"; -import { GithubIcon } from "lucide-react"; -import { useState, useRef, useEffect } from 'react'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { Button } from '@/components/ui/button'; +import { PlusIcon } from 'lucide-react'; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogFooter, +} from '@/components/ui/dialog'; +import { Input } from '@/components/ui/input'; +import { GithubIcon } from 'lucide-react'; +import { useState, useRef } from 'react'; import { httpClient } from '@/app/infra/http/HttpClient'; enum PluginInstallStatus { @@ -18,9 +26,9 @@ enum PluginInstallStatus { } export default function PluginConfigPage() { - const [modalOpen, setModalOpen] = useState(false); - const [pluginInstallStatus, setPluginInstallStatus] = useState(PluginInstallStatus.WAIT_INPUT); + const [pluginInstallStatus, setPluginInstallStatus] = + useState(PluginInstallStatus.WAIT_INPUT); const [installError, setInstallError] = useState(null); const [githubURL, setGithubURL] = useState(''); const pluginInstalledRef = useRef(null); @@ -44,7 +52,8 @@ export default function PluginConfigPage() { if (resp.runtime.exception) { setInstallError(resp.runtime.exception); setPluginInstallStatus(PluginInstallStatus.ERROR); - } else { // success + } else { + // success setGithubURL(''); setModalOpen(false); pluginInstalledRef.current?.refreshPluginList(); @@ -52,7 +61,6 @@ export default function PluginConfigPage() { } }); }, 1000); - }) .catch((err) => { console.log('error when install plugin:', err); @@ -64,20 +72,27 @@ export default function PluginConfigPage() { return (
-
- - 已安装 - 插件市场 - +
+ + + 已安装 + + + 插件市场 + -
-
@@ -86,16 +101,17 @@ export default function PluginConfigPage() { - { - setGithubURL(githubURL); - setModalOpen(true); - setPluginInstallStatus(PluginInstallStatus.WAIT_INPUT); - setInstallError(null); - }} /> + { + setGithubURL(githubURL); + setModalOpen(true); + setPluginInstallStatus(PluginInstallStatus.WAIT_INPUT); + setInstallError(null); + }} + /> - @@ -105,14 +121,14 @@ export default function PluginConfigPage() { {pluginInstallStatus === PluginInstallStatus.WAIT_INPUT && ( -
-

目前仅支持从 GitHub 安装

- setGithubURL(e.target.value)} - className="mb-4" - /> +
+

目前仅支持从 GitHub 安装

+ setGithubURL(e.target.value)} + className="mb-4" + />
)} {pluginInstallStatus === PluginInstallStatus.INSTALLING && ( @@ -129,12 +145,16 @@ export default function PluginConfigPage() { {pluginInstallStatus === PluginInstallStatus.WAIT_INPUT && ( <> - + )} {pluginInstallStatus === PluginInstallStatus.ERROR && ( - + )} diff --git a/web/src/app/home/plugins/plugin-installed/PluginInstalledComponent.tsx b/web/src/app/home/plugins/plugin-installed/PluginInstalledComponent.tsx index fa410f26..21002be7 100644 --- a/web/src/app/home/plugins/plugin-installed/PluginInstalledComponent.tsx +++ b/web/src/app/home/plugins/plugin-installed/PluginInstalledComponent.tsx @@ -1,105 +1,108 @@ 'use client'; import { useState, useEffect, forwardRef, useImperativeHandle } from 'react'; -import CreateCardComponent from '@/app/infra/basic-component/create-card-component/CreateCardComponent'; 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 styles from '@/app/home/plugins/plugins.module.css'; -import { GithubIcon } from 'lucide-react'; import { httpClient } from '@/app/infra/http/HttpClient'; import { Dialog, DialogContent, DialogHeader, DialogTitle, - DialogFooter, -} from "@/components/ui/dialog"; -import { Input } from "@/components/ui/input"; -import { Button } from "@/components/ui/button"; +} from '@/components/ui/dialog'; export interface PluginInstalledComponentRef { refreshPluginList: () => void; } -const PluginInstalledComponent = forwardRef((props, ref) => { - const [pluginList, setPluginList] = useState([]); - const [modalOpen, setModalOpen] = useState(false); - const [selectedPlugin, setSelectedPlugin] = useState(null); +// eslint-disable-next-line react/display-name +const PluginInstalledComponent = forwardRef( + (props, ref) => { + const [pluginList, setPluginList] = useState([]); + const [modalOpen, setModalOpen] = useState(false); + const [selectedPlugin, setSelectedPlugin] = useState( + null, + ); - useEffect(() => { - initData(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + useEffect(() => { + initData(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); - function initData() { - getPluginList(); - } + function initData() { + getPluginList(); + } - function getPluginList() { - httpClient.getPlugins().then((value) => { - setPluginList( - value.plugins.map((plugin) => { - return new PluginCardVO({ - author: plugin.author, - description: plugin.description.zh_CN, - enabled: plugin.enabled, - name: plugin.name, - version: plugin.version, - status: plugin.status, - tools: plugin.tools, - event_handlers: plugin.event_handlers, - repository: plugin.repository, - priority: plugin.priority, - }); - }), - ); - }); - } - - useImperativeHandle(ref, () => ({ - refreshPluginList: getPluginList - })); - - function handlePluginClick(plugin: PluginCardVO) { - setSelectedPlugin(plugin); - setModalOpen(true); - } - - return ( -
- - - - 插件配置 - -
- {selectedPlugin && ( - { - setModalOpen(false); - getPluginList(); - }} - onFormCancel={() => { - setModalOpen(false); - }} - /> - )} -
-
-
- - {pluginList.map((vo, index) => { - return ( -
- handlePluginClick(vo)} /> -
+ function getPluginList() { + httpClient.getPlugins().then((value) => { + setPluginList( + value.plugins.map((plugin) => { + return new PluginCardVO({ + author: plugin.author, + description: plugin.description.zh_CN, + enabled: plugin.enabled, + name: plugin.name, + version: plugin.version, + status: plugin.status, + tools: plugin.tools, + event_handlers: plugin.event_handlers, + repository: plugin.repository, + priority: plugin.priority, + }); + }), ); - })} -
- ); -}); + }); + } + + useImperativeHandle(ref, () => ({ + refreshPluginList: getPluginList, + })); + + function handlePluginClick(plugin: PluginCardVO) { + setSelectedPlugin(plugin); + setModalOpen(true); + } + + return ( +
+ + + + 插件配置 + +
+ {selectedPlugin && ( + { + setModalOpen(false); + getPluginList(); + }} + onFormCancel={() => { + setModalOpen(false); + }} + /> + )} +
+
+
+ + {pluginList.map((vo, index) => { + return ( +
+ handlePluginClick(vo)} + /> +
+ ); + })} +
+ ); + }, +); export default PluginInstalledComponent; diff --git a/web/src/app/home/plugins/plugin-installed/plugin-card/PluginCardComponent.tsx b/web/src/app/home/plugins/plugin-installed/plugin-card/PluginCardComponent.tsx index 7db55d68..ffe25976 100644 --- a/web/src/app/home/plugins/plugin-installed/plugin-card/PluginCardComponent.tsx +++ b/web/src/app/home/plugins/plugin-installed/plugin-card/PluginCardComponent.tsx @@ -1,10 +1,9 @@ import { PluginCardVO } from '@/app/home/plugins/plugin-installed/PluginCardVO'; import { useState } from 'react'; import { httpClient } from '@/app/infra/http/HttpClient'; -import { Badge } from "@/components/ui/badge" -import { Switch } from "@/components/ui/switch" -import { Button } from "@/components/ui/button" -import { toast } from "sonner" +import { Badge } from '@/components/ui/badge'; +import { Switch } from '@/components/ui/switch'; +import { toast } from 'sonner'; export default function PluginCardComponent({ cardVO, @@ -25,39 +24,73 @@ export default function PluginCardComponent({ setEnabled(!enabled); }) .catch((err) => { - toast.error("修改失败:" + err.message); + toast.error('修改失败:' + err.message); }) .finally(() => { setSwitchEnable(true); }); } return ( -
+
- + + +
-
{cardVO.author} /
+
+ {cardVO.author} /{' '} +
{cardVO.name}
- v{cardVO.version} + + v{cardVO.version} +
-
{cardVO.description}
+
+ {cardVO.description} +
- -
事件 {Object.keys(cardVO.event_handlers).length}
+ + + +
+ 事件 {Object.keys(cardVO.event_handlers).length} +
- -
工具 {cardVO.tools.length}
+ + + +
+ 工具 {cardVO.tools.length} +
diff --git a/web/src/app/home/plugins/plugin-installed/plugin-form/PluginForm.tsx b/web/src/app/home/plugins/plugin-installed/plugin-form/PluginForm.tsx index aa9c9262..823d14e3 100644 --- a/web/src/app/home/plugins/plugin-installed/plugin-form/PluginForm.tsx +++ b/web/src/app/home/plugins/plugin-installed/plugin-form/PluginForm.tsx @@ -1,9 +1,8 @@ import { useState, useEffect } from 'react'; -import { ApiRespPlugin, ApiRespPluginConfig, Plugin } from '@/app/infra/entities/api'; +import { ApiRespPluginConfig, Plugin } from '@/app/infra/entities/api'; import { httpClient } from '@/app/infra/http/HttpClient'; import DynamicFormComponent from '@/app/home/components/dynamic-form/DynamicFormComponent'; -import { IDynamicFormItemSchema } from '@/app/infra/entities/form/dynamic'; -import { Button } from "@/components/ui/button"; +import { Button } from '@/components/ui/button'; import { Dialog, DialogContent, @@ -11,8 +10,8 @@ import { DialogHeader, DialogTitle, DialogFooter, -} from "@/components/ui/dialog"; -import { toast } from "sonner"; +} from '@/components/ui/dialog'; +import { toast } from 'sonner'; enum PluginRemoveStatus { WAIT_INPUT = 'WAIT_INPUT', @@ -36,8 +35,11 @@ export default function PluginForm({ const [isSaving, setIsLoading] = useState(false); const [showDeleteConfirmModal, setShowDeleteConfirmModal] = useState(false); - const [pluginRemoveStatus, setPluginRemoveStatus] = useState(PluginRemoveStatus.WAIT_INPUT); - const [pluginRemoveError, setPluginRemoveError] = useState(null); + const [pluginRemoveStatus, setPluginRemoveStatus] = + useState(PluginRemoveStatus.WAIT_INPUT); + const [pluginRemoveError, setPluginRemoveError] = useState( + null, + ); useEffect(() => { // 获取插件信息 @@ -52,12 +54,16 @@ export default function PluginForm({ const handleSubmit = async (values: object) => { setIsLoading(true); - httpClient.updatePluginConfig(pluginAuthor, pluginName, values).then(() => { + httpClient + .updatePluginConfig(pluginAuthor, pluginName, values) + .then(() => { onFormSubmit(); - toast.success("保存成功"); - }).catch((error) => { - toast.error("保存失败:" + error.message); - }).finally(() => { + toast.success('保存成功'); + }) + .catch((error) => { + toast.error('保存失败:' + error.message); + }) + .finally(() => { setIsLoading(false); }); }; @@ -68,36 +74,39 @@ export default function PluginForm({ function deletePlugin() { setPluginRemoveStatus(PluginRemoveStatus.REMOVING); - httpClient.removePlugin(pluginAuthor, pluginName).then((res) => { - - const taskId = res.task_id; - - const interval = setInterval(() => { - httpClient.getAsyncTask(taskId).then((res) => { - if (res.runtime.done) { - clearInterval(interval); - if (res.runtime.exception) { - setPluginRemoveError(res.runtime.exception); - setPluginRemoveStatus(PluginRemoveStatus.ERROR); - } else { - setPluginRemoveStatus(PluginRemoveStatus.WAIT_INPUT); - setShowDeleteConfirmModal(false); - onFormSubmit(); - } - } - }); - }, 1000); + httpClient + .removePlugin(pluginAuthor, pluginName) + .then((res) => { + const taskId = res.task_id; - }).catch((error) => { - setPluginRemoveError(error.message); - setPluginRemoveStatus(PluginRemoveStatus.ERROR); - }) + const interval = setInterval(() => { + httpClient.getAsyncTask(taskId).then((res) => { + if (res.runtime.done) { + clearInterval(interval); + if (res.runtime.exception) { + setPluginRemoveError(res.runtime.exception); + setPluginRemoveStatus(PluginRemoveStatus.ERROR); + } else { + setPluginRemoveStatus(PluginRemoveStatus.WAIT_INPUT); + setShowDeleteConfirmModal(false); + onFormSubmit(); + } + } + }); + }, 1000); + }) + .catch((error) => { + setPluginRemoveError(error.message); + setPluginRemoveStatus(PluginRemoveStatus.ERROR); + }); } return (
- - + 删除确认 @@ -120,17 +129,23 @@ export default function PluginForm({ {pluginRemoveStatus === PluginRemoveStatus.WAIT_INPUT && ( - + )} {pluginRemoveStatus === PluginRemoveStatus.WAIT_INPUT && ( - )} @@ -140,10 +155,13 @@ export default function PluginForm({ )} {pluginRemoveStatus === PluginRemoveStatus.ERROR && ( - )} @@ -151,7 +169,6 @@ export default function PluginForm({ -
{pluginInfo.name}
@@ -160,7 +177,7 @@ export default function PluginForm({ {pluginInfo.config_schema.length > 0 && ( } onSubmit={(values) => { let config = pluginConfig.config; config = { @@ -174,15 +191,12 @@ export default function PluginForm({ /> )} {pluginInfo.config_schema.length === 0 && ( -
- 该插件没有配置项。 -
+
该插件没有配置项。
)}
-
); -} \ No newline at end of file +} diff --git a/web/src/app/home/plugins/plugin-market/PluginMarketComponent.tsx b/web/src/app/home/plugins/plugin-market/PluginMarketComponent.tsx index 5d9e590f..1d1f131d 100644 --- a/web/src/app/home/plugins/plugin-market/PluginMarketComponent.tsx +++ b/web/src/app/home/plugins/plugin-market/PluginMarketComponent.tsx @@ -9,18 +9,23 @@ import { Input } from '@/components/ui/input'; import { Pagination, PaginationContent, - PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, -} from "@/components/ui/pagination" -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" +} from '@/components/ui/pagination'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; export default function PluginMarketComponent({ askInstallPlugin, }: { - askInstallPlugin: (githubURL: string) => void, + askInstallPlugin: (githubURL: string) => void; }) { const [marketPluginList, setMarketPluginList] = useState< PluginMarketCardVO[] @@ -45,7 +50,7 @@ export default function PluginMarketComponent({ function onInputSearchKeyword(keyword: string) { setSearchKeyword(keyword); - + // 清除之前的定时器 if (searchTimeout.current) { clearTimeout(searchTimeout.current); @@ -69,33 +74,31 @@ export default function PluginMarketComponent({ .getMarketPlugins(page, pageSize, keyword, sortBy, sortOrder) .then((res) => { setMarketPluginList( - res.plugins.map( - (marketPlugin) => { - let repository = marketPlugin.repository; - if (repository.startsWith('https://github.com/')) { - repository = repository.replace('https://github.com/', ''); - } + res.plugins.map((marketPlugin) => { + let repository = marketPlugin.repository; + if (repository.startsWith('https://github.com/')) { + repository = repository.replace('https://github.com/', ''); + } - if (repository.startsWith('github.com/')) { - repository = repository.replace('github.com/', ''); - } + if (repository.startsWith('github.com/')) { + repository = repository.replace('github.com/', ''); + } - const author = repository.split('/')[0]; - const name = repository.split('/')[1]; - return new PluginMarketCardVO({ - author: author, - description: marketPlugin.description, - githubURL: `https://github.com/${repository}`, - name: name, - pluginId: String(marketPlugin.ID), - starCount: marketPlugin.stars, - version: - 'version' in marketPlugin - ? String(marketPlugin.version) - : '1.0.0', // Default version if not provided - }); - }, - ), + const author = repository.split('/')[0]; + const name = repository.split('/')[1]; + return new PluginMarketCardVO({ + author: author, + description: marketPlugin.description, + githubURL: `https://github.com/${repository}`, + name: name, + pluginId: String(marketPlugin.ID), + starCount: marketPlugin.stars, + version: + 'version' in marketPlugin + ? String(marketPlugin.version) + : '1.0.0', // Default version if not provided + }); + }), ); setTotalCount(res.total); setLoading(false); @@ -113,7 +116,7 @@ export default function PluginMarketComponent({ } function handleSortChange(value: string) { - const [newSortBy, newSortOrder] = value.split(',').map(s => s.trim()); + const [newSortBy, newSortOrder] = value.split(',').map((s) => s.trim()); setSortByValue(newSortBy); setSortOrderValue(newSortOrder); setNowPage(1); @@ -132,7 +135,10 @@ export default function PluginMarketComponent({ onChange={(e) => onInputSearchKeyword(e.target.value)} /> - @@ -147,10 +153,12 @@ export default function PluginMarketComponent({ {totalCount > 0 && ( - + handlePageChange(nowPage - 1)} - className={nowPage <= 1 ? 'pointer-events-none opacity-50' : ''} + className={ + nowPage <= 1 ? 'pointer-events-none opacity-50' : '' + } /> @@ -158,35 +166,50 @@ export default function PluginMarketComponent({ {(() => { const totalPages = Math.ceil(totalCount / pageSize); const maxVisiblePages = 5; - let startPage = Math.max(1, nowPage - Math.floor(maxVisiblePages / 2)); - let endPage = Math.min(totalPages, startPage + maxVisiblePages - 1); + let startPage = Math.max( + 1, + nowPage - Math.floor(maxVisiblePages / 2), + ); + const endPage = Math.min( + totalPages, + startPage + maxVisiblePages - 1, + ); if (endPage - startPage + 1 < maxVisiblePages) { startPage = Math.max(1, endPage - maxVisiblePages + 1); } - return Array.from({ length: endPage - startPage + 1 }, (_, i) => { - const pageNum = startPage + i; - return ( - - handlePageChange(pageNum)} + return Array.from( + { length: endPage - startPage + 1 }, + (_, i) => { + const pageNum = startPage + i; + return ( + - - {pageNum} - - - - ); - }); + handlePageChange(pageNum)} + > + + {pageNum} + + + + ); + }, + ); })()} - - + handlePageChange(nowPage + 1)} - className={nowPage >= Math.ceil(totalCount / pageSize) ? 'pointer-events-none opacity-50' : ''} + className={ + nowPage >= Math.ceil(totalCount / pageSize) + ? 'pointer-events-none opacity-50' + : '' + } /> @@ -207,9 +230,12 @@ export default function PluginMarketComponent({ ) : ( marketPluginList.map((vo, index) => (
- { - askInstallPlugin(githubURL); - }} /> + { + askInstallPlugin(githubURL); + }} + />
)) )} diff --git a/web/src/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardComponent.tsx b/web/src/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardComponent.tsx index 85d52878..618c9783 100644 --- a/web/src/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardComponent.tsx +++ b/web/src/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardComponent.tsx @@ -15,40 +15,66 @@ export default function PluginMarketCardComponent({ return (
- + + +
-
{cardVO.author} /
+
+ {cardVO.author} /{' '} +
{cardVO.name}
-
{cardVO.description}
+
+ {cardVO.description} +
- -
星标 {cardVO.starCount}
+ + + +
+ 星标 {cardVO.starCount} +
- window.open(cardVO.githubURL, '_blank')} - > - + > + 安装 +
diff --git a/web/src/app/infra/entities/api/index.ts b/web/src/app/infra/entities/api/index.ts index 0e16fe87..28c9de7f 100644 --- a/web/src/app/infra/entities/api/index.ts +++ b/web/src/app/infra/entities/api/index.ts @@ -1,5 +1,4 @@ import { IDynamicFormItemSchema } from '@/app/infra/entities/form/dynamic'; -import { I18nLabel } from '@/app/infra/entities/common'; import { PipelineConfigTab } from '@/app/infra/entities/pipeline'; export interface ApiResponse { @@ -32,7 +31,7 @@ export interface Requester { icon?: string; spec: { config: IDynamicFormItemSchema[]; - } + }; } export interface ApiRespProviderLLMModels { @@ -307,4 +306,4 @@ export interface GetPipelineResponseData { export interface GetPipelineMetadataResponseData { configs: PipelineConfigTab[]; -} \ No newline at end of file +} diff --git a/web/src/app/infra/entities/form/dynamic.ts b/web/src/app/infra/entities/form/dynamic.ts index 6e70062b..6a185c8b 100644 --- a/web/src/app/infra/entities/form/dynamic.ts +++ b/web/src/app/infra/entities/form/dynamic.ts @@ -1,29 +1,29 @@ import { I18nLabel } from '@/app/infra/entities/common'; export interface IDynamicFormItemSchema { - id: string; - default: string | number | boolean | Array; - label: I18nLabel; - name: string; - required: boolean; - type: DynamicFormItemType; - description?: I18nLabel; - options?: IDynamicFormItemOption[]; - } + id: string; + default: string | number | boolean | Array; + label: I18nLabel; + name: string; + required: boolean; + type: DynamicFormItemType; + description?: I18nLabel; + options?: IDynamicFormItemOption[]; +} export enum DynamicFormItemType { - INT = 'integer', - FLOAT = 'float', - BOOLEAN = 'boolean', - STRING = 'string', - STRING_ARRAY = 'array[string]', - SELECT = 'select', - LLM_MODEL_SELECTOR = 'llm-model-selector', - PROMPT_EDITOR = 'prompt-editor', - UNKNOWN = 'unknown', - } + INT = 'integer', + FLOAT = 'float', + BOOLEAN = 'boolean', + STRING = 'string', + STRING_ARRAY = 'array[string]', + SELECT = 'select', + LLM_MODEL_SELECTOR = 'llm-model-selector', + PROMPT_EDITOR = 'prompt-editor', + UNKNOWN = 'unknown', +} export interface IDynamicFormItemOption { - name: string; - label: I18nLabel; - } \ No newline at end of file + name: string; + label: I18nLabel; +} diff --git a/web/src/app/infra/entities/pipeline/index.ts b/web/src/app/infra/entities/pipeline/index.ts index 2c364b08..29a5f6af 100644 --- a/web/src/app/infra/entities/pipeline/index.ts +++ b/web/src/app/infra/entities/pipeline/index.ts @@ -20,4 +20,4 @@ export interface PipelineConfigStage { label: I18nLabel; description?: I18nLabel; config: IDynamicFormItemSchema[]; -} \ No newline at end of file +} diff --git a/web/src/app/infra/http/HttpClient.ts b/web/src/app/infra/http/HttpClient.ts index 90cdb842..843d81ac 100644 --- a/web/src/app/infra/http/HttpClient.ts +++ b/web/src/app/infra/http/HttpClient.ts @@ -28,10 +28,8 @@ import { MarketPluginResponse, GetPipelineResponseData, GetPipelineMetadataResponseData, - AsyncTask + AsyncTask, } from '@/app/infra/entities/api'; -import { toast } from "sonner" - type JSONValue = string | number | boolean | JSONObject | JSONArray | null; interface JSONObject { @@ -130,9 +128,8 @@ class HttpClient { switch (status) { case 401: - console.log('401 error: ', errMessage, error.request); - console.log('responseURL', error.request.responseURL) + console.log('responseURL', error.request.responseURL); localStorage.removeItem('token'); if (!error.request.responseURL.includes('/check-token')) { window.location.href = '/login'; @@ -231,7 +228,10 @@ class HttpClient { } public getProviderRequesterIconURL(name: string): string { - return this.instance.defaults.baseURL + `/api/v1/provider/requesters/${name}/icon`; + return ( + this.instance.defaults.baseURL + + `/api/v1/provider/requesters/${name}/icon` + ); } // ============ Provider Model LLM ============ @@ -289,7 +289,9 @@ class HttpClient { } public getAdapterIconURL(name: string): string { - return this.instance.defaults.baseURL + `/api/v1/platform/adapters/${name}/icon`; + return ( + this.instance.defaults.baseURL + `/api/v1/platform/adapters/${name}/icon` + ); } // ============ Platform Bots ============ @@ -418,7 +420,7 @@ class HttpClient { } // export const httpClient = new HttpClient("https://version-4.langbot.dev"); -export const httpClient = new HttpClient("http://localhost:5300"); +export const httpClient = new HttpClient('http://localhost:5300'); // 临时写法,未来两种Client都继承自HttpClient父类,不允许共享方法 export const spaceClient = new HttpClient('https://space.langbot.app'); diff --git a/web/src/app/login/page.tsx b/web/src/app/login/page.tsx index 5d43bdcc..297f8b83 100644 --- a/web/src/app/login/page.tsx +++ b/web/src/app/login/page.tsx @@ -1,12 +1,16 @@ 'use client'; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Checkbox } from "@/components/ui/checkbox"; -import { Label } from "@/components/ui/label"; -import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import * as z from "zod"; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { + Card, + CardContent, + CardHeader, + CardTitle, + CardDescription, +} from '@/components/ui/card'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import * as z from 'zod'; import { Form, FormControl, @@ -14,17 +18,17 @@ import { FormItem, FormLabel, FormMessage, -} from "@/components/ui/form"; -import { useEffect, useState } from 'react'; +} from '@/components/ui/form'; +import { useEffect } from 'react'; import { httpClient } from '@/app/infra/http/HttpClient'; import { useRouter } from 'next/navigation'; -import { Mail, Lock } from "lucide-react"; +import { Mail, Lock } from 'lucide-react'; import langbotIcon from '@/app/assets/langbot-logo.webp'; -import { toast } from "sonner" +import { toast } from 'sonner'; const formSchema = z.object({ - email: z.string().email("请输入有效的邮箱地址"), - password: z.string().min(1, "请输入密码"), + email: z.string().email('请输入有效的邮箱地址'), + password: z.string().min(1, '请输入密码'), }); export default function Login() { @@ -33,8 +37,8 @@ export default function Login() { const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { - email: "", - password: "", + email: '', + password: '', }, }); @@ -57,7 +61,8 @@ export default function Login() { } function checkIfAlreadyLoggedIn() { - httpClient.checkUserToken() + httpClient + .checkUserToken() .then((res) => { if (res.token) { localStorage.setItem('token', res.token); @@ -79,12 +84,12 @@ export default function Login() { localStorage.setItem('token', res.token); console.log('login success: ', res); router.push('/home'); - toast.success("登录成功"); + toast.success('登录成功'); }) .catch((err) => { console.log('login error: ', err); - toast.error("登录失败,请检查邮箱和密码是否正确"); + toast.error('登录失败,请检查邮箱和密码是否正确'); }); } @@ -92,13 +97,15 @@ export default function Login() {
- LangBot + LangBot 欢迎回到 LangBot 👋 - - 登录以继续 - + 登录以继续
@@ -146,10 +153,7 @@ export default function Login() { )} /> -
diff --git a/web/src/app/not-found.tsx b/web/src/app/not-found.tsx index 720a8c7e..90941c7a 100644 --- a/web/src/app/not-found.tsx +++ b/web/src/app/not-found.tsx @@ -1,8 +1,7 @@ 'use client'; import { useRouter } from 'next/navigation'; -import { Button } from "@/components/ui/button"; -import { GithubIcon } from "lucide-react"; +import { Button } from '@/components/ui/button'; export default function NotFound() { const router = useRouter(); @@ -22,7 +21,8 @@ export default function NotFound() { 页面不存在

- 您要查找的页面似乎不存在。请检查您输入的 URL 是否正确,或者返回首页。 + 您要查找的页面似乎不存在。请检查您输入的 URL + 是否正确,或者返回首页。

diff --git a/web/src/app/register/page.tsx b/web/src/app/register/page.tsx index 39ef1ba2..c1a38f58 100644 --- a/web/src/app/register/page.tsx +++ b/web/src/app/register/page.tsx @@ -1,12 +1,16 @@ 'use client'; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Checkbox } from "@/components/ui/checkbox"; -import { Label } from "@/components/ui/label"; -import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import * as z from "zod"; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { + Card, + CardContent, + CardHeader, + CardTitle, + CardDescription, +} from '@/components/ui/card'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import * as z from 'zod'; import { Form, FormControl, @@ -14,18 +18,17 @@ import { FormItem, FormLabel, FormMessage, - FormDescription, -} from "@/components/ui/form"; -import { useEffect, useState } from 'react'; +} from '@/components/ui/form'; +import { useEffect } from 'react'; import { httpClient } from '@/app/infra/http/HttpClient'; import { useRouter } from 'next/navigation'; -import { Mail, Lock } from "lucide-react"; +import { Mail, Lock } from 'lucide-react'; import langbotIcon from '@/app/assets/langbot-logo.webp'; -import { toast } from "sonner"; +import { toast } from 'sonner'; const formSchema = z.object({ - email: z.string().email("请输入有效的邮箱地址"), - password: z.string().min(1, "请输入密码"), + email: z.string().email('请输入有效的邮箱地址'), + password: z.string().min(1, '请输入密码'), }); export default function Register() { @@ -34,8 +37,8 @@ export default function Register() { const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { - email: "", - password: "", + email: '', + password: '', }, }); @@ -65,12 +68,12 @@ export default function Register() { .initUser(username, password) .then((res) => { console.log('init user success: ', res); - toast.success("初始化成功 请登录"); + toast.success('初始化成功 请登录'); router.push('/login'); }) .catch((err) => { console.log('init user error: ', err); - toast.error("初始化失败:" + err.message); + toast.error('初始化失败:' + err.message); }); } @@ -78,7 +81,11 @@ export default function Register() {
- LangBot + LangBot 初始化 LangBot 👋 @@ -134,10 +141,7 @@ export default function Register() { )} /> - diff --git a/web/src/components/ui/alert.tsx b/web/src/components/ui/alert.tsx index 14213546..93a76f8c 100644 --- a/web/src/components/ui/alert.tsx +++ b/web/src/components/ui/alert.tsx @@ -1,29 +1,29 @@ -import * as React from "react" -import { cva, type VariantProps } from "class-variance-authority" +import * as React from 'react'; +import { cva, type VariantProps } from 'class-variance-authority'; -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils'; const alertVariants = cva( - "relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current", + 'relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current', { variants: { variant: { - default: "bg-card text-card-foreground", + default: 'bg-card text-card-foreground', destructive: - "text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90", + 'text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90', }, }, defaultVariants: { - variant: "default", + variant: 'default', }, - } -) + }, +); function Alert({ className, variant, ...props -}: React.ComponentProps<"div"> & VariantProps) { +}: React.ComponentProps<'div'> & VariantProps) { return (
- ) + ); } -function AlertTitle({ className, ...props }: React.ComponentProps<"div">) { +function AlertTitle({ className, ...props }: React.ComponentProps<'div'>) { return (
- ) + ); } function AlertDescription({ className, ...props -}: React.ComponentProps<"div">) { +}: React.ComponentProps<'div'>) { return (
- ) + ); } -export { Alert, AlertTitle, AlertDescription } +export { Alert, AlertTitle, AlertDescription }; diff --git a/web/src/components/ui/badge.tsx b/web/src/components/ui/badge.tsx index 02054139..b0c1fc93 100644 --- a/web/src/components/ui/badge.tsx +++ b/web/src/components/ui/badge.tsx @@ -1,38 +1,38 @@ -import * as React from "react" -import { Slot } from "@radix-ui/react-slot" -import { cva, type VariantProps } from "class-variance-authority" +import * as React from 'react'; +import { Slot } from '@radix-ui/react-slot'; +import { cva, type VariantProps } from 'class-variance-authority'; -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils'; const badgeVariants = cva( - "inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden", + 'inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden', { variants: { variant: { default: - "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90", + 'border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90', secondary: - "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90", + 'border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90', destructive: - "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + 'border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60', outline: - "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground", + 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground', }, }, defaultVariants: { - variant: "default", + variant: 'default', }, - } -) + }, +); function Badge({ className, variant, asChild = false, ...props -}: React.ComponentProps<"span"> & +}: React.ComponentProps<'span'> & VariantProps & { asChild?: boolean }) { - const Comp = asChild ? Slot : "span" + const Comp = asChild ? Slot : 'span'; return ( - ) + ); } -export { Badge, badgeVariants } +export { Badge, badgeVariants }; diff --git a/web/src/components/ui/button.tsx b/web/src/components/ui/button.tsx index 24302e0b..2916f3fb 100644 --- a/web/src/components/ui/button.tsx +++ b/web/src/components/ui/button.tsx @@ -1,8 +1,8 @@ -import * as React from "react" -import { Slot } from "@radix-ui/react-slot" -import { cva, type VariantProps } from "class-variance-authority" +import * as React from 'react'; +import { Slot } from '@radix-ui/react-slot'; +import { cva, type VariantProps } from 'class-variance-authority'; -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils'; const buttonVariants = cva( "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", @@ -10,30 +10,30 @@ const buttonVariants = cva( variants: { variant: { default: - "bg-[#2288ee] text-primary-foreground shadow-xs hover:bg-[#2277e0]", + 'bg-[#2288ee] text-primary-foreground shadow-xs hover:bg-[#2277e0]', destructive: - "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + 'bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60', outline: - "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", + 'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50', secondary: - "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", + 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80', ghost: - "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", - link: "text-primary underline-offset-4 hover:underline", + 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50', + link: 'text-primary underline-offset-4 hover:underline', }, size: { - default: "h-9 px-4 py-2 has-[>svg]:px-3", - sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", - lg: "h-10 rounded-md px-6 has-[>svg]:px-4", - icon: "size-9", + default: 'h-9 px-4 py-2 has-[>svg]:px-3', + sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5', + lg: 'h-10 rounded-md px-6 has-[>svg]:px-4', + icon: 'size-9', }, }, defaultVariants: { - variant: "default", - size: "default", + variant: 'default', + size: 'default', }, - } -) + }, +); function Button({ className, @@ -41,11 +41,11 @@ function Button({ size, asChild = false, ...props -}: React.ComponentProps<"button"> & +}: React.ComponentProps<'button'> & VariantProps & { - asChild?: boolean + asChild?: boolean; }) { - const Comp = asChild ? Slot : "button" + const Comp = asChild ? Slot : 'button'; return ( - ) + ); } -export { Button, buttonVariants } +export { Button, buttonVariants }; diff --git a/web/src/components/ui/card.tsx b/web/src/components/ui/card.tsx index d05bbc6c..4898dfa6 100644 --- a/web/src/components/ui/card.tsx +++ b/web/src/components/ui/card.tsx @@ -1,84 +1,84 @@ -import * as React from "react" +import * as React from 'react'; -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils'; -function Card({ className, ...props }: React.ComponentProps<"div">) { +function Card({ className, ...props }: React.ComponentProps<'div'>) { return (
- ) + ); } -function CardHeader({ className, ...props }: React.ComponentProps<"div">) { +function CardHeader({ className, ...props }: React.ComponentProps<'div'>) { return (
- ) + ); } -function CardTitle({ className, ...props }: React.ComponentProps<"div">) { +function CardTitle({ className, ...props }: React.ComponentProps<'div'>) { return (
- ) + ); } -function CardDescription({ className, ...props }: React.ComponentProps<"div">) { +function CardDescription({ className, ...props }: React.ComponentProps<'div'>) { return (
- ) + ); } -function CardAction({ className, ...props }: React.ComponentProps<"div">) { +function CardAction({ className, ...props }: React.ComponentProps<'div'>) { return (
- ) + ); } -function CardContent({ className, ...props }: React.ComponentProps<"div">) { +function CardContent({ className, ...props }: React.ComponentProps<'div'>) { return (
- ) + ); } -function CardFooter({ className, ...props }: React.ComponentProps<"div">) { +function CardFooter({ className, ...props }: React.ComponentProps<'div'>) { return (
- ) + ); } export { @@ -89,4 +89,4 @@ export { CardAction, CardDescription, CardContent, -} +}; diff --git a/web/src/components/ui/checkbox.tsx b/web/src/components/ui/checkbox.tsx index fa0e4b59..6905d3c9 100644 --- a/web/src/components/ui/checkbox.tsx +++ b/web/src/components/ui/checkbox.tsx @@ -1,10 +1,10 @@ -"use client" +'use client'; -import * as React from "react" -import * as CheckboxPrimitive from "@radix-ui/react-checkbox" -import { CheckIcon } from "lucide-react" +import * as React from 'react'; +import * as CheckboxPrimitive from '@radix-ui/react-checkbox'; +import { CheckIcon } from 'lucide-react'; -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils'; function Checkbox({ className, @@ -14,8 +14,8 @@ function Checkbox({ @@ -26,7 +26,7 @@ function Checkbox({ - ) + ); } -export { Checkbox } +export { Checkbox }; diff --git a/web/src/components/ui/dialog.tsx b/web/src/components/ui/dialog.tsx index 7d7a9d31..a544a9ce 100644 --- a/web/src/components/ui/dialog.tsx +++ b/web/src/components/ui/dialog.tsx @@ -1,33 +1,33 @@ -"use client" +'use client'; -import * as React from "react" -import * as DialogPrimitive from "@radix-ui/react-dialog" -import { XIcon } from "lucide-react" +import * as React from 'react'; +import * as DialogPrimitive from '@radix-ui/react-dialog'; +import { XIcon } from 'lucide-react'; -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils'; function Dialog({ ...props }: React.ComponentProps) { - return + return ; } function DialogTrigger({ ...props }: React.ComponentProps) { - return + return ; } function DialogPortal({ ...props }: React.ComponentProps) { - return + return ; } function DialogClose({ ...props }: React.ComponentProps) { - return + return ; } function DialogOverlay({ @@ -38,12 +38,12 @@ function DialogOverlay({ - ) + ); } function DialogContent({ @@ -57,8 +57,8 @@ function DialogContent({ @@ -69,30 +69,30 @@ function DialogContent({ - ) + ); } -function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { +function DialogHeader({ className, ...props }: React.ComponentProps<'div'>) { return (
- ) + ); } -function DialogFooter({ className, ...props }: React.ComponentProps<"div">) { +function DialogFooter({ className, ...props }: React.ComponentProps<'div'>) { return (
- ) + ); } function DialogTitle({ @@ -102,10 +102,10 @@ function DialogTitle({ return ( - ) + ); } function DialogDescription({ @@ -115,10 +115,10 @@ function DialogDescription({ return ( - ) + ); } export { @@ -132,4 +132,4 @@ export { DialogPortal, DialogTitle, DialogTrigger, -} +}; diff --git a/web/src/components/ui/form.tsx b/web/src/components/ui/form.tsx index 524b986b..657c64a6 100644 --- a/web/src/components/ui/form.tsx +++ b/web/src/components/ui/form.tsx @@ -1,8 +1,8 @@ -"use client" +'use client'; -import * as React from "react" -import * as LabelPrimitive from "@radix-ui/react-label" -import { Slot } from "@radix-ui/react-slot" +import * as React from 'react'; +import * as LabelPrimitive from '@radix-ui/react-label'; +import { Slot } from '@radix-ui/react-slot'; import { Controller, FormProvider, @@ -11,23 +11,23 @@ import { type ControllerProps, type FieldPath, type FieldValues, -} from "react-hook-form" +} from 'react-hook-form'; -import { cn } from "@/lib/utils" -import { Label } from "@/components/ui/label" +import { cn } from '@/lib/utils'; +import { Label } from '@/components/ui/label'; -const Form = FormProvider +const Form = FormProvider; type FormFieldContextValue< TFieldValues extends FieldValues = FieldValues, TName extends FieldPath = FieldPath, > = { - name: TName -} + name: TName; +}; const FormFieldContext = React.createContext( - {} as FormFieldContextValue -) + {} as FormFieldContextValue, +); const FormField = < TFieldValues extends FieldValues = FieldValues, @@ -39,21 +39,21 @@ const FormField = < - ) -} + ); +}; const useFormField = () => { - const fieldContext = React.useContext(FormFieldContext) - const itemContext = React.useContext(FormItemContext) - const { getFieldState } = useFormContext() - const formState = useFormState({ name: fieldContext.name }) - const fieldState = getFieldState(fieldContext.name, formState) + const fieldContext = React.useContext(FormFieldContext); + const itemContext = React.useContext(FormItemContext); + const { getFieldState } = useFormContext(); + const formState = useFormState({ name: fieldContext.name }); + const fieldState = getFieldState(fieldContext.name, formState); if (!fieldContext) { - throw new Error("useFormField should be used within ") + throw new Error('useFormField should be used within '); } - const { id } = itemContext + const { id } = itemContext; return { id, @@ -62,50 +62,51 @@ const useFormField = () => { formDescriptionId: `${id}-form-item-description`, formMessageId: `${id}-form-item-message`, ...fieldState, - } -} + }; +}; type FormItemContextValue = { - id: string -} + id: string; +}; const FormItemContext = React.createContext( - {} as FormItemContextValue -) + {} as FormItemContextValue, +); -function FormItem({ className, ...props }: React.ComponentProps<"div">) { - const id = React.useId() +function FormItem({ className, ...props }: React.ComponentProps<'div'>) { + const id = React.useId(); return (
- ) + ); } function FormLabel({ className, ...props }: React.ComponentProps) { - const { error, formItemId } = useFormField() + const { error, formItemId } = useFormField(); return (