@lobehub/chat 1.98.2 → 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 +27 -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 +9 -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 +15 -0
- 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 +15 -0
- 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 +15 -0
- 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 +15 -0
- 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 +15 -0
- 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 +15 -0
- 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 +15 -0
- 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 +15 -0
- 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 +15 -0
- 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 +15 -0
- 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 +15 -0
- 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 +15 -0
- 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 +15 -0
- 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 +15 -0
- 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 +15 -0
- 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 +15 -0
- 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 +15 -0
- 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 +15 -0
- 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,25 @@
|
|
1
|
+
import prand from 'pure-rand';
|
2
|
+
|
3
|
+
export const MAX_SEED = 2 ** 31 - 1;
|
4
|
+
export function generateUniqueSeeds(seedCount: number): number[] {
|
5
|
+
// Use current timestamp as the initial seed
|
6
|
+
const initialSeed = Date.now();
|
7
|
+
|
8
|
+
// Create an instance of Xoroshiro128+ random number generator
|
9
|
+
let rng = prand.xoroshiro128plus(initialSeed);
|
10
|
+
|
11
|
+
const seeds = new Set<number>();
|
12
|
+
|
13
|
+
while (seeds.size < seedCount) {
|
14
|
+
// Generate a 31-bit random integer (PostgreSQL INTEGER compatible)
|
15
|
+
const [randomInt, nextRng] = prand.uniformIntDistribution(0, MAX_SEED, rng);
|
16
|
+
|
17
|
+
// Update RNG state
|
18
|
+
rng = nextRng;
|
19
|
+
|
20
|
+
// Add to the set, ensuring uniqueness
|
21
|
+
seeds.add(randomInt);
|
22
|
+
}
|
23
|
+
|
24
|
+
return Array.from(seeds);
|
25
|
+
}
|
@@ -63,11 +63,13 @@ describe('parseDefaultThemeFromCountry', () => {
|
|
63
63
|
timezones: ['America/New_York'],
|
64
64
|
});
|
65
65
|
|
66
|
-
|
66
|
+
// 设置UTC时间16:00,这样在纽约时区(EDT,UTC-4)就是12:00
|
67
|
+
const mockDate = new Date('2025-04-01T16:00:00.000Z');
|
67
68
|
vi.setSystemTime(mockDate);
|
68
69
|
|
69
70
|
const request = mockRequest();
|
70
|
-
|
71
|
+
const result = parseDefaultThemeFromCountry(request);
|
72
|
+
expect(result).toBe('light');
|
71
73
|
});
|
72
74
|
|
73
75
|
it('should return dark theme during night hours', () => {
|
@@ -78,7 +80,8 @@ describe('parseDefaultThemeFromCountry', () => {
|
|
78
80
|
timezones: ['America/New_York'],
|
79
81
|
});
|
80
82
|
|
81
|
-
|
83
|
+
// 设置UTC时间02:00,这样在纽约时区(EDT,UTC-4)就是22:00
|
84
|
+
const mockDate = new Date('2025-04-01T02:00:00.000Z');
|
82
85
|
vi.setSystemTime(mockDate);
|
83
86
|
|
84
87
|
const request = mockRequest();
|
@@ -0,0 +1,152 @@
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
2
|
+
|
3
|
+
import { setNamespace } from './storeDebug';
|
4
|
+
|
5
|
+
describe('storeDebug utilities', () => {
|
6
|
+
describe('setNamespace', () => {
|
7
|
+
it('should create a namespaced action creator', () => {
|
8
|
+
const createAction = setNamespace('user');
|
9
|
+
|
10
|
+
expect(typeof createAction).toBe('function');
|
11
|
+
});
|
12
|
+
|
13
|
+
it('should return string action type when no payload provided', () => {
|
14
|
+
const createAction = setNamespace('user');
|
15
|
+
const actionType = createAction('login');
|
16
|
+
|
17
|
+
expect(actionType).toBe('user/login');
|
18
|
+
expect(typeof actionType).toBe('string');
|
19
|
+
});
|
20
|
+
|
21
|
+
it('should return action object when payload provided', () => {
|
22
|
+
const createAction = setNamespace('user');
|
23
|
+
const action = createAction('login', { email: 'test@example.com' });
|
24
|
+
|
25
|
+
expect(action).toEqual({
|
26
|
+
type: 'user/login',
|
27
|
+
payload: { email: 'test@example.com' },
|
28
|
+
});
|
29
|
+
expect(typeof action).toBe('object');
|
30
|
+
});
|
31
|
+
|
32
|
+
it('should handle empty namespace', () => {
|
33
|
+
const createAction = setNamespace('');
|
34
|
+
const actionType = createAction('action');
|
35
|
+
|
36
|
+
expect(actionType).toBe('action');
|
37
|
+
});
|
38
|
+
|
39
|
+
it('should handle truthy payload values (return objects)', () => {
|
40
|
+
const createAction = setNamespace('data');
|
41
|
+
|
42
|
+
// String payload
|
43
|
+
const stringAction = createAction('update', 'hello');
|
44
|
+
expect(stringAction).toEqual({
|
45
|
+
type: 'data/update',
|
46
|
+
payload: 'hello',
|
47
|
+
});
|
48
|
+
|
49
|
+
// Number payload
|
50
|
+
const numberAction = createAction('increment', 42);
|
51
|
+
expect(numberAction).toEqual({
|
52
|
+
type: 'data/increment',
|
53
|
+
payload: 42,
|
54
|
+
});
|
55
|
+
|
56
|
+
// Boolean payload
|
57
|
+
const booleanAction = createAction('toggle', true);
|
58
|
+
expect(booleanAction).toEqual({
|
59
|
+
type: 'data/toggle',
|
60
|
+
payload: true,
|
61
|
+
});
|
62
|
+
|
63
|
+
// Array payload
|
64
|
+
const arrayAction = createAction('batch', [1, 2, 3]);
|
65
|
+
expect(arrayAction).toEqual({
|
66
|
+
type: 'data/batch',
|
67
|
+
payload: [1, 2, 3],
|
68
|
+
});
|
69
|
+
});
|
70
|
+
|
71
|
+
it('should handle falsy payload values (return strings)', () => {
|
72
|
+
const createAction = setNamespace('data');
|
73
|
+
|
74
|
+
// Null payload (falsy)
|
75
|
+
const nullAction = createAction('reset', null);
|
76
|
+
expect(nullAction).toBe('data/reset');
|
77
|
+
|
78
|
+
// Undefined payload (falsy)
|
79
|
+
const undefinedAction = createAction('clear', undefined);
|
80
|
+
expect(undefinedAction).toBe('data/clear');
|
81
|
+
|
82
|
+
// Zero payload (falsy)
|
83
|
+
const zeroAction = createAction('setValue', 0);
|
84
|
+
expect(zeroAction).toBe('data/setValue');
|
85
|
+
|
86
|
+
// Empty string payload (falsy)
|
87
|
+
const emptyStringAction = createAction('clearField', '');
|
88
|
+
expect(emptyStringAction).toBe('data/clearField');
|
89
|
+
|
90
|
+
// False payload (falsy)
|
91
|
+
const falseAction = createAction('disable', false);
|
92
|
+
expect(falseAction).toBe('data/disable');
|
93
|
+
});
|
94
|
+
|
95
|
+
it('should handle complex nested object payload', () => {
|
96
|
+
const createAction = setNamespace('app');
|
97
|
+
const complexPayload = {
|
98
|
+
user: {
|
99
|
+
id: 123,
|
100
|
+
profile: {
|
101
|
+
name: 'John Doe',
|
102
|
+
settings: {
|
103
|
+
theme: 'dark',
|
104
|
+
notifications: true,
|
105
|
+
},
|
106
|
+
},
|
107
|
+
},
|
108
|
+
metadata: {
|
109
|
+
timestamp: Date.now(),
|
110
|
+
source: 'api',
|
111
|
+
},
|
112
|
+
};
|
113
|
+
|
114
|
+
const action = createAction('userUpdate', complexPayload);
|
115
|
+
|
116
|
+
expect(action).toEqual({
|
117
|
+
type: 'app/userUpdate',
|
118
|
+
payload: complexPayload,
|
119
|
+
});
|
120
|
+
});
|
121
|
+
|
122
|
+
it('should create unique namespaces for different instances', () => {
|
123
|
+
const userActions = setNamespace('user');
|
124
|
+
const postActions = setNamespace('post');
|
125
|
+
|
126
|
+
const userLogin = userActions('login');
|
127
|
+
const postCreate = postActions('create');
|
128
|
+
|
129
|
+
expect(userLogin).toBe('user/login');
|
130
|
+
expect(postCreate).toBe('post/create');
|
131
|
+
});
|
132
|
+
|
133
|
+
it('should handle special characters in namespace and type', () => {
|
134
|
+
const createAction = setNamespace('my-feature_v2');
|
135
|
+
const actionType = createAction('save-data_with-id');
|
136
|
+
|
137
|
+
expect(actionType).toBe('my-feature_v2/save-data_with-id');
|
138
|
+
});
|
139
|
+
|
140
|
+
it('should maintain type consistency across calls', () => {
|
141
|
+
const createAction = setNamespace('counter');
|
142
|
+
|
143
|
+
// Multiple calls should work consistently
|
144
|
+
const increment1 = createAction('increment');
|
145
|
+
const increment2 = createAction('increment');
|
146
|
+
|
147
|
+
expect(increment1).toBe('counter/increment');
|
148
|
+
expect(increment2).toBe('counter/increment');
|
149
|
+
expect(increment1).toBe(increment2);
|
150
|
+
});
|
151
|
+
});
|
152
|
+
});
|
package/src/utils/storeDebug.ts
CHANGED
@@ -1,11 +1,20 @@
|
|
1
1
|
export const setNamespace = (namespace: string) => {
|
2
|
-
return (type: string, payload?:
|
2
|
+
return <P = undefined>(type: string, payload?: P) => {
|
3
3
|
const name = [namespace, type].filter(Boolean).join('/');
|
4
|
-
return
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
return (
|
5
|
+
payload
|
6
|
+
? {
|
7
|
+
payload,
|
8
|
+
type: name,
|
9
|
+
}
|
10
|
+
: name
|
11
|
+
) as P extends undefined ? string : { payload: P; type: string };
|
10
12
|
};
|
11
13
|
};
|
14
|
+
|
15
|
+
export type Action =
|
16
|
+
| string
|
17
|
+
| {
|
18
|
+
[x: string | number | symbol]: unknown;
|
19
|
+
type: string;
|
20
|
+
};
|
@@ -0,0 +1,259 @@
|
|
1
|
+
import dayjs from 'dayjs';
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
3
|
+
|
4
|
+
import {
|
5
|
+
daysAgo,
|
6
|
+
getYYYYmmddHHMMss,
|
7
|
+
hoursAgo,
|
8
|
+
lastMonth,
|
9
|
+
monthsAgo,
|
10
|
+
thisMonth,
|
11
|
+
thisQuarter,
|
12
|
+
thisWeek,
|
13
|
+
thisYear,
|
14
|
+
today,
|
15
|
+
weeksAgo,
|
16
|
+
} from './time';
|
17
|
+
|
18
|
+
describe('time utilities', () => {
|
19
|
+
beforeEach(() => {
|
20
|
+
vi.useFakeTimers();
|
21
|
+
// Set a fixed date for consistent testing: 2024-06-15 14:30:45 (local time)
|
22
|
+
vi.setSystemTime(new Date('2024-06-15T14:30:45.123'));
|
23
|
+
});
|
24
|
+
|
25
|
+
afterEach(() => {
|
26
|
+
vi.useRealTimers();
|
27
|
+
});
|
28
|
+
|
29
|
+
describe('today', () => {
|
30
|
+
it('should return the start of the current day', () => {
|
31
|
+
const result = today();
|
32
|
+
|
33
|
+
expect(result.format('YYYY-MM-DD HH:mm:ss')).toBe('2024-06-15 00:00:00');
|
34
|
+
});
|
35
|
+
});
|
36
|
+
|
37
|
+
describe('thisWeek', () => {
|
38
|
+
it('should return the start of the current week', () => {
|
39
|
+
const result = thisWeek();
|
40
|
+
|
41
|
+
// dayjs starts week on Sunday by default
|
42
|
+
expect(result.format('YYYY-MM-DD HH:mm:ss')).toBe('2024-06-09 00:00:00');
|
43
|
+
});
|
44
|
+
});
|
45
|
+
|
46
|
+
describe('thisMonth', () => {
|
47
|
+
it('should return the start of the current month', () => {
|
48
|
+
const result = thisMonth();
|
49
|
+
|
50
|
+
expect(result.format('YYYY-MM-DD HH:mm:ss')).toBe('2024-06-01 00:00:00');
|
51
|
+
});
|
52
|
+
});
|
53
|
+
|
54
|
+
describe('thisQuarter', () => {
|
55
|
+
it('should return the start of the current quarter (Q2)', () => {
|
56
|
+
const result = thisQuarter();
|
57
|
+
|
58
|
+
// June is in Q2, which starts in April (month 3, 0-indexed)
|
59
|
+
expect(result.format('YYYY-MM-DD HH:mm:ss')).toBe('2024-04-01 00:00:00');
|
60
|
+
});
|
61
|
+
|
62
|
+
it('should work correctly for Q1 (January)', () => {
|
63
|
+
vi.setSystemTime(new Date('2024-01-15T14:30:45.123'));
|
64
|
+
const result = thisQuarter();
|
65
|
+
|
66
|
+
expect(result.format('YYYY-MM-DD HH:mm:ss')).toBe('2024-01-01 00:00:00');
|
67
|
+
});
|
68
|
+
|
69
|
+
it('should work correctly for Q3 (September)', () => {
|
70
|
+
vi.setSystemTime(new Date('2024-09-15T14:30:45.123'));
|
71
|
+
const result = thisQuarter();
|
72
|
+
|
73
|
+
expect(result.format('YYYY-MM-DD HH:mm:ss')).toBe('2024-07-01 00:00:00');
|
74
|
+
});
|
75
|
+
|
76
|
+
it('should work correctly for Q4 (December)', () => {
|
77
|
+
vi.setSystemTime(new Date('2024-12-15T14:30:45.123'));
|
78
|
+
const result = thisQuarter();
|
79
|
+
|
80
|
+
expect(result.format('YYYY-MM-DD HH:mm:ss')).toBe('2024-10-01 00:00:00');
|
81
|
+
});
|
82
|
+
});
|
83
|
+
|
84
|
+
describe('thisYear', () => {
|
85
|
+
it('should return the start of the current year', () => {
|
86
|
+
const result = thisYear();
|
87
|
+
|
88
|
+
expect(result.format('YYYY-MM-DD HH:mm:ss')).toBe('2024-01-01 00:00:00');
|
89
|
+
});
|
90
|
+
});
|
91
|
+
|
92
|
+
describe('hoursAgo', () => {
|
93
|
+
it('should return the correct time hours ago', () => {
|
94
|
+
const result = hoursAgo(3);
|
95
|
+
|
96
|
+
expect(result.format('YYYY-MM-DD HH:mm:ss')).toBe('2024-06-15 11:00:00');
|
97
|
+
});
|
98
|
+
|
99
|
+
it('should handle 0 hours ago', () => {
|
100
|
+
const result = hoursAgo(0);
|
101
|
+
|
102
|
+
expect(result.format('YYYY-MM-DD HH:mm:ss')).toBe('2024-06-15 14:00:00');
|
103
|
+
});
|
104
|
+
|
105
|
+
it('should handle large hour values (cross day boundary)', () => {
|
106
|
+
const result = hoursAgo(25);
|
107
|
+
|
108
|
+
expect(result.format('YYYY-MM-DD HH:mm:ss')).toBe('2024-06-14 13:00:00');
|
109
|
+
});
|
110
|
+
});
|
111
|
+
|
112
|
+
describe('daysAgo', () => {
|
113
|
+
it('should return the correct date days ago', () => {
|
114
|
+
const result = daysAgo(7);
|
115
|
+
|
116
|
+
expect(result.format('YYYY-MM-DD HH:mm:ss')).toBe('2024-06-08 00:00:00');
|
117
|
+
});
|
118
|
+
|
119
|
+
it('should handle 0 days ago (today)', () => {
|
120
|
+
const result = daysAgo(0);
|
121
|
+
|
122
|
+
expect(result.format('YYYY-MM-DD HH:mm:ss')).toBe('2024-06-15 00:00:00');
|
123
|
+
});
|
124
|
+
|
125
|
+
it('should handle large day values (cross month boundary)', () => {
|
126
|
+
const result = daysAgo(30);
|
127
|
+
|
128
|
+
expect(result.format('YYYY-MM-DD HH:mm:ss')).toBe('2024-05-16 00:00:00');
|
129
|
+
});
|
130
|
+
});
|
131
|
+
|
132
|
+
describe('weeksAgo', () => {
|
133
|
+
it('should return the correct week weeks ago', () => {
|
134
|
+
const result = weeksAgo(2);
|
135
|
+
|
136
|
+
expect(result.format('YYYY-MM-DD HH:mm:ss')).toBe('2024-05-26 00:00:00');
|
137
|
+
});
|
138
|
+
|
139
|
+
it('should handle 0 weeks ago (this week)', () => {
|
140
|
+
const result = weeksAgo(0);
|
141
|
+
|
142
|
+
expect(result.format('YYYY-MM-DD HH:mm:ss')).toBe('2024-06-09 00:00:00');
|
143
|
+
});
|
144
|
+
});
|
145
|
+
|
146
|
+
describe('monthsAgo', () => {
|
147
|
+
it('should return the correct month months ago', () => {
|
148
|
+
const result = monthsAgo(3);
|
149
|
+
|
150
|
+
expect(result.format('YYYY-MM-DD HH:mm:ss')).toBe('2024-03-01 00:00:00');
|
151
|
+
});
|
152
|
+
|
153
|
+
it('should handle 0 months ago (this month)', () => {
|
154
|
+
const result = monthsAgo(0);
|
155
|
+
|
156
|
+
expect(result.format('YYYY-MM-DD HH:mm:ss')).toBe('2024-06-01 00:00:00');
|
157
|
+
});
|
158
|
+
|
159
|
+
it('should handle cross year boundary', () => {
|
160
|
+
const result = monthsAgo(12);
|
161
|
+
|
162
|
+
expect(result.format('YYYY-MM-DD HH:mm:ss')).toBe('2023-06-01 00:00:00');
|
163
|
+
});
|
164
|
+
});
|
165
|
+
|
166
|
+
describe('lastMonth', () => {
|
167
|
+
it('should return the end of last month', () => {
|
168
|
+
const result = lastMonth();
|
169
|
+
|
170
|
+
expect(result.format('YYYY-MM-DD HH:mm:ss')).toBe('2024-05-31 23:59:59');
|
171
|
+
});
|
172
|
+
|
173
|
+
it('should work correctly for January (December of previous year)', () => {
|
174
|
+
vi.setSystemTime(new Date('2024-01-15T14:30:45.123'));
|
175
|
+
const result = lastMonth();
|
176
|
+
|
177
|
+
expect(result.format('YYYY-MM-DD HH:mm:ss')).toBe('2023-12-31 23:59:59');
|
178
|
+
});
|
179
|
+
|
180
|
+
it('should work correctly for March (February with 29 days in leap year)', () => {
|
181
|
+
vi.setSystemTime(new Date('2024-03-15T14:30:45.123'));
|
182
|
+
const result = lastMonth();
|
183
|
+
|
184
|
+
expect(result.format('YYYY-MM-DD HH:mm:ss')).toBe('2024-02-29 23:59:59');
|
185
|
+
});
|
186
|
+
});
|
187
|
+
|
188
|
+
describe('getYYYYmmddHHMMss', () => {
|
189
|
+
it('should format date to YYYYMMDD_HHmmss format', () => {
|
190
|
+
const date = new Date('2024-01-01T23:59:59.123');
|
191
|
+
const result = getYYYYmmddHHMMss(date);
|
192
|
+
|
193
|
+
expect(result).toBe('20240101_235959');
|
194
|
+
});
|
195
|
+
|
196
|
+
it('should format date with zero padding', () => {
|
197
|
+
const date = new Date('2024-02-05T01:02:03.456');
|
198
|
+
const result = getYYYYmmddHHMMss(date);
|
199
|
+
|
200
|
+
expect(result).toBe('20240205_010203');
|
201
|
+
});
|
202
|
+
|
203
|
+
it('should handle end of year date', () => {
|
204
|
+
const date = new Date('2024-12-31T00:00:00.000');
|
205
|
+
const result = getYYYYmmddHHMMss(date);
|
206
|
+
|
207
|
+
expect(result).toBe('20241231_000000');
|
208
|
+
});
|
209
|
+
|
210
|
+
it('should handle leap year date', () => {
|
211
|
+
const date = new Date('2024-02-29T12:30:45.789');
|
212
|
+
const result = getYYYYmmddHHMMss(date);
|
213
|
+
|
214
|
+
expect(result).toBe('20240229_123045');
|
215
|
+
});
|
216
|
+
|
217
|
+
it('should work with the current mocked time', () => {
|
218
|
+
const date = new Date('2024-06-15T14:30:45.123');
|
219
|
+
const result = getYYYYmmddHHMMss(date);
|
220
|
+
|
221
|
+
expect(result).toBe('20240615_143045');
|
222
|
+
});
|
223
|
+
});
|
224
|
+
|
225
|
+
describe('edge cases and integration', () => {
|
226
|
+
it('should all return dayjs objects for date functions', () => {
|
227
|
+
expect(dayjs.isDayjs(today())).toBe(true);
|
228
|
+
expect(dayjs.isDayjs(thisWeek())).toBe(true);
|
229
|
+
expect(dayjs.isDayjs(thisMonth())).toBe(true);
|
230
|
+
expect(dayjs.isDayjs(thisQuarter())).toBe(true);
|
231
|
+
expect(dayjs.isDayjs(thisYear())).toBe(true);
|
232
|
+
expect(dayjs.isDayjs(hoursAgo(1))).toBe(true);
|
233
|
+
expect(dayjs.isDayjs(daysAgo(1))).toBe(true);
|
234
|
+
expect(dayjs.isDayjs(weeksAgo(1))).toBe(true);
|
235
|
+
expect(dayjs.isDayjs(monthsAgo(1))).toBe(true);
|
236
|
+
expect(dayjs.isDayjs(lastMonth())).toBe(true);
|
237
|
+
});
|
238
|
+
|
239
|
+
it('should maintain consistency between different time functions', () => {
|
240
|
+
const todayResult = today();
|
241
|
+
const daysAgoResult = daysAgo(0);
|
242
|
+
|
243
|
+
expect(todayResult.format('YYYY-MM-DD')).toBe(daysAgoResult.format('YYYY-MM-DD'));
|
244
|
+
});
|
245
|
+
|
246
|
+
it('should work with different time zones in getYYYYmmddHHMMss', () => {
|
247
|
+
// Note: dayjs uses the local timezone by default
|
248
|
+
const localDate = new Date('2024-06-15T14:30:45.000');
|
249
|
+
const result = getYYYYmmddHHMMss(localDate);
|
250
|
+
|
251
|
+
// The exact result depends on the test environment's timezone
|
252
|
+
// But it should always be a valid format
|
253
|
+
expect(result).toMatch(/^\d{8}_\d{6}$/);
|
254
|
+
expect(result).toHaveLength(15);
|
255
|
+
// Should match the local time format
|
256
|
+
expect(result).toBe('20240615_143045');
|
257
|
+
});
|
258
|
+
});
|
259
|
+
});
|
package/src/utils/time.ts
CHANGED
@@ -21,3 +21,21 @@ export const weeksAgo = (weeks: number) => dayjs().subtract(weeks, 'week').start
|
|
21
21
|
export const monthsAgo = (months: number) => dayjs().subtract(months, 'month').startOf('month');
|
22
22
|
|
23
23
|
export const lastMonth = () => monthsAgo(1).endOf('month');
|
24
|
+
|
25
|
+
/**
|
26
|
+
* Get the date in the format of YYYYMMdd_HHmmss like 20240101_235959
|
27
|
+
*
|
28
|
+
* @example
|
29
|
+
*
|
30
|
+
* ```ts
|
31
|
+
* getYYYYmmddHHMMss(new Date('2024-01-01 23:59:59')); // returns '20240101_235959'
|
32
|
+
* getYYYYmmddHHMMss(new Date('2024-12-31 00:00:00')); // returns '20241231_000000'
|
33
|
+
* ```
|
34
|
+
*
|
35
|
+
* @param date - The date to format
|
36
|
+
* @returns A string in YYYYMMdd_HHmmss format with underscore separator between date and time
|
37
|
+
* @see https://day.js.org/docs/en/display/format
|
38
|
+
*/
|
39
|
+
export function getYYYYmmddHHMMss(date: Date) {
|
40
|
+
return dayjs(date).format('YYYYMMDD_HHmmss');
|
41
|
+
}
|
@@ -0,0 +1,61 @@
|
|
1
|
+
// ------------------------ Time -----------------------------
|
2
|
+
|
3
|
+
export const MS = 1;
|
4
|
+
|
5
|
+
/**
|
6
|
+
* `1000ms`
|
7
|
+
*/
|
8
|
+
export const SECOND = 1000 * MS;
|
9
|
+
|
10
|
+
/**
|
11
|
+
* `60s`
|
12
|
+
*/
|
13
|
+
export const MINUTE = 60 * SECOND;
|
14
|
+
|
15
|
+
/**
|
16
|
+
* `3600s`
|
17
|
+
*/
|
18
|
+
export const HOUR = 60 * MINUTE;
|
19
|
+
|
20
|
+
/**
|
21
|
+
* `86400s`
|
22
|
+
*/
|
23
|
+
export const DAY = 24 * HOUR;
|
24
|
+
|
25
|
+
/**
|
26
|
+
* `2592000s`
|
27
|
+
*/
|
28
|
+
export const MONTH = 30 * DAY;
|
29
|
+
|
30
|
+
/**
|
31
|
+
* `31536000s`
|
32
|
+
*/
|
33
|
+
export const YEAR = 365 * DAY;
|
34
|
+
|
35
|
+
export const API_TEST_TIMEOUT = 10 * SECOND;
|
36
|
+
|
37
|
+
// ------------------------ Size -----------------------------
|
38
|
+
|
39
|
+
/**
|
40
|
+
* `1024 bytes`
|
41
|
+
*/
|
42
|
+
export const KiB = 1024;
|
43
|
+
|
44
|
+
/**
|
45
|
+
* `1024 KiB` (1,048,576 bytes)
|
46
|
+
*/
|
47
|
+
export const MiB = 1024 * KiB;
|
48
|
+
|
49
|
+
/**
|
50
|
+
* `1024 MiB` (1,073,741,824 bytes)
|
51
|
+
*/
|
52
|
+
export const GiB = 1024 * MiB;
|
53
|
+
|
54
|
+
/**
|
55
|
+
* `1024 GiB` (1,099,511,627,776 bytes)
|
56
|
+
*/
|
57
|
+
export const TiB = 1024 * GiB;
|
58
|
+
|
59
|
+
export const DOLLAR_PER_CREDIT = 0.003;
|
60
|
+
export const DOLLAR_PER_RMB = 0.14;
|
61
|
+
export const RMB_PER_CREDIT = DOLLAR_PER_RMB / DOLLAR_PER_CREDIT;
|