@lobehub/chat 1.98.1 → 1.99.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/rules/backend-architecture.mdc +93 -17
- package/.cursor/rules/cursor-ux.mdc +45 -35
- package/.cursor/rules/project-introduce.mdc +72 -6
- package/.cursor/rules/rules-attach.mdc +16 -7
- package/.eslintrc.js +10 -0
- package/CHANGELOG.md +52 -0
- package/apps/desktop/README.md +7 -0
- package/apps/desktop/electron-builder.js +5 -0
- package/apps/desktop/package.json +2 -1
- package/apps/desktop/src/main/const/dir.ts +3 -0
- package/apps/desktop/src/main/controllers/UploadFileCtr.ts +13 -8
- package/apps/desktop/src/main/core/App.ts +8 -0
- package/apps/desktop/src/main/core/StaticFileServerManager.ts +221 -0
- package/apps/desktop/src/main/services/fileSrv.ts +231 -44
- package/apps/desktop/src/main/utils/next-electron-rsc.ts +36 -5
- package/changelog/v1.json +18 -0
- package/docs/development/database-schema.dbml +70 -0
- package/locales/ar/common.json +2 -0
- package/locales/ar/components.json +35 -0
- package/locales/ar/error.json +2 -0
- package/locales/ar/image.json +100 -0
- package/locales/ar/metadata.json +4 -0
- package/locales/ar/modelProvider.json +1 -0
- package/locales/ar/models.json +51 -9
- package/locales/ar/plugin.json +22 -0
- package/locales/ar/providers.json +3 -0
- package/locales/ar/setting.json +5 -0
- package/locales/bg-BG/common.json +2 -0
- package/locales/bg-BG/components.json +35 -0
- package/locales/bg-BG/error.json +2 -0
- package/locales/bg-BG/image.json +100 -0
- package/locales/bg-BG/metadata.json +4 -0
- package/locales/bg-BG/modelProvider.json +1 -0
- package/locales/bg-BG/models.json +51 -9
- package/locales/bg-BG/plugin.json +22 -0
- package/locales/bg-BG/providers.json +3 -0
- package/locales/bg-BG/setting.json +5 -0
- package/locales/de-DE/common.json +2 -0
- package/locales/de-DE/components.json +35 -0
- package/locales/de-DE/error.json +2 -0
- package/locales/de-DE/image.json +100 -0
- package/locales/de-DE/metadata.json +4 -0
- package/locales/de-DE/modelProvider.json +1 -0
- package/locales/de-DE/models.json +51 -9
- package/locales/de-DE/plugin.json +22 -0
- package/locales/de-DE/providers.json +3 -0
- package/locales/de-DE/setting.json +5 -0
- package/locales/en-US/common.json +2 -0
- package/locales/en-US/components.json +35 -0
- package/locales/en-US/error.json +2 -0
- package/locales/en-US/image.json +100 -0
- package/locales/en-US/metadata.json +4 -0
- package/locales/en-US/modelProvider.json +1 -0
- package/locales/en-US/models.json +51 -9
- package/locales/en-US/plugin.json +22 -0
- package/locales/en-US/providers.json +3 -0
- package/locales/en-US/setting.json +5 -0
- package/locales/es-ES/common.json +2 -0
- package/locales/es-ES/components.json +35 -0
- package/locales/es-ES/error.json +2 -0
- package/locales/es-ES/image.json +100 -0
- package/locales/es-ES/metadata.json +4 -0
- package/locales/es-ES/modelProvider.json +1 -0
- package/locales/es-ES/models.json +51 -9
- package/locales/es-ES/plugin.json +22 -0
- package/locales/es-ES/providers.json +3 -0
- package/locales/es-ES/setting.json +5 -0
- package/locales/fa-IR/common.json +2 -0
- package/locales/fa-IR/components.json +35 -0
- package/locales/fa-IR/error.json +2 -0
- package/locales/fa-IR/image.json +100 -0
- package/locales/fa-IR/metadata.json +4 -0
- package/locales/fa-IR/modelProvider.json +1 -0
- package/locales/fa-IR/models.json +51 -9
- package/locales/fa-IR/plugin.json +22 -0
- package/locales/fa-IR/providers.json +3 -0
- package/locales/fa-IR/setting.json +5 -0
- package/locales/fr-FR/common.json +2 -0
- package/locales/fr-FR/components.json +35 -0
- package/locales/fr-FR/error.json +2 -0
- package/locales/fr-FR/image.json +100 -0
- package/locales/fr-FR/metadata.json +4 -0
- package/locales/fr-FR/modelProvider.json +1 -0
- package/locales/fr-FR/models.json +51 -9
- package/locales/fr-FR/plugin.json +22 -0
- package/locales/fr-FR/providers.json +3 -0
- package/locales/fr-FR/setting.json +5 -0
- package/locales/it-IT/common.json +2 -0
- package/locales/it-IT/components.json +35 -0
- package/locales/it-IT/error.json +2 -0
- package/locales/it-IT/image.json +100 -0
- package/locales/it-IT/metadata.json +4 -0
- package/locales/it-IT/modelProvider.json +1 -0
- package/locales/it-IT/models.json +51 -9
- package/locales/it-IT/plugin.json +22 -0
- package/locales/it-IT/providers.json +3 -0
- package/locales/it-IT/setting.json +5 -0
- package/locales/ja-JP/common.json +2 -0
- package/locales/ja-JP/components.json +35 -0
- package/locales/ja-JP/error.json +2 -0
- package/locales/ja-JP/image.json +100 -0
- package/locales/ja-JP/metadata.json +4 -0
- package/locales/ja-JP/modelProvider.json +1 -0
- package/locales/ja-JP/models.json +51 -9
- package/locales/ja-JP/plugin.json +22 -0
- package/locales/ja-JP/providers.json +3 -0
- package/locales/ja-JP/setting.json +5 -0
- package/locales/ko-KR/common.json +2 -0
- package/locales/ko-KR/components.json +35 -0
- package/locales/ko-KR/error.json +2 -0
- package/locales/ko-KR/image.json +100 -0
- package/locales/ko-KR/metadata.json +4 -0
- package/locales/ko-KR/modelProvider.json +1 -0
- package/locales/ko-KR/models.json +51 -9
- package/locales/ko-KR/plugin.json +22 -0
- package/locales/ko-KR/providers.json +3 -0
- package/locales/ko-KR/setting.json +5 -0
- package/locales/nl-NL/common.json +2 -0
- package/locales/nl-NL/components.json +35 -0
- package/locales/nl-NL/error.json +2 -0
- package/locales/nl-NL/image.json +100 -0
- package/locales/nl-NL/metadata.json +4 -0
- package/locales/nl-NL/modelProvider.json +1 -0
- package/locales/nl-NL/models.json +51 -9
- package/locales/nl-NL/plugin.json +22 -0
- package/locales/nl-NL/providers.json +3 -0
- package/locales/nl-NL/setting.json +5 -0
- package/locales/pl-PL/common.json +2 -0
- package/locales/pl-PL/components.json +35 -0
- package/locales/pl-PL/error.json +2 -0
- package/locales/pl-PL/image.json +100 -0
- package/locales/pl-PL/metadata.json +4 -0
- package/locales/pl-PL/modelProvider.json +1 -0
- package/locales/pl-PL/models.json +51 -9
- package/locales/pl-PL/plugin.json +22 -0
- package/locales/pl-PL/providers.json +3 -0
- package/locales/pl-PL/setting.json +5 -0
- package/locales/pt-BR/common.json +2 -0
- package/locales/pt-BR/components.json +35 -0
- package/locales/pt-BR/error.json +2 -0
- package/locales/pt-BR/image.json +100 -0
- package/locales/pt-BR/metadata.json +4 -0
- package/locales/pt-BR/modelProvider.json +1 -0
- package/locales/pt-BR/models.json +51 -9
- package/locales/pt-BR/plugin.json +22 -0
- package/locales/pt-BR/providers.json +3 -0
- package/locales/pt-BR/setting.json +5 -0
- package/locales/ru-RU/common.json +2 -0
- package/locales/ru-RU/components.json +35 -0
- package/locales/ru-RU/error.json +2 -0
- package/locales/ru-RU/image.json +100 -0
- package/locales/ru-RU/metadata.json +4 -0
- package/locales/ru-RU/modelProvider.json +1 -0
- package/locales/ru-RU/models.json +51 -9
- package/locales/ru-RU/plugin.json +22 -0
- package/locales/ru-RU/providers.json +3 -0
- package/locales/ru-RU/setting.json +5 -0
- package/locales/tr-TR/common.json +2 -0
- package/locales/tr-TR/components.json +35 -0
- package/locales/tr-TR/error.json +2 -0
- package/locales/tr-TR/image.json +100 -0
- package/locales/tr-TR/metadata.json +4 -0
- package/locales/tr-TR/modelProvider.json +1 -0
- package/locales/tr-TR/models.json +51 -9
- package/locales/tr-TR/plugin.json +22 -0
- package/locales/tr-TR/providers.json +3 -0
- package/locales/tr-TR/setting.json +5 -0
- package/locales/vi-VN/common.json +2 -0
- package/locales/vi-VN/components.json +35 -0
- package/locales/vi-VN/error.json +2 -0
- package/locales/vi-VN/image.json +100 -0
- package/locales/vi-VN/metadata.json +4 -0
- package/locales/vi-VN/modelProvider.json +1 -0
- package/locales/vi-VN/models.json +51 -9
- package/locales/vi-VN/plugin.json +22 -0
- package/locales/vi-VN/providers.json +3 -0
- package/locales/vi-VN/setting.json +5 -0
- package/locales/zh-CN/common.json +2 -0
- package/locales/zh-CN/components.json +35 -0
- package/locales/zh-CN/error.json +2 -0
- package/locales/zh-CN/image.json +100 -0
- package/locales/zh-CN/metadata.json +4 -0
- package/locales/zh-CN/modelProvider.json +1 -0
- package/locales/zh-CN/models.json +51 -9
- package/locales/zh-CN/plugin.json +22 -0
- package/locales/zh-CN/providers.json +3 -0
- package/locales/zh-CN/setting.json +5 -0
- package/locales/zh-TW/common.json +2 -0
- package/locales/zh-TW/components.json +35 -0
- package/locales/zh-TW/error.json +2 -0
- package/locales/zh-TW/image.json +100 -0
- package/locales/zh-TW/metadata.json +4 -0
- package/locales/zh-TW/modelProvider.json +1 -0
- package/locales/zh-TW/models.json +51 -9
- package/locales/zh-TW/plugin.json +22 -0
- package/locales/zh-TW/providers.json +3 -0
- package/locales/zh-TW/setting.json +5 -0
- package/package.json +11 -4
- package/packages/electron-server-ipc/src/events/file.ts +3 -1
- package/packages/electron-server-ipc/src/types/file.ts +15 -0
- package/src/app/[variants]/(main)/_layout/Desktop/SideBar/TopActions.tsx +11 -1
- package/src/app/[variants]/(main)/image/@menu/components/AspectRatioSelect/index.tsx +73 -0
- package/src/app/[variants]/(main)/image/@menu/components/SeedNumberInput/index.tsx +39 -0
- package/src/app/[variants]/(main)/image/@menu/components/SizeSelect/index.tsx +89 -0
- package/src/app/[variants]/(main)/image/@menu/default.tsx +11 -0
- package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/AspectRatioSelect.tsx +24 -0
- package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/DimensionControlGroup.tsx +107 -0
- package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/ImageNum.tsx +290 -0
- package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/ImageUpload.tsx +504 -0
- package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/ImageUrl.tsx +18 -0
- package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/ImageUrlsUpload.tsx +19 -0
- package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/ModelSelect.tsx +155 -0
- package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/MultiImagesUpload/ImageManageModal.tsx +415 -0
- package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/MultiImagesUpload/index.tsx +732 -0
- package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/SeedNumberInput.tsx +24 -0
- package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/SizeSelect.tsx +17 -0
- package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/SizeSliderInput.tsx +15 -0
- package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/StepsSliderInput.tsx +11 -0
- package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/constants.ts +1 -0
- package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/index.tsx +93 -0
- package/src/app/[variants]/(main)/image/@topic/default.tsx +17 -0
- package/src/app/[variants]/(main)/image/@topic/features/Topics/NewTopicButton.tsx +64 -0
- package/src/app/[variants]/(main)/image/@topic/features/Topics/SkeletonList.tsx +34 -0
- package/src/app/[variants]/(main)/image/@topic/features/Topics/TopicItem.tsx +136 -0
- package/src/app/[variants]/(main)/image/@topic/features/Topics/TopicItemContainer.tsx +91 -0
- package/src/app/[variants]/(main)/image/@topic/features/Topics/TopicList.tsx +57 -0
- package/src/app/[variants]/(main)/image/@topic/features/Topics/TopicUrlSync.tsx +37 -0
- package/src/app/[variants]/(main)/image/@topic/features/Topics/index.tsx +19 -0
- package/src/app/[variants]/(main)/image/NotSupportClient.tsx +153 -0
- package/src/app/[variants]/(main)/image/_layout/Desktop/Container.tsx +35 -0
- package/src/app/[variants]/(main)/image/_layout/Desktop/RegisterHotkeys.tsx +10 -0
- package/src/app/[variants]/(main)/image/_layout/Desktop/index.tsx +30 -0
- package/src/app/[variants]/(main)/image/_layout/Mobile/index.tsx +14 -0
- package/src/app/[variants]/(main)/image/_layout/type.ts +7 -0
- package/src/app/[variants]/(main)/image/features/GenerationFeed/BatchItem.tsx +196 -0
- package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/ActionButtons.tsx +60 -0
- package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/ElapsedTime.tsx +90 -0
- package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/ErrorState.tsx +65 -0
- package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/LoadingState.tsx +43 -0
- package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/SuccessState.tsx +49 -0
- package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/index.tsx +156 -0
- package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/styles.ts +51 -0
- package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/types.ts +39 -0
- package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/utils.ts +11 -0
- package/src/app/[variants]/(main)/image/features/GenerationFeed/index.tsx +97 -0
- package/src/app/[variants]/(main)/image/features/ImageWorkspace/Content.tsx +48 -0
- package/src/app/[variants]/(main)/image/features/ImageWorkspace/EmptyState.tsx +37 -0
- package/src/app/[variants]/(main)/image/features/ImageWorkspace/SkeletonList.tsx +50 -0
- package/src/app/[variants]/(main)/image/features/ImageWorkspace/index.tsx +23 -0
- package/src/app/[variants]/(main)/image/features/PromptInput/Title.tsx +38 -0
- package/src/app/[variants]/(main)/image/features/PromptInput/index.tsx +114 -0
- package/src/app/[variants]/(main)/image/layout.tsx +19 -0
- package/src/app/[variants]/(main)/image/loading.tsx +3 -0
- package/src/app/[variants]/(main)/image/page.tsx +47 -0
- package/src/app/[variants]/(main)/settings/system-agent/index.tsx +2 -1
- package/src/chains/summaryGenerationTitle.ts +25 -0
- package/src/components/ImageItem/index.tsx +9 -6
- package/src/{features/Conversation/Error → components/InvalidAPIKey}/APIKeyForm/Bedrock.tsx +3 -4
- package/src/{features/Conversation/Error → components/InvalidAPIKey}/APIKeyForm/ProviderApiKeyForm.tsx +5 -4
- package/src/components/InvalidAPIKey/APIKeyForm/index.tsx +108 -0
- package/src/{features/Conversation/Error → components/InvalidAPIKey}/APIKeyForm/useApiKey.ts +2 -1
- package/src/components/InvalidAPIKey/index.tsx +30 -0
- package/src/components/KeyValueEditor/index.tsx +203 -0
- package/src/components/KeyValueEditor/utils.ts +42 -0
- package/src/config/aiModels/fal.ts +52 -0
- package/src/config/aiModels/index.ts +3 -0
- package/src/config/aiModels/openai.ts +20 -6
- package/src/config/llm.ts +6 -0
- package/src/config/modelProviders/fal.ts +21 -0
- package/src/config/modelProviders/index.ts +3 -0
- package/src/config/paramsSchemas/fal/flux-kontext-dev.ts +8 -0
- package/src/config/paramsSchemas/fal/flux-pro-kontext.ts +11 -0
- package/src/config/paramsSchemas/fal/flux-schnell.ts +9 -0
- package/src/config/paramsSchemas/fal/imagen4.ts +10 -0
- package/src/config/paramsSchemas/openai/gpt-image-1.ts +10 -0
- package/src/const/hotkeys.ts +2 -2
- package/src/const/image.ts +6 -0
- package/src/const/settings/systemAgent.ts +1 -0
- package/src/database/client/migrations.json +27 -0
- package/src/database/migrations/0026_add_autovacuum_tuning.sql +2 -0
- package/src/database/migrations/0027_ai_image.sql +47 -0
- package/src/database/migrations/meta/0027_snapshot.json +6003 -0
- package/src/database/migrations/meta/_journal.json +7 -0
- package/src/database/models/__tests__/asyncTask.test.ts +7 -5
- package/src/database/models/__tests__/file.test.ts +287 -0
- package/src/database/models/__tests__/generation.test.ts +786 -0
- package/src/database/models/__tests__/generationBatch.test.ts +614 -0
- package/src/database/models/__tests__/generationTopic.test.ts +411 -0
- package/src/database/models/aiModel.ts +2 -0
- package/src/database/models/asyncTask.ts +1 -1
- package/src/database/models/file.ts +28 -20
- package/src/database/models/generation.ts +197 -0
- package/src/database/models/generationBatch.ts +212 -0
- package/src/database/models/generationTopic.ts +131 -0
- package/src/database/repositories/aiInfra/index.test.ts +151 -1
- package/src/database/repositories/aiInfra/index.ts +28 -19
- package/src/database/repositories/tableViewer/index.test.ts +1 -1
- package/src/database/schemas/file.ts +8 -0
- package/src/database/schemas/generation.ts +127 -0
- package/src/database/schemas/index.ts +1 -0
- package/src/database/schemas/relations.ts +45 -1
- package/src/database/type.ts +2 -0
- package/src/database/utils/idGenerator.ts +3 -0
- package/src/features/Conversation/Error/ChatInvalidApiKey.tsx +39 -0
- package/src/features/Conversation/Error/InvalidAccessCode.tsx +2 -2
- package/src/features/Conversation/Error/index.tsx +3 -3
- package/src/features/ImageSidePanel/index.tsx +83 -0
- package/src/features/ImageTopicPanel/index.tsx +79 -0
- package/src/features/PluginDevModal/MCPManifestForm/CollapsibleSection.tsx +62 -0
- package/src/features/PluginDevModal/MCPManifestForm/QuickImportSection.tsx +158 -0
- package/src/features/PluginDevModal/MCPManifestForm/index.tsx +99 -155
- package/src/features/PluginStore/McpList/Detail/Settings/index.tsx +5 -2
- package/src/hooks/useDownloadImage.ts +31 -0
- package/src/hooks/useFetchGenerationTopics.ts +13 -0
- package/src/hooks/useHotkeys/imageScope.ts +48 -0
- package/src/libs/mcp/client.ts +55 -22
- package/src/libs/mcp/types.ts +42 -6
- package/src/libs/model-runtime/BaseAI.ts +3 -1
- package/src/libs/model-runtime/ModelRuntime.test.ts +80 -0
- package/src/libs/model-runtime/ModelRuntime.ts +15 -1
- package/src/libs/model-runtime/UniformRuntime/index.ts +4 -1
- package/src/libs/model-runtime/fal/index.test.ts +442 -0
- package/src/libs/model-runtime/fal/index.ts +88 -0
- package/src/libs/model-runtime/openai/index.test.ts +396 -2
- package/src/libs/model-runtime/openai/index.ts +129 -3
- package/src/libs/model-runtime/runtimeMap.ts +2 -0
- package/src/libs/model-runtime/types/image.ts +25 -0
- package/src/libs/model-runtime/types/type.ts +1 -0
- package/src/libs/model-runtime/utils/openaiCompatibleFactory/index.ts +10 -0
- package/src/libs/standard-parameters/index.ts +1 -0
- package/src/libs/standard-parameters/meta-schema.test.ts +214 -0
- package/src/libs/standard-parameters/meta-schema.ts +147 -0
- package/src/libs/swr/index.ts +1 -0
- package/src/libs/trpc/async/asyncAuth.ts +29 -8
- package/src/libs/trpc/async/context.ts +42 -4
- package/src/libs/trpc/async/index.ts +17 -4
- package/src/libs/trpc/async/init.ts +8 -0
- package/src/libs/trpc/client/lambda.ts +19 -2
- package/src/locales/default/common.ts +2 -0
- package/src/locales/default/components.ts +35 -0
- package/src/locales/default/error.ts +2 -0
- package/src/locales/default/image.ts +100 -0
- package/src/locales/default/index.ts +2 -0
- package/src/locales/default/metadata.ts +4 -0
- package/src/locales/default/modelProvider.ts +2 -0
- package/src/locales/default/plugin.ts +22 -0
- package/src/locales/default/setting.ts +5 -0
- package/src/middleware.ts +1 -0
- package/src/server/modules/ElectronIPCClient/index.ts +9 -1
- package/src/server/modules/S3/index.ts +15 -0
- package/src/server/routers/async/caller.ts +9 -1
- package/src/server/routers/async/image.ts +253 -0
- package/src/server/routers/async/index.ts +2 -0
- package/src/server/routers/lambda/aiProvider.test.ts +1 -0
- package/src/server/routers/lambda/generation.test.ts +267 -0
- package/src/server/routers/lambda/generation.ts +86 -0
- package/src/server/routers/lambda/generationBatch.test.ts +376 -0
- package/src/server/routers/lambda/generationBatch.ts +56 -0
- package/src/server/routers/lambda/generationTopic.test.ts +508 -0
- package/src/server/routers/lambda/generationTopic.ts +93 -0
- package/src/server/routers/lambda/image.ts +248 -0
- package/src/server/routers/lambda/index.ts +8 -0
- package/src/server/routers/tools/mcp.ts +15 -0
- package/src/server/services/file/__tests__/index.test.ts +135 -0
- package/src/server/services/file/impls/local.test.ts +153 -52
- package/src/server/services/file/impls/local.ts +70 -46
- package/src/server/services/file/impls/s3.test.ts +114 -0
- package/src/server/services/file/impls/s3.ts +40 -0
- package/src/server/services/file/impls/type.ts +10 -0
- package/src/server/services/file/index.ts +14 -0
- package/src/server/services/generation/index.ts +239 -0
- package/src/server/services/mcp/index.ts +20 -2
- package/src/services/__tests__/generation.test.ts +40 -0
- package/src/services/__tests__/generationBatch.test.ts +36 -0
- package/src/services/__tests__/generationTopic.test.ts +72 -0
- package/src/services/electron/file.ts +3 -1
- package/src/services/generation.ts +16 -0
- package/src/services/generationBatch.ts +25 -0
- package/src/services/generationTopic.ts +28 -0
- package/src/services/image.ts +33 -0
- package/src/services/mcp.ts +12 -7
- package/src/services/upload.ts +43 -9
- package/src/store/aiInfra/slices/aiProvider/action.ts +25 -5
- package/src/store/aiInfra/slices/aiProvider/initialState.ts +1 -0
- package/src/store/aiInfra/slices/aiProvider/selectors.ts +3 -0
- package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +5 -5
- package/src/store/chat/slices/message/action.ts +2 -2
- package/src/store/chat/slices/translate/action.ts +1 -1
- package/src/store/global/initialState.ts +9 -0
- package/src/store/global/selectors/systemStatus.ts +8 -0
- package/src/store/image/index.ts +2 -0
- package/src/store/image/initialState.ts +25 -0
- package/src/store/image/selectors.ts +4 -0
- package/src/store/image/slices/createImage/action.test.ts +330 -0
- package/src/store/image/slices/createImage/action.ts +134 -0
- package/src/store/image/slices/createImage/initialState.ts +9 -0
- package/src/store/image/slices/createImage/selectors.test.ts +114 -0
- package/src/store/image/slices/createImage/selectors.ts +9 -0
- package/src/store/image/slices/generationBatch/action.test.ts +495 -0
- package/src/store/image/slices/generationBatch/action.ts +303 -0
- package/src/store/image/slices/generationBatch/initialState.ts +13 -0
- package/src/store/image/slices/generationBatch/reducer.test.ts +568 -0
- package/src/store/image/slices/generationBatch/reducer.ts +101 -0
- package/src/store/image/slices/generationBatch/selectors.test.ts +307 -0
- package/src/store/image/slices/generationBatch/selectors.ts +36 -0
- package/src/store/image/slices/generationConfig/action.test.ts +351 -0
- package/src/store/image/slices/generationConfig/action.ts +295 -0
- package/src/store/image/slices/generationConfig/hooks.test.ts +304 -0
- package/src/store/image/slices/generationConfig/hooks.ts +118 -0
- package/src/store/image/slices/generationConfig/index.ts +1 -0
- package/src/store/image/slices/generationConfig/initialState.ts +37 -0
- package/src/store/image/slices/generationConfig/selectors.test.ts +204 -0
- package/src/store/image/slices/generationConfig/selectors.ts +25 -0
- package/src/store/image/slices/generationTopic/action.test.ts +687 -0
- package/src/store/image/slices/generationTopic/action.ts +319 -0
- package/src/store/image/slices/generationTopic/index.ts +2 -0
- package/src/store/image/slices/generationTopic/initialState.ts +14 -0
- package/src/store/image/slices/generationTopic/reducer.test.ts +198 -0
- package/src/store/image/slices/generationTopic/reducer.ts +66 -0
- package/src/store/image/slices/generationTopic/selectors.test.ts +103 -0
- package/src/store/image/slices/generationTopic/selectors.ts +15 -0
- package/src/store/image/store.ts +42 -0
- package/src/store/image/utils/size.ts +51 -0
- package/src/store/tool/slices/customPlugin/action.ts +10 -1
- package/src/store/tool/slices/mcpStore/action.ts +6 -4
- package/src/store/user/slices/settings/selectors/__snapshots__/settings.test.ts.snap +4 -0
- package/src/store/user/slices/settings/selectors/systemAgent.ts +2 -0
- package/src/types/aiModel.ts +8 -3
- package/src/types/aiProvider.ts +1 -0
- package/src/types/asyncTask.ts +2 -0
- package/src/types/files/index.ts +5 -0
- package/src/types/generation/index.ts +80 -0
- package/src/types/hotkey.ts +2 -0
- package/src/types/plugins/mcp.ts +2 -6
- package/src/types/tool/plugin.ts +8 -0
- package/src/types/user/settings/keyVaults.ts +5 -0
- package/src/types/user/settings/systemAgent.ts +1 -0
- package/src/utils/client/downloadFile.ts +33 -4
- package/src/utils/number.test.ts +105 -0
- package/src/utils/number.ts +25 -0
- package/src/utils/server/__tests__/geo.test.ts +6 -3
- package/src/utils/storeDebug.test.ts +152 -0
- package/src/utils/storeDebug.ts +16 -7
- package/src/utils/time.test.ts +259 -0
- package/src/utils/time.ts +18 -0
- package/src/utils/units.ts +61 -0
- package/src/utils/url.test.ts +358 -9
- package/src/utils/url.ts +105 -3
- package/{vitest.server.config.ts → vitest.config.server.ts} +3 -0
- package/.cursor/rules/i18n/i18n-auto-attached.mdc +0 -6
- package/src/features/Conversation/Error/APIKeyForm/index.tsx +0 -105
- package/src/features/Conversation/Error/InvalidAPIKey.tsx +0 -16
- package/src/features/PluginDevModal/MCPManifestForm/EnvEditor.tsx +0 -227
- /package/.cursor/rules/{i18n/i18n.mdc → i18n.mdc} +0 -0
- /package/src/app/[variants]/(main)/settings/system-agent/features/{createForm.tsx → SystemAgentForm.tsx} +0 -0
- /package/src/{features/Conversation/Error → components/InvalidAPIKey}/APIKeyForm/LoadingContext.ts +0 -0
@@ -0,0 +1,103 @@
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
2
|
+
|
3
|
+
import { ImageStore } from '@/store/image';
|
4
|
+
import { initialState } from '@/store/image/initialState';
|
5
|
+
import { ImageGenerationTopic } from '@/types/generation';
|
6
|
+
import { merge } from '@/utils/merge';
|
7
|
+
|
8
|
+
import { generationTopicSelectors } from './selectors';
|
9
|
+
|
10
|
+
const initialStore = initialState as ImageStore;
|
11
|
+
|
12
|
+
const mockGenerationTopics: ImageGenerationTopic[] = [
|
13
|
+
{
|
14
|
+
id: 'gt_aBc123xYz456',
|
15
|
+
title: 'Generation Topic 1',
|
16
|
+
coverUrl: 'https://example.com/cover1.jpg',
|
17
|
+
createdAt: new Date('2023-01-01'),
|
18
|
+
updatedAt: new Date('2023-01-01'),
|
19
|
+
},
|
20
|
+
{
|
21
|
+
id: 'gt_dEf789uVw012',
|
22
|
+
title: 'Generation Topic 2',
|
23
|
+
coverUrl: null,
|
24
|
+
createdAt: new Date('2023-01-02'),
|
25
|
+
updatedAt: new Date('2023-01-02'),
|
26
|
+
},
|
27
|
+
];
|
28
|
+
|
29
|
+
describe('generationTopicSelectors', () => {
|
30
|
+
describe('activeGenerationTopicId', () => {
|
31
|
+
it('should return null when no active topic is set', () => {
|
32
|
+
const activeId = generationTopicSelectors.activeGenerationTopicId(initialStore);
|
33
|
+
expect(activeId).toBeNull();
|
34
|
+
});
|
35
|
+
|
36
|
+
it('should return the active generation topic id', () => {
|
37
|
+
const state = merge(initialStore, { activeGenerationTopicId: 'gt_aBc123xYz456' });
|
38
|
+
const activeId = generationTopicSelectors.activeGenerationTopicId(state);
|
39
|
+
expect(activeId).toBe('gt_aBc123xYz456');
|
40
|
+
});
|
41
|
+
});
|
42
|
+
|
43
|
+
describe('generationTopics', () => {
|
44
|
+
it('should return empty array when no topics exist', () => {
|
45
|
+
const topics = generationTopicSelectors.generationTopics(initialStore);
|
46
|
+
expect(topics).toEqual([]);
|
47
|
+
});
|
48
|
+
|
49
|
+
it('should return all generation topics from the store', () => {
|
50
|
+
const state = merge(initialStore, { generationTopics: mockGenerationTopics });
|
51
|
+
const topics = generationTopicSelectors.generationTopics(state);
|
52
|
+
expect(topics).toEqual(mockGenerationTopics);
|
53
|
+
});
|
54
|
+
});
|
55
|
+
|
56
|
+
describe('getGenerationTopicById', () => {
|
57
|
+
it('should return undefined when topic is not found', () => {
|
58
|
+
const state = merge(initialStore, { generationTopics: mockGenerationTopics });
|
59
|
+
const topic = generationTopicSelectors.getGenerationTopicById('nonexistent')(state);
|
60
|
+
expect(topic).toBeUndefined();
|
61
|
+
});
|
62
|
+
|
63
|
+
it('should return the topic with the given id', () => {
|
64
|
+
const state = merge(initialStore, { generationTopics: mockGenerationTopics });
|
65
|
+
const topic = generationTopicSelectors.getGenerationTopicById('gt_aBc123xYz456')(state);
|
66
|
+
expect(topic).toEqual(mockGenerationTopics[0]);
|
67
|
+
});
|
68
|
+
|
69
|
+
it('should return the correct topic when multiple topics exist', () => {
|
70
|
+
const state = merge(initialStore, { generationTopics: mockGenerationTopics });
|
71
|
+
const topic = generationTopicSelectors.getGenerationTopicById('gt_dEf789uVw012')(state);
|
72
|
+
expect(topic).toEqual(mockGenerationTopics[1]);
|
73
|
+
});
|
74
|
+
});
|
75
|
+
|
76
|
+
describe('isLoadingGenerationTopic', () => {
|
77
|
+
it('should return false when no topics are loading', () => {
|
78
|
+
const isLoading =
|
79
|
+
generationTopicSelectors.isLoadingGenerationTopic('gt_aBc123xYz456')(initialStore);
|
80
|
+
expect(isLoading).toBe(false);
|
81
|
+
});
|
82
|
+
|
83
|
+
it('should return false when topic is not in loading list', () => {
|
84
|
+
const state = merge(initialStore, { loadingGenerationTopicIds: ['gt_dEf789uVw012'] });
|
85
|
+
const isLoading = generationTopicSelectors.isLoadingGenerationTopic('gt_aBc123xYz456')(state);
|
86
|
+
expect(isLoading).toBe(false);
|
87
|
+
});
|
88
|
+
|
89
|
+
it('should return true when topic is in loading list', () => {
|
90
|
+
const state = merge(initialStore, {
|
91
|
+
loadingGenerationTopicIds: ['gt_aBc123xYz456', 'gt_dEf789uVw012'],
|
92
|
+
});
|
93
|
+
const isLoading = generationTopicSelectors.isLoadingGenerationTopic('gt_aBc123xYz456')(state);
|
94
|
+
expect(isLoading).toBe(true);
|
95
|
+
});
|
96
|
+
|
97
|
+
it('should return true when topic is the only one in loading list', () => {
|
98
|
+
const state = merge(initialStore, { loadingGenerationTopicIds: ['gt_aBc123xYz456'] });
|
99
|
+
const isLoading = generationTopicSelectors.isLoadingGenerationTopic('gt_aBc123xYz456')(state);
|
100
|
+
expect(isLoading).toBe(true);
|
101
|
+
});
|
102
|
+
});
|
103
|
+
});
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import { ImageStoreState } from '../../initialState';
|
2
|
+
|
3
|
+
const activeGenerationTopicId = (s: ImageStoreState) => s.activeGenerationTopicId;
|
4
|
+
const generationTopics = (s: ImageStoreState) => s.generationTopics;
|
5
|
+
const getGenerationTopicById = (id: string) => (s: ImageStoreState) =>
|
6
|
+
s.generationTopics.find((topic) => topic.id === id);
|
7
|
+
const isLoadingGenerationTopic = (id: string) => (s: ImageStoreState) =>
|
8
|
+
s.loadingGenerationTopicIds.includes(id);
|
9
|
+
|
10
|
+
export const generationTopicSelectors = {
|
11
|
+
activeGenerationTopicId,
|
12
|
+
generationTopics,
|
13
|
+
getGenerationTopicById,
|
14
|
+
isLoadingGenerationTopic,
|
15
|
+
};
|
@@ -0,0 +1,42 @@
|
|
1
|
+
import { subscribeWithSelector } from 'zustand/middleware';
|
2
|
+
import { shallow } from 'zustand/shallow';
|
3
|
+
import { createWithEqualityFn } from 'zustand/traditional';
|
4
|
+
import { StateCreator } from 'zustand/vanilla';
|
5
|
+
|
6
|
+
import { createDevtools } from '../middleware/createDevtools';
|
7
|
+
import { ImageStoreState, initialState } from './initialState';
|
8
|
+
import { CreateImageAction, createCreateImageSlice } from './slices/createImage/action';
|
9
|
+
import { GenerationBatchAction, createGenerationBatchSlice } from './slices/generationBatch/action';
|
10
|
+
import {
|
11
|
+
GenerationConfigAction,
|
12
|
+
createGenerationConfigSlice,
|
13
|
+
} from './slices/generationConfig/action';
|
14
|
+
import { GenerationTopicAction, createGenerationTopicSlice } from './slices/generationTopic/action';
|
15
|
+
|
16
|
+
// =============== aggregate createStoreFn ============ //
|
17
|
+
|
18
|
+
export interface ImageStore
|
19
|
+
extends GenerationConfigAction,
|
20
|
+
GenerationTopicAction,
|
21
|
+
GenerationBatchAction,
|
22
|
+
CreateImageAction,
|
23
|
+
ImageStoreState {}
|
24
|
+
|
25
|
+
const createStore: StateCreator<ImageStore, [['zustand/devtools', never]]> = (...parameters) => ({
|
26
|
+
...initialState,
|
27
|
+
...createGenerationConfigSlice(...parameters),
|
28
|
+
...createGenerationTopicSlice(...parameters),
|
29
|
+
...createGenerationBatchSlice(...parameters),
|
30
|
+
...createCreateImageSlice(...parameters),
|
31
|
+
});
|
32
|
+
|
33
|
+
// =============== implement useStore ============ //
|
34
|
+
|
35
|
+
const devtools = createDevtools('image');
|
36
|
+
|
37
|
+
export const useImageStore = createWithEqualityFn<ImageStore>()(
|
38
|
+
subscribeWithSelector(devtools(createStore)),
|
39
|
+
shallow,
|
40
|
+
);
|
41
|
+
|
42
|
+
export const getImageStoreState = () => useImageStore.getState();
|
@@ -0,0 +1,51 @@
|
|
1
|
+
/**
|
2
|
+
* 解析比例字符串,例如 "16:9" -> 1.777
|
3
|
+
* @param ratio - 格式为 "width:height" 的比例字符串
|
4
|
+
* @returns 比例数值,出现错误时返回 1:1 比例
|
5
|
+
*/
|
6
|
+
export function parseRatio(ratio: string): number {
|
7
|
+
if (!ratio || typeof ratio !== 'string') return 1;
|
8
|
+
|
9
|
+
const parts = ratio.split(':');
|
10
|
+
if (parts.length !== 2) return 1;
|
11
|
+
|
12
|
+
const [widthStr, heightStr] = parts;
|
13
|
+
const width = Number(widthStr);
|
14
|
+
const height = Number(heightStr);
|
15
|
+
|
16
|
+
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
|
17
|
+
return 1;
|
18
|
+
}
|
19
|
+
|
20
|
+
return width / height;
|
21
|
+
}
|
22
|
+
|
23
|
+
/**
|
24
|
+
* 根据目标比例和默认尺寸,计算最合适的尺寸
|
25
|
+
* @param ratio - 目标宽高比例
|
26
|
+
* @param defaultWidth - 默认宽度
|
27
|
+
* @param defaultHeight - 默认高度
|
28
|
+
* @returns 计算出的尺寸对象
|
29
|
+
*/
|
30
|
+
export function adaptSizeToRatio(ratio: number, defaultWidth: number, defaultHeight: number) {
|
31
|
+
// 验证输入参数
|
32
|
+
if (!Number.isFinite(ratio) || ratio <= 0) {
|
33
|
+
throw new Error('Invalid ratio: must be a positive finite number');
|
34
|
+
}
|
35
|
+
if (!Number.isFinite(defaultWidth) || defaultWidth <= 0) {
|
36
|
+
throw new Error('Invalid defaultWidth: must be a positive finite number');
|
37
|
+
}
|
38
|
+
if (!Number.isFinite(defaultHeight) || defaultHeight <= 0) {
|
39
|
+
throw new Error('Invalid defaultHeight: must be a positive finite number');
|
40
|
+
}
|
41
|
+
|
42
|
+
const currentRatio = defaultWidth / defaultHeight;
|
43
|
+
|
44
|
+
if (ratio > currentRatio) {
|
45
|
+
// 目标比例更宽,保持宽度,调整高度
|
46
|
+
return { width: defaultWidth, height: Math.round(defaultWidth / ratio) };
|
47
|
+
} else {
|
48
|
+
// 目标比例更高,保持高度,调整宽度
|
49
|
+
return { width: Math.round(defaultHeight * ratio), height: defaultHeight };
|
50
|
+
}
|
51
|
+
}
|
@@ -51,7 +51,16 @@ export const createCustomPluginSlice: StateCreator<
|
|
51
51
|
const url = plugin.customParams?.mcp?.url;
|
52
52
|
if (!url) return;
|
53
53
|
|
54
|
-
manifest = await mcpService.getStreamableMcpServerManifest(
|
54
|
+
manifest = await mcpService.getStreamableMcpServerManifest({
|
55
|
+
auth: plugin.customParams.mcp.auth,
|
56
|
+
headers: plugin.customParams.mcp.headers,
|
57
|
+
identifier: plugin.identifier,
|
58
|
+
metadata: {
|
59
|
+
avatar: plugin.customParams.avatar,
|
60
|
+
description: plugin.customParams.description,
|
61
|
+
},
|
62
|
+
url,
|
63
|
+
});
|
55
64
|
} else {
|
56
65
|
manifest = await toolService.getToolManifest(
|
57
66
|
plugin.customParams?.manifestUrl,
|
@@ -229,11 +229,13 @@ export const createMCPPluginStoreSlice: StateCreator<
|
|
229
229
|
}
|
230
230
|
if (connection?.type === 'http') {
|
231
231
|
manifest = await mcpService.getStreamableMcpServerManifest(
|
232
|
-
identifier,
|
233
|
-
connection.url!,
|
234
232
|
{
|
235
|
-
|
236
|
-
|
233
|
+
identifier,
|
234
|
+
metadata: {
|
235
|
+
avatar: plugin.icon,
|
236
|
+
description: plugin.description,
|
237
|
+
},
|
238
|
+
url: connection.url!,
|
237
239
|
},
|
238
240
|
abortController.signal,
|
239
241
|
);
|
@@ -55,6 +55,10 @@ exports[`settingsSelectors > currentSystemAgent > should merge DEFAULT_SYSTEM_AG
|
|
55
55
|
"provider": "openai",
|
56
56
|
},
|
57
57
|
"enableAutoReply": true,
|
58
|
+
"generationTopic": {
|
59
|
+
"model": "gpt-4.1-mini",
|
60
|
+
"provider": "openai",
|
61
|
+
},
|
58
62
|
"historyCompress": {
|
59
63
|
"model": "gpt-4.1-mini",
|
60
64
|
"provider": "openai",
|
@@ -13,9 +13,11 @@ const thread = (s: UserStore) => currentSystemAgent(s).thread;
|
|
13
13
|
const agentMeta = (s: UserStore) => currentSystemAgent(s).agentMeta;
|
14
14
|
const queryRewrite = (s: UserStore) => currentSystemAgent(s).queryRewrite;
|
15
15
|
const historyCompress = (s: UserStore) => currentSystemAgent(s).historyCompress;
|
16
|
+
const generationTopic = (s: UserStore) => currentSystemAgent(s).generationTopic;
|
16
17
|
|
17
18
|
export const systemAgentSelectors = {
|
18
19
|
agentMeta,
|
20
|
+
generationTopic,
|
19
21
|
historyCompress,
|
20
22
|
queryRewrite,
|
21
23
|
thread,
|
package/src/types/aiModel.ts
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
import { z } from 'zod';
|
2
2
|
|
3
|
+
import { ModelParamsSchema } from '@/libs/standard-parameters';
|
4
|
+
|
3
5
|
export type ModelPriceCurrency = 'CNY' | 'USD';
|
4
6
|
|
5
7
|
export const AiModelSourceEnum = {
|
@@ -186,15 +188,16 @@ export interface AIEmbeddingModelCard extends AIBaseModelCard {
|
|
186
188
|
type: 'embedding';
|
187
189
|
}
|
188
190
|
|
189
|
-
export interface
|
191
|
+
export interface AIImageModelCard extends AIBaseModelCard {
|
192
|
+
parameters?: ModelParamsSchema;
|
190
193
|
pricing?: {
|
191
194
|
/**
|
192
195
|
* the currency of the pricing
|
193
196
|
* @default USD
|
194
197
|
*/
|
195
198
|
currency?: ModelPriceCurrency;
|
196
|
-
} & Record<string, number>;
|
197
|
-
resolutions
|
199
|
+
} & Record<string, number>;
|
200
|
+
resolutions?: string[];
|
198
201
|
type: 'image';
|
199
202
|
}
|
200
203
|
|
@@ -299,6 +302,7 @@ export interface AiProviderModelListItem {
|
|
299
302
|
displayName?: string;
|
300
303
|
enabled: boolean;
|
301
304
|
id: string;
|
305
|
+
parameters?: Record<string, any>;
|
302
306
|
pricing?: ChatModelPricing;
|
303
307
|
releasedAt?: string;
|
304
308
|
settings?: AiModelSettings;
|
@@ -350,6 +354,7 @@ export interface EnabledAiModel {
|
|
350
354
|
displayName?: string;
|
351
355
|
enabled?: boolean;
|
352
356
|
id: string;
|
357
|
+
parameters?: Record<string, any>;
|
353
358
|
providerId: string;
|
354
359
|
settings?: AiModelSettings;
|
355
360
|
sort?: number;
|
package/src/types/aiProvider.ts
CHANGED
@@ -255,5 +255,6 @@ export interface AiProviderRuntimeConfig {
|
|
255
255
|
export interface AiProviderRuntimeState {
|
256
256
|
enabledAiModels: EnabledAiModel[];
|
257
257
|
enabledAiProviders: EnabledProvider[];
|
258
|
+
enabledImageAiProviders: EnabledProvider[];
|
258
259
|
runtimeConfig: Record<string, AiProviderRuntimeConfig>;
|
259
260
|
}
|
package/src/types/asyncTask.ts
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
export enum AsyncTaskType {
|
2
2
|
Chunking = 'chunk',
|
3
3
|
Embedding = 'embedding',
|
4
|
+
ImageGeneration = 'image_generation',
|
4
5
|
}
|
5
6
|
|
6
7
|
export enum AsyncTaskStatus {
|
@@ -12,6 +13,7 @@ export enum AsyncTaskStatus {
|
|
12
13
|
|
13
14
|
export enum AsyncTaskErrorType {
|
14
15
|
EmbeddingError = 'EmbeddingError',
|
16
|
+
InvalidProviderAPIKey = 'InvalidProviderAPIKey',
|
15
17
|
/**
|
16
18
|
* the chunk parse result it empty
|
17
19
|
*/
|
package/src/types/files/index.ts
CHANGED
@@ -7,12 +7,17 @@ export enum FilesTabs {
|
|
7
7
|
Websites = 'websites',
|
8
8
|
}
|
9
9
|
|
10
|
+
export enum FileSource {
|
11
|
+
ImageGeneration = 'image_generation',
|
12
|
+
}
|
13
|
+
|
10
14
|
export interface FileItem {
|
11
15
|
createdAt: Date;
|
12
16
|
enabled?: boolean;
|
13
17
|
id: string;
|
14
18
|
name: string;
|
15
19
|
size: number;
|
20
|
+
source?: FileSource | null;
|
16
21
|
type: string;
|
17
22
|
updatedAt: Date;
|
18
23
|
url: string;
|
@@ -0,0 +1,80 @@
|
|
1
|
+
import { AsyncTaskError, AsyncTaskStatus } from '../asyncTask';
|
2
|
+
|
3
|
+
export interface ImageGenerationTopic {
|
4
|
+
id: string;
|
5
|
+
title?: string | null;
|
6
|
+
coverUrl?: string | null;
|
7
|
+
createdAt: Date;
|
8
|
+
updatedAt: Date;
|
9
|
+
}
|
10
|
+
|
11
|
+
export interface BaseGenerationAsset {
|
12
|
+
type: string;
|
13
|
+
}
|
14
|
+
|
15
|
+
export interface ImageGenerationAsset extends BaseGenerationAsset {
|
16
|
+
/**
|
17
|
+
* api provider 家的 cdn url,一般很快就会失效
|
18
|
+
*/
|
19
|
+
originalUrl?: string;
|
20
|
+
/**
|
21
|
+
* 存到自己 oss 的 url, 只存了 key, 完整的 url 需要使用 FileService.getFullFileUrl 获取
|
22
|
+
*/
|
23
|
+
url?: string;
|
24
|
+
/**
|
25
|
+
* 缩略图,图片那就是尺寸裁剪过的,视频那就是封面的缩略图
|
26
|
+
*/
|
27
|
+
thumbnailUrl?: string;
|
28
|
+
/**
|
29
|
+
* 图片/视频的宽度
|
30
|
+
*/
|
31
|
+
width?: number;
|
32
|
+
/**
|
33
|
+
* 图片/视频的高度
|
34
|
+
*/
|
35
|
+
height?: number;
|
36
|
+
}
|
37
|
+
|
38
|
+
export type GenerationAsset = ImageGenerationAsset;
|
39
|
+
|
40
|
+
export interface GenerationConfig {
|
41
|
+
prompt: string;
|
42
|
+
imageUrls?: string[];
|
43
|
+
width?: number;
|
44
|
+
height?: number;
|
45
|
+
aspectRatio?: string;
|
46
|
+
size?: string;
|
47
|
+
steps?: number;
|
48
|
+
cfg?: number;
|
49
|
+
}
|
50
|
+
|
51
|
+
export interface GenerationAsyncTask {
|
52
|
+
id: string;
|
53
|
+
status: AsyncTaskStatus;
|
54
|
+
error?: AsyncTaskError;
|
55
|
+
}
|
56
|
+
|
57
|
+
export interface Generation {
|
58
|
+
id: string;
|
59
|
+
/**
|
60
|
+
* The asset associated with the generation, containing image URLs and dimensions.
|
61
|
+
*/
|
62
|
+
asset?: GenerationAsset | null;
|
63
|
+
seed?: number | null;
|
64
|
+
createdAt: Date;
|
65
|
+
asyncTaskId: string | null;
|
66
|
+
|
67
|
+
task: GenerationAsyncTask;
|
68
|
+
}
|
69
|
+
|
70
|
+
export interface GenerationBatch {
|
71
|
+
id: string;
|
72
|
+
provider: string;
|
73
|
+
model: string;
|
74
|
+
prompt: string;
|
75
|
+
width?: number | null;
|
76
|
+
height?: number | null;
|
77
|
+
config?: GenerationConfig;
|
78
|
+
createdAt: Date;
|
79
|
+
generations: Generation[];
|
80
|
+
}
|
package/src/types/hotkey.ts
CHANGED
@@ -84,6 +84,8 @@ export const HotkeyScopeEnum = {
|
|
84
84
|
// 默认全局注册的快捷键 scope
|
85
85
|
// https://react-hotkeys-hook.vercel.app/docs/documentation/hotkeys-provider
|
86
86
|
Global: 'global',
|
87
|
+
|
88
|
+
Image: 'image',
|
87
89
|
} as const;
|
88
90
|
|
89
91
|
export type HotkeyId = (typeof HotkeyEnum)[keyof typeof HotkeyEnum];
|
package/src/types/plugins/mcp.ts
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import { PluginQueryParams, SystemDependency } from '@lobehub/market-sdk';
|
2
2
|
|
3
|
+
import { MCPErrorType } from '@/libs/mcp';
|
3
4
|
import { MCPInstallStep } from '@/store/tool/slices/mcpStore';
|
4
5
|
|
5
6
|
export interface CheckMcpInstallParams {
|
@@ -147,12 +148,7 @@ export interface MCPErrorInfo {
|
|
147
148
|
/**
|
148
149
|
* 错误类型
|
149
150
|
*/
|
150
|
-
type:
|
151
|
-
| 'CONNECTION_FAILED'
|
152
|
-
| 'PROCESS_SPAWN_ERROR'
|
153
|
-
| 'INITIALIZATION_TIMEOUT'
|
154
|
-
| 'VALIDATION_ERROR'
|
155
|
-
| 'UNKNOWN_ERROR';
|
151
|
+
type: MCPErrorType;
|
156
152
|
}
|
157
153
|
|
158
154
|
export interface MCPInstallProgress {
|
package/src/types/tool/plugin.ts
CHANGED
@@ -27,6 +27,14 @@ export interface CustomPluginParams {
|
|
27
27
|
command?: string;
|
28
28
|
type: 'http' | 'stdio';
|
29
29
|
url?: string;
|
30
|
+
// 新增认证配置支持
|
31
|
+
auth?: {
|
32
|
+
type: 'none' | 'bearer' | 'oauth2';
|
33
|
+
token?: string; // Bearer Token
|
34
|
+
accessToken?: string; // OAuth2 Access Token
|
35
|
+
};
|
36
|
+
// 新增 headers 配置支持
|
37
|
+
headers?: Record<string, string>;
|
30
38
|
};
|
31
39
|
avatar?: string;
|
32
40
|
description?: string;
|
@@ -3,6 +3,10 @@ export interface OpenAICompatibleKeyVault {
|
|
3
3
|
baseURL?: string;
|
4
4
|
}
|
5
5
|
|
6
|
+
export interface FalKeyVault {
|
7
|
+
apiKey?: string;
|
8
|
+
}
|
9
|
+
|
6
10
|
export interface AzureOpenAIKeyVault {
|
7
11
|
apiKey?: string;
|
8
12
|
apiVersion?: string;
|
@@ -43,6 +47,7 @@ export interface UserKeyVaults extends SearchEngineKeyVaults {
|
|
43
47
|
cloudflare?: CloudflareKeyVault;
|
44
48
|
cohere?: OpenAICompatibleKeyVault;
|
45
49
|
deepseek?: OpenAICompatibleKeyVault;
|
50
|
+
fal?: FalKeyVault;
|
46
51
|
fireworksai?: OpenAICompatibleKeyVault;
|
47
52
|
giteeai?: OpenAICompatibleKeyVault;
|
48
53
|
github?: OpenAICompatibleKeyVault;
|
@@ -11,6 +11,7 @@ export interface QueryRewriteSystemAgent extends Omit<SystemAgentItem, 'enabled'
|
|
11
11
|
|
12
12
|
export interface UserSystemAgentConfig {
|
13
13
|
agentMeta: SystemAgentItem;
|
14
|
+
generationTopic: SystemAgentItem;
|
14
15
|
historyCompress: SystemAgentItem;
|
15
16
|
queryRewrite: QueryRewriteSystemAgent;
|
16
17
|
thread: SystemAgentItem;
|
@@ -1,19 +1,48 @@
|
|
1
|
-
export const downloadFile = async (
|
1
|
+
export const downloadFile = async (
|
2
|
+
url: string,
|
3
|
+
fileName: string,
|
4
|
+
fallbackToOpen: boolean = true,
|
5
|
+
) => {
|
2
6
|
try {
|
3
|
-
|
4
|
-
const
|
7
|
+
// Use better CORS handling similar to download-image.ts
|
8
|
+
const response = await fetch(url, {
|
9
|
+
// Avoid image disk cache which can cause incorrect CORS headers
|
10
|
+
cache: 'no-store',
|
5
11
|
|
12
|
+
credentials: 'omit',
|
13
|
+
|
14
|
+
mode: 'cors',
|
15
|
+
});
|
16
|
+
|
17
|
+
if (!response.ok) {
|
18
|
+
throw new Error(`Failed to fetch image: ${response.status} ${response.statusText}`);
|
19
|
+
}
|
20
|
+
|
21
|
+
const blob = await response.blob();
|
22
|
+
|
23
|
+
// Create download link
|
6
24
|
const blobUrl = window.URL.createObjectURL(blob);
|
7
25
|
const link = document.createElement('a');
|
8
26
|
link.href = blobUrl;
|
9
27
|
link.download = fileName;
|
10
28
|
link.style.display = 'none';
|
29
|
+
|
30
|
+
// Trigger download
|
11
31
|
document.body.append(link);
|
12
32
|
link.click();
|
33
|
+
|
34
|
+
// Cleanup
|
13
35
|
link.remove();
|
14
36
|
window.URL.revokeObjectURL(blobUrl);
|
15
37
|
} catch (error) {
|
16
38
|
console.log('Download failed:', error);
|
17
|
-
|
39
|
+
|
40
|
+
// Fallback: open in new tab if enabled
|
41
|
+
if (fallbackToOpen) {
|
42
|
+
window.open(url);
|
43
|
+
} else {
|
44
|
+
// Re-throw the error if fallback is disabled
|
45
|
+
throw error;
|
46
|
+
}
|
18
47
|
}
|
19
48
|
};
|
@@ -0,0 +1,105 @@
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
2
|
+
|
3
|
+
import { MAX_SEED, generateUniqueSeeds } from './number';
|
4
|
+
|
5
|
+
describe('number utilities', () => {
|
6
|
+
describe('MAX_SEED constant', () => {
|
7
|
+
it('should be 2^31 - 1', () => {
|
8
|
+
expect(MAX_SEED).toBe(2147483647);
|
9
|
+
expect(MAX_SEED).toBe(2 ** 31 - 1);
|
10
|
+
});
|
11
|
+
});
|
12
|
+
|
13
|
+
describe('generateUniqueSeeds', () => {
|
14
|
+
it('should generate the correct number of seeds', () => {
|
15
|
+
const seedCount = 5;
|
16
|
+
const seeds = generateUniqueSeeds(seedCount);
|
17
|
+
|
18
|
+
expect(seeds).toHaveLength(seedCount);
|
19
|
+
});
|
20
|
+
|
21
|
+
it('should generate unique seeds', () => {
|
22
|
+
const seedCount = 10;
|
23
|
+
const seeds = generateUniqueSeeds(seedCount);
|
24
|
+
|
25
|
+
// Convert to Set to check uniqueness
|
26
|
+
const uniqueSeeds = new Set(seeds);
|
27
|
+
expect(uniqueSeeds.size).toBe(seedCount);
|
28
|
+
});
|
29
|
+
|
30
|
+
it('should generate seeds within valid range', () => {
|
31
|
+
const seedCount = 20;
|
32
|
+
const seeds = generateUniqueSeeds(seedCount);
|
33
|
+
|
34
|
+
seeds.forEach((seed) => {
|
35
|
+
expect(seed).toBeGreaterThanOrEqual(0);
|
36
|
+
expect(seed).toBeLessThanOrEqual(MAX_SEED);
|
37
|
+
expect(Number.isInteger(seed)).toBe(true);
|
38
|
+
});
|
39
|
+
});
|
40
|
+
|
41
|
+
it('should handle edge case of 0 seeds', () => {
|
42
|
+
const seeds = generateUniqueSeeds(0);
|
43
|
+
|
44
|
+
expect(seeds).toHaveLength(0);
|
45
|
+
expect(Array.isArray(seeds)).toBe(true);
|
46
|
+
});
|
47
|
+
|
48
|
+
it('should handle edge case of 1 seed', () => {
|
49
|
+
const seeds = generateUniqueSeeds(1);
|
50
|
+
|
51
|
+
expect(seeds).toHaveLength(1);
|
52
|
+
expect(seeds[0]).toBeGreaterThanOrEqual(0);
|
53
|
+
expect(seeds[0]).toBeLessThanOrEqual(MAX_SEED);
|
54
|
+
});
|
55
|
+
|
56
|
+
it('should generate different results on different calls', () => {
|
57
|
+
const seedCount = 5;
|
58
|
+
|
59
|
+
// Generate seeds at different times to ensure different timestamps
|
60
|
+
const seeds1 = generateUniqueSeeds(seedCount);
|
61
|
+
|
62
|
+
// Add a small delay to ensure different timestamp
|
63
|
+
vi.useFakeTimers();
|
64
|
+
vi.advanceTimersByTime(1);
|
65
|
+
const seeds2 = generateUniqueSeeds(seedCount);
|
66
|
+
vi.useRealTimers();
|
67
|
+
|
68
|
+
// The arrays should not be identical (very low probability of collision)
|
69
|
+
expect(seeds1).not.toEqual(seeds2);
|
70
|
+
});
|
71
|
+
|
72
|
+
it('should handle larger seed counts', () => {
|
73
|
+
const seedCount = 100;
|
74
|
+
const seeds = generateUniqueSeeds(seedCount);
|
75
|
+
|
76
|
+
expect(seeds).toHaveLength(seedCount);
|
77
|
+
|
78
|
+
// Verify uniqueness
|
79
|
+
const uniqueSeeds = new Set(seeds);
|
80
|
+
expect(uniqueSeeds.size).toBe(seedCount);
|
81
|
+
|
82
|
+
// Verify all are in valid range
|
83
|
+
seeds.forEach((seed) => {
|
84
|
+
expect(seed).toBeGreaterThanOrEqual(0);
|
85
|
+
expect(seed).toBeLessThanOrEqual(MAX_SEED);
|
86
|
+
});
|
87
|
+
});
|
88
|
+
|
89
|
+
it('should use current timestamp as initial seed', () => {
|
90
|
+
const mockTimestamp = 1234567890123;
|
91
|
+
vi.useFakeTimers();
|
92
|
+
vi.setSystemTime(mockTimestamp);
|
93
|
+
|
94
|
+
// Mock Date.now to verify it's being used
|
95
|
+
const dateSpy = vi.spyOn(Date, 'now').mockReturnValue(mockTimestamp);
|
96
|
+
|
97
|
+
generateUniqueSeeds(3);
|
98
|
+
|
99
|
+
expect(dateSpy).toHaveBeenCalled();
|
100
|
+
|
101
|
+
dateSpy.mockRestore();
|
102
|
+
vi.useRealTimers();
|
103
|
+
});
|
104
|
+
});
|
105
|
+
});
|