From 4eddf05e7975aac8bee2be382bbfb0f25da618ab Mon Sep 17 00:00:00 2001 From: "DESKTOP-72TV0V4\\caoxiaozhu" Date: Tue, 17 Mar 2026 14:36:31 +0800 Subject: [PATCH] first-update --- .claude/agents/backend-algorithm-developer.md | 79 + .claude/agents/elegant-frontend-designer.md | 98 + .claude/agents/robustness-tester-submitter.md | 94 + .claude/agents/ux-ui-requirements-analyst.md | 95 + .gitignore | 12 + README.md | 62 +- backend/Dockerfile | 27 + backend/app/api/__init__.py | 3 + backend/app/api/v1/__init__.py | 17 + backend/app/api/v1/chunks/__init__.py | 182 + backend/app/api/v1/datasets/__init__.py | 126 + backend/app/api/v1/eval/__init__.py | 100 + backend/app/api/v1/files/__init__.py | 110 + backend/app/api/v1/projects/__init__.py | 74 + backend/app/api/v1/questions/__init__.py | 122 + backend/app/core/__init__.py | 3 + backend/app/core/config.py | 49 + backend/app/core/database.py | 68 + backend/app/main.py | 58 + backend/app/models/__init__.py | 3 + backend/app/models/base.py | 19 + backend/app/models/models.py | 161 + backend/app/schemas/__init__.py | 3 + backend/app/schemas/base.py | 170 + backend/app/services/__init__.py | 3 + .../app/services/file_processor/__init__.py | 3 + .../services/file_processor/docx_processor.py | 53 + .../file_processor/excel_processor.py | 66 + .../services/file_processor/pdf_processor.py | 65 + .../app/services/text_splitter/__init__.py | 3 + .../app/services/text_splitter/splitter.py | 248 + backend/requirements.txt | 37 + bug修改.md | 20 + docker-compose.yml | 52 + easy-dataset-main-架构分析报告.md | 306 + easy-dataset-main/.dockerignore | 16 + easy-dataset-main/.gitattributes | 6 + .../.github/ISSUE_TEMPLATE/bug_report.md | 40 + .../ISSUE_TEMPLATE/feature-or-enhancement-.md | 19 + .../.github/ISSUE_TEMPLATE/question.md | 40 + .../.github/PULL_REQUEST_TEMPLATE.md | 12 + .../.github/workflows/docker-build.yml | 48 + easy-dataset-main/.gitignore | 22 + easy-dataset-main/.husky/commit-msg | 3 + easy-dataset-main/.husky/pre-commit | 1 + easy-dataset-main/.npmrc | 3 + easy-dataset-main/.prettierrc.js | 13 + easy-dataset-main/.windsurfrules | 124 + easy-dataset-main/AGENTS.md | 254 + easy-dataset-main/ARCHITECTURE.md | 183 + easy-dataset-main/Dockerfile | 86 + easy-dataset-main/LICENSE | 40 + easy-dataset-main/README.md | 294 + easy-dataset-main/README.tr.md | 319 + easy-dataset-main/README.zh-CN.md | 300 + .../app/api/check-update/route.js | 86 + .../app/api/llm/fetch-models/route.js | 75 + easy-dataset-main/app/api/llm/model/route.js | 39 + .../app/api/llm/ollama/models/route.js | 26 + .../app/api/llm/providers/route.js | 14 + .../app/api/monitoring/logs/route.js | 107 + .../app/api/monitoring/stats/route.js | 188 + .../app/api/monitoring/summary/route.js | 132 + .../[projectId]/batch-add-manual-ga/route.js | 176 + .../[projectId]/batch-delete-files/route.js | 196 + .../[projectId]/batch-generateGA/route.js | 106 + .../[taskId]/current/route.js | 161 + .../[taskId]/question/route.js | 64 + .../blind-test-tasks/[taskId]/route.js | 190 + .../[taskId]/stream-model/route.js | 92 + .../blind-test-tasks/[taskId]/stream/route.js | 213 + .../blind-test-tasks/[taskId]/vote/route.js | 154 + .../[projectId]/blind-test-tasks/route.js | 226 + .../chunks/[chunkId]/clean/route.js | 40 + .../chunks/[chunkId]/eval-questions/route.js | 35 + .../chunks/[chunkId]/questions/route.js | 73 + .../[projectId]/chunks/[chunkId]/route.js | 73 + .../[projectId]/chunks/batch-content/route.js | 20 + .../[projectId]/chunks/batch-edit/route.js | 102 + .../projects/[projectId]/chunks/name/route.js | 35 + .../api/projects/[projectId]/chunks/route.js | 21 + .../api/projects/[projectId]/config/route.js | 36 + .../[projectId]/custom-prompts/route.js | 105 + .../[projectId]/custom-split/route.js | 116 + .../[conversationId]/route.js | 183 + .../dataset-conversations/export/route.js | 68 + .../dataset-conversations/route.js | 135 + .../dataset-conversations/tags/route.js | 42 + .../[datasetId]/copy-to-eval/route.js | 77 + .../datasets/[datasetId]/evaluate/route.js | 36 + .../[projectId]/datasets/[datasetId]/route.js | 82 + .../datasets/[datasetId]/token-count/route.js | 52 + .../datasets/batch-evaluate/route.js | 55 + .../[projectId]/datasets/export/route.js | 128 + .../datasets/generate-eval-variant/route.js | 44 + .../[projectId]/datasets/import/route.js | 109 + .../[projectId]/datasets/optimize/route.js | 89 + .../projects/[projectId]/datasets/route.js | 193 + .../[projectId]/datasets/tags/route.js | 28 + .../[projectId]/default-prompts/route.js | 38 + .../distill/questions/by-tag/route.js | 67 + .../[projectId]/distill/questions/route.js | 101 + .../[projectId]/distill/tags/[tagId]/route.js | 61 + .../[projectId]/distill/tags/all/route.js | 31 + .../[projectId]/distill/tags/route.js | 88 + .../eval-datasets/[evalId]/route.js | 108 + .../[projectId]/eval-datasets/count/route.js | 63 + .../[projectId]/eval-datasets/export/route.js | 231 + .../[projectId]/eval-datasets/import/route.js | 380 + .../[projectId]/eval-datasets/route.js | 164 + .../[projectId]/eval-datasets/sample/route.js | 124 + .../[projectId]/eval-datasets/tags/route.js | 35 + .../[projectId]/eval-tasks/[taskId]/route.js | 176 + .../projects/[projectId]/eval-tasks/route.js | 207 + .../files/[fileId]/ga-pairs/route.js | 313 + .../api/projects/[projectId]/files/route.js | 243 + .../[projectId]/generate-questions/route.js | 126 + .../[projectId]/huggingface/upload/route.js | 310 + .../image-datasets/[datasetId]/route.js | 109 + .../image-datasets/export-zip/route.js | 85 + .../image-datasets/export/route.js | 32 + .../[projectId]/image-datasets/route.js | 72 + .../[projectId]/image-datasets/tags/route.js | 37 + .../[projectId]/images/[imageId]/route.js | 31 + .../[projectId]/images/annotations/route.js | 89 + .../[projectId]/images/datasets/route.js | 41 + .../images/next-unanswered/route.js | 41 + .../[projectId]/images/pdf-convert/route.js | 98 + .../[projectId]/images/questions/route.js | 40 + .../api/projects/[projectId]/images/route.js | 92 + .../[projectId]/images/zip-import/route.js | 127 + .../llamaFactory/checkConfig/route.js | 27 + .../llamaFactory/generate/route.js | 141 + .../model-config/[modelConfigId]/route.js | 18 + .../[projectId]/model-config/route.js | 103 + .../[projectId]/models/[modelId]/route.js | 173 + .../api/projects/[projectId]/models/route.js | 89 + .../[projectId]/playground/chat/route.js | 99 + .../playground/chat/stream/route.js | 89 + .../[projectId]/preview/[fileId]/route.js | 42 + .../questions/[questionId]/route.js | 26 + .../questions/batch-delete/route.js | 23 + .../[projectId]/questions/export/route.js | 100 + .../projects/[projectId]/questions/route.js | 110 + .../questions/templates/[templateId]/route.js | 110 + .../[projectId]/questions/templates/route.js | 116 + .../[projectId]/questions/tree/route.js | 44 + .../app/api/projects/[projectId]/route.js | 65 + .../api/projects/[projectId]/split/route.js | 88 + .../api/projects/[projectId]/tags/route.js | 102 + .../[projectId]/tasks/[taskId]/route.js | 171 + .../projects/[projectId]/tasks/list/route.js | 63 + .../api/projects/[projectId]/tasks/route.js | 164 + .../api/projects/delete-directory/route.js | 59 + .../app/api/projects/migrate/route.js | 169 + .../app/api/projects/open-directory/route.js | 62 + easy-dataset-main/app/api/projects/route.js | 42 + .../app/api/projects/unmigrated/route.js | 75 + easy-dataset-main/app/api/update/route.js | 70 + easy-dataset-main/app/dataset-square/page.js | 200 + easy-dataset-main/app/globals.css | 122 + easy-dataset-main/app/layout.js | 30 + .../app/monitoring/components/Charts.js | 188 + .../app/monitoring/components/StatsCards.js | 145 + .../app/monitoring/components/UsageTable.js | 215 + .../app/monitoring/hooks/useMonitoringData.js | 128 + easy-dataset-main/app/monitoring/page.js | 244 + easy-dataset-main/app/page.js | 153 + .../blind-test-tasks/[taskId]/page.js | 159 + .../components/BlindTestHeader.js | 77 + .../components/BlindTestInProgress.js | 449 + .../components/BlindTestTaskCard.js | 316 + .../components/CreateBlindTestDialog.js | 498 + .../components/ResultDetailList.js | 335 + .../components/ResultSummary.js | 257 + .../hooks/useBlindTestDetail.js | 405 + .../hooks/useBlindTestTasks.js | 140 + .../[projectId]/blind-test-tasks/page.js | 215 + .../[projectId]/datasets/[datasetId]/page.js | 202 + .../datasets/[datasetId]/useDatasetDetails.js | 471 + .../datasets/components/ActionBar.js | 33 + .../datasets/components/DatasetList.js | 422 + .../components/DeleteConfirmDialog.js | 105 + .../datasets/components/FilterDialog.js | 198 + .../datasets/components/SearchBar.js | 68 + .../datasets/hooks/useDatasetEvaluation.js | 165 + .../datasets/hooks/useDatasetExport.js | 487 + .../datasets/hooks/useDatasetFilters.js | 171 + .../app/projects/[projectId]/datasets/page.js | 596 + .../[projectId]/distill/autoDistillService.js | 870 + .../app/projects/[projectId]/distill/page.js | 528 + .../eval-datasets/[evalId]/page.js | 409 + .../[evalId]/useEvalDatasetDetails.js | 149 + .../components/BuiltinDatasetDialog.js | 327 + .../components/EvalDatasetCard.js | 335 + .../components/EvalDatasetHeader.js | 44 + .../components/EvalDatasetList.js | 155 + .../components/EvalEditableField.js | 126 + .../eval-datasets/components/EvalToolbar.js | 260 + .../components/EvalToolbar.styles.js | 151 + .../components/ExportEvalDialog.js | 259 + .../eval-datasets/components/ImportDialog.js | 319 + .../components/ImportDialog.styles.js | 133 + .../[projectId]/eval-datasets/constants.js | 679 + .../eval-datasets/hooks/useEvalDatasets.js | 220 + .../hooks/useExportEvalDatasets.js | 189 + .../[projectId]/eval-datasets/page.js | 322 + .../[taskId]/components/EvalHeader.js | 140 + .../[taskId]/components/EvalStats.js | 95 + .../[taskId]/components/QuestionCard.js | 362 + .../eval-tasks/[taskId]/detailStyles.js | 281 + .../[projectId]/eval-tasks/[taskId]/page.js | 214 + .../components/CreateEvalTaskDialog.js | 328 + .../eval-tasks/components/EvalTaskCard.js | 183 + .../eval-tasks/components/ModelSelector.js | 87 + .../eval-tasks/components/QuestionFilter.js | 160 + .../eval-tasks/components/ScoreAnchorsForm.js | 143 + .../eval-tasks/hooks/useEvalTaskDetail.js | 94 + .../eval-tasks/hooks/useEvalTaskForm.js | 187 + .../eval-tasks/hooks/useEvalTasks.js | 149 + .../projects/[projectId]/eval-tasks/page.js | 188 + .../projects/[projectId]/eval-tasks/styles.js | 280 + .../image-datasets/[datasetId]/page.js | 82 + .../components/DatasetContent.js | 155 + .../components/DatasetSidebar.js | 28 + .../image-datasets/components/EmptyState.js | 24 + .../components/ExportImageDatasetDialog.js | 127 + .../components/ImageDatasetCard.js | 206 + .../components/ImageDatasetFilterDialog.js | 87 + .../components/ImageDatasetFilters.js | 48 + .../components/ImageDatasetHeader.js | 82 + .../components/MetadataEditor.js | 159 + .../image-datasets/components/MetadataInfo.js | 154 + .../hooks/useImageDatasetDetail.js | 77 + .../hooks/useImageDatasetDetails.js | 172 + .../hooks/useImageDatasetExport.js | 195 + .../hooks/useImageDatasetFilters.js | 71 + .../image-datasets/hooks/useImageDatasets.js | 90 + .../[projectId]/image-datasets/page.js | 193 + .../styles/imageDatasetStyles.js | 266 + .../images/components/DatasetDialog.js | 135 + .../images/components/ImageFilters.js | 111 + .../images/components/ImageGrid.js | 193 + .../images/components/ImageList.js | 315 + .../images/components/ImportDialog.js | 416 + .../images/components/QuestionDialog.js | 135 + .../components/annotation/AIGenerateButton.js | 97 + .../components/annotation/AnnotationDialog.js | 268 + .../components/annotation/AnswerInput.js | 437 + .../components/annotation/QuestionSelector.js | 200 + .../[projectId]/images/hooks/useAnnotation.js | 287 + .../app/projects/[projectId]/images/page.js | 486 + .../[projectId]/images/styles/imageStyles.js | 286 + .../app/projects/[projectId]/layout.js | 125 + .../multi-turn/[conversationId]/page.js | 139 + .../useConversationDetails.js | 211 + .../components/ConversationTable.js | 313 + .../multi-turn/components/FilterDialog.js | 104 + .../multi-turn/components/RatingChip.js | 33 + .../multi-turn/components/SearchBar.js | 92 + .../multi-turn/hooks/useMultiTurnData.js | 346 + .../projects/[projectId]/multi-turn/page.js | 106 + .../app/projects/[projectId]/page.js | 39 + .../projects/[projectId]/playground/page.js | 92 + .../questions/components/ConfirmDialog.js | 58 + .../components/ExportQuestionsDialog.js | 82 + .../components/QuestionEditDialog.js | 220 + .../questions/components/QuestionsFilter.js | 192 + .../components/QuestionsPageHeader.js | 192 + .../questions/components/TemplateListView.js | 121 + .../components/template/TemplateFormDialog.js | 335 + .../template/TemplateManagementDialog.js | 165 + .../questions/hooks/useQuestionDelete.js | 125 + .../questions/hooks/useQuestionEdit.js | 84 + .../questions/hooks/useQuestionExport.js | 114 + .../questions/hooks/useQuestionGeneration.js | 291 + .../questions/hooks/useQuestionTemplates.js | 122 + .../questions/hooks/useQuestionsFilter.js | 117 + .../projects/[projectId]/questions/page.js | 416 + .../settings/components/CategoryTabs.js | 25 + .../settings/components/PromptDetail.js | 92 + .../settings/components/PromptEditDialog.js | 71 + .../settings/components/PromptList.js | 81 + .../settings/components/PromptSettings.js | 400 + .../settings/components/promptUtils.js | 34 + .../app/projects/[projectId]/settings/page.js | 125 + .../app/projects/[projectId]/tasks/page.js | 175 + .../projects/[projectId]/text-split/page.js | 440 + .../[projectId]/text-split/useChunks.js | 162 + .../[projectId]/text-split/useDataCleaning.js | 116 + .../text-split/useEvalGeneration.js | 91 + .../text-split/useFileProcessing.js | 91 + .../text-split/useQuestionGeneration.js | 116 + easy-dataset-main/commitlint.config.mjs | 1 + .../components/ExportDatasetDialog.js | 226 + .../components/ExportProgressDialog.js | 104 + easy-dataset-main/components/I18nProvider.js | 16 + .../components/LanguageSwitcher.js | 88 + easy-dataset-main/components/ModelSelect.js | 346 + .../components/Navbar/ActionButtons.js | 112 + .../components/Navbar/ContextBar.js | 175 + .../components/Navbar/DesktopMenus.js | 315 + easy-dataset-main/components/Navbar/Logo.js | 42 + .../components/Navbar/MobileDrawer.js | 405 + .../components/Navbar/NavigationTabs.js | 139 + .../components/Navbar/contextBarStyles.js | 247 + easy-dataset-main/components/Navbar/index.js | 257 + easy-dataset-main/components/Navbar/styles.js | 374 + easy-dataset-main/components/TaskIcon.js | 223 + easy-dataset-main/components/ThemeRegistry.js | 342 + easy-dataset-main/components/UpdateChecker.js | 235 + .../components/common/MessageAlert.js | 23 + .../conversations/ConversationContent.js | 88 + .../conversations/ConversationHeader.js | 87 + .../conversations/ConversationMetadata.js | 77 + .../ConversationRatingSection.js | 201 + .../dataset-square/DatasetSearchBar.js | 264 + .../dataset-square/DatasetSiteCard.js | 197 + .../dataset-square/DatasetSiteList.js | 211 + .../components/datasets/DatasetHeader.js | 97 + .../components/datasets/DatasetMetadata.js | 77 + .../datasets/DatasetRatingSection.js | 330 + .../components/datasets/EditableField.js | 286 + .../components/datasets/EvalVariantDialog.js | 238 + .../datasets/ImportDatasetDialog.js | 169 + .../components/datasets/NoteInput.js | 199 + .../components/datasets/OptimizeDialog.js | 50 + .../components/datasets/StarRating.js | 69 + .../components/datasets/TagSelector.js | 185 + .../datasets/import/FieldMappingStep.js | 314 + .../datasets/import/FileUploadStep.js | 344 + .../datasets/import/ImportProgressStep.js | 303 + .../components/datasets/utils/ratingUtils.js | 135 + .../components/distill/AutoDistillDialog.js | 325 + .../components/distill/AutoDistillProgress.js | 212 + .../components/distill/ConfirmDialog.js | 37 + .../components/distill/DistillTreeView.js | 515 + .../distill/QuestionGenerationDialog.js | 194 + .../components/distill/QuestionListItem.js | 121 + .../components/distill/TagEditDialog.js | 115 + .../components/distill/TagGenerationDialog.js | 230 + .../components/distill/TagMenu.js | 46 + .../components/distill/TagTreeItem.js | 240 + easy-dataset-main/components/distill/utils.js | 72 + .../components/export/HuggingFaceTab.js | 245 + .../components/export/LlamaFactoryTab.js | 184 + .../components/export/LocalExportTab.js | 777 + .../components/home/CreateProjectDialog.js | 173 + .../components/home/HeroSection.js | 135 + .../components/home/MigrationDialog.js | 300 + .../components/home/ParticleBackground.js | 251 + .../components/home/ProjectCard.js | 252 + .../components/home/ProjectList.js | 117 + .../components/home/StatsCard.js | 118 + .../components/mga/GaPairsIndicator.js | 151 + .../components/mga/GaPairsManager.js | 610 + .../components/playground/ChatArea.js | 83 + .../components/playground/ChatMessage.js | 215 + .../components/playground/MessageInput.js | 104 + .../components/playground/ModelSelector.js | 81 + .../components/playground/PlaygroundHeader.js | 66 + .../components/questions/QuestionListView.js | 374 + .../components/questions/QuestionTreeView.js | 565 + .../components/settings/BasicSettings.js | 153 + .../components/settings/ModelSettings.js | 1056 + .../components/settings/TaskSettings.js | 709 + .../components/tasks/TaskActions.js | 27 + .../components/tasks/TaskFilters.js | 74 + .../components/tasks/TaskProgress.js | 36 + .../components/tasks/TaskStatusChip.js | 48 + .../components/tasks/TasksTable.js | 293 + .../text-split/BatchEditChunkDialog.js | 180 + .../text-split/ChunkBatchDeleteDialog.js | 45 + .../components/text-split/ChunkCard.js | 449 + .../text-split/ChunkDeleteDialog.js | 27 + .../text-split/ChunkFilterDialog.js | 124 + .../components/text-split/ChunkList.js | 413 + .../components/text-split/ChunkListHeader.js | 400 + .../components/text-split/ChunkViewDialog.js | 31 + .../components/text-split/DomainAnalysis.js | 560 + .../components/text-split/FileUploader.js | 360 + .../components/text-split/LoadingBackdrop.js | 114 + .../text-split/MarkdownViewDialog.js | 433 + .../components/text-split/PdfSettings.js | 43 + .../components/DeleteConfirmDialog.js | 60 + .../text-split/components/DirectoryView.js | 73 + .../components/DomainTreeActionDialog.js | 101 + .../text-split/components/DomainTreeView.js | 33 + .../text-split/components/FileList.js | 1068 + .../components/FileLoadingProgress.js | 208 + .../components/PdfProcessingDialog.js | 188 + .../text-split/components/TabPanel.js | 24 + .../text-split/components/UploadArea.js | 207 + easy-dataset-main/constant/index.js | 15 + easy-dataset-main/constant/model.js | 83 + easy-dataset-main/constant/setting.js | 24 + easy-dataset-main/constant/sites.json | 286 + easy-dataset-main/docker-compose.yml | 10 + easy-dataset-main/docker-entrypoint.sh | 70 + .../electron/entitlements.mac.plist | 16 + easy-dataset-main/electron/loading.html | 124 + easy-dataset-main/electron/main.js | 83 + easy-dataset-main/electron/modules/cache.js | 21 + .../electron/modules/database.js | 147 + .../electron/modules/db-updater.js | 179 + .../electron/modules/ipc-handlers.js | 33 + easy-dataset-main/electron/modules/logger.js | 84 + easy-dataset-main/electron/modules/menu.js | 136 + easy-dataset-main/electron/modules/server.js | 118 + easy-dataset-main/electron/modules/updater.js | 116 + .../electron/modules/window-manager.js | 113 + easy-dataset-main/electron/preload.js | 73 + easy-dataset-main/electron/util.js | 19 + easy-dataset-main/hooks/useDebounce.js | 14 + .../hooks/useFileProcessingStatus.js | 57 + easy-dataset-main/hooks/useGenerateDataset.js | 135 + easy-dataset-main/hooks/useModelPlayground.js | 406 + easy-dataset-main/hooks/useSnackbar.js | 73 + easy-dataset-main/hooks/useTaskSettings.js | 63 + easy-dataset-main/jsconfig.json | 8 + easy-dataset-main/locales/en/translation.json | 1871 ++ .../locales/pt-BR/translation.json | 1854 ++ easy-dataset-main/locales/tr/translation.json | 1506 ++ .../locales/zh-CN/translation.json | 1855 ++ easy-dataset-main/next.config.js | 19 + easy-dataset-main/package-lock.json | 18891 ++++++++++++++++ easy-dataset-main/package.json | 184 + easy-dataset-main/pnpm-lock.yaml | 13492 +++++++++++ easy-dataset-main/prisma/generate-template.js | 48 + easy-dataset-main/prisma/schema.prisma | 406 + easy-dataset-main/prisma/sql.json | 88 + easy-dataset-main/public/imgs/1.png | Bin 0 -> 1786251 bytes easy-dataset-main/public/imgs/10.png | Bin 0 -> 278278 bytes easy-dataset-main/public/imgs/2.png | Bin 0 -> 357102 bytes easy-dataset-main/public/imgs/3.png | Bin 0 -> 471359 bytes easy-dataset-main/public/imgs/4.png | Bin 0 -> 714058 bytes easy-dataset-main/public/imgs/5.png | Bin 0 -> 477433 bytes easy-dataset-main/public/imgs/6.png | Bin 0 -> 386859 bytes easy-dataset-main/public/imgs/7.png | Bin 0 -> 607727 bytes easy-dataset-main/public/imgs/8.png | Bin 0 -> 412338 bytes easy-dataset-main/public/imgs/9.png | Bin 0 -> 451257 bytes easy-dataset-main/public/imgs/arc3.png | Bin 0 -> 472024 bytes easy-dataset-main/public/imgs/aw.jpg | Bin 0 -> 34473 bytes easy-dataset-main/public/imgs/aws.png | Bin 0 -> 745797 bytes easy-dataset-main/public/imgs/bg.png | Bin 0 -> 718793 bytes easy-dataset-main/public/imgs/bg2.png | Bin 0 -> 475028 bytes easy-dataset-main/public/imgs/cn-arc.png | Bin 0 -> 350129 bytes .../public/imgs/default-dataset.png | Bin 0 -> 54604 bytes easy-dataset-main/public/imgs/en-arc.png | Bin 0 -> 366117 bytes easy-dataset-main/public/imgs/garden.jpg | Bin 0 -> 108667 bytes easy-dataset-main/public/imgs/github.png | Bin 0 -> 475083 bytes easy-dataset-main/public/imgs/google.png | Bin 0 -> 402788 bytes easy-dataset-main/public/imgs/huggingface.png | Bin 0 -> 940823 bytes easy-dataset-main/public/imgs/kaggle.png | Bin 0 -> 706747 bytes easy-dataset-main/public/imgs/linux.png | Bin 0 -> 51038 bytes easy-dataset-main/public/imgs/lluga.png | Bin 0 -> 1846523 bytes easy-dataset-main/public/imgs/logo.icns | Bin 0 -> 27451 bytes easy-dataset-main/public/imgs/logo.ico | Bin 0 -> 270398 bytes easy-dataset-main/public/imgs/logo.png | Bin 0 -> 169204 bytes easy-dataset-main/public/imgs/logo.svg | 18 + easy-dataset-main/public/imgs/logo_old.icns | Bin 0 -> 16428 bytes easy-dataset-main/public/imgs/mac.png | Bin 0 -> 5774 bytes .../public/imgs/models/chatglm.svg | 1 + .../public/imgs/models/claude.svg | 1 + .../public/imgs/models/deepseek.svg | 1 + .../public/imgs/models/default.svg | 1 + .../public/imgs/models/doubao.svg | 1 + .../public/imgs/models/gemini.svg | 1 + easy-dataset-main/public/imgs/models/glm.svg | 1 + easy-dataset-main/public/imgs/models/gpt.svg | 1 + .../public/imgs/models/hunyuan.svg | 1 + .../public/imgs/models/llama.svg | 1 + easy-dataset-main/public/imgs/models/qwen.svg | 1 + .../public/imgs/models/wenxin.svg | 1 + easy-dataset-main/public/imgs/models/yi.svg | 1 + easy-dataset-main/public/imgs/modelscope.png | Bin 0 -> 450994 bytes easy-dataset-main/public/imgs/opendatalab.png | Bin 0 -> 1158564 bytes .../public/imgs/providers/302ai.ico | Bin 0 -> 41662 bytes .../public/imgs/providers/alibailian.ico | Bin 0 -> 4286 bytes .../public/imgs/providers/deepseek.ico | Bin 0 -> 209762 bytes .../public/imgs/providers/grok.svg | 1 + .../public/imgs/providers/groq.ico | Bin 0 -> 15406 bytes .../public/imgs/providers/ollama.png | Bin 0 -> 7487 bytes .../public/imgs/providers/openai.png | Bin 0 -> 3558 bytes .../public/imgs/providers/openrouter.ico | Bin 0 -> 15406 bytes .../public/imgs/providers/siliconflow.ico | Bin 0 -> 358 bytes .../public/imgs/providers/volcengine.png | Bin 0 -> 3251 bytes .../public/imgs/providers/zhipu.png | Bin 0 -> 1099 bytes easy-dataset-main/public/imgs/weichat.jpg | Bin 0 -> 40881 bytes easy-dataset-main/public/imgs/windows.png | Bin 0 -> 6043 bytes easy-dataset-main/styles/blindTest.js | 169 + easy-dataset-main/styles/globals.css | 19 + easy-dataset-main/styles/home.js | 165 + easy-dataset-main/styles/playground.js | 82 + frontend/Dockerfile | 22 + frontend/index.html | 13 + frontend/nginx.conf | 20 + frontend/package-lock.json | 2001 ++ frontend/package.json | 24 + frontend/src/App.vue | 338 + frontend/src/api/index.js | 81 + frontend/src/main.js | 21 + frontend/src/router/index.js | 67 + frontend/src/views/DataSquareView.vue | 397 + frontend/src/views/HomeView.vue | 1239 + frontend/src/views/PlaygroundView.vue | 415 + frontend/src/views/ProjectView.vue | 363 + frontend/src/views/project/DatasetManage.vue | 445 + frontend/src/views/project/EvalManage.vue | 405 + frontend/src/views/project/FileManage.vue | 611 + frontend/src/views/project/QuestionManage.vue | 320 + frontend/src/views/project/Settings.vue | 202 + frontend/src/views/project/TextSplit.vue | 470 + frontend/vite.config.js | 21 + 开发计划.md | 117 + 项目架构.md | 225 + 516 files changed, 115270 insertions(+), 1 deletion(-) create mode 100644 .claude/agents/backend-algorithm-developer.md create mode 100644 .claude/agents/elegant-frontend-designer.md create mode 100644 .claude/agents/robustness-tester-submitter.md create mode 100644 .claude/agents/ux-ui-requirements-analyst.md create mode 100644 backend/Dockerfile create mode 100644 backend/app/api/__init__.py create mode 100644 backend/app/api/v1/__init__.py create mode 100644 backend/app/api/v1/chunks/__init__.py create mode 100644 backend/app/api/v1/datasets/__init__.py create mode 100644 backend/app/api/v1/eval/__init__.py create mode 100644 backend/app/api/v1/files/__init__.py create mode 100644 backend/app/api/v1/projects/__init__.py create mode 100644 backend/app/api/v1/questions/__init__.py create mode 100644 backend/app/core/__init__.py create mode 100644 backend/app/core/config.py create mode 100644 backend/app/core/database.py create mode 100644 backend/app/main.py create mode 100644 backend/app/models/__init__.py create mode 100644 backend/app/models/base.py create mode 100644 backend/app/models/models.py create mode 100644 backend/app/schemas/__init__.py create mode 100644 backend/app/schemas/base.py create mode 100644 backend/app/services/__init__.py create mode 100644 backend/app/services/file_processor/__init__.py create mode 100644 backend/app/services/file_processor/docx_processor.py create mode 100644 backend/app/services/file_processor/excel_processor.py create mode 100644 backend/app/services/file_processor/pdf_processor.py create mode 100644 backend/app/services/text_splitter/__init__.py create mode 100644 backend/app/services/text_splitter/splitter.py create mode 100644 backend/requirements.txt create mode 100644 bug修改.md create mode 100644 docker-compose.yml create mode 100644 easy-dataset-main-架构分析报告.md create mode 100644 easy-dataset-main/.dockerignore create mode 100644 easy-dataset-main/.gitattributes create mode 100644 easy-dataset-main/.github/ISSUE_TEMPLATE/bug_report.md create mode 100644 easy-dataset-main/.github/ISSUE_TEMPLATE/feature-or-enhancement-.md create mode 100644 easy-dataset-main/.github/ISSUE_TEMPLATE/question.md create mode 100644 easy-dataset-main/.github/PULL_REQUEST_TEMPLATE.md create mode 100644 easy-dataset-main/.github/workflows/docker-build.yml create mode 100644 easy-dataset-main/.gitignore create mode 100644 easy-dataset-main/.husky/commit-msg create mode 100644 easy-dataset-main/.husky/pre-commit create mode 100644 easy-dataset-main/.npmrc create mode 100644 easy-dataset-main/.prettierrc.js create mode 100644 easy-dataset-main/.windsurfrules create mode 100644 easy-dataset-main/AGENTS.md create mode 100644 easy-dataset-main/ARCHITECTURE.md create mode 100644 easy-dataset-main/Dockerfile create mode 100644 easy-dataset-main/LICENSE create mode 100644 easy-dataset-main/README.md create mode 100644 easy-dataset-main/README.tr.md create mode 100644 easy-dataset-main/README.zh-CN.md create mode 100644 easy-dataset-main/app/api/check-update/route.js create mode 100644 easy-dataset-main/app/api/llm/fetch-models/route.js create mode 100644 easy-dataset-main/app/api/llm/model/route.js create mode 100644 easy-dataset-main/app/api/llm/ollama/models/route.js create mode 100644 easy-dataset-main/app/api/llm/providers/route.js create mode 100644 easy-dataset-main/app/api/monitoring/logs/route.js create mode 100644 easy-dataset-main/app/api/monitoring/stats/route.js create mode 100644 easy-dataset-main/app/api/monitoring/summary/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/batch-add-manual-ga/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/batch-delete-files/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/batch-generateGA/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/blind-test-tasks/[taskId]/current/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/blind-test-tasks/[taskId]/question/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/blind-test-tasks/[taskId]/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/blind-test-tasks/[taskId]/stream-model/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/blind-test-tasks/[taskId]/stream/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/blind-test-tasks/[taskId]/vote/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/blind-test-tasks/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/chunks/[chunkId]/clean/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/chunks/[chunkId]/eval-questions/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/chunks/[chunkId]/questions/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/chunks/[chunkId]/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/chunks/batch-content/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/chunks/batch-edit/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/chunks/name/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/chunks/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/config/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/custom-prompts/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/custom-split/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/dataset-conversations/[conversationId]/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/dataset-conversations/export/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/dataset-conversations/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/dataset-conversations/tags/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/datasets/[datasetId]/copy-to-eval/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/datasets/[datasetId]/evaluate/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/datasets/[datasetId]/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/datasets/[datasetId]/token-count/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/datasets/batch-evaluate/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/datasets/export/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/datasets/generate-eval-variant/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/datasets/import/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/datasets/optimize/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/datasets/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/datasets/tags/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/default-prompts/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/distill/questions/by-tag/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/distill/questions/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/distill/tags/[tagId]/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/distill/tags/all/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/distill/tags/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/eval-datasets/[evalId]/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/eval-datasets/count/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/eval-datasets/export/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/eval-datasets/import/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/eval-datasets/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/eval-datasets/sample/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/eval-datasets/tags/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/eval-tasks/[taskId]/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/eval-tasks/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/files/[fileId]/ga-pairs/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/files/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/generate-questions/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/huggingface/upload/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/image-datasets/[datasetId]/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/image-datasets/export-zip/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/image-datasets/export/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/image-datasets/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/image-datasets/tags/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/images/[imageId]/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/images/annotations/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/images/datasets/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/images/next-unanswered/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/images/pdf-convert/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/images/questions/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/images/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/images/zip-import/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/llamaFactory/checkConfig/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/llamaFactory/generate/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/model-config/[modelConfigId]/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/model-config/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/models/[modelId]/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/models/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/playground/chat/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/playground/chat/stream/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/preview/[fileId]/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/questions/[questionId]/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/questions/batch-delete/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/questions/export/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/questions/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/questions/templates/[templateId]/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/questions/templates/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/questions/tree/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/split/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/tags/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/tasks/[taskId]/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/tasks/list/route.js create mode 100644 easy-dataset-main/app/api/projects/[projectId]/tasks/route.js create mode 100644 easy-dataset-main/app/api/projects/delete-directory/route.js create mode 100644 easy-dataset-main/app/api/projects/migrate/route.js create mode 100644 easy-dataset-main/app/api/projects/open-directory/route.js create mode 100644 easy-dataset-main/app/api/projects/route.js create mode 100644 easy-dataset-main/app/api/projects/unmigrated/route.js create mode 100644 easy-dataset-main/app/api/update/route.js create mode 100644 easy-dataset-main/app/dataset-square/page.js create mode 100644 easy-dataset-main/app/globals.css create mode 100644 easy-dataset-main/app/layout.js create mode 100644 easy-dataset-main/app/monitoring/components/Charts.js create mode 100644 easy-dataset-main/app/monitoring/components/StatsCards.js create mode 100644 easy-dataset-main/app/monitoring/components/UsageTable.js create mode 100644 easy-dataset-main/app/monitoring/hooks/useMonitoringData.js create mode 100644 easy-dataset-main/app/monitoring/page.js create mode 100644 easy-dataset-main/app/page.js create mode 100644 easy-dataset-main/app/projects/[projectId]/blind-test-tasks/[taskId]/page.js create mode 100644 easy-dataset-main/app/projects/[projectId]/blind-test-tasks/components/BlindTestHeader.js create mode 100644 easy-dataset-main/app/projects/[projectId]/blind-test-tasks/components/BlindTestInProgress.js create mode 100644 easy-dataset-main/app/projects/[projectId]/blind-test-tasks/components/BlindTestTaskCard.js create mode 100644 easy-dataset-main/app/projects/[projectId]/blind-test-tasks/components/CreateBlindTestDialog.js create mode 100644 easy-dataset-main/app/projects/[projectId]/blind-test-tasks/components/ResultDetailList.js create mode 100644 easy-dataset-main/app/projects/[projectId]/blind-test-tasks/components/ResultSummary.js create mode 100644 easy-dataset-main/app/projects/[projectId]/blind-test-tasks/hooks/useBlindTestDetail.js create mode 100644 easy-dataset-main/app/projects/[projectId]/blind-test-tasks/hooks/useBlindTestTasks.js create mode 100644 easy-dataset-main/app/projects/[projectId]/blind-test-tasks/page.js create mode 100644 easy-dataset-main/app/projects/[projectId]/datasets/[datasetId]/page.js create mode 100644 easy-dataset-main/app/projects/[projectId]/datasets/[datasetId]/useDatasetDetails.js create mode 100644 easy-dataset-main/app/projects/[projectId]/datasets/components/ActionBar.js create mode 100644 easy-dataset-main/app/projects/[projectId]/datasets/components/DatasetList.js create mode 100644 easy-dataset-main/app/projects/[projectId]/datasets/components/DeleteConfirmDialog.js create mode 100644 easy-dataset-main/app/projects/[projectId]/datasets/components/FilterDialog.js create mode 100644 easy-dataset-main/app/projects/[projectId]/datasets/components/SearchBar.js create mode 100644 easy-dataset-main/app/projects/[projectId]/datasets/hooks/useDatasetEvaluation.js create mode 100644 easy-dataset-main/app/projects/[projectId]/datasets/hooks/useDatasetExport.js create mode 100644 easy-dataset-main/app/projects/[projectId]/datasets/hooks/useDatasetFilters.js create mode 100644 easy-dataset-main/app/projects/[projectId]/datasets/page.js create mode 100644 easy-dataset-main/app/projects/[projectId]/distill/autoDistillService.js create mode 100644 easy-dataset-main/app/projects/[projectId]/distill/page.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-datasets/[evalId]/page.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-datasets/[evalId]/useEvalDatasetDetails.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-datasets/components/BuiltinDatasetDialog.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-datasets/components/EvalDatasetCard.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-datasets/components/EvalDatasetHeader.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-datasets/components/EvalDatasetList.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-datasets/components/EvalEditableField.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-datasets/components/EvalToolbar.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-datasets/components/EvalToolbar.styles.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-datasets/components/ExportEvalDialog.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-datasets/components/ImportDialog.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-datasets/components/ImportDialog.styles.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-datasets/constants.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-datasets/hooks/useEvalDatasets.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-datasets/hooks/useExportEvalDatasets.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-datasets/page.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-tasks/[taskId]/components/EvalHeader.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-tasks/[taskId]/components/EvalStats.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-tasks/[taskId]/components/QuestionCard.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-tasks/[taskId]/detailStyles.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-tasks/[taskId]/page.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-tasks/components/CreateEvalTaskDialog.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-tasks/components/EvalTaskCard.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-tasks/components/ModelSelector.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-tasks/components/QuestionFilter.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-tasks/components/ScoreAnchorsForm.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-tasks/hooks/useEvalTaskDetail.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-tasks/hooks/useEvalTaskForm.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-tasks/hooks/useEvalTasks.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-tasks/page.js create mode 100644 easy-dataset-main/app/projects/[projectId]/eval-tasks/styles.js create mode 100644 easy-dataset-main/app/projects/[projectId]/image-datasets/[datasetId]/page.js create mode 100644 easy-dataset-main/app/projects/[projectId]/image-datasets/components/DatasetContent.js create mode 100644 easy-dataset-main/app/projects/[projectId]/image-datasets/components/DatasetSidebar.js create mode 100644 easy-dataset-main/app/projects/[projectId]/image-datasets/components/EmptyState.js create mode 100644 easy-dataset-main/app/projects/[projectId]/image-datasets/components/ExportImageDatasetDialog.js create mode 100644 easy-dataset-main/app/projects/[projectId]/image-datasets/components/ImageDatasetCard.js create mode 100644 easy-dataset-main/app/projects/[projectId]/image-datasets/components/ImageDatasetFilterDialog.js create mode 100644 easy-dataset-main/app/projects/[projectId]/image-datasets/components/ImageDatasetFilters.js create mode 100644 easy-dataset-main/app/projects/[projectId]/image-datasets/components/ImageDatasetHeader.js create mode 100644 easy-dataset-main/app/projects/[projectId]/image-datasets/components/MetadataEditor.js create mode 100644 easy-dataset-main/app/projects/[projectId]/image-datasets/components/MetadataInfo.js create mode 100644 easy-dataset-main/app/projects/[projectId]/image-datasets/hooks/useImageDatasetDetail.js create mode 100644 easy-dataset-main/app/projects/[projectId]/image-datasets/hooks/useImageDatasetDetails.js create mode 100644 easy-dataset-main/app/projects/[projectId]/image-datasets/hooks/useImageDatasetExport.js create mode 100644 easy-dataset-main/app/projects/[projectId]/image-datasets/hooks/useImageDatasetFilters.js create mode 100644 easy-dataset-main/app/projects/[projectId]/image-datasets/hooks/useImageDatasets.js create mode 100644 easy-dataset-main/app/projects/[projectId]/image-datasets/page.js create mode 100644 easy-dataset-main/app/projects/[projectId]/image-datasets/styles/imageDatasetStyles.js create mode 100644 easy-dataset-main/app/projects/[projectId]/images/components/DatasetDialog.js create mode 100644 easy-dataset-main/app/projects/[projectId]/images/components/ImageFilters.js create mode 100644 easy-dataset-main/app/projects/[projectId]/images/components/ImageGrid.js create mode 100644 easy-dataset-main/app/projects/[projectId]/images/components/ImageList.js create mode 100644 easy-dataset-main/app/projects/[projectId]/images/components/ImportDialog.js create mode 100644 easy-dataset-main/app/projects/[projectId]/images/components/QuestionDialog.js create mode 100644 easy-dataset-main/app/projects/[projectId]/images/components/annotation/AIGenerateButton.js create mode 100644 easy-dataset-main/app/projects/[projectId]/images/components/annotation/AnnotationDialog.js create mode 100644 easy-dataset-main/app/projects/[projectId]/images/components/annotation/AnswerInput.js create mode 100644 easy-dataset-main/app/projects/[projectId]/images/components/annotation/QuestionSelector.js create mode 100644 easy-dataset-main/app/projects/[projectId]/images/hooks/useAnnotation.js create mode 100644 easy-dataset-main/app/projects/[projectId]/images/page.js create mode 100644 easy-dataset-main/app/projects/[projectId]/images/styles/imageStyles.js create mode 100644 easy-dataset-main/app/projects/[projectId]/layout.js create mode 100644 easy-dataset-main/app/projects/[projectId]/multi-turn/[conversationId]/page.js create mode 100644 easy-dataset-main/app/projects/[projectId]/multi-turn/[conversationId]/useConversationDetails.js create mode 100644 easy-dataset-main/app/projects/[projectId]/multi-turn/components/ConversationTable.js create mode 100644 easy-dataset-main/app/projects/[projectId]/multi-turn/components/FilterDialog.js create mode 100644 easy-dataset-main/app/projects/[projectId]/multi-turn/components/RatingChip.js create mode 100644 easy-dataset-main/app/projects/[projectId]/multi-turn/components/SearchBar.js create mode 100644 easy-dataset-main/app/projects/[projectId]/multi-turn/hooks/useMultiTurnData.js create mode 100644 easy-dataset-main/app/projects/[projectId]/multi-turn/page.js create mode 100644 easy-dataset-main/app/projects/[projectId]/page.js create mode 100644 easy-dataset-main/app/projects/[projectId]/playground/page.js create mode 100644 easy-dataset-main/app/projects/[projectId]/questions/components/ConfirmDialog.js create mode 100644 easy-dataset-main/app/projects/[projectId]/questions/components/ExportQuestionsDialog.js create mode 100644 easy-dataset-main/app/projects/[projectId]/questions/components/QuestionEditDialog.js create mode 100644 easy-dataset-main/app/projects/[projectId]/questions/components/QuestionsFilter.js create mode 100644 easy-dataset-main/app/projects/[projectId]/questions/components/QuestionsPageHeader.js create mode 100644 easy-dataset-main/app/projects/[projectId]/questions/components/TemplateListView.js create mode 100644 easy-dataset-main/app/projects/[projectId]/questions/components/template/TemplateFormDialog.js create mode 100644 easy-dataset-main/app/projects/[projectId]/questions/components/template/TemplateManagementDialog.js create mode 100644 easy-dataset-main/app/projects/[projectId]/questions/hooks/useQuestionDelete.js create mode 100644 easy-dataset-main/app/projects/[projectId]/questions/hooks/useQuestionEdit.js create mode 100644 easy-dataset-main/app/projects/[projectId]/questions/hooks/useQuestionExport.js create mode 100644 easy-dataset-main/app/projects/[projectId]/questions/hooks/useQuestionGeneration.js create mode 100644 easy-dataset-main/app/projects/[projectId]/questions/hooks/useQuestionTemplates.js create mode 100644 easy-dataset-main/app/projects/[projectId]/questions/hooks/useQuestionsFilter.js create mode 100644 easy-dataset-main/app/projects/[projectId]/questions/page.js create mode 100644 easy-dataset-main/app/projects/[projectId]/settings/components/CategoryTabs.js create mode 100644 easy-dataset-main/app/projects/[projectId]/settings/components/PromptDetail.js create mode 100644 easy-dataset-main/app/projects/[projectId]/settings/components/PromptEditDialog.js create mode 100644 easy-dataset-main/app/projects/[projectId]/settings/components/PromptList.js create mode 100644 easy-dataset-main/app/projects/[projectId]/settings/components/PromptSettings.js create mode 100644 easy-dataset-main/app/projects/[projectId]/settings/components/promptUtils.js create mode 100644 easy-dataset-main/app/projects/[projectId]/settings/page.js create mode 100644 easy-dataset-main/app/projects/[projectId]/tasks/page.js create mode 100644 easy-dataset-main/app/projects/[projectId]/text-split/page.js create mode 100644 easy-dataset-main/app/projects/[projectId]/text-split/useChunks.js create mode 100644 easy-dataset-main/app/projects/[projectId]/text-split/useDataCleaning.js create mode 100644 easy-dataset-main/app/projects/[projectId]/text-split/useEvalGeneration.js create mode 100644 easy-dataset-main/app/projects/[projectId]/text-split/useFileProcessing.js create mode 100644 easy-dataset-main/app/projects/[projectId]/text-split/useQuestionGeneration.js create mode 100644 easy-dataset-main/commitlint.config.mjs create mode 100644 easy-dataset-main/components/ExportDatasetDialog.js create mode 100644 easy-dataset-main/components/ExportProgressDialog.js create mode 100644 easy-dataset-main/components/I18nProvider.js create mode 100644 easy-dataset-main/components/LanguageSwitcher.js create mode 100644 easy-dataset-main/components/ModelSelect.js create mode 100644 easy-dataset-main/components/Navbar/ActionButtons.js create mode 100644 easy-dataset-main/components/Navbar/ContextBar.js create mode 100644 easy-dataset-main/components/Navbar/DesktopMenus.js create mode 100644 easy-dataset-main/components/Navbar/Logo.js create mode 100644 easy-dataset-main/components/Navbar/MobileDrawer.js create mode 100644 easy-dataset-main/components/Navbar/NavigationTabs.js create mode 100644 easy-dataset-main/components/Navbar/contextBarStyles.js create mode 100644 easy-dataset-main/components/Navbar/index.js create mode 100644 easy-dataset-main/components/Navbar/styles.js create mode 100644 easy-dataset-main/components/TaskIcon.js create mode 100644 easy-dataset-main/components/ThemeRegistry.js create mode 100644 easy-dataset-main/components/UpdateChecker.js create mode 100644 easy-dataset-main/components/common/MessageAlert.js create mode 100644 easy-dataset-main/components/conversations/ConversationContent.js create mode 100644 easy-dataset-main/components/conversations/ConversationHeader.js create mode 100644 easy-dataset-main/components/conversations/ConversationMetadata.js create mode 100644 easy-dataset-main/components/conversations/ConversationRatingSection.js create mode 100644 easy-dataset-main/components/dataset-square/DatasetSearchBar.js create mode 100644 easy-dataset-main/components/dataset-square/DatasetSiteCard.js create mode 100644 easy-dataset-main/components/dataset-square/DatasetSiteList.js create mode 100644 easy-dataset-main/components/datasets/DatasetHeader.js create mode 100644 easy-dataset-main/components/datasets/DatasetMetadata.js create mode 100644 easy-dataset-main/components/datasets/DatasetRatingSection.js create mode 100644 easy-dataset-main/components/datasets/EditableField.js create mode 100644 easy-dataset-main/components/datasets/EvalVariantDialog.js create mode 100644 easy-dataset-main/components/datasets/ImportDatasetDialog.js create mode 100644 easy-dataset-main/components/datasets/NoteInput.js create mode 100644 easy-dataset-main/components/datasets/OptimizeDialog.js create mode 100644 easy-dataset-main/components/datasets/StarRating.js create mode 100644 easy-dataset-main/components/datasets/TagSelector.js create mode 100644 easy-dataset-main/components/datasets/import/FieldMappingStep.js create mode 100644 easy-dataset-main/components/datasets/import/FileUploadStep.js create mode 100644 easy-dataset-main/components/datasets/import/ImportProgressStep.js create mode 100644 easy-dataset-main/components/datasets/utils/ratingUtils.js create mode 100644 easy-dataset-main/components/distill/AutoDistillDialog.js create mode 100644 easy-dataset-main/components/distill/AutoDistillProgress.js create mode 100644 easy-dataset-main/components/distill/ConfirmDialog.js create mode 100644 easy-dataset-main/components/distill/DistillTreeView.js create mode 100644 easy-dataset-main/components/distill/QuestionGenerationDialog.js create mode 100644 easy-dataset-main/components/distill/QuestionListItem.js create mode 100644 easy-dataset-main/components/distill/TagEditDialog.js create mode 100644 easy-dataset-main/components/distill/TagGenerationDialog.js create mode 100644 easy-dataset-main/components/distill/TagMenu.js create mode 100644 easy-dataset-main/components/distill/TagTreeItem.js create mode 100644 easy-dataset-main/components/distill/utils.js create mode 100644 easy-dataset-main/components/export/HuggingFaceTab.js create mode 100644 easy-dataset-main/components/export/LlamaFactoryTab.js create mode 100644 easy-dataset-main/components/export/LocalExportTab.js create mode 100644 easy-dataset-main/components/home/CreateProjectDialog.js create mode 100644 easy-dataset-main/components/home/HeroSection.js create mode 100644 easy-dataset-main/components/home/MigrationDialog.js create mode 100644 easy-dataset-main/components/home/ParticleBackground.js create mode 100644 easy-dataset-main/components/home/ProjectCard.js create mode 100644 easy-dataset-main/components/home/ProjectList.js create mode 100644 easy-dataset-main/components/home/StatsCard.js create mode 100644 easy-dataset-main/components/mga/GaPairsIndicator.js create mode 100644 easy-dataset-main/components/mga/GaPairsManager.js create mode 100644 easy-dataset-main/components/playground/ChatArea.js create mode 100644 easy-dataset-main/components/playground/ChatMessage.js create mode 100644 easy-dataset-main/components/playground/MessageInput.js create mode 100644 easy-dataset-main/components/playground/ModelSelector.js create mode 100644 easy-dataset-main/components/playground/PlaygroundHeader.js create mode 100644 easy-dataset-main/components/questions/QuestionListView.js create mode 100644 easy-dataset-main/components/questions/QuestionTreeView.js create mode 100644 easy-dataset-main/components/settings/BasicSettings.js create mode 100644 easy-dataset-main/components/settings/ModelSettings.js create mode 100644 easy-dataset-main/components/settings/TaskSettings.js create mode 100644 easy-dataset-main/components/tasks/TaskActions.js create mode 100644 easy-dataset-main/components/tasks/TaskFilters.js create mode 100644 easy-dataset-main/components/tasks/TaskProgress.js create mode 100644 easy-dataset-main/components/tasks/TaskStatusChip.js create mode 100644 easy-dataset-main/components/tasks/TasksTable.js create mode 100644 easy-dataset-main/components/text-split/BatchEditChunkDialog.js create mode 100644 easy-dataset-main/components/text-split/ChunkBatchDeleteDialog.js create mode 100644 easy-dataset-main/components/text-split/ChunkCard.js create mode 100644 easy-dataset-main/components/text-split/ChunkDeleteDialog.js create mode 100644 easy-dataset-main/components/text-split/ChunkFilterDialog.js create mode 100644 easy-dataset-main/components/text-split/ChunkList.js create mode 100644 easy-dataset-main/components/text-split/ChunkListHeader.js create mode 100644 easy-dataset-main/components/text-split/ChunkViewDialog.js create mode 100644 easy-dataset-main/components/text-split/DomainAnalysis.js create mode 100644 easy-dataset-main/components/text-split/FileUploader.js create mode 100644 easy-dataset-main/components/text-split/LoadingBackdrop.js create mode 100644 easy-dataset-main/components/text-split/MarkdownViewDialog.js create mode 100644 easy-dataset-main/components/text-split/PdfSettings.js create mode 100644 easy-dataset-main/components/text-split/components/DeleteConfirmDialog.js create mode 100644 easy-dataset-main/components/text-split/components/DirectoryView.js create mode 100644 easy-dataset-main/components/text-split/components/DomainTreeActionDialog.js create mode 100644 easy-dataset-main/components/text-split/components/DomainTreeView.js create mode 100644 easy-dataset-main/components/text-split/components/FileList.js create mode 100644 easy-dataset-main/components/text-split/components/FileLoadingProgress.js create mode 100644 easy-dataset-main/components/text-split/components/PdfProcessingDialog.js create mode 100644 easy-dataset-main/components/text-split/components/TabPanel.js create mode 100644 easy-dataset-main/components/text-split/components/UploadArea.js create mode 100644 easy-dataset-main/constant/index.js create mode 100644 easy-dataset-main/constant/model.js create mode 100644 easy-dataset-main/constant/setting.js create mode 100644 easy-dataset-main/constant/sites.json create mode 100644 easy-dataset-main/docker-compose.yml create mode 100644 easy-dataset-main/docker-entrypoint.sh create mode 100644 easy-dataset-main/electron/entitlements.mac.plist create mode 100644 easy-dataset-main/electron/loading.html create mode 100644 easy-dataset-main/electron/main.js create mode 100644 easy-dataset-main/electron/modules/cache.js create mode 100644 easy-dataset-main/electron/modules/database.js create mode 100644 easy-dataset-main/electron/modules/db-updater.js create mode 100644 easy-dataset-main/electron/modules/ipc-handlers.js create mode 100644 easy-dataset-main/electron/modules/logger.js create mode 100644 easy-dataset-main/electron/modules/menu.js create mode 100644 easy-dataset-main/electron/modules/server.js create mode 100644 easy-dataset-main/electron/modules/updater.js create mode 100644 easy-dataset-main/electron/modules/window-manager.js create mode 100644 easy-dataset-main/electron/preload.js create mode 100644 easy-dataset-main/electron/util.js create mode 100644 easy-dataset-main/hooks/useDebounce.js create mode 100644 easy-dataset-main/hooks/useFileProcessingStatus.js create mode 100644 easy-dataset-main/hooks/useGenerateDataset.js create mode 100644 easy-dataset-main/hooks/useModelPlayground.js create mode 100644 easy-dataset-main/hooks/useSnackbar.js create mode 100644 easy-dataset-main/hooks/useTaskSettings.js create mode 100644 easy-dataset-main/jsconfig.json create mode 100644 easy-dataset-main/locales/en/translation.json create mode 100644 easy-dataset-main/locales/pt-BR/translation.json create mode 100644 easy-dataset-main/locales/tr/translation.json create mode 100644 easy-dataset-main/locales/zh-CN/translation.json create mode 100644 easy-dataset-main/next.config.js create mode 100644 easy-dataset-main/package-lock.json create mode 100644 easy-dataset-main/package.json create mode 100644 easy-dataset-main/pnpm-lock.yaml create mode 100644 easy-dataset-main/prisma/generate-template.js create mode 100644 easy-dataset-main/prisma/schema.prisma create mode 100644 easy-dataset-main/prisma/sql.json create mode 100644 easy-dataset-main/public/imgs/1.png create mode 100644 easy-dataset-main/public/imgs/10.png create mode 100644 easy-dataset-main/public/imgs/2.png create mode 100644 easy-dataset-main/public/imgs/3.png create mode 100644 easy-dataset-main/public/imgs/4.png create mode 100644 easy-dataset-main/public/imgs/5.png create mode 100644 easy-dataset-main/public/imgs/6.png create mode 100644 easy-dataset-main/public/imgs/7.png create mode 100644 easy-dataset-main/public/imgs/8.png create mode 100644 easy-dataset-main/public/imgs/9.png create mode 100644 easy-dataset-main/public/imgs/arc3.png create mode 100644 easy-dataset-main/public/imgs/aw.jpg create mode 100644 easy-dataset-main/public/imgs/aws.png create mode 100644 easy-dataset-main/public/imgs/bg.png create mode 100644 easy-dataset-main/public/imgs/bg2.png create mode 100644 easy-dataset-main/public/imgs/cn-arc.png create mode 100644 easy-dataset-main/public/imgs/default-dataset.png create mode 100644 easy-dataset-main/public/imgs/en-arc.png create mode 100644 easy-dataset-main/public/imgs/garden.jpg create mode 100644 easy-dataset-main/public/imgs/github.png create mode 100644 easy-dataset-main/public/imgs/google.png create mode 100644 easy-dataset-main/public/imgs/huggingface.png create mode 100644 easy-dataset-main/public/imgs/kaggle.png create mode 100644 easy-dataset-main/public/imgs/linux.png create mode 100644 easy-dataset-main/public/imgs/lluga.png create mode 100644 easy-dataset-main/public/imgs/logo.icns create mode 100644 easy-dataset-main/public/imgs/logo.ico create mode 100644 easy-dataset-main/public/imgs/logo.png create mode 100644 easy-dataset-main/public/imgs/logo.svg create mode 100644 easy-dataset-main/public/imgs/logo_old.icns create mode 100644 easy-dataset-main/public/imgs/mac.png create mode 100644 easy-dataset-main/public/imgs/models/chatglm.svg create mode 100644 easy-dataset-main/public/imgs/models/claude.svg create mode 100644 easy-dataset-main/public/imgs/models/deepseek.svg create mode 100644 easy-dataset-main/public/imgs/models/default.svg create mode 100644 easy-dataset-main/public/imgs/models/doubao.svg create mode 100644 easy-dataset-main/public/imgs/models/gemini.svg create mode 100644 easy-dataset-main/public/imgs/models/glm.svg create mode 100644 easy-dataset-main/public/imgs/models/gpt.svg create mode 100644 easy-dataset-main/public/imgs/models/hunyuan.svg create mode 100644 easy-dataset-main/public/imgs/models/llama.svg create mode 100644 easy-dataset-main/public/imgs/models/qwen.svg create mode 100644 easy-dataset-main/public/imgs/models/wenxin.svg create mode 100644 easy-dataset-main/public/imgs/models/yi.svg create mode 100644 easy-dataset-main/public/imgs/modelscope.png create mode 100644 easy-dataset-main/public/imgs/opendatalab.png create mode 100644 easy-dataset-main/public/imgs/providers/302ai.ico create mode 100644 easy-dataset-main/public/imgs/providers/alibailian.ico create mode 100644 easy-dataset-main/public/imgs/providers/deepseek.ico create mode 100644 easy-dataset-main/public/imgs/providers/grok.svg create mode 100644 easy-dataset-main/public/imgs/providers/groq.ico create mode 100644 easy-dataset-main/public/imgs/providers/ollama.png create mode 100644 easy-dataset-main/public/imgs/providers/openai.png create mode 100644 easy-dataset-main/public/imgs/providers/openrouter.ico create mode 100644 easy-dataset-main/public/imgs/providers/siliconflow.ico create mode 100644 easy-dataset-main/public/imgs/providers/volcengine.png create mode 100644 easy-dataset-main/public/imgs/providers/zhipu.png create mode 100644 easy-dataset-main/public/imgs/weichat.jpg create mode 100644 easy-dataset-main/public/imgs/windows.png create mode 100644 easy-dataset-main/styles/blindTest.js create mode 100644 easy-dataset-main/styles/globals.css create mode 100644 easy-dataset-main/styles/home.js create mode 100644 easy-dataset-main/styles/playground.js create mode 100644 frontend/Dockerfile create mode 100644 frontend/index.html create mode 100644 frontend/nginx.conf create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json create mode 100644 frontend/src/App.vue create mode 100644 frontend/src/api/index.js create mode 100644 frontend/src/main.js create mode 100644 frontend/src/router/index.js create mode 100644 frontend/src/views/DataSquareView.vue create mode 100644 frontend/src/views/HomeView.vue create mode 100644 frontend/src/views/PlaygroundView.vue create mode 100644 frontend/src/views/ProjectView.vue create mode 100644 frontend/src/views/project/DatasetManage.vue create mode 100644 frontend/src/views/project/EvalManage.vue create mode 100644 frontend/src/views/project/FileManage.vue create mode 100644 frontend/src/views/project/QuestionManage.vue create mode 100644 frontend/src/views/project/Settings.vue create mode 100644 frontend/src/views/project/TextSplit.vue create mode 100644 frontend/vite.config.js create mode 100644 开发计划.md create mode 100644 项目架构.md diff --git a/.claude/agents/backend-algorithm-developer.md b/.claude/agents/backend-algorithm-developer.md new file mode 100644 index 0000000..98fce63 --- /dev/null +++ b/.claude/agents/backend-algorithm-developer.md @@ -0,0 +1,79 @@ +--- +name: backend-algorithm-developer +description: "Use this agent when you need to develop backend services, implement algorithms, or build system components using Java, Python, or Go. Examples include: designing and implementing RESTful APIs, writing efficient algorithms for data processing, creating microservices, optimizing database queries, or building high-performance server applications." +model: sonnet +color: red +memory: user +--- + +You are an expert backend algorithm development engineer with deep proficiency in Java, Python, and Go. You specialize in designing and implementing efficient, scalable backend services and solving complex algorithmic problems. + +**Core Responsibilities:** +- Design and implement robust backend services and APIs +- Write efficient algorithms optimized for performance and scalability +- Choose the appropriate language (Java/Python/Go) based on use case requirements +- Ensure code quality through proper testing and optimization +- Handle database design, caching, and performance tuning + +**Language-Specific Expertise:** +- **Java**: Spring Boot, Spring Cloud, Maven/Gradle, concurrency handling, JVM optimization +- **Python**: FastAPI/Flask/Django, asyncio, data processing libraries, ML integration +- **Go**: Goroutines, channels, Gin/Echo frameworks, microservices patterns + +**Development Approach:** +1. Understand requirements thoroughly before writing code +2. Choose the most appropriate technology stack for the specific use case +3. Write clean, well-documented, and maintainable code +4. Implement proper error handling and logging +5. Consider scalability, performance, and security at every step +6. Write unit tests and integration tests +7. Optimize critical code paths using appropriate data structures and algorithms + +**Quality Standards:** +- Follow language-specific best practices and coding conventions +- Use appropriate design patterns +- Implement proper input validation and security measures +- Ensure code is testable and documented +- Consider edge cases and failure scenarios + +**When to use each language:** +- Use **Java** for enterprise-scale applications, complex transaction systems, and when strong typing and ecosystem libraries are needed +- Use **Python** for rapid prototyping, data processing, ML integration, and scripts +- Use **Go** for high-concurrency services, microservices, and performance-critical components + +Provide well-structured, production-ready code with clear explanations. Always consider the trade-offs of your technical choices. + +# Persistent Agent Memory + +You have a persistent Persistent Agent Memory directory at `C:\Users\caoxiaozhu\.claude\agent-memory\backend-algorithm-developer\`. This directory already exists — write to it directly with the Write tool (do not run mkdir or check for its existence). Its contents persist across conversations. + +As you work, consult your memory files to build on previous experience. When you encounter a mistake that seems like it could be common, check your Persistent Agent Memory for relevant notes — and if nothing is written yet, record what you learned. + +Guidelines: +- `MEMORY.md` is always loaded into your system prompt — lines after 200 will be truncated, so keep it concise +- Create separate topic files (e.g., `debugging.md`, `patterns.md`) for detailed notes and link to them from MEMORY.md +- Update or remove memories that turn out to be wrong or outdated +- Organize memory semantically by topic, not chronologically +- Use the Write and Edit tools to update your memory files + +What to save: +- Stable patterns and conventions confirmed across multiple interactions +- Key architectural decisions, important file paths, and project structure +- User preferences for workflow, tools, and communication style +- Solutions to recurring problems and debugging insights + +What NOT to save: +- Session-specific context (current task details, in-progress work, temporary state) +- Information that might be incomplete — verify against project docs before writing +- Anything that duplicates or contradicts existing CLAUDE.md instructions +- Speculative or unverified conclusions from reading a single file + +Explicit user requests: +- When the user asks you to remember something across sessions (e.g., "always use bun", "never auto-commit"), save it — no need to wait for multiple interactions +- When the user asks to forget or stop remembering something, find and remove the relevant entries from your memory files +- When the user corrects you on something you stated from memory, you MUST update or remove the incorrect entry. A correction means the stored memory is wrong — fix it at the source before continuing, so the same mistake does not repeat in future conversations. +- Since this memory is user-scope, keep learnings general since they apply across all projects + +## MEMORY.md + +Your MEMORY.md is currently empty. When you notice a pattern worth preserving across sessions, save it here. Anything in MEMORY.md will be included in your system prompt next time. diff --git a/.claude/agents/elegant-frontend-designer.md b/.claude/agents/elegant-frontend-designer.md new file mode 100644 index 0000000..32f5a55 --- /dev/null +++ b/.claude/agents/elegant-frontend-designer.md @@ -0,0 +1,98 @@ +--- +name: elegant-frontend-designer +description: "Use this agent when you need to create elegant, visually stunning front-end designs for products. Examples include: designing a new landing page, creating a component library, improving existing UI/UX, building a design system, or crafting a complete product interface with modern, sophisticated aesthetics." +model: sonnet +color: purple +memory: project +--- + +You are an elite front-end designer with deep expertise in creating elegant, sophisticated user interfaces. You have mastered the art of combining aesthetics with functionality, understanding that true elegance lies in the balance between visual beauty and seamless user experience. + +**Your Design Philosophy:** +- Embrace minimalism: Less is more. Every element must serve a purpose. +- Typography is paramount: Choose fonts that communicate personality while ensuring readability. +- Color should be intentional: Use restrained palettes with purposeful accent colors. +-Whitespace is your friend: Generous spacing creates breath and sophistication. +- Motion should feel natural: Animations should enhance, not distract. +- Consistency builds trust: A cohesive design system ensures harmony across the product. + +**Technical Expertise:** +You are proficient in: +- Modern CSS (Flexbox, Grid, CSS Variables, Subgrid) +- CSS frameworks (Tailwind CSS, UnoCSS,styled-components) +- Design systems and component libraries +- Responsive and mobile-first design +- Micro-interactions and transitions +- CSS animations and keyframes +- Dark mode and theme switching +- Accessibility standards (WCAG) + +**Design Style References:** +- Apple's human interface guidelines +- Material Design 3 +- Minimalist Japanese design aesthetics +- Swiss design principles +- Modern neumorphism and glassmorphism (when appropriate) +- Subtle gradients and frosted glass effects + +**When designing, you will:** +1. Analyze the requirements and determine the optimal design approach +2. Choose appropriate color palettes, typography, and spacing systems +3. Create responsive, mobile-first layouts +4. Implement elegant micro-interactions and transitions +5. Ensure accessibility and semantic HTML +6. Provide clean, well-structured code +7. Consider performance implications of visual effects + +**Output Format:** +When presenting designs, provide: +- Conceptual overview and design rationale +- Color palette with hex codes +- Typography choices with font families and sizes +- Layout structure (can use ASCII or describe flex/grid) +- Component designs with states +- Animation specifications +- Code implementation (HTML/CSS/JS as appropriate) + +**You will proactively ask clarifying questions when:** +- The target audience or use case is unclear +- Brand guidelines or existing design language conflict with elegant design suggestions +- Technical constraints might limit design choices +- The scope is too broad to provide focused recommendations + +Be confident in your design decisions while remaining open to feedback and iteration. + +# Persistent Agent Memory + +You have a persistent Persistent Agent Memory directory at `D:\Code\Project\YG-Datasets\.claude\agent-memory\elegant-frontend-designer\`. This directory already exists — write to it directly with the Write tool (do not run mkdir or check for its existence). Its contents persist across conversations. + +As you work, consult your memory files to build on previous experience. When you encounter a mistake that seems like it could be common, check your Persistent Agent Memory for relevant notes — and if nothing is written yet, record what you learned. + +Guidelines: +- `MEMORY.md` is always loaded into your system prompt — lines after 200 will be truncated, so keep it concise +- Create separate topic files (e.g., `debugging.md`, `patterns.md`) for detailed notes and link to them from MEMORY.md +- Update or remove memories that turn out to be wrong or outdated +- Organize memory semantically by topic, not chronologically +- Use the Write and Edit tools to update your memory files + +What to save: +- Stable patterns and conventions confirmed across multiple interactions +- Key architectural decisions, important file paths, and project structure +- User preferences for workflow, tools, and communication style +- Solutions to recurring problems and debugging insights + +What NOT to save: +- Session-specific context (current task details, in-progress work, temporary state) +- Information that might be incomplete — verify against project docs before writing +- Anything that duplicates or contradicts existing CLAUDE.md instructions +- Speculative or unverified conclusions from reading a single file + +Explicit user requests: +- When the user asks you to remember something across sessions (e.g., "always use bun", "never auto-commit"), save it — no need to wait for multiple interactions +- When the user asks to forget or stop remembering something, find and remove the relevant entries from your memory files +- When the user corrects you on something you stated from memory, you MUST update or remove the incorrect entry. A correction means the stored memory is wrong — fix it at the source before continuing, so the same mistake does not repeat in future conversations. +- Since this memory is project-scope and shared with your team via version control, tailor your memories to this project + +## MEMORY.md + +Your MEMORY.md is currently empty. When you notice a pattern worth preserving across sessions, save it here. Anything in MEMORY.md will be included in your system prompt next time. diff --git a/.claude/agents/robustness-tester-submitter.md b/.claude/agents/robustness-tester-submitter.md new file mode 100644 index 0000000..1941d75 --- /dev/null +++ b/.claude/agents/robustness-tester-submitter.md @@ -0,0 +1,94 @@ +--- +name: robustness-tester-submitter +description: "Use this agent when you need to validate code quality before submission, including testing robustness, error handling, edge cases, and submitting code to repositories. Examples:\\n- After writing a new function, use this agent to test boundary conditions, invalid inputs, and error scenarios to ensure the code handles them gracefully.\\n- Before committing code to the repository, use this agent to run comprehensive robustness tests and submit the validated code.\\n- When refactoring code, use this agent to verify the changes don't introduce new vulnerabilities or failure points." +tools: Glob, Grep, Read, WebFetch, WebSearch +model: opus +color: yellow +memory: project +--- + +You are a senior QA engineer and code robustness expert specializing in testing software reliability and handling code submission workflows. + +**Core Responsibilities:** +1. **Robustness Testing**: Evaluate code for resilience against: + - Edge cases and boundary conditions + - Invalid or unexpected inputs + - Race conditions and concurrency issues + - Resource exhaustion (memory, CPU, file handles) + - Network failures and timeouts + - Error handling completeness + +2. **Code Submission**: Handle the process of committing and pushing code to repositories, including: + - Running pre-submission checks + - Creating meaningful commit messages + - Following repository conventions + - Handling merge conflicts if needed + +**Testing Methodologies:** +- **Boundary Value Analysis**: Test at and beyond input limits +- **Equivalence Partitioning**: Group inputs into valid/invalid partitions +- **Fault Injection**: Introduce failures to test recovery mechanisms +- **Stress Testing**: Push code beyond normal operational limits +- **Negative Testing**: Verify proper handling of invalid scenarios + +**Quality Standards:** +- All critical paths must have proper error handling +- Input validation must occur at entry points +- Resource cleanup must be guaranteed (use defer, finally, etc.) +- Concurrent code must have proper synchronization +- External dependencies should have appropriate timeouts and fallbacks + +**Submission Process:** +1. Run all existing tests to ensure no regressions +2. Execute robustness test suite +3. Verify code passes linting and formatting standards +4. Stage changes with appropriate git commands +5. Create descriptive commit messages following conventional commits format +6. Push to remote repository + +**Output Expectations:** +- Provide detailed test results with pass/fail status +- Document any robustness issues found with severity levels +- Suggest specific fixes for identified problems +- Confirm successful submission with commit hash + +**Update your agent memory** as you discover common robustness patterns, testing strategies, and code submission workflows. Record: +- Common failure modes in different code patterns +- Effective test cases that catch edge case bugs +- Repository-specific submission conventions +- Successful robustness testing approaches + +# Persistent Agent Memory + +You have a persistent Persistent Agent Memory directory at `D:\Code\Project\YG-Datasets\.claude\agent-memory\robustness-tester-submitter\`. This directory already exists — write to it directly with the Write tool (do not run mkdir or check for its existence). Its contents persist across conversations. + +As you work, consult your memory files to build on previous experience. When you encounter a mistake that seems like it could be common, check your Persistent Agent Memory for relevant notes — and if nothing is written yet, record what you learned. + +Guidelines: +- `MEMORY.md` is always loaded into your system prompt — lines after 200 will be truncated, so keep it concise +- Create separate topic files (e.g., `debugging.md`, `patterns.md`) for detailed notes and link to them from MEMORY.md +- Update or remove memories that turn out to be wrong or outdated +- Organize memory semantically by topic, not chronologically +- Use the Write and Edit tools to update your memory files + +What to save: +- Stable patterns and conventions confirmed across multiple interactions +- Key architectural decisions, important file paths, and project structure +- User preferences for workflow, tools, and communication style +- Solutions to recurring problems and debugging insights + +What NOT to save: +- Session-specific context (current task details, in-progress work, temporary state) +- Information that might be incomplete — verify against project docs before writing +- Anything that duplicates or contradicts existing CLAUDE.md instructions +- Speculative or unverified conclusions from reading a single file + +Explicit user requests: +- When the user asks you to remember something across sessions (e.g., "always use bun", "never auto-commit"), save it — no need to wait for multiple interactions +- When the user asks to forget or stop remembering something, find and remove the relevant entries from your memory files +- When the user corrects you on something you stated from memory, you MUST update or remove the incorrect entry. A correction means the stored memory is wrong — fix it at the source before continuing, so the same mistake does not repeat in future conversations. +- Since this memory is project-scope and shared with your team via version control, tailor your memories to this project + +## MEMORY.md + +Your MEMORY.md is currently empty. When you notice a pattern worth preserving across sessions, save it here. Anything in MEMORY.md will be included in your system prompt next time. diff --git a/.claude/agents/ux-ui-requirements-analyst.md b/.claude/agents/ux-ui-requirements-analyst.md new file mode 100644 index 0000000..ed12da0 --- /dev/null +++ b/.claude/agents/ux-ui-requirements-analyst.md @@ -0,0 +1,95 @@ +--- +name: ux-ui-requirements-analyst +description: "Use this agent when you need to analyze user requirements, evaluate UX/UI design quality, assess interface reasonableness, provide recommendations for improving user experience, or review design consistency and usability in a project." +tools: Glob, Grep, Read, WebFetch, WebSearch +model: sonnet +color: blue +memory: project +--- + +You are an expert Requirements Analyst specializing in UX/UI evaluation and interface design analysis. Your role is to help projects thoroughly analyze user requirements, evaluate the quality and reasonableness of UX/UI designs, and provide actionable recommendations for improvement. + +**Your expertise includes:** +- User experience (UX) analysis and best practices +- User interface (UI) design principles and standards +- Interface usability and reasonableness evaluation +- User requirements gathering and analysis +- Design consistency and coherence assessment +- Accessibility considerations (WCAG guidelines) +- User flow and journey mapping +- Information architecture evaluation + +**Your approach to analysis:** +1. Examine the design or requirements from multiple perspectives: + - Visual hierarchy and layout structure + - Color scheme, typography, and visual consistency + - Interactive elements and feedback mechanisms + - Navigation and information architecture + - Consistency across different screens/pages + - Accessibility and inclusivity + - Overall user satisfaction and task efficiency + +2. For each analysis, identify: + - Strengths and good practices + - Issues, pain points, or potential improvements + - Specific, actionable recommendations + - Priority of improvements based on user impact + +3. Provide rationale for your recommendations, referencing established UX/UI principles and best practices when possible. + +**When analyzing interface reasonableness:** +- Evaluate if the interface aligns with user expectations and mental models +- Check if workflows are intuitive and efficient +- Assess if error prevention and recovery mechanisms are adequate +- Verify that key features are easily discoverable +- Consider the learning curve for new users + +**Important guidelines:** +- Ask clarifying questions when project context, target users, or business objectives are unclear +- Consider both user needs and technical feasibility in recommendations +- Provide concrete examples or references to design patterns when helpful +- Be constructive and solution-oriented in your feedback +- When analyzing existing designs, be specific about what works and what doesn't + +**Output format:** +Structure your analysis clearly with: +- Summary of findings +- Strengths identified +- Issues/areas for improvement (prioritized) +- Specific recommendations with rationale +- Optional: Questions for further clarification + +# Persistent Agent Memory + +You have a persistent Persistent Agent Memory directory at `D:\Code\Project\YG-Datasets\.claude\agent-memory\ux-ui-requirements-analyst\`. This directory already exists — write to it directly with the Write tool (do not run mkdir or check for its existence). Its contents persist across conversations. + +As you work, consult your memory files to build on previous experience. When you encounter a mistake that seems like it could be common, check your Persistent Agent Memory for relevant notes — and if nothing is written yet, record what you learned. + +Guidelines: +- `MEMORY.md` is always loaded into your system prompt — lines after 200 will be truncated, so keep it concise +- Create separate topic files (e.g., `debugging.md`, `patterns.md`) for detailed notes and link to them from MEMORY.md +- Update or remove memories that turn out to be wrong or outdated +- Organize memory semantically by topic, not chronologically +- Use the Write and Edit tools to update your memory files + +What to save: +- Stable patterns and conventions confirmed across multiple interactions +- Key architectural decisions, important file paths, and project structure +- User preferences for workflow, tools, and communication style +- Solutions to recurring problems and debugging insights + +What NOT to save: +- Session-specific context (current task details, in-progress work, temporary state) +- Information that might be incomplete — verify against project docs before writing +- Anything that duplicates or contradicts existing CLAUDE.md instructions +- Speculative or unverified conclusions from reading a single file + +Explicit user requests: +- When the user asks you to remember something across sessions (e.g., "always use bun", "never auto-commit"), save it — no need to wait for multiple interactions +- When the user asks to forget or stop remembering something, find and remove the relevant entries from your memory files +- When the user corrects you on something you stated from memory, you MUST update or remove the incorrect entry. A correction means the stored memory is wrong — fix it at the source before continuing, so the same mistake does not repeat in future conversations. +- Since this memory is project-scope and shared with your team via version control, tailor your memories to this project + +## MEMORY.md + +Your MEMORY.md is currently empty. When you notice a pattern worth preserving across sessions, save it here. Anything in MEMORY.md will be included in your system prompt next time. diff --git a/.gitignore b/.gitignore index 36b13f1..34775a9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,15 @@ +# Node.js +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Package lock files (optional - uncomment if you want to ignore them) +# package-lock.json +# yarn.lock +# pnpm-lock.yaml + # ---> Python # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/README.md b/README.md index 2daa85e..23cf4f9 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,62 @@ -# YG-Datasets +# YG-Dataset 本地启动指南 +## 快速启动 + +### 1. 安装后端依赖 + +```bash +cd backend +pip install -r requirements.txt +``` + +### 2. 启动后端 + +```bash +cd backend +uvicorn app.main:app --reload --port 8000 +``` + +后端地址: http://localhost:8000 +API 文档: http://localhost:8000/docs + +### 3. 安装前端依赖 + +```bash +cd frontend +npm install +``` + +### 4. 启动前端 + +```bash +npm run dev +``` + +前端地址: http://localhost:3000 + +--- + +## 目录结构 + +``` +YG-Datasets/ +├── backend/ # FastAPI 后端 +│ ├── app/ +│ │ ├── api/v1/ # API 路由 +│ │ ├── models/ # 数据库模型 +│ │ └── services/ # 业务逻辑 +│ └── requirements.txt +├── frontend/ # Vue 3 前端 +│ ├── src/ +│ │ ├── views/ # 页面 +│ │ └── api/ # API 封装 +│ └── package.json +└── uploads/ # 上传文件存储目录 +``` + +## 默认配置 + +- 数据库: SQLite (`backend/ygdataset.db`) +- 上传目录: `backend/uploads/` +- 后端端口: 8000 +- 前端端口: 3000 diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..15f7d65 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,27 @@ +FROM python:3.11-slim + +WORKDIR /app + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + build-essential \ + libpq-dev \ + && rm -rf /var/lib/apt/lists/* + +# Copy requirements +COPY requirements.txt . + +# Install Python dependencies +RUN pip install --no-cache-dir -r requirements.txt + +# Copy application +COPY . . + +# Create uploads directory +RUN mkdir -p uploads + +# Expose port +EXPOSE 8000 + +# Run application +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"] diff --git a/backend/app/api/__init__.py b/backend/app/api/__init__.py new file mode 100644 index 0000000..aeea91b --- /dev/null +++ b/backend/app/api/__init__.py @@ -0,0 +1,3 @@ +""" +API module initialization +""" diff --git a/backend/app/api/v1/__init__.py b/backend/app/api/v1/__init__.py new file mode 100644 index 0000000..aacac86 --- /dev/null +++ b/backend/app/api/v1/__init__.py @@ -0,0 +1,17 @@ +""" +API v1 Router +""" + +from fastapi import APIRouter + +from app.api.v1 import files, projects, chunks, questions, datasets, eval + +api_router = APIRouter() + +# Include sub-routers +api_router.include_router(projects.router, prefix="/projects", tags=["projects"]) +api_router.include_router(files.router, prefix="/files", tags=["files"]) +api_router.include_router(chunks.router, prefix="/chunks", tags=["chunks"]) +api_router.include_router(questions.router, prefix="/questions", tags=["questions"]) +api_router.include_router(datasets.router, prefix="/datasets", tags=["datasets"]) +api_router.include_router(eval.router, prefix="/eval", tags=["eval"]) diff --git a/backend/app/api/v1/chunks/__init__.py b/backend/app/api/v1/chunks/__init__.py new file mode 100644 index 0000000..d5d4b6b --- /dev/null +++ b/backend/app/api/v1/chunks/__init__.py @@ -0,0 +1,182 @@ +""" +Chunks API Router +""" +from typing import List, Optional +from uuid import UUID +from pydantic import BaseModel +from fastapi import APIRouter, Depends, HTTPException, Query +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy import select +from app.core.database import get_db +from app.models.models import Chunk, File +from app.schemas.base import ChunkCreate, ChunkResponse +from app.services.text_splitter.splitter import get_splitter +from app.services.file_processor.pdf_processor import process_pdf +from app.services.file_processor.docx_processor import process_docx +from app.services.file_processor.excel_processor import process_csv, process_excel + +router = APIRouter() + + +class SplitRequest(BaseModel): + """Request model for splitting text""" + file_id: Optional[UUID] = None + method: str = "recursive" + chunk_size: int = 500 + overlap: int = 50 + separator: Optional[str] = None + + +class ChunkListResponse(BaseModel): + """Response for chunk list""" + chunks: List[ChunkResponse] + total: int + + +def process_file_by_type(file: File) -> str: + """Process file based on its type""" + if not file.file_path: + raise HTTPException(status_code=400, detail="File path not found") + + processors = { + "pdf": process_pdf, + "docx": process_docx, + "xlsx": process_excel, + "csv": process_csv, + } + + processor = processors.get(file.file_type) + if not processor: + # Return raw text for txt, md files + with open(file.file_path, 'r', encoding='utf-8') as f: + return f.read() + + return processor(file.file_path) + + +@router.post("/split", response_model=dict) +async def split_text( + project_id: UUID, + request: SplitRequest, + db: AsyncSession = Depends(get_db) +): + """Split text into chunks""" + # Get file + if request.file_id: + result = await db.execute( + select(File).where(File.id == request.file_id, File.project_id == project_id) + ) + file = result.scalar_one_or_none() + if not file: + raise HTTPException(status_code=404, detail="File not found") + + # Process file + text = process_file_by_type(file) + + # Update file status + file.status = "processing" + await db.commit() + else: + raise HTTPException(status_code=400, detail="file_id is required") + + # Split text + kwargs = {"chunk_size": request.chunk_size, "overlap": request.overlap} + if request.method == "custom" and request.separator: + kwargs["separator"] = request.separator + + splitter = get_splitter(request.method, **kwargs) + split_results = splitter.split(text) + + # Save chunks + chunks = [] + for chunk_data in split_results: + db_chunk = Chunk( + project_id=project_id, + file_id=file.id, + name=chunk_data.get("name", f"Chunk {chunk_data['index'] + 1}"), + content=chunk_data["content"], + word_count=chunk_data.get("word_count", len(chunk_data["content"].split())) + ) + db.add(db_chunk) + chunks.append(db_chunk) + + await db.commit() + + # Update file status + file.status = "completed" + await db.commit() + + return {"chunks": len(chunks), "message": f"Successfully split into {len(chunks)} chunks"} + + +@router.get("/", response_model=dict) +async def list_chunks( + project_id: UUID, + file_id: Optional[UUID] = Query(None), + db: AsyncSession = Depends(get_db) +): + """List chunks for a project""" + query = select(Chunk).where(Chunk.project_id == project_id) + + if file_id: + query = query.where(Chunk.file_id == file_id) + + query = query.order_by(Chunk.created_at.desc()) + + result = await db.execute(query) + chunks = result.scalars().all() + + return { + "chunks": [ChunkResponse.model_validate(c) for c in chunks], + "total": len(chunks) + } + + +@router.get("/{chunk_id}", response_model=dict) +async def get_chunk(project_id: UUID, chunk_id: UUID, db: AsyncSession = Depends(get_db)): + """Get chunk by ID""" + result = await db.execute( + select(Chunk).where(Chunk.id == chunk_id, Chunk.project_id == project_id) + ) + chunk = result.scalar_one_or_none() + if not chunk: + raise HTTPException(status_code=404, detail="Chunk not found") + return ChunkResponse.model_validate(chunk) + + +@router.put("/{chunk_id}", response_model=dict) +async def update_chunk( + project_id: UUID, + chunk_id: UUID, + chunk: ChunkCreate, + db: AsyncSession = Depends(get_db) +): + """Update chunk""" + result = await db.execute( + select(Chunk).where(Chunk.id == chunk_id, Chunk.project_id == project_id) + ) + db_chunk = result.scalar_one_or_none() + if not db_chunk: + raise HTTPException(status_code=404, detail="Chunk not found") + + for key, value in chunk.model_dump(exclude_unset=True).items(): + setattr(db_chunk, key, value) + + await db.commit() + await db.refresh(db_chunk) + return ChunkResponse.model_validate(db_chunk) + + +@router.delete("/{chunk_id}", response_model=dict) +async def delete_chunk(project_id: UUID, chunk_id: UUID, db: AsyncSession = Depends(get_db)): + """Delete chunk""" + result = await db.execute( + select(Chunk).where(Chunk.id == chunk_id, Chunk.project_id == project_id) + ) + chunk = result.scalar_one_or_none() + if not chunk: + raise HTTPException(status_code=404, detail="Chunk not found") + + await db.delete(chunk) + await db.commit() + return {"message": "Chunk deleted successfully"} diff --git a/backend/app/api/v1/datasets/__init__.py b/backend/app/api/v1/datasets/__init__.py new file mode 100644 index 0000000..ba5c30d --- /dev/null +++ b/backend/app/api/v1/datasets/__init__.py @@ -0,0 +1,126 @@ +""" +Datasets API Router +""" +from typing import List, Optional +from uuid import UUID +from pydantic import BaseModel +from fastapi import APIRouter, Depends, HTTPException, Query +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy import select, func +from app.core.database import get_db +from app.models.models import Dataset, Question +from app.schemas.base import DatasetCreate, DatasetResponse + +router = APIRouter() + + +class ExportRequest(BaseModel): + """Export request schema""" + format: str = "alpaca" # alpaca, sharegpt, llama_factory, json + + +@router.get("/", response_model=dict) +async def list_datasets(project_id: UUID, db: AsyncSession = Depends(get_db)): + """List datasets for a project""" + result = await db.execute( + select(Dataset).where(Dataset.project_id == project_id).order_by(Dataset.created_at.desc()) + ) + datasets = result.scalars().all() + + # Get question count for each dataset + dataset_list = [] + for dataset in datasets: + dataset_data = DatasetResponse.model_validate(dataset) + # TODO: Count questions in dataset + dataset_data.question_count = 0 + dataset_list.append(dataset_data) + + return {"datasets": dataset_list} + + +@router.post("/", response_model=dict) +async def create_dataset( + project_id: UUID, + dataset: DatasetCreate, + db: AsyncSession = Depends(get_db) +): + """Create a new dataset""" + db_dataset = Dataset(project_id=project_id, **dataset.model_dump()) + db.add(db_dataset) + await db.commit() + await db.refresh(db_dataset) + + return {"id": str(db_dataset.id)} + + +@router.get("/{dataset_id}", response_model=dict) +async def get_dataset( + project_id: UUID, + dataset_id: UUID, + db: AsyncSession = Depends(get_db) +): + """Get dataset by ID""" + result = await db.execute( + select(Dataset).where(Dataset.id == dataset_id, Dataset.project_id == project_id) + ) + dataset = result.scalar_one_or_none() + if not dataset: + raise HTTPException(status_code=404, detail="Dataset not found") + + return DatasetResponse.model_validate(dataset) + + +@router.delete("/{dataset_id}", response_model=dict) +async def delete_dataset( + project_id: UUID, + dataset_id: UUID, + db: AsyncSession = Depends(get_db) +): + """Delete dataset""" + result = await db.execute( + select(Dataset).where(Dataset.id == dataset_id, Dataset.project_id == project_id) + ) + dataset = result.scalar_one_or_none() + if not dataset: + raise HTTPException(status_code=404, detail="Dataset not found") + + await db.delete(dataset) + await db.commit() + + return {"message": "Dataset deleted successfully"} + + +@router.post("/{dataset_id}/export") +async def export_dataset( + project_id: UUID, + dataset_id: UUID, + request: ExportRequest, + db: AsyncSession = Depends(get_db) +): + """Export dataset in specified format""" + # TODO: Implement actual export logic + + # Get dataset + result = await db.execute( + select(Dataset).where(Dataset.id == dataset_id, Dataset.project_id == project_id) + ) + dataset = result.scalar_one_or_none() + if not dataset: + raise HTTPException(status_code=404, detail="Dataset not found") + + # Get questions for this dataset (placeholder) + # In real implementation, would link questions to datasets + + # Return sample data based on format + sample_data = [ + { + "instruction": "这是一个示例指令", + "input": "", + "output": "这是一个示例输出" + } + ] + + if request.format == "json": + return sample_data + + return {"data": sample_data, "format": request.format} diff --git a/backend/app/api/v1/eval/__init__.py b/backend/app/api/v1/eval/__init__.py new file mode 100644 index 0000000..6feb6fd --- /dev/null +++ b/backend/app/api/v1/eval/__init__.py @@ -0,0 +1,100 @@ +""" +Evaluation API Router +""" +from typing import List, Optional +from uuid import UUID +from pydantic import BaseModel +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy import select +from app.core.database import get_db +from app.models.models import EvalDataset, Task +from app.schemas.base import EvalDatasetCreate, EvalDatasetResponse, TaskResponse + +router = APIRouter() + + +class GenerateEvalRequest(BaseModel): + """Request for generating evaluation dataset""" + name: str + question_type: str = "mixed" + count: int = 50 + + +class RunEvalRequest(BaseModel): + """Request for running evaluation""" + model_config_id: Optional[UUID] = None + + +@router.get("/", response_model=dict) +async def list_eval_datasets(project_id: UUID, db: AsyncSession = Depends(get_db)): + """List evaluation datasets""" + result = await db.execute( + select(EvalDataset).where(EvalDataset.project_id == project_id).order_by(EvalDataset.created_at.desc()) + ) + datasets = result.scalars().all() + + return {"datasets": [EvalDatasetResponse.model_validate(d) for d in datasets]} + + +@router.post("/", response_model=dict) +async def create_eval_dataset( + project_id: UUID, + request: GenerateEvalRequest, + db: AsyncSession = Depends(get_db) +): + """Create evaluation dataset""" + db_dataset = EvalDataset( + project_id=project_id, + name=request.name, + question_type=request.question_type + ) + db.add(db_dataset) + await db.commit() + await db.refresh(db_dataset) + + return {"id": str(db_dataset.id)} + + +@router.post("/{eval_id}/evaluate", response_model=dict) +async def run_evaluation( + project_id: UUID, + eval_id: UUID, + request: RunEvalRequest, + db: AsyncSession = Depends(get_db) +): + """Run evaluation on dataset""" + # Check dataset exists + result = await db.execute( + select(EvalDataset).where(EvalDataset.id == eval_id, EvalDataset.project_id == project_id) + ) + dataset = result.scalar_one_or_none() + if not dataset: + raise HTTPException(status_code=404, detail="Evaluation dataset not found") + + # Create evaluation task + task = Task( + project_id=project_id, + task_type="eval", + status="pending" + ) + db.add(task) + await db.commit() + await db.refresh(task) + + # TODO: Start evaluation in background + + return {"task_id": str(task.id), "message": "Evaluation task started"} + + +@router.get("/results", response_model=dict) +async def get_eval_results(project_id: UUID, task_id: UUID, db: AsyncSession = Depends(get_db)): + """Get evaluation results""" + result = await db.execute( + select(Task).where(Task.id == task_id, Task.project_id == project_id) + ) + task = result.scalar_one_or_none() + if not task: + raise HTTPException(status_code=404, detail="Task not found") + + return TaskResponse.model_validate(task) diff --git a/backend/app/api/v1/files/__init__.py b/backend/app/api/v1/files/__init__.py new file mode 100644 index 0000000..b9a281e --- /dev/null +++ b/backend/app/api/v1/files/__init__.py @@ -0,0 +1,110 @@ +""" +Files API Router +""" +import os +import aiofiles +from pathlib import Path +from typing import List +from uuid import UUID +from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Form +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy import select +from app.core.database import get_db +from app.core.config import get_settings +from app.models.models import File +from app.schemas.base import FileResponse + +settings = get_settings() +router = APIRouter() + +# Ensure upload directory exists +UPLOAD_DIR = Path(settings.UPLOAD_DIR) +UPLOAD_DIR.mkdir(parents=True, exist_ok=True) + + +def get_file_type(filename: str) -> str: + """Get file type from extension""" + ext = filename.rsplit('.', 1)[-1].lower() if '.' in filename else '' + type_map = { + 'pdf': 'pdf', + 'docx': 'docx', + 'doc': 'docx', + 'xlsx': 'xlsx', + 'xls': 'xlsx', + 'csv': 'csv', + 'epub': 'epub', + 'md': 'md', + 'markdown': 'md', + 'txt': 'txt' + } + return type_map.get(ext, 'txt') + + +@router.post("/upload", response_model=dict) +async def upload_file( + project_id: UUID, + file: UploadFile = File(...), + db: AsyncSession = Depends(get_db) +): + """Upload a file""" + # Save file to disk + file_path = UPLOAD_DIR / f"{project_id}_{file.filename}" + async with aiofiles.open(file_path, 'wb') as f: + content = await file.read() + await f.write(content) + + # Create file record + db_file = File( + project_id=project_id, + filename=file.filename, + file_type=get_file_type(file.filename), + file_path=str(file_path), + size=len(content), + status="pending" + ) + db.add(db_file) + await db.commit() + await db.refresh(db_file) + + return {"id": str(db_file.id), "filename": db_file.filename, "status": db_file.status} + + +@router.get("/", response_model=dict) +async def list_files(project_id: UUID, db: AsyncSession = Depends(get_db)): + """List files for a project""" + result = await db.execute( + select(File).where(File.project_id == project_id).order_by(File.created_at.desc()) + ) + files = result.scalars().all() + return {"files": [FileResponse.model_validate(f) for f in files]} + + +@router.get("/{file_id}", response_model=dict) +async def get_file(project_id: UUID, file_id: UUID, db: AsyncSession = Depends(get_db)): + """Get file by ID""" + result = await db.execute( + select(File).where(File.id == file_id, File.project_id == project_id) + ) + file = result.scalar_one_or_none() + if not file: + raise HTTPException(status_code=404, detail="File not found") + return FileResponse.model_validate(file) + + +@router.delete("/{file_id}", response_model=dict) +async def delete_file(project_id: UUID, file_id: UUID, db: AsyncSession = Depends(get_db)): + """Delete file""" + result = await db.execute( + select(File).where(File.id == file_id, File.project_id == project_id) + ) + file = result.scalar_one_or_none() + if not file: + raise HTTPException(status_code=404, detail="File not found") + + # Delete file from disk + if file.file_path and os.path.exists(file.file_path): + os.remove(file.file_path) + + await db.delete(file) + await db.commit() + return {"message": "File deleted successfully"} diff --git a/backend/app/api/v1/projects/__init__.py b/backend/app/api/v1/projects/__init__.py new file mode 100644 index 0000000..535d190 --- /dev/null +++ b/backend/app/api/v1/projects/__init__.py @@ -0,0 +1,74 @@ +""" +Projects API Router +""" +from typing import List +from uuid import UUID +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy import select +from app.core.database import get_db +from app.models.models import Project +from app.schemas.base import ( + ProjectCreate, + ProjectUpdate, + ProjectResponse +) + +router = APIRouter() + + +@router.get("/", response_model=dict) +async def list_projects(db: AsyncSession = Depends(get_db)): + """List all projects""" + result = await db.execute(select(Project).order_by(Project.created_at.desc())) + projects = result.scalars().all() + return {"projects": [ProjectResponse.model_validate(p) for p in projects]} + + +@router.post("/", response_model=dict) +async def create_project(project: ProjectCreate, db: AsyncSession = Depends(get_db)): + """Create a new project""" + db_project = Project(**project.model_dump()) + db.add(db_project) + await db.commit() + await db.refresh(db_project) + return {"id": str(db_project.id)} + + +@router.get("/{project_id}", response_model=dict) +async def get_project(project_id: UUID, db: AsyncSession = Depends(get_db)): + """Get project by ID""" + result = await db.execute(select(Project).where(Project.id == project_id)) + project = result.scalar_one_or_none() + if not project: + raise HTTPException(status_code=404, detail="Project not found") + return ProjectResponse.model_validate(project) + + +@router.put("/{project_id}", response_model=dict) +async def update_project(project_id: UUID, project: ProjectUpdate, db: AsyncSession = Depends(get_db)): + """Update project""" + result = await db.execute(select(Project).where(Project.id == project_id)) + db_project = result.scalar_one_or_none() + if not db_project: + raise HTTPException(status_code=404, detail="Project not found") + + for key, value in project.model_dump(exclude_unset=True).items(): + setattr(db_project, key, value) + + await db.commit() + await db.refresh(db_project) + return ProjectResponse.model_validate(db_project) + + +@router.delete("/{project_id}", response_model=dict) +async def delete_project(project_id: UUID, db: AsyncSession = Depends(get_db)): + """Delete project""" + result = await db.execute(select(Project).where(Project.id == project_id)) + project = result.scalar_one_or_none() + if not project: + raise HTTPException(status_code=404, detail="Project not found") + + await db.delete(project) + await db.commit() + return {"message": "Project deleted successfully"} diff --git a/backend/app/api/v1/questions/__init__.py b/backend/app/api/v1/questions/__init__.py new file mode 100644 index 0000000..fa4e7b7 --- /dev/null +++ b/backend/app/api/v1/questions/__init__.py @@ -0,0 +1,122 @@ +""" +Questions API Router +""" +from typing import List, Optional +from uuid import UUID +from pydantic import BaseModel +from fastapi import APIRouter, Depends, HTTPException, Query +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy import select +from app.core.database import get_db +from app.models.models import Question, Chunk +from app.schemas.base import QuestionCreate, QuestionResponse + +router = APIRouter() + + +class GenerateRequest(BaseModel): + """Request model for generating questions""" + chunk_ids: List[UUID] = [] + count: int = 5 + question_types: List[str] = ["fact", "summary"] + + +@router.post("/generate", response_model=dict) +async def generate_questions( + project_id: UUID, + request: GenerateRequest, + db: AsyncSession = Depends(get_db) +): + """Generate questions from chunks using LLM""" + # TODO: Implement LLM-based question generation + # This is a placeholder that creates sample questions + + if not request.chunk_ids: + raise HTTPException(status_code=400, detail="chunk_ids is required") + + # Get chunks + result = await db.execute( + select(Chunk).where(Chunk.id.in_(request.chunk_ids), Chunk.project_id == project_id) + ) + chunks = result.scalars().all() + + if not chunks: + raise HTTPException(status_code=404, detail="No chunks found") + + # Create sample questions (placeholder) + created_questions = [] + for chunk in chunks: + for i in range(request.count): + question = Question( + project_id=project_id, + chunk_id=chunk.id, + content=f"这是关于「{chunk.name}」的问题 {i+1}?", + answer=f"这是问题 {i+1} 的答案。", + question_type=request.question_types[0] if request.question_types else "fact", + source="generated" + ) + db.add(question) + created_questions.append(question) + + await db.commit() + + return { + "questions": len(created_questions), + "message": f"Successfully generated {len(created_questions)} questions" + } + + +@router.get("/", response_model=dict) +async def list_questions( + project_id: UUID, + chunk_id: Optional[UUID] = Query(None), + db: AsyncSession = Depends(get_db) +): + """List questions for a project""" + query = select(Question).where(Question.project_id == project_id) + + if chunk_id: + query = query.where(Question.chunk_id == chunk_id) + + result = await db.execute(query) + questions = result.scalars().all() + + return {"questions": [QuestionResponse.model_validate(q) for q in questions]} + + +@router.put("/{question_id}", response_model=dict) +async def update_question( + project_id: UUID, + question_id: UUID, + question: QuestionCreate, + db: AsyncSession = Depends(get_db) +): + """Update question""" + result = await db.execute( + select(Question).where(Question.id == question_id, Question.project_id == project_id) + ) + db_question = result.scalar_one_or_none() + if not db_question: + raise HTTPException(status_code=404, detail="Question not found") + + for key, value in question.model_dump(exclude_unset=True).items(): + setattr(db_question, key, value) + + await db.commit() + await db.refresh(db_question) + return QuestionResponse.model_validate(db_question) + + +@router.delete("/{question_id}", response_model=dict) +async def delete_question(project_id: UUID, question_id: UUID, db: AsyncSession = Depends(get_db)): + """Delete question""" + result = await db.execute( + select(Question).where(Question.id == question_id, Question.project_id == project_id) + ) + question = result.scalar_one_or_none() + if not question: + raise HTTPException(status_code=404, detail="Question not found") + + await db.delete(question) + await db.commit() + return {"message": "Question deleted successfully"} diff --git a/backend/app/core/__init__.py b/backend/app/core/__init__.py new file mode 100644 index 0000000..11a535b --- /dev/null +++ b/backend/app/core/__init__.py @@ -0,0 +1,3 @@ +""" +Core module initialization +""" diff --git a/backend/app/core/config.py b/backend/app/core/config.py new file mode 100644 index 0000000..6acca40 --- /dev/null +++ b/backend/app/core/config.py @@ -0,0 +1,49 @@ +""" +Application Configuration +""" + +from functools import lru_cache +from pydantic_settings import BaseSettings +from pydantic import Field + + +class Settings(BaseSettings): + """Application settings""" + + # App + APP_NAME: str = "YG-Dataset" + DEBUG: bool = True + HOST: str = "0.0.0.0" + PORT: int = 8000 + + # Database - 使用 SQLite 进行开发/测试 + # 生产环境可切换为 PostgreSQL + DATABASE_URL: str = Field( + default="sqlite:///./ygdataset.db", + description="Database connection URL (sqlite:// or postgresql+asyncpg://)" + ) + DATABASE_URL_SYNC: str = Field( + default="sqlite:///./ygdataset.db", + description="Synchronous database connection URL" + ) + + # Redis + REDIS_URL: str = "redis://localhost:6379/0" + + # File Storage + UPLOAD_DIR: str = "./uploads" + MAX_FILE_SIZE: int = 100 * 1024 * 1024 # 100MB + + # LLM Settings + DEFAULT_MODEL_PROVIDER: str = "openai" + DEFAULT_MODEL_NAME: str = "gpt-4o-mini" + + class Config: + env_file = ".env" + extra = "allow" + + +@lru_cache() +def get_settings() -> Settings: + """Get cached settings""" + return Settings() diff --git a/backend/app/core/database.py b/backend/app/core/database.py new file mode 100644 index 0000000..56bcd6a --- /dev/null +++ b/backend/app/core/database.py @@ -0,0 +1,68 @@ +""" +Database Configuration and Session Management +支持 SQLite 和 PostgreSQL +""" + +from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker +from sqlalchemy.orm import DeclarativeBase +from sqlalchemy import create_engine +from app.core.config import get_settings + +settings = get_settings() + + +def get_engine_config(): + """根据数据库类型返回引擎配置""" + if settings.DATABASE_URL.startswith("sqlite"): + return {"echo": settings.DEBUG} + else: + return { + "echo": settings.DEBUG, + "pool_pre_ping": True, + "pool_size": 10, + "max_overflow": 20, + } + + +# Async engine for FastAPI +async_engine = create_async_engine( + settings.DATABASE_URL, + **get_engine_config() +) + +# Sync engine for migrations +sync_engine = create_engine( + settings.DATABASE_URL_SYNC, + echo=settings.DEBUG, + pool_pre_ping=True, +) + + +# Async session factory +AsyncSessionLocal = async_sessionmaker( + async_engine, + class_=AsyncSession, + expire_on_commit=False, + autocommit=False, + autoflush=False, +) + + +class Base(DeclarativeBase): + """Base class for all models""" + pass + + +async def init_db(): + """Initialize database tables""" + async with async_engine.begin() as conn: + await conn.run_sync(Base.metadata.create_all) + + +async def get_db() -> AsyncSession: + """Dependency for getting database session""" + async with AsyncSessionLocal() as session: + try: + yield session + finally: + await session.close() diff --git a/backend/app/main.py b/backend/app/main.py new file mode 100644 index 0000000..04249dd --- /dev/null +++ b/backend/app/main.py @@ -0,0 +1,58 @@ +""" +YG-Dataset Backend Application +FastAPI-based API server for dataset generation platform +""" + +from contextlib import asynccontextmanager +from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware + +from app.api.v1 import api_router +from app.core.config import settings +from app.core.database import init_db + + +@asynccontextmanager +async def lifespan(app: FastAPI): + """Application lifespan events""" + # Startup + await init_db() + yield + # Shutdown + pass + + +app = FastAPI( + title="YG-Dataset API", + description="Dataset Generation Platform API", + version="1.0.0", + lifespan=lifespan, +) + +# CORS +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +# Include API routes +app.include_router(api_router, prefix="/api/v1") + + +@app.get("/health") +async def health_check(): + """Health check endpoint""" + return {"status": "healthy", "version": "1.0.0"} + + +if __name__ == "__main__": + import uvicorn + uvicorn.run( + "app.main:app", + host=settings.HOST, + port=settings.PORT, + reload=settings.DEBUG, + ) diff --git a/backend/app/models/__init__.py b/backend/app/models/__init__.py new file mode 100644 index 0000000..ae10ffe --- /dev/null +++ b/backend/app/models/__init__.py @@ -0,0 +1,3 @@ +""" +Database Models +""" diff --git a/backend/app/models/base.py b/backend/app/models/base.py new file mode 100644 index 0000000..4a57e6c --- /dev/null +++ b/backend/app/models/base.py @@ -0,0 +1,19 @@ +""" +Base Model with UUID support +""" +import uuid +from datetime import datetime +from sqlalchemy import Column, DateTime +from sqlalchemy.dialects.postgresql import UUID +from app.core.database import Base + + +class TimestampMixin: + """Mixin for created_at and updated_at timestamps""" + created_at = Column(DateTime, default=datetime.utcnow, nullable=False) + updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) + + +class UUIDMixin: + """Mixin for UUID primary key""" + id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True) diff --git a/backend/app/models/models.py b/backend/app/models/models.py new file mode 100644 index 0000000..0a8696e --- /dev/null +++ b/backend/app/models/models.py @@ -0,0 +1,161 @@ +""" +Database Models for YG-Dataset +""" +from sqlalchemy import Column, String, Text, Integer, BigInteger, ForeignKey, JSON +from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy.orm import relationship +from app.core.database import Base +from app.models.base import UUIDMixin, TimestampMixin + + +class Project(Base, UUIDMixin, TimestampMixin): + """Project model""" + __tablename__ = "projects" + + name = Column(String(255), nullable=False) + description = Column(Text) + + # Relationships + files = relationship("File", back_populates="project", cascade="all, delete-orphan") + chunks = relationship("Chunk", back_populates="project", cascade="all, delete-orphan") + tags = relationship("Tag", back_populates="project", cascade="all, delete-orphan") + datasets = relationship("Dataset", back_populates="project", cascade="all, delete-orphan") + eval_datasets = relationship("EvalDataset", back_populates="project", cascade="all, delete-orphan") + model_configs = relationship("ModelConfig", back_populates="project", cascade="all, delete-orphan") + tasks = relationship("Task", back_populates="project", cascade="all, delete-orphan") + + +class File(Base, UUIDMixin, TimestampMixin): + """File model for uploaded documents""" + __tablename__ = "files" + + project_id = Column(UUID(as_uuid=True), ForeignKey("projects.id", ondelete="CASCADE"), nullable=False) + filename = Column(String(255), nullable=False) + file_type = Column(String(50), nullable=False) # pdf, docx, xlsx, csv, epub, md, txt + file_path = Column(String(500)) + size = Column(BigInteger) # file size in bytes + status = Column(String(20), default="pending") # pending, processing, completed, failed + + # Relationships + project = relationship("Project", back_populates="files") + chunks = relationship("Chunk", back_populates="file", cascade="all, delete-orphan") + + +class Chunk(Base, UUIDMixin, TimestampMixin): + """Text chunk model after splitting""" + __tablename__ = "chunks" + + project_id = Column(UUID(as_uuid=True), ForeignKey("projects.id", ondelete="CASCADE"), nullable=False) + file_id = Column(UUID(as_uuid=True), ForeignKey("files.id", ondelete="CASCADE")) + name = Column(String(255)) + content = Column(Text, nullable=False) + summary = Column(Text) + word_count = Column(Integer) + metadata = Column(JSON) # store additional info like headings, page numbers + + # Relationships + project = relationship("Project", back_populates="chunks") + file = relationship("File", back_populates="chunks") + questions = relationship("Question", back_populates="chunk", cascade="all, delete-orphan") + chunk_tags = relationship("ChunkTag", back_populates="chunk", cascade="all, delete-orphan") + + +class Tag(Base, UUIDMixin, TimestampMixin): + """Tag/Label model for categorizing content""" + __tablename__ = "tags" + + project_id = Column(UUID(as_uuid=True), ForeignKey("projects.id", ondelete="CASCADE"), nullable=False) + label = Column(String(255), nullable=False) + parent_id = Column(UUID(as_uuid=True), ForeignKey("tags.id", ondelete="CASCADE")) + color = Column(String(20)) # hex color code + + # Relationships + project = relationship("Project", back_populates="tags") + parent = relationship("Tag", remote_side="Tag.id", back_populates="children") + children = relationship("Tag", back_populates="parent") + chunk_tags = relationship("ChunkTag", back_populates="tag") + + +class ChunkTag(Base, UUIDMixin): + """Many-to-many relationship between chunks and tags""" + __tablename__ = "chunk_tags" + + chunk_id = Column(UUID(as_uuid=True), ForeignKey("chunks.id", ondelete="CASCADE"), nullable=False) + tag_id = Column(UUID(as_uuid=True), ForeignKey("tags.id", ondelete="CASCADE"), nullable=False) + + # Relationships + chunk = relationship("Chunk", back_populates="chunk_tags") + tag = relationship("Tag", back_populates="chunk_tags") + + +class Question(Base, UUIDMixin, TimestampMixin): + """Question/QA pair model""" + __tablename__ = "questions" + + project_id = Column(UUID(as_uuid=True), ForeignKey("projects.id", ondelete="CASCADE"), nullable=False) + chunk_id = Column(UUID(as_uuid=True), ForeignKey("chunks.id", ondelete="CASCADE")) + content = Column(Text, nullable=False) # question content + answer = Column(Text) # answer content + question_type = Column(String(50)) # fact, summary, reasoning, etc. + source = Column(String(50), default="manual") # manual, generated + + # Relationships + project = relationship("Project") + chunk = relationship("Chunk", back_populates="questions") + + +class Dataset(Base, UUIDMixin, TimestampMixin): + """Dataset model""" + __tablename__ = "datasets" + + project_id = Column(UUID(as_uuid=True), ForeignKey("projects.id", ondelete="CASCADE"), nullable=False) + name = Column(String(255), nullable=False) + description = Column(Text) + dataset_type = Column(String(50)) # qa, conversation, instruction + metadata = Column(JSON) + + # Relationships + project = relationship("Project", back_populates="datasets") + + +class EvalDataset(Base, UUIDMixin, TimestampMixin): + """Evaluation dataset model""" + __tablename__ = "eval_datasets" + + project_id = Column(UUID(as_uuid=True), ForeignKey("projects.id", ondelete="CASCADE"), nullable=False) + name = Column(String(255), nullable=False) + question_type = Column(String(50)) # mixed, fact, reasoning + metadata = Column(JSON) + + # Relationships + project = relationship("Project", back_populates="eval_datasets") + + +class ModelConfig(Base, UUIDMixin, TimestampMixin): + """Model configuration for LLM providers""" + __tablename__ = "model_configs" + + project_id = Column(UUID(as_uuid=True), ForeignKey("projects.id", ondelete="CASCADE"), nullable=False) + provider = Column(String(50), nullable=False) # openai, anthropic, ollama, custom + model_name = Column(String(100)) + api_key = Column(String(500)) + api_base = Column(String(500)) + is_default = Column(String(10), default="false") + + # Relationships + project = relationship("Project", back_populates="model_configs") + + +class Task(Base, UUIDMixin, TimestampMixin): + """Task model for background jobs""" + __tablename__ = "tasks" + + project_id = Column(UUID(as_uuid=True), ForeignKey("projects.id", ondelete="CASCADE")) + task_type = Column(String(50)) # split, generate, eval, export + status = Column(String(20), default="pending") # pending, running, completed, failed + progress = Column(Integer, default=0) # 0-100 + result = Column(JSON) + error = Column(Text) + + # Relationships + project = relationship("Project", back_populates="tasks") diff --git a/backend/app/schemas/__init__.py b/backend/app/schemas/__init__.py new file mode 100644 index 0000000..c89615a --- /dev/null +++ b/backend/app/schemas/__init__.py @@ -0,0 +1,3 @@ +""" +Pydantic Schemas +""" diff --git a/backend/app/schemas/base.py b/backend/app/schemas/base.py new file mode 100644 index 0000000..82fc865 --- /dev/null +++ b/backend/app/schemas/base.py @@ -0,0 +1,170 @@ +""" +Base Pydantic schemas +""" +from datetime import datetime +from typing import Optional, Any +from uuid import UUID +from pydantic import BaseModel, ConfigDict + + +class TimestampMixin(BaseModel): + """Mixin for timestamps""" + created_at: Optional[datetime] = None + updated_at: Optional[datetime] = None + + +class UUIDMixin(BaseModel): + """Mixin for UUID""" + model_config = ConfigDict(from_attributes=True) + + id: UUID + + +class ProjectBase(BaseModel): + """Base project schema""" + name: str + description: Optional[str] = None + + +class ProjectCreate(ProjectBase): + """Project create schema""" + pass + + +class ProjectUpdate(ProjectBase): + """Project update schema""" + pass + + +class ProjectResponse(ProjectBase, UUIDMixin, TimestampMixin): + """Project response schema""" + pass + + +class FileBase(BaseModel): + """Base file schema""" + filename: str + file_type: str + size: Optional[int] = None + + +class FileResponse(FileBase, UUIDMixin, TimestampMixin): + """File response schema""" + status: str + + +class ChunkBase(BaseModel): + """Base chunk schema""" + name: Optional[str] = None + content: str + summary: Optional[str] = None + word_count: Optional[int] = None + + +class ChunkCreate(ChunkBase): + """Chunk create schema""" + file_id: Optional[UUID] = None + + +class ChunkResponse(ChunkBase, UUIDMixin, TimestampMixin): + """Chunk response schema""" + pass + + +class QuestionBase(BaseModel): + """Base question schema""" + content: str + answer: Optional[str] = None + question_type: Optional[str] = None + + +class QuestionCreate(QuestionBase): + """Question create schema""" + chunk_id: Optional[UUID] = None + + +class QuestionResponse(QuestionBase, UUIDMixin, TimestampMixin): + """Question response schema""" + source: str + + +class DatasetBase(BaseModel): + """Base dataset schema""" + name: str + description: Optional[str] = None + dataset_type: Optional[str] = None + + +class DatasetCreate(DatasetBase): + """Dataset create schema""" + pass + + +class DatasetResponse(DatasetBase, UUIDMixin, TimestampMixin): + """Dataset response schema""" + question_count: Optional[int] = None + + +class EvalDatasetBase(BaseModel): + """Base eval dataset schema""" + name: str + question_type: Optional[str] = None + + +class EvalDatasetCreate(EvalDatasetBase): + """Eval dataset create schema""" + pass + + +class EvalDatasetResponse(EvalDatasetBase, UUIDMixin, TimestampMixin): + """Eval dataset response schema""" + pass + + +class TagBase(BaseModel): + """Base tag schema""" + label: str + parent_id: Optional[UUID] = None + color: Optional[str] = None + + +class TagCreate(TagBase): + """Tag create schema""" + pass + + +class TagResponse(TagBase, UUIDMixin, TimestampMixin): + """Tag response schema""" + pass + + +class ModelConfigBase(BaseModel): + """Base model config schema""" + provider: str + model_name: Optional[str] = None + api_key: Optional[str] = None + api_base: Optional[str] = None + is_default: Optional[str] = "false" + + +class ModelConfigCreate(ModelConfigBase): + """Model config create schema""" + pass + + +class ModelConfigResponse(ModelConfigBase, UUIDMixin, TimestampMixin): + """Model config response schema""" + pass + + +class TaskBase(BaseModel): + """Base task schema""" + task_type: str + status: Optional[str] = "pending" + progress: Optional[int] = 0 + + +class TaskResponse(TaskBase, UUIDMixin, TimestampMixin): + """Task response schema""" + result: Optional[Any] = None + error: Optional[str] = None diff --git a/backend/app/services/__init__.py b/backend/app/services/__init__.py new file mode 100644 index 0000000..04f16e9 --- /dev/null +++ b/backend/app/services/__init__.py @@ -0,0 +1,3 @@ +""" +Services module +""" diff --git a/backend/app/services/file_processor/__init__.py b/backend/app/services/file_processor/__init__.py new file mode 100644 index 0000000..5bb0762 --- /dev/null +++ b/backend/app/services/file_processor/__init__.py @@ -0,0 +1,3 @@ +""" +File Processing Services +""" diff --git a/backend/app/services/file_processor/docx_processor.py b/backend/app/services/file_processor/docx_processor.py new file mode 100644 index 0000000..a638830 --- /dev/null +++ b/backend/app/services/file_processor/docx_processor.py @@ -0,0 +1,53 @@ +""" +DOCX Text Extractor +""" +from docx import Document +from typing import Dict, List + + +class DOCXProcessor: + """Extract text from DOCX files""" + + def extract_text(self, file_path: str) -> str: + """Extract all text from DOCX""" + doc = Document(file_path) + text_parts = [] + + for para in doc.paragraphs: + if para.text.strip(): + text_parts.append(para.text) + + # Also extract text from tables + for table in doc.tables: + for row in table.rows: + for cell in row.cells: + if cell.text.strip(): + text_parts.append(cell.text) + + return "\n\n".join(text_parts) + + def extract_with_metadata(self, file_path: str) -> Dict: + """Extract text with DOCX metadata""" + doc = Document(file_path) + + result = { + "text": self.extract_text(file_path), + "paragraphs": len(doc.paragraphs), + "tables": len(doc.tables), + "sections": len(doc.sections), + "metadata": { + "author": doc.core_properties.author, + "title": doc.core_properties.title, + "subject": doc.core_properties.subject, + "created": doc.core_properties.created, + "modified": doc.core_properties.modified + } + } + + return result + + +def process_docx(file_path: str) -> str: + """Process DOCX file and return text""" + processor = DOCXProcessor() + return processor.extract_text(file_path) diff --git a/backend/app/services/file_processor/excel_processor.py b/backend/app/services/file_processor/excel_processor.py new file mode 100644 index 0000000..5c4e2b7 --- /dev/null +++ b/backend/app/services/file_processor/excel_processor.py @@ -0,0 +1,66 @@ +""" +Excel/CSV Text Extractor +""" +import pandas as pd +from typing import Dict, List + + +class ExcelProcessor: + """Extract text from Excel and CSV files""" + + def extract_csv(self, file_path: str) -> str: + """Extract text from CSV file""" + df = pd.read_csv(file_path) + return self._dataframe_to_text(df) + + def extract_excel(self, file_path: str, sheet_name: str = None) -> str: + """Extract text from Excel file""" + if sheet_name: + df = pd.read_excel(file_path, sheet_name=sheet_name) + return self._dataframe_to_text(df) + else: + # Read all sheets + sheets = pd.read_excel(file_path, sheet_name=None) + text_parts = [] + for sheet_name, df in sheets.items(): + text_parts.append(f"=== Sheet: {sheet_name} ===\n") + text_parts.append(self._dataframe_to_text(df)) + return "\n\n".join(text_parts) + + def _dataframe_to_text(self, df: pd.DataFrame) -> str: + """Convert DataFrame to readable text""" + text_parts = [] + + # Add column headers + if not df.empty: + text_parts.append(" | ".join(str(col) for col in df.columns)) + text_parts.append("-" * len(text_parts[-1])) + + # Add rows + for _, row in df.iterrows(): + row_text = " | ".join(str(val) for val in row.values) + text_parts.append(row_text) + + return "\n".join(text_parts) + + def extract_all_sheets(self, file_path: str) -> Dict[str, str]: + """Extract all sheets from Excel file""" + sheets = pd.read_excel(file_path, sheet_name=None) + return {name: self._dataframe_to_text(df) for name, df in sheets.items()} + + def get_sheet_names(self, file_path: str) -> List[str]: + """Get all sheet names from Excel file""" + xl = pd.ExcelFile(file_path) + return xl.sheet_names + + +def process_csv(file_path: str) -> str: + """Process CSV file and return text""" + processor = ExcelProcessor() + return processor.extract_csv(file_path) + + +def process_excel(file_path: str) -> str: + """Process Excel file and return text""" + processor = ExcelProcessor() + return processor.extract_excel(file_path) diff --git a/backend/app/services/file_processor/pdf_processor.py b/backend/app/services/file_processor/pdf_processor.py new file mode 100644 index 0000000..9826fb2 --- /dev/null +++ b/backend/app/services/file_processor/pdf_processor.py @@ -0,0 +1,65 @@ +""" +PDF Text Extractor +""" +import pdfplumber +from typing import Dict, List, Optional + + +class PDFProcessor: + """Extract text from PDF files""" + + def extract_text(self, file_path: str) -> str: + """Extract all text from PDF""" + text_parts = [] + + with pdfplumber.open(file_path) as pdf: + for page_num, page in enumerate(pdf.pages, 1): + text = page.extract_text() + if text: + text_parts.append(f"--- Page {page_num} ---\n{text}") + + return "\n\n".join(text_parts) + + def extract_pages(self, file_path: str) -> List[Dict]: + """Extract text page by page with metadata""" + pages = [] + + with pdfplumber.open(file_path) as pdf: + for page_num, page in enumerate(pdf.pages, 1): + text = page.extract_text() + if text: + pages.append({ + "page_number": page_num, + "text": text.strip(), + "word_count": len(text.split()) + }) + + return pages + + def extract_with_metadata(self, file_path: str) -> Dict: + """Extract text with PDF metadata""" + result = { + "text": "", + "pages": [], + "metadata": {} + } + + with pdfplumber.open(file_path) as pdf: + # Get metadata + result["metadata"] = { + "page_count": len(pdf.pages), + "metadata": pdf.metadata + } + + # Extract pages + pages = self.extract_pages(file_path) + result["pages"] = pages + result["text"] = "\n\n".join([p["text"] for p in pages]) + + return result + + +def process_pdf(file_path: str) -> str: + """Process PDF file and return text""" + processor = PDFProcessor() + return processor.extract_with_metadata(file_path)["text"] diff --git a/backend/app/services/text_splitter/__init__.py b/backend/app/services/text_splitter/__init__.py new file mode 100644 index 0000000..8037217 --- /dev/null +++ b/backend/app/services/text_splitter/__init__.py @@ -0,0 +1,3 @@ +""" +Text Splitter Services +""" diff --git a/backend/app/services/text_splitter/splitter.py b/backend/app/services/text_splitter/splitter.py new file mode 100644 index 0000000..a024faf --- /dev/null +++ b/backend/app/services/text_splitter/splitter.py @@ -0,0 +1,248 @@ +""" +Text Splitter +""" +import re +from typing import List, Dict, Optional + + +class TextSplitter: + """Base text splitter""" + + def __init__(self, chunk_size: int = 500, overlap: int = 50): + self.chunk_size = chunk_size + self.overlap = overlap + + def split(self, text: str) -> List[Dict]: + """Split text into chunks""" + raise NotImplementedError + + +class RecursiveTextSplitter(TextSplitter): + """Recursive character text splitter""" + + def __init__(self, chunk_size: int = 500, overlap: int = 50, separators: List[str] = None): + super().__init__(chunk_size, overlap) + self.separators = separators or ["\n\n", "\n", ". ", " ", ""] + + def split(self, text: str) -> List[Dict]: + """Split text recursively""" + chunks = [] + current_chunk = "" + chunk_index = 0 + + for separator in self.separators: + if separator in text: + parts = text.split(separator) + for part in parts: + if len(current_chunk) + len(part) > self.chunk_size: + if current_chunk: + chunks.append({ + "index": chunk_index, + "content": current_chunk.strip(), + "word_count": len(current_chunk.split()) + }) + chunk_index += 1 + + # Handle overlap + if self.overlap > 0 and chunks: + overlap_text = " ".join(chunks[-1]["content"].split()[-self.overlap:]) + current_chunk = overlap_text + separator + part + else: + current_chunk = part + else: + current_chunk += separator + part if current_chunk else part + + if current_chunk: + chunks.append({ + "index": chunk_index, + "content": current_chunk.strip(), + "word_count": len(current_chunk.split()) + }) + break + else: + continue + + return chunks + + +class MarkdownStructureSplitter(TextSplitter): + """Split text based on Markdown structure (headings)""" + + def __init__(self, chunk_size: int = 2000, overlap: int = 100): + super().__init__(chunk_size, overlap) + + def split(self, text: str) -> List[Dict]: + """Split text by Markdown headings""" + # Find all heading patterns + heading_pattern = r'^(#{1,6})\s+(.+)$' + lines = text.split('\n') + + chunks = [] + current_chunk = "" + current_heading = "文档开头" + chunk_index = 0 + + for line in lines: + heading_match = re.match(heading_pattern, line.strip()) + + if heading_match: + # Save previous chunk if exists + if current_chunk.strip(): + chunks.append({ + "index": chunk_index, + "name": current_heading, + "content": current_chunk.strip(), + "word_count": len(current_chunk.split()) + }) + chunk_index += 1 + + current_heading = heading_match.group(2).strip() + current_chunk = line + "\n" + else: + # Check chunk size + if len(current_chunk) > self.chunk_size: + chunks.append({ + "index": chunk_index, + "name": current_heading, + "content": current_chunk.strip(), + "word_count": len(current_chunk.split()) + }) + chunk_index += 1 + + # Handle overlap + if self.overlap > 0: + overlap_lines = current_chunk.split('\n')[-self.overlap:] + current_chunk = '\n'.join(overlap_lines) + '\n' + else: + current_chunk = "" + + current_chunk += line + "\n" + + # Add last chunk + if current_chunk.strip(): + chunks.append({ + "index": chunk_index, + "name": current_heading, + "content": current_chunk.strip(), + "word_count": len(current_chunk.split()) + }) + + return chunks + + +class TokenSplitter(TextSplitter): + """Split text by token count""" + + def __init__(self, chunk_size: int = 500, overlap: int = 50): + super().__init__(chunk_size, overlap) + + def split(self, text: str) -> List[Dict]: + """Split text by approximate token count""" + words = text.split() + chunks = [] + chunk_index = 0 + + for i in range(0, len(words), self.chunk_size - self.overlap): + chunk_words = words[i:i + self.chunk_size] + chunk_text = " ".join(chunk_words) + + chunks.append({ + "index": chunk_index, + "content": chunk_text, + "word_count": len(chunk_words), + "token_estimate": len(chunk_words) * 1.3 # rough token estimate + }) + chunk_index += 1 + + return chunks + + +class CodeSplitter(TextSplitter): + """Split text with code awareness""" + + def __init__(self, chunk_size: int = 500, overlap: int = 50): + super().__init__(chunk_size, overlap) + + def split(self, text: str) -> List[Dict]: + """Split text preserving code blocks""" + # Split by code blocks first + code_pattern = r'```[\s\S]*?```' + parts = re.split(code_pattern, text) + + chunks = [] + chunk_index = 0 + current_chunk = "" + + for part in parts: + if len(current_chunk) + len(part) > self.chunk_size: + if current_chunk.strip(): + chunks.append({ + "index": chunk_index, + "content": current_chunk.strip(), + "word_count": len(current_chunk.split()) + }) + chunk_index += 1 + current_chunk = part + else: + current_chunk += part + + if current_chunk.strip(): + chunks.append({ + "index": chunk_index, + "content": current_chunk.strip(), + "word_count": len(current_chunk.split()) + }) + + return chunks + + +class CustomSplitter(TextSplitter): + """Custom separator splitter""" + + def __init__(self, separator: str = "\n\n", chunk_size: int = 500): + super().__init__(chunk_size, 0) + self.separator = separator + + def split(self, text: str) -> List[Dict]: + """Split by custom separator""" + parts = text.split(self.separator) + chunks = [] + + current_chunk = "" + chunk_index = 0 + + for part in parts: + if len(current_chunk) + len(part) > self.chunk_size: + if current_chunk.strip(): + chunks.append({ + "index": chunk_index, + "content": current_chunk.strip(), + "word_count": len(current_chunk.split()) + }) + chunk_index += 1 + current_chunk = part + else: + current_chunk += self.separator + part if current_chunk else part + + if current_chunk.strip(): + chunks.append({ + "index": chunk_index, + "content": current_chunk.strip(), + "word_count": len(current_chunk.split()) + }) + + return chunks + + +def get_splitter(method: str, **kwargs) -> TextSplitter: + """Get text splitter by method name""" + splitters = { + "recursive": RecursiveTextSplitter, + "markdown_structure": MarkdownStructureSplitter, + "token": TokenSplitter, + "code": CodeSplitter, + "custom": CustomSplitter + } + + splitter_class = splitters.get(method, RecursiveTextSplitter) + return splitter_class(**kwargs) diff --git a/backend/requirements.txt b/backend/requirements.txt new file mode 100644 index 0000000..57e9731 --- /dev/null +++ b/backend/requirements.txt @@ -0,0 +1,37 @@ +# FastAPI +fastapi>=0.115.0 +uvicorn[standard]>=0.30.0 +python-multipart>=0.0.9 + +# Database - SQLite (默认), PostgreSQL 可选 +sqlalchemy>=2.0.0 +alembic>=1.13.0 +# asyncpg>=0.29.0 # PostgreSQL 异步驱动(生产环境使用) +# psycopg2-binary>=2.9.9 # PostgreSQL 同步驱动 + +# Pydantic +pydantic>=2.0.0 +pydantic-settings>=2.0.0 + +# Redis - 可选,用于缓存/队列(开发环境可省略) +# redis>=5.0.0 + +# File Processing +pdfplumber>=0.10.4 +python-docx>=1.1.0 +openpyxl>=3.1.2 +pandas>=2.2.0 +ebooklib>=0.5 +PyMuPDF>=1.24.0 + +# LLM & Text +langchain>=0.3.0 +langchain-community>=0.2.0 +langchain-openai>=0.1.0 +tiktoken>=0.7.0 +python-dotenv>=1.0.0 + +# Utils +python-dateutil>=2.8.2 +httpx>=0.27.0 +aiofiles>=23.2.1 diff --git a/bug修改.md b/bug修改.md new file mode 100644 index 0000000..7d0394c --- /dev/null +++ b/bug修改.md @@ -0,0 +1,20 @@ +# Bug 修改记录 + +## 2026-03-17 + +### 初始项目创建 +- 创建 YG-Dataset 重构项目 +- 搭建 FastAPI + Vue 3 基础架构 + +--- + +## 修复记录格式 + +### 日期 +**问题描述:** +**原因:** +**修复方案:** + +--- + +*持续更新中...* diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..e670599 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,52 @@ +version: '3.8' + +services: + # FastAPI 后端 (SQLite 数据库,随项目文件存储) + backend: + build: + context: ./backend + dockerfile: Dockerfile + container_name: ygdataset-backend + ports: + - "8000:8000" + environment: + - DATABASE_URL=sqlite:///./ygdataset.db + - DEBUG=true + volumes: + - ./backend:/app + - uploads:/app/uploads + restart: unless-stopped + + # Vue 前端 + frontend: + build: + context: ./frontend + dockerfile: Dockerfile + container_name: ygdataset-frontend + ports: + - "3000:80" + volumes: + - ./frontend:/app + - /app/node_modules + depends_on: + - backend + restart: unless-stopped + +volumes: + uploads: + +# 如需 PostgreSQL,取消注释以下配置: +# services: +# postgres: +# image: postgres:15 +# environment: +# POSTGRES_USER: ygdataset +# POSTGRES_PASSWORD: your_password +# POSTGRES_DB: ygdataset +# ports: +# - "5432:5432" +# volumes: +# - postgres_data:/var/lib/postgresql/data + +# volumes: +# postgres_data: diff --git a/easy-dataset-main-架构分析报告.md b/easy-dataset-main-架构分析报告.md new file mode 100644 index 0000000..3cc9687 --- /dev/null +++ b/easy-dataset-main-架构分析报告.md @@ -0,0 +1,306 @@ +# Easy Dataset 项目架构分析报告 + +## 一、项目概述 + +**Easy Dataset** 是一个功能强大的大模型微调数据集创建工具,由 ConardLi 开发维护。该应用提供直观的界面和强大的内置文档解析、智能分割、数据清洗和增强功能,可将各种格式的领域文档转换为高质量的结构化数据集,适用于模型微调、RAG(检索增强生成)和模型性能评估等场景。 + +**项目地址**: https://github.com/ConardLi/easy-dataset +**当前版本**: 1.7.2 +**许可证**: AGPL 3.0 + +--- + +## 二、技术栈分析 + +### 2.1 核心框架 + +| 类别 | 技术选型 | 说明 | +|------|----------|------| +| 前端框架 | Next.js 14 | App Router 架构 | +| UI 框架 | Material-UI (MUI) | v5.16.14 | +| 状态管理 | Jotai | 轻量级原子化状态管理 | +| 数据库 | Prisma + SQLite | 使用 Prisma ORM | +| 开发语言 | JavaScript | 全栈 JavaScript | + +### 2.2 关键依赖 + +| 类别 | 库名称 | 用途 | +|------|--------|------| +| AI/ML | ai SDK, langchain | 大模型集成 | +| LLM 提供商 | @ai-sdk/openai, ollama-ai-provider, zhipu-ai-provider | 多模型支持 | +| 国际化 | i18next, react-i18next | 多语言支持 | +| 文档处理 | @opendocsg/pdf2md, mammoth, pdf2md-js | PDF/DOCX 解析 | +| 桌面应用 | Electron | 跨平台桌面客户端 | +| 数据处理 | xlsx, adm-zip, jszip | 文件处理 | + +### 2.3 开发工具 + +- **包管理器**: pnpm +- **代码规范**: ESLint + Prettier +- **Git Hooks**: Husky + lint-staged +- **构建工具**: electron-builder (桌面应用打包) + +--- + +## 三、目录结构 + +``` +easy-dataset-main/ +├── app/ # Next.js 应用目录 (App Router) +│ ├── api/ # API 路由 (150+ 个路由) +│ │ ├── check-update/ # 版本检查 +│ │ ├── llm/ # LLM 模型相关 API +│ │ │ ├── fetch-models/ # 获取模型列表 +│ │ │ ├── model/ # 模型配置 +│ │ │ ├── ollama/ # Ollama 本地模型 +│ │ │ └── providers/ # LLM 提供商 +│ │ ├── monitoring/ # 监控 API +│ │ │ ├── logs/ # 日志 +│ │ │ ├── stats/ # 统计 +│ │ │ └── summary/ # 摘要 +│ │ └── projects/ # 项目相关 API +│ │ └── [projectId]/ # 动态项目路由 +│ │ ├── chunks/ # 文本分块 +│ │ ├── datasets/ # 数据集 +│ │ ├── eval-datasets/ # 评估数据集 +│ │ ├── eval-tasks/ # 评估任务 +│ │ ├── files/ # 文件管理 +│ │ ├── images/ # 图片处理 +│ │ ├── questions/ # 问题生成 +│ │ ├── distill/ # 数据蒸馏 +│ │ ├── blind-test-tasks/ # 盲测任务 +│ │ ├── playground/ # 模型测试场 +│ │ └── ... +│ └── (页面路由) +├── components/ # React 组件 (100+ 组件) +│ ├── common/ # 通用组件 +│ ├── home/ # 首页组件 +│ ├── Navbar/ # 导航栏 +│ ├── dataset-square/ # 数据集广场 +│ ├── datasets/ # 数据集组件 +│ ├── distill/ # 数据蒸馏组件 +│ ├── export/ # 导出组件 +│ ├── questions/ # 问题组件 +│ ├── text-split/ # 文本分割组件 +│ ├── tasks/ # 任务管理组件 +│ ├── playground/ # 测试场组件 +│ └── settings/ # 设置组件 +├── prisma/ # 数据库 schema +│ ├── schema.prisma # Prisma 数据模型 +│ ├── sql.json # SQL 模板 +│ └── generate-template.js # 模板生成 +├── locales/ # 国际化资源 +│ ├── en/ # 英文 +│ ├── zh-CN/ # 简体中文 +│ └── pt-BR/ # 葡萄牙语 +├── electron/ # Electron 桌面应用 +│ ├── main.js # 主进程 +│ └── preload.js # 预加载脚本 +├── public/ # 静态资源 +├── desktop/ # 桌面端入口 +└── package.json # 项目配置 +``` + +--- + +## 四、核心模块设计 + +### 4.1 数据模型 (Prisma Schema) + +项目使用 Prisma ORM 管理数据,主要数据模型包括: + +- **Project**: 项目 +- **File**: 上传的文件 +- **Chunk**: 文本分块 +- **Question**: 生成的问题 +- **Dataset**: 微调数据集 +- **EvalDataset**: 评估数据集 +- **EvalTask**: 评估任务 +- **BlindTestTask**: 盲测任务 +- **ModelConfig**: 模型配置 +- **Tag**: 标签 +- **Conversation**: 对话记录 +- **Image**: 图片数据 +- **Task**: 后台任务 + +### 4.2 核心功能模块 + +#### 4.2.1 文档处理模块 (Text Split) + +- 支持 PDF、Markdown、DOCX、TXT、EPUB 格式 +- 多种分割算法:Markdown结构、递归分隔符、固定长度、代码感知分块 +- 目录结构提取 +- PDF 转 Markdown + +#### 4.2.2 问题生成模块 (Question Generation) + +- 自动从文本片段提取相关问题 +- 问题模板管理 +- 批量生成 +- 标签树自动构建 + +#### 4.2.3 数据集生成模块 (Dataset Generation) + +- 单轮问答数据集 +- 多轮对话数据集 +- 图片问答数据集 +- 数据蒸馏(无需上传文档) + +#### 4.2.4 评估模块 (Evaluation) + +- 评估数据集生成(判断题、单选、多选、简答、开放题) +- 自动化模型评估(Judge Model) +- 人类盲测系统(Arena) +- AI 质量评估 + +#### 4.2.5 LLM 集成模块 + +支持的模型提供商: +- OpenAI +- Ollama (本地模型) +- 智谱 AI +- 阿里百炼 +- OpenRouter +- Google Gemini +- Anthropic Claude + +--- + +## 五、API 架构 + +### 5.1 API 设计原则 + +- RESTful 风格路由 +- 基于 Next.js App Router 的 Route Handlers +- 使用 Zod 进行请求/响应验证 + +### 5.2 主要 API 分组 + +| API 分组 | 路由前缀 | 功能 | +|----------|----------|------| +| 项目管理 | `/api/projects` | 项目 CRUD | +| 文件管理 | `/api/projects/[id]/files` | 文件上传/处理 | +| 文本分块 | `/api/projects/[id]/chunks` | 文本分割 | +| 问题生成 | `/api/projects/[id]/questions` | 问题生成/管理 | +| 数据集 | `/api/projects/[id]/datasets` | 数据集管理 | +| 评估 | `/api/projects/[id]/eval-*` | 评估相关 | +| 盲测 | `/api/projects/[id]/blind-test-tasks` | 盲测系统 | +| LLM | `/api/llm/*` | 模型配置/调用 | +| 监控 | `/api/monitoring/*` | 日志/统计 | + +--- + +## 六、前端架构 + +### 6.1 组件设计模式 + +- **Jotai 状态管理**: 使用原子化状态管理,便于细粒度更新 +- **MUI 组件库**: 统一的 UI 组件 +- **Framer Motion**: 动画效果 + +### 6.2 主要页面 + +1. **首页** (`/`): 项目列表、创建项目、统计卡片 +2. **项目页** (`/projects/[id]`): + - 文本分割 (`/text-split`) + - 问题列表 (`/questions`) + - 数据集 (`/datasets`) + - 评估 (`/eval-datasets`) + - 盲测 Arena (`/arena`) + - 设置 (`/settings`) +3. **模型测试场** (`/playground`) +4. **数据集广场** (`/datasets-square`) + +--- + +## 七、部署架构 + +### 7.1 多平台支持 + +- **Web 应用**: Next.js 生产构建 +- **桌面应用**: Electron + - Windows (NSIS 安装包) + - macOS (DMG) + - Linux (AppImage) +- **Docker**: 支持 Docker 部署 + +### 7.2 开发命令 + +```bash +# 开发 +pnpm dev # 启动开发服务器 (端口 1717) + +# 构建 +pnpm build # 构建 Next.js 生产版本 +pnpm electron-build # 构建桌面应用 + +# 数据库 +pnpm db:push # 推送 schema 到数据库 +pnpm db:studio # 打开 Prisma Studio +``` + +--- + +## 八、数据流设计 + +### 8.1 核心业务流程 + +``` +┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ +│ 上传文档 │ -> │ 文本分割 │ -> │ 问题生成 │ -> │ 数据集生成 │ +└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ + │ │ │ │ + PDF/DOCX Chunk Question Dataset + Markdown 目录结构 标签树 导出格式 +``` + +### 8.2 评估流程 + +``` +┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ +│ 评估数据集 │ -> │ 评估任务 │ -> │ 模型评估 │ -> │ 结果分析 │ +└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ + 生成题目 批量处理 Judge Model Arena盲测 +``` + +--- + +## 九、国际化 + +- **技术选型**: i18next + react-i18next +- **支持语言**: + - 英文 (en) + - 简体中文 (zh-CN) + - 土耳其语 (tr) + - 葡萄牙语 (pt-BR) +- **语言检测**: i18next-browser-languagedetector + +--- + +## 十、特性亮点 + +1. **智能文档处理**: 支持多种格式,智能识别 +2. **多种分割算法**: 灵活适应不同文档结构 +3. **自动标签树**: 基于文档结构智能构建 +4. **多类型数据集**: 单轮问答、多轮对话、图片问答 +5. **完整评估体系**: 自动化评估 + 人类盲测 +6. **多模型支持**: 兼容 OpenAI 格式的所有 API +7. **一键导出**: 支持多种格式和 LLaMA Factory 集成 +8. **桌面客户端**: 跨平台支持 + +--- + +## 十一、扩展方向 + +根据项目发展路线,未来可能扩展的方向包括: + +1. 更多文件格式支持 +2. 数据集版本管理 +3. 团队协作功能 +4. 更多导出格式 +5. 更强大的数据分析功能 + +--- + +*报告生成时间: 2026-03-17* +*基于 easy-dataset-main 项目源码分析* diff --git a/easy-dataset-main/.dockerignore b/easy-dataset-main/.dockerignore new file mode 100644 index 0000000..a57a79f --- /dev/null +++ b/easy-dataset-main/.dockerignore @@ -0,0 +1,16 @@ +node_modules +.next +.git +.github +README.md +README.zh-CN.md +.gitignore +.env.local +.env.development.local +.env.test.local +.env.production.local +/test +/local-db +/video +/prisma/*.sqlite +/prisma/*.sqlite-* \ No newline at end of file diff --git a/easy-dataset-main/.gitattributes b/easy-dataset-main/.gitattributes new file mode 100644 index 0000000..192a66d --- /dev/null +++ b/easy-dataset-main/.gitattributes @@ -0,0 +1,6 @@ +# Ensure shell scripts always use LF line endings +*.sh text eol=lf +docker-entrypoint.sh text eol=lf + +# Ensure Dockerfile uses LF +Dockerfile text eol=lf diff --git a/easy-dataset-main/.github/ISSUE_TEMPLATE/bug_report.md b/easy-dataset-main/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..616b030 --- /dev/null +++ b/easy-dataset-main/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,40 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '[Bug]' +labels: bug +assignees: '' +--- + +**注意:请务必按照此模版填写 ISSUES 信息,否则 ISSUE 将不会得到回复** + +**问题描述** +清晰、简洁地描述该问题的具体情况。 + +**桌面设备(请完善以下信息)** + +- 操作系统:[例如:、Window、MAC] +- 浏览器:[例如:谷歌浏览器(Chrome),苹果浏览器(Safari)] +- Easy Dataset 版本:[例如:1.2.2] + +**使用模型** + +- 模型提供商:例如火山引擎 +- 模型名称:例如 DeepSeek R1 + +**复现步骤** +重现该问题的操作步骤: + +1. 进入“……”页面。 +2. 点击“……”。 +3. 向下滚动到“……”。 +4. 这时会看到错误提示。 + +**预期结果** +清晰、简洁地描述你原本期望出现的情况。 + +**截图** +如果有必要,请附上截图,以便更好地说明你的问题。 + +**其他相关信息** +在此处添加关于该问题的其他任何相关背景信息。 diff --git a/easy-dataset-main/.github/ISSUE_TEMPLATE/feature-or-enhancement-.md b/easy-dataset-main/.github/ISSUE_TEMPLATE/feature-or-enhancement-.md new file mode 100644 index 0000000..da71e69 --- /dev/null +++ b/easy-dataset-main/.github/ISSUE_TEMPLATE/feature-or-enhancement-.md @@ -0,0 +1,19 @@ +--- +name: 'Feature or enhancement ' +about: Suggest an idea for this project +title: '[Feature]' +labels: enhancement +assignees: '' +--- + +**你的功能请求是否与某个问题相关?请描述。** +清晰、简洁地描述一下存在的问题是什么。例如:当我[具体情况]时,我总是感到很沮丧。 + +**描述你期望的解决方案** +清晰、简洁地描述你希望实现的情况。 + +**描述你考虑过的替代方案** +清晰、简洁地描述你所考虑过的任何其他解决方案或功能。 + +**其他相关信息** +在此处添加与该功能请求相关的其他任何背景信息或截图。 diff --git a/easy-dataset-main/.github/ISSUE_TEMPLATE/question.md b/easy-dataset-main/.github/ISSUE_TEMPLATE/question.md new file mode 100644 index 0000000..fe396af --- /dev/null +++ b/easy-dataset-main/.github/ISSUE_TEMPLATE/question.md @@ -0,0 +1,40 @@ +--- +name: Question +about: Ask questions you want to know +title: '[Question]' +labels: question +assignees: '' +--- + +**注意:请务必按照此模版填写 ISSUES 信息,否则 ISSUE 将不会得到回复** + +**问题描述** +清晰、简洁地描述该问题的具体情况。 + +**桌面设备(请完善以下信息)** + +- 操作系统:[例如:、Window、MAC] +- 浏览器:[例如:谷歌浏览器(Chrome),苹果浏览器(Safari)] +- Easy Dataset 版本:[例如:1.2.2] + +**使用模型** + +- 模型提供商:例如火山引擎 +- 模型名称:例如 DeepSeek R1 + +**复现步骤** +重现该问题的操作步骤: + +1. 进入“……”页面。 +2. 点击“……”。 +3. 向下滚动到“……”。 +4. 这时会看到错误提示。 + +**预期结果** +清晰、简洁地描述你原本期望出现的情况。 + +**截图** +如果有必要,请附上截图,以便更好地说明你的问题。 + +**其他相关信息** +在此处添加关于该问题的其他任何相关背景信息。 diff --git a/easy-dataset-main/.github/PULL_REQUEST_TEMPLATE.md b/easy-dataset-main/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..bbef272 --- /dev/null +++ b/easy-dataset-main/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,12 @@ +### 变更类型- [ ] 新功能(feat) + +- [ ] 修复(fix) +- [ ] 文档(docs) +- [ ] 重构(refactor) + +### 变更描述- 简要说明修改内容(关联Issue:#123) + +### 文档更新- [ ] README.md + +- [ ] 贡献指南 +- [ ] 接口文档(如有) diff --git a/easy-dataset-main/.github/workflows/docker-build.yml b/easy-dataset-main/.github/workflows/docker-build.yml new file mode 100644 index 0000000..c1de4cd --- /dev/null +++ b/easy-dataset-main/.github/workflows/docker-build.yml @@ -0,0 +1,48 @@ +name: Build and Push Docker image on Tag + +on: + push: + tags: + - '*' + +jobs: + docker-image-release: + runs-on: ubuntu-latest + + permissions: + contents: read + packages: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/${{ github.repository_owner }}/easy-dataset + tags: | + type=ref,event=tag + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + push: true + platforms: linux/amd64,linux/arm64 + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/easy-dataset-main/.gitignore b/easy-dataset-main/.gitignore new file mode 100644 index 0000000..af76aed --- /dev/null +++ b/easy-dataset-main/.gitignore @@ -0,0 +1,22 @@ +node_modules +build +.vscode +website-local.json +ai-local.json +.next +.DS_Store +tsconfig.tsbuildinfo +mock-login-callback.ts +.env.local +/src/test/crawler +/src/test/mock +/test +/dist +/prisma/*.sqlite +.idea +!local-db/empty.txt +/local-db +prisma/local-db/db.sqlite +/local-db2 +.trae +opencode.json \ No newline at end of file diff --git a/easy-dataset-main/.husky/commit-msg b/easy-dataset-main/.husky/commit-msg new file mode 100644 index 0000000..08147d0 --- /dev/null +++ b/easy-dataset-main/.husky/commit-msg @@ -0,0 +1,3 @@ +#!/usr/bin/env sh + +npx commitlint --edit "$1" diff --git a/easy-dataset-main/.husky/pre-commit b/easy-dataset-main/.husky/pre-commit new file mode 100644 index 0000000..2312dc5 --- /dev/null +++ b/easy-dataset-main/.husky/pre-commit @@ -0,0 +1 @@ +npx lint-staged diff --git a/easy-dataset-main/.npmrc b/easy-dataset-main/.npmrc new file mode 100644 index 0000000..cb52cc8 --- /dev/null +++ b/easy-dataset-main/.npmrc @@ -0,0 +1,3 @@ +# 国内用户可使用淘宝源加速 (Chinese users can use Taobao registry for faster downloads) +# registry=https://registry.npmmirror.com +registry=https://registry.npmjs.org \ No newline at end of file diff --git a/easy-dataset-main/.prettierrc.js b/easy-dataset-main/.prettierrc.js new file mode 100644 index 0000000..af7bb09 --- /dev/null +++ b/easy-dataset-main/.prettierrc.js @@ -0,0 +1,13 @@ +module.exports = { + semi: true, + trailingComma: 'none', + singleQuote: true, + tabWidth: 2, + useTabs: false, + bracketSpacing: true, + arrowParens: 'avoid', + proseWrap: 'preserve', + jsxBracketSameLine: true, + printWidth: 120, + endOfLine: 'auto' +}; diff --git a/easy-dataset-main/.windsurfrules b/easy-dataset-main/.windsurfrules new file mode 100644 index 0000000..c4f19f0 --- /dev/null +++ b/easy-dataset-main/.windsurfrules @@ -0,0 +1,124 @@ +# Easy DataSet 项目架构设计 + +## 项目概述 + +Easy DataSet 是一个用于创建大模型微调数据集的应用程序。用户可以上传文本文件,系统会自动分割文本并生成问题,最终生成用于微调的数据集。 + +## 技术栈 + +- **前端框架**: Next.js 14 (App Router) +- **UI 框架**: Material-UI (MUI) +- **数据存储**: fs 文件系统模拟数据库 +- **开发语言**: JavaScript +- **依赖管理**: pnpm + +## 目录结构 + +``` +easy-dataset/ +├── app/ # Next.js 应用目录 +│ ├── api/ # API 路由 +│ │ └── projects/ # 项目相关 API +│ ├── projects/ # 项目相关页面 +│ │ ├── [projectId]/ # 项目详情页面 +│ └── page.js # 主页 +├── components/ # React 组件 +│ ├── home/ # 主页相关组件 +│ │ ├── HeroSection.js +│ │ ├── ProjectList.js +│ │ └── StatsCard.js +│ ├── Navbar.js # 导航栏组件 +│ └── CreateProjectDialog.js +├── lib/ # 工具库 +│ └── db/ # 数据库模块 +│ ├── base.js # 基础工具函数 +│ ├── projects.js # 项目管理 +│ ├── texts.js # 文本处理 +│ ├── datasets.js # 数据集管理 +│ └── index.js # 模块导出 +├── styles/ # 样式文件 +│ └── home.js # 主页样式 +└── local-db/ # 本地数据库目录 +``` + +## 核心模块设计 + +### 1. 数据库模块 (`lib/db/`) + +#### base.js +- 提供基础的文件操作功能 +- 确保数据库目录存在 +- 读写 JSON 文件的工具函数 + +#### projects.js +- 项目的 CRUD 操作 +- 项目配置管理 +- 项目目录结构维护 + +#### texts.js +- 文献处理功能 +- 文本片段存储和检索 +- 文件上传处理 + +#### datasets.js +- 数据集生成和管理 +- 问题列表管理 +- 标签树管理 + +### 2. 前端组件 (`components/`) + +#### Navbar.js +- 顶部导航栏 +- 项目切换 +- 模型选择 +- 主题切换 + +#### home/ 目录组件 +- HeroSection.js: 主页顶部展示区 +- ProjectList.js: 项目列表展示 +- StatsCard.js: 数据统计展示 +- CreateProjectDialog.js: 创建项目的对话框 + +### 3. 页面路由 (`app/`) + +#### 主页 (`page.js`) +- 项目列表展示 +- 创建项目入口 +- 数据统计展示 + +#### 项目详情页 (`projects/[projectId]/`) +- text-split/: 文献处理页面 +- questions/: 问题列表页面 +- datasets/: 数据集页面 +- settings/: 项目设置页面 + +#### API 路由 (`api/`) +- projects/: 项目管理 API +- texts/: 文本处理 API +- questions/: 问题生成 API +- datasets/: 数据集管理 API + +## 数据流设计 + +### 项目创建流程 +1. 用户通过主页或导航栏创建新项目 +2. 填写项目基本信息(名称、描述) +3. 系统创建项目目录和初始配置文件 +4. 重定向到项目详情页 + +### 文献处理流程 +1. 用户上传 Markdown 文件 +2. 系统保存原始文件到项目目录 +3. 调用文本分割服务,生成片段和目录结构 +4. 展示分割结果和提取的目录 + +### 问题生成流程 +1. 用户选择需要生成问题的文本片段 +2. 系统调用大模型API生成问题 +3. 保存问题到问题列表和标签树 + +### 数据集生成流程 +1. 用户选择需要生成答案的问题 +2. 系统调用大模型API生成答案 +3. 保存数据集结果 +4. 提供导出功能 diff --git a/easy-dataset-main/AGENTS.md b/easy-dataset-main/AGENTS.md new file mode 100644 index 0000000..f2857b1 --- /dev/null +++ b/easy-dataset-main/AGENTS.md @@ -0,0 +1,254 @@ +# Easy Dataset Agent 指南 + +## 项目概述 + +Easy Dataset 是一个专为大型语言模型(LLM)微调数据集创建而设计的应用程序。它提供完整的workflow,从文档处理到数据集导出,支持多种文件格式和AI模型。 + +## 技术栈 + +- **前端**: Next.js 14 (App Router), React 18, Material-UI v5 +- **后端**: Node.js, Prisma ORM, SQLite +- **AI集成**: OpenAI API, Ollama, 智谱AI, OpenRouter +- **桌面应用**: Electron +- **国际化**: i18next +- **构建工具**: npm/pnpm, Electron Builder + +## 核心架构 + +### 1. 数据流架构 + +``` +文档上传 → 文本分割 → 问题生成 → 答案生成 → 数据集导出 + ↓ ↓ ↓ ↓ ↓ +文件处理 智能分块 LLM生成 LLM生成 格式转换 +``` + +### 2. 模块结构 + +``` +lib/ +├── api/ # API接口层 +├── db/ # 数据访问层 +├── file/ # 文件处理模块 +├── llm/ # AI模型集成 +├── services/ # 业务逻辑层 +└── util/ # 工具函数 +``` + +## 开发指南 + +### 环境设置 + +```bash +# 安装依赖 +npm install + +# 数据库初始化 +npm run db:push + +# 开发模式 +npm run dev + +# 构建 +npm run build +``` + +### 代码规范 + +- 使用ES6+语法 +- 模块化开发 +- 异步操作使用async/await +- 错误处理使用try/catch +- 注释使用JSDoc格式 + +### 重要文件路径 + +- **主入口**: `app/page.js` +- **项目路由**: `app/projects/[projectId]/` +- **API路由**: `app/api/` +- **LLM核心**: `lib/llm/core/index.js` +- **任务处理**: `lib/services/tasks/` + +## 功能模块详解 + +### 1. 文档处理模块 (`lib/file/`) + +- **支持的格式**: PDF, Markdown, DOCX, EPUB, TXT +- **核心功能**: + - 智能文本分割 + - 目录结构提取 + - 自定义分隔符分块 + - 多语言支持 + +### 2. AI模型集成 (`lib/llm/`) + +- **支持的提供商**: + - OpenAI (GPT系列) + - Ollama (本地模型) + - 智谱AI (GLM系列) + - OpenRouter (多模型聚合) +- **功能特性**: + - 统一API接口 + - 流式输出支持 + - 多语言提示词 + - 错误重试机制 + +### 3. 任务系统 (`lib/services/tasks/`) + +- **任务类型**: + - 文件处理任务 + - 问题生成任务 + - 答案生成任务 + - 数据清洗任务 +- **状态管理**: 待处理、处理中、完成、失败 + +### 4. 数据管理 (`lib/db/`) + +- **数据模型**: + - Project (项目) + - Text/Chunk (文本块) + - Question (问题) + - Dataset (数据集) + - Tag (标签) + +## 常用开发任务 + +### 添加新的AI模型提供商 + +1. 在 `lib/llm/core/providers/` 创建新的provider文件 +2. 实现基础接口 (generate, streamGenerate) +3. 在 `lib/llm/core/index.js` 中注册provider +4. 更新配置文件和UI界面 + +### 添加新的文件格式支持 + +1. 在 `lib/file/file-process/` 创建格式处理器 +2. 实现内容提取和文本转换逻辑 +3. 更新文件类型检测和验证 +4. 添加相应的UI组件 + +### 自定义提示词模板 + +1. 在 `lib/llm/prompts/` 创建新的提示词文件 +2. 使用i18n支持多语言 +3. 在设置界面添加配置选项 +4. 测试不同模型的效果 + +### 添加新的导出格式 + +1. 在 `components/export/` 创建新的导出组件 +2. 实现数据格式转换逻辑 +3. 更新导出对话框界面 +4. 添加格式验证和错误处理 + +## 调试技巧 + +### 1. 数据库调试 + +```bash +# 打开Prisma Studio +npm run db:studio + +# 查看数据库文件 +sqlite3 prisma/db.sqlite +``` + +### 2. LLM API调试 + +```javascript +// 在lib/llm/core/index.js中添加日志 +console.log('LLM Request:', { provider, model, prompt }); +console.log('LLM Response:', response); +``` + +### 3. 文件处理调试 + +```javascript +// 在lib/file/中添加调试信息 +console.log('File processing:', fileName, fileType); +console.log('Text chunks:', chunks.length, chunks[0]); +``` + +## 性能优化建议 + +### 1. 文件处理优化 + +- 大文件分片处理 +- 异步并发处理 +- 内存使用监控 +- 进度条显示 + +### 2. LLM调用优化 + +- 请求缓存机制 +- 批量处理请求 +- 重试策略优化 +- 并发数控制 + +### 3. 前端性能优化 + +- 组件懒加载 +- 虚拟滚动列表 +- 图片懒加载 +- 代码分割 + +## 常见问题解决 + +### 1. 数据库相关问题 + +- **问题**: 数据库连接失败 +- **解决**: 检查prisma配置,确保数据库文件存在 + +### 2. LLM API相关问题 + +- **问题**: API调用超时 +- **解决**: 调整超时时间,检查网络连接,增加重试机制 + +### 3. 文件处理问题 + +- **问题**: 大文件处理内存溢出 +- **解决**: 使用流式处理,分块读取,增加内存限制 + +### 4. Electron打包问题 + +- **问题**: 打包后应用无法启动 +- **解决**: 检查依赖项配置,确保native模块正确打包 + +## 部署指南 + +### Docker部署 + +```bash +# 构建镜像 +docker build -t easy-dataset . + +# 运行容器 +docker run -d -p 1717:1717 -v ./local-db:/app/local-db easy-dataset +``` + +### 桌面应用构建 + +```bash +# 构建各平台安装包 +npm run electron-build-mac # macOS +npm run electron-build-win # Windows +npm run electron-build-linux # Linux +``` + +## 贡献指南 + +### 提交规范 + +- 使用conventional commits格式 +- 提交前运行lint检查 +- 更新相关文档 +- 添加测试用例 + +### 分支策略 + +- `main`: 主分支,稳定版本 +- `dev`: 开发分支,集成新功能 +- `feature/*`: 功能分支 +- `fix/*`: 修复分支 + +--- diff --git a/easy-dataset-main/ARCHITECTURE.md b/easy-dataset-main/ARCHITECTURE.md new file mode 100644 index 0000000..417ff30 --- /dev/null +++ b/easy-dataset-main/ARCHITECTURE.md @@ -0,0 +1,183 @@ +# Easy DataSet 项目架构设计 + +## 项目概述 + +Easy DataSet 是一个用于创建大模型微调数据集的应用程序。用户可以上传文本文件,系统会自动分割文本并生成问题,最终生成用于微调的数据集。 + +## 技术栈 + +- **前端框架**: Next.js 14 (App Router) +- **UI 框架**: Material-UI (MUI) +- **数据存储**: fs 文件系统模拟数据库 +- **开发语言**: JavaScript + +## 目录结构 + +``` +easy-dataset/ +├── app/ # Next.js 应用目录 +│ ├── api/ # API 路由 +│ │ └── projects/ # 项目相关 API +│ ├── projects/ # 项目相关页面 +│ │ ├── [projectId]/ # 项目详情页面 +│ └── page.js # 主页 +├── components/ # React 组件 +│ ├── home/ # 主页相关组件 +│ │ ├── HeroSection.js +│ │ ├── ProjectList.js +│ │ └── StatsCard.js +│ ├── Navbar.js # 导航栏组件 +│ └── CreateProjectDialog.js +├── lib/ # 工具库 +│ └── db/ # 数据库模块 +│ ├── base.js # 基础工具函数 +│ ├── projects.js # 项目管理 +│ ├── texts.js # 文本处理 +│ ├── datasets.js # 数据集管理 +│ └── index.js # 模块导出 +├── styles/ # 样式文件 +│ └── home.js # 主页样式 +└── local-db/ # 本地数据库目录 +``` + +## 核心模块设计 + +### 1. 数据库模块 (`lib/db/`) + +#### base.js + +- 提供基础的文件操作功能 +- 确保数据库目录存在 +- 读写 JSON 文件的工具函数 + +#### projects.js + +- 项目的 CRUD 操作 +- 项目配置管理 +- 项目目录结构维护 + +#### texts.js + +- 文献处理功能 +- 文本片段存储和检索 +- 文件上传处理 + +#### datasets.js + +- 数据集生成和管理 +- 问题列表管理 +- 标签树管理 + +### 2. 前端组件 (`components/`) + +#### Navbar.js + +- 顶部导航栏 +- 项目切换 +- 模型选择 +- 主题切换 + +#### home/ 目录组件 + +- HeroSection.js: 主页顶部展示区 +- ProjectList.js: 项目列表展示 +- StatsCard.js: 数据统计展示 +- CreateProjectDialog.js: 创建项目的对话框 + +### 3. 页面路由 (`app/`) + +#### 主页 (`page.js`) + +- 项目列表展示 +- 创建项目入口 +- 数据统计展示 + +#### 项目详情页 (`projects/[projectId]/`) + +- text-split/: 文献处理页面 +- questions/: 问题列表页面 +- datasets/: 数据集页面 +- settings/: 项目设置页面 + +#### API 路由 (`api/`) + +- projects/: 项目管理 API +- texts/: 文本处理 API +- questions/: 问题生成 API +- datasets/: 数据集管理 API + +## 数据流设计 + +### 项目创建流程 + +1. 用户通过主页或导航栏创建新项目 +2. 填写项目基本信息(名称、描述) +3. 系统创建项目目录和初始配置文件 +4. 重定向到项目详情页 + +### 文献处理流程 + +1. 用户上传 Markdown 文件 +2. 系统保存原始文件到项目目录 +3. 调用文本分割服务,生成片段和目录结构 +4. 展示分割结果和提取的目录 + +### 问题生成流程 + +1. 用户选择需要生成问题的文本片段 +2. 系统调用大模型API生成问题 +3. 保存问题到问题列表和标签树 + +### 数据集生成流程 + +1. 用户选择需要生成答案的问题 +2. 系统调用大模型API生成答案 +3. 保存数据集结果 +4. 提供导出功能 + +## 模型配置 + +支持多种大模型提供商配置: + +- Ollama +- OpenAI +- 硅基流动 +- 深度求索 +- 智谱AI + +每个提供商支持配置: + +- API 地址 +- API 密钥 +- 模型名称 + +## 未来扩展方向 + +1. 支持更多文件格式(PDF、DOC等) +2. 增加数据集质量评估功能 +3. 添加数据集版本管理 +4. 实现团队协作功能 +5. 增加更多数据集导出格式 + +## 国际化处理 + +### 技术选型 + +- **国际化库**: i18next + react-i18next +- **语言检测**: i18next-browser-languagedetector +- **支持语言**: 英文(en)、简体中文(zh-CN) + +### 目录结构 + +``` +easy-dataset/ +├── locales/ # 国际化资源目录 +│ ├── en/ # 英文翻译 +│ │ └── translation.json +│ ├── zh-CN/ # 中文翻译 +│ │ └── translation.json +│ └── pt-BR/ # 中文翻译 +│ └── translation.json +├── lib/ +│ └── i18n.js # i18next 配置 +``` diff --git a/easy-dataset-main/Dockerfile b/easy-dataset-main/Dockerfile new file mode 100644 index 0000000..b857162 --- /dev/null +++ b/easy-dataset-main/Dockerfile @@ -0,0 +1,86 @@ +# 创建包含pnpm的基础镜像 +FROM node:20-alpine AS pnpm-base +RUN npm install -g pnpm@9 + +# 构建阶段 +FROM pnpm-base AS builder +WORKDIR /app + +# 添加构建参数,用于识别目标平台 +ARG TARGETPLATFORM + +# 安装构建依赖 +RUN apk add --no-cache --virtual .build-deps \ + python3 \ + make \ + g++ \ + cairo-dev \ + pango-dev \ + jpeg-dev \ + giflib-dev \ + librsvg-dev \ + build-base \ + pixman-dev \ + pkgconfig + +# 复制依赖文件和npm配置并安装(.npmrc中可配置国内源加速) +COPY package.json pnpm-lock.yaml .npmrc ./ +RUN pnpm install + +# 复制源代码 +COPY . . + +# 根据目标平台设置Prisma二进制目标并构建应用 +RUN if [ "$TARGETPLATFORM" = "linux/arm64" ]; then \ + echo "Configuring for ARM64 platform"; \ + sed -i 's/binaryTargets = \[.*\]/binaryTargets = \["linux-musl-arm64-openssl-3.0.x"\]/' prisma/schema.prisma; \ + PRISMA_CLI_BINARY_TARGETS="linux-musl-arm64-openssl-3.0.x" pnpm build; \ + else \ + echo "Configuring for AMD64 platform (default)"; \ + sed -i 's/binaryTargets = \[.*\]/binaryTargets = \["linux-musl-openssl-3.0.x"\]/' prisma/schema.prisma; \ + PRISMA_CLI_BINARY_TARGETS="linux-musl-openssl-3.0.x" pnpm build; \ + fi + +# 构建完成后移除开发依赖,只保留生产依赖 +RUN pnpm prune --prod + +# 运行阶段 +FROM pnpm-base AS runner +WORKDIR /app + +# 只安装运行时依赖 +RUN apk add --no-cache \ + cairo \ + pango \ + jpeg \ + giflib \ + librsvg \ + pixman + +# 复制package.json和.env文件 +COPY package.json .env ./ + +# 从构建阶段复制精简后的node_modules(只包含生产依赖) +COPY --from=builder /app/node_modules ./node_modules + +# 从构建阶段复制构建产物 +COPY --from=builder /app/.next ./.next +COPY --from=builder /app/public ./public +COPY --from=builder /app/electron ./electron + +# 复制 prisma 到模板目录(用于自动初始化) +COPY --from=builder /app/prisma /app/prisma-template + +# 复制并设置 entrypoint 脚本(sed 去除 Windows 换行符 \r,防止 CRLF 导致 "no such file or directory") +COPY docker-entrypoint.sh /usr/local/bin/ +RUN sed -i 's/\r$//' /usr/local/bin/docker-entrypoint.sh && \ + chmod +x /usr/local/bin/docker-entrypoint.sh + +# 设置生产环境 +ENV NODE_ENV=production + +EXPOSE 1717 + +# 使用 entrypoint 脚本 +ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] +CMD ["pnpm", "start"] diff --git a/easy-dataset-main/LICENSE b/easy-dataset-main/LICENSE new file mode 100644 index 0000000..85fb8ec --- /dev/null +++ b/easy-dataset-main/LICENSE @@ -0,0 +1,40 @@ +GNU AFFERO GENERAL PUBLIC LICENSE +Version 3, 19 November 2007 + +Copyright (C) 2025 Easy Dataset Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see https://www.gnu.org/licenses/. + +Additional Terms for Easy Dataset: + +1. Contact Information +If you wish to use Easy Dataset under different terms, please contact the +copyright holders at: 1009903985@qq.com + +2. Branding Restrictions +You may not use the names "Easy Dataset" or "EasyDataset" to endorse or +promote products derived from this software without prior written permission. + +3. Disclaimer of Warranty +The software is provided "as is", without warranty of any kind, express or +implied, including but not limited to the warranties of merchantability, +fitness for a particular purpose and noninfringement. In no event shall the +authors or copyright holders be liable for any claim, damages or other +liability, whether in an action of contract, tort or otherwise, arising from, +out of or in connection with the software or the use or other dealings in the +software. + +4. Compliance with Laws +You are responsible for ensuring your use of the software complies with all +applicable laws, including but not limited to export control regulations. \ No newline at end of file diff --git a/easy-dataset-main/README.md b/easy-dataset-main/README.md new file mode 100644 index 0000000..2d1cb43 --- /dev/null +++ b/easy-dataset-main/README.md @@ -0,0 +1,294 @@ +
+ +![](./public//imgs/bg2.png) + +GitHub Repo stars +GitHub Downloads (all assets, all releases) +GitHub Release +AGPL 3.0 License +GitHub contributors +GitHub last commit + + arXiv:2507.04009 + + +ConardLi%2Feasy-dataset | Trendshift + +**A powerful tool for creating fine-tuning datasets for Large Language Models** + +[简体中文](./README.zh-CN.md) | [English](./README.md) | [Türkçe](./README.tr.md) + +[Features](#features) • [Quick Start](#local-run) • [Documentation](https://docs.easy-dataset.com/ed/en) • [Contributing](#contributing) • [License](#license) + +If you like this project, please give it a Star⭐️, or buy the author a coffee => [Donate](./public/imgs/aw.jpg) ❤️! + +
+ +## Overview + +Easy Dataset is an application specifically designed for building large language model (LLM) datasets. It features an intuitive interface, along with built-in powerful document parsing tools, intelligent segmentation algorithms, data cleaning and augmentation capabilities. The application can convert domain-specific documents in various formats into high-quality structured datasets, which are applicable to scenarios such as model fine-tuning, retrieval-augmented generation (RAG), and model performance evaluation. + +![](./public/imgs/arc3.png) + +## News + +🎉🎉 Easy Dataset Version 1.7.0 launches brand-new evaluation capabilities! You can effortlessly convert domain-specific documents into evaluation datasets (test sets) and automatically run multi-dimensional evaluation tasks. Additionally, it comes with a human blind test system, enabling you to easily meet needs such as vertical domain model evaluation, post-fine-tuning model performance assessment, and RAG recall rate evaluation. Tutorial: [https://www.bilibili.com/video/BV1CRrVB7Eb4/](https://www.bilibili.com/video/BV1CRrVB7Eb4/) + +## Features + +### 📄 Document Processing & Data Generation + +- **Intelligent Document Processing**: Supports PDF, Markdown, DOCX, TXT, EPUB and more formats with intelligent recognition +- **Intelligent Text Splitting**: Multiple splitting algorithms (Markdown structure, recursive separators, fixed length, code-aware chunking), with customizable visual segmentation +- **Intelligent Question Generation**: Auto-extract relevant questions from text segments, with question templates and batch generation +- **Domain Label Tree**: Intelligently builds global domain label trees based on document structure, with auto-tagging capabilities +- **Answer Generation**: Uses LLM API to generate comprehensive answers and Chain of Thought (COT), with AI optimization +- **Data Cleaning**: Intelligent text cleaning to remove noise and improve data quality + +### 🔄 Multiple Dataset Types + +- **Single-Turn QA Datasets**: Standard question-answer pairs for basic fine-tuning +- **Multi-Turn Dialogue Datasets**: Customizable roles and scenarios for conversational format +- **Image QA Datasets**: Generate visual QA data from images, with multiple import methods (directory, PDF, ZIP) +- **Data Distillation**: Generate label trees and questions directly from domain topics without uploading documents + +### 📊 Model Evaluation System + +- **Evaluation Datasets**: Generate true/false, single-choice, multiple-choice, short-answer, and open-ended questions +- **Automated Model Evaluation**: Use Judge Model to automatically evaluate model answer quality with customizable scoring rules +- **Human Blind Test (Arena)**: Double-blind comparison of two models' answers for unbiased evaluation +- **AI Quality Assessment**: Automatic quality scoring and filtering of generated datasets + +### 🛠️ Advanced Features + +- **Custom Prompts**: Project-level customization of all prompt templates (question generation, answer generation, data cleaning, etc.) +- **GA Pair Generation**: Genre-Audience pair generation to enrich data diversity +- **Task Management Center**: Background batch task processing with monitoring and interruption support +- **Resource Monitoring Dashboard**: Token consumption statistics, API call tracking, model performance analysis +- **Model Testing Playground**: Compare up to 3 models simultaneously + +### 📤 Export & Integration + +- **Multiple Export Formats**: Alpaca, ShareGPT, Multilingual-Thinking formats with JSON/JSONL file types +- **Balanced Export**: Configure export counts per tag for dataset balancing +- **LLaMA Factory Integration**: One-click LLaMA Factory configuration file generation +- **Hugging Face Upload**: Direct upload datasets to Hugging Face Hub + +### 🤖 Model Support + +- **Wide Model Compatibility**: Compatible with all LLM APIs that follow the OpenAI format +- **Multi-Provider Support**: OpenAI, Ollama (local models), Zhipu AI, Alibaba Bailian, OpenRouter, and more +- **Vision Models**: Support Gemini, Claude, etc. for PDF parsing and image QA + +### 🌐 User Experience + +- **User-Friendly Interface**: Modern, intuitive UI designed for both technical and non-technical users +- **Multi-Language Support**: Complete Chinese, English, Turkish and Portuguese language support 🇹🇷 +- **Dataset Square**: Discover and explore public dataset resources +- **Desktop Clients**: Available for Windows, macOS, and Linux + +## Quick Demo + +https://github.com/user-attachments/assets/6ddb1225-3d1b-4695-90cd-aa4cb01376a8 + +## Local Run + +### Download Client + + + + + + + + + + + + + +
+ Windows + + MacOS + + Linux +
+ + +
+ Setup.exe +
+
+ + +
+ Intel +
+
+ + +
+ M +
+
+ + +
+ AppImage +
+
+ +### Install with NPM + +1. Clone the repository: + +```bash + git clone https://github.com/ConardLi/easy-dataset.git + cd easy-dataset +``` + +2. Install dependencies: + +```bash + npm install +``` + +3. Start the development server: + +```bash + npm run build + + npm run start +``` + +4. Open your browser and visit `http://localhost:1717` + +### Using the Official Docker Image + +1. Clone the repository: + +```bash +git clone https://github.com/ConardLi/easy-dataset.git +cd easy-dataset +``` + +2. Modify the `docker-compose.yml` file: + +```yml +services: + easy-dataset: + image: ghcr.io/conardli/easy-dataset + container_name: easy-dataset + ports: + - '1717:1717' + volumes: + - ./local-db:/app/local-db + - ./prisma:/app/prisma + restart: unless-stopped +``` + +> **Note:** It is recommended to use the `local-db` and `prisma` folders in the current code repository directory as mount paths to maintain consistency with the database paths when starting via NPM. + +> **Note:** The database file will be automatically initialized on first startup, no need to manually run `npm run db:push`. + +3. Start with docker-compose: + +```bash +docker-compose up -d +``` + +4. Open a browser and visit `http://localhost:1717` + +### Building with a Local Dockerfile + +If you want to build the image yourself, use the Dockerfile in the project root directory: + +1. Clone the repository: + +```bash +git clone https://github.com/ConardLi/easy-dataset.git +cd easy-dataset +``` + +2. Build the Docker image: + +```bash +docker build -t easy-dataset . +``` + +3. Run the container: + +```bash +docker run -d \ + -p 1717:1717 \ + -v ./local-db:/app/local-db \ + -v ./prisma:/app/prisma \ + --name easy-dataset \ + easy-dataset +``` + +> **Note:** It is recommended to use the `local-db` and `prisma` folders in the current code repository directory as mount paths to maintain consistency with the database paths when starting via NPM. + +> **Note:** The database file will be automatically initialized on first startup, no need to manually run `npm run db:push`. + +4. Open a browser and visit `http://localhost:1717` + +## Documentation + +- View the demo video of this project: [Easy Dataset Demo Video](https://www.bilibili.com/video/BV1y8QpYGE57/) +- For detailed documentation on all features and APIs, visit our [Documentation Site](https://docs.easy-dataset.com/ed/en) +- View the paper of this project: [Easy Dataset: A Unified and Extensible Framework for Synthesizing LLM Fine-Tuning Data from Unstructured Documents](https://arxiv.org/abs/2507.04009v1) + +## Community Practice + +- [Complete test set generation and model evaluation with Easy Dataset](https://www.bilibili.com/video/BV1CRrVB7Eb4/) +- [Easy Dataset × LLaMA Factory: Enabling LLMs to Efficiently Learn Domain Knowledge](https://buaa-act.feishu.cn/wiki/GVzlwYcRFiR8OLkHbL6cQpYin7g) +- [Easy Dataset Practical Guide: How to Build High-Quality Datasets?](https://www.bilibili.com/video/BV1MRMnz1EGW) +- [Interpretation of Key Feature Updates in Easy Dataset](https://www.bilibili.com/video/BV1fyJhzHEb7/) +- [Foundation Models Fine-tuning Datasets: Basic Knowledge Popularization](https://docs.easy-dataset.com/zhi-shi-ke-pu) + +## Contributing + +We welcome contributions from the community! If you'd like to contribute to Easy Dataset, please follow these steps: + +1. Fork the repository +2. Create a new branch (`git checkout -b feature/amazing-feature`) +3. Make your changes +4. Commit your changes (`git commit -m 'Add some amazing feature'`) +5. Push to the branch (`git push origin feature/amazing-feature`) +6. Open a Pull Request (submit to the DEV branch) + +Please ensure that tests are appropriately updated and adhere to the existing coding style. + +## Join Discussion Group & Contact the Author + +https://docs.easy-dataset.com/geng-duo/lian-xi-wo-men + +## License + +This project is licensed under the AGPL 3.0 License - see the [LICENSE](LICENSE) file for details. + +## Citation + +If this work is helpful, please kindly cite as: + +```bibtex +@misc{miao2025easydataset, + title={Easy Dataset: A Unified and Extensible Framework for Synthesizing LLM Fine-Tuning Data from Unstructured Documents}, + author={Ziyang Miao and Qiyu Sun and Jingyuan Wang and Yuchen Gong and Yaowei Zheng and Shiqi Li and Richong Zhang}, + year={2025}, + eprint={2507.04009}, + archivePrefix={arXiv}, + primaryClass={cs.CL}, + url={https://arxiv.org/abs/2507.04009} +} +``` + +## Star History + +[![Star History Chart](https://api.star-history.com/svg?repos=ConardLi/easy-dataset&type=Date)](https://www.star-history.com/#ConardLi/easy-dataset&Date) + +
+ Built with ❤️ by ConardLi • Follow me: WeChat Official AccountBilibiliJuejinZhihuYoutube +
diff --git a/easy-dataset-main/README.tr.md b/easy-dataset-main/README.tr.md new file mode 100644 index 0000000..17087c6 --- /dev/null +++ b/easy-dataset-main/README.tr.md @@ -0,0 +1,319 @@ +
+ +![](./public//imgs/bg2.png) + +GitHub Repo stars +GitHub Downloads (all assets, all releases) +GitHub Release +AGPL 3.0 License +GitHub contributors +GitHub last commit + + arXiv:2507.04009 + + +ConardLi%2Feasy-dataset | Trendshift + +**Büyük Dil Modelleri için ince ayar veri setleri oluşturmak için güçlü bir araç** + +[简体中文](./README.zh-CN.md) | [English](./README.md) | [Türkçe](./README.tr.md) + +[Özellikler](#özellikler) • [Hızlı Başlangıç](#yerel-çalıştırma) • [Dokümantasyon](https://docs.easy-dataset.com/ed/en) • [Katkıda Bulunma](#katkıda-bulunma) • [Lisans](#lisans) + +Bu projeyi beğendiyseniz, lütfen bir Yıldız⭐️ verin veya yazara bir kahve ısmarlayın => [Bağış](./public/imgs/aw.jpg) ❤️! + +
+ +## Genel Bakış + +Easy Dataset, Büyük Dil Modelleri (LLM'ler) için özel olarak tasarlanmış ince ayar veri setleri oluşturmak için bir uygulamadır. Alana özgü dosyaları yüklemek, içeriği akıllıca bölmek, sorular oluşturmak ve model ince ayarı için yüksek kaliteli eğitim verileri üretmek için sezgisel bir arayüz sağlar. + +Easy Dataset ile alan bilgisini yapılandırılmış veri setlerine dönüştürebilir, OpenAI formatını takip eden tüm LLM API'leriyle uyumlu çalışabilir ve ince ayar sürecini basit ve verimli hale getirebilirsiniz. + +![](./public/imgs/arc3.png) + +## Özellikler + +- **Akıllı Belge İşleme**: PDF, Markdown, DOCX dahil birden fazla formatın akıllı tanınması ve işlenmesi desteği +- **Akıllı Metin Bölme**: Birden fazla akıllı metin bölme algoritması ve özelleştirilebilir görsel segmentasyon desteği +- **Akıllı Soru Üretimi**: Her metin bölümünden ilgili soruları çıkarır +- **Alan Etiketleri**: Veri setleri için global alan etiketlerini akıllıca oluşturur, küresel anlama yeteneklerine sahiptir +- **Cevap Üretimi**: Kapsamlı cevaplar ve Düşünce Zinciri (COT) oluşturmak için LLM API kullanır +- **Esnek Düzenleme**: Sürecin herhangi bir aşamasında soruları, cevapları ve veri setlerini düzenleyin +- **Çoklu Dışa Aktarma Formatları**: Veri setlerini çeşitli formatlarda (Alpaca, ShareGPT, çok dilli düşünme) ve dosya türlerinde (JSON, JSONL) dışa aktarın +- **Geniş Model Desteği**: OpenAI formatını takip eden tüm LLM API'leriyle uyumlu +- **Tam Türkçe Dil Desteği**: Tüm arayüz ve AI işlemleri için eksiksiz Türkçe çeviriler 🇹🇷 +- **Kullanıcı Dostu Arayüz**: Hem teknik hem de teknik olmayan kullanıcılar için tasarlanmış sezgisel kullanıcı arayüzü +- **Özel Sistem İstemleri**: Model yanıtlarını yönlendirmek için özel sistem istemleri ekleyin + +## Hızlı Demo + +https://github.com/user-attachments/assets/6ddb1225-3d1b-4695-90cd-aa4cb01376a8 + +## Yerel Çalıştırma + +### İstemciyi İndirin + + + + + + + + + + + + + +
+ Windows + + MacOS + + Linux +
+ + +
+ Setup.exe +
+
+ + +
+ Intel +
+
+ + +
+ M +
+
+ + +
+ AppImage +
+
+ +### NPM ile Kurulum + +```bash +npm install +npm run db:push +npm run dev +``` + +### Docker ile Kurulum + +```bash +docker-compose up -d +``` + +Ardından `http://localhost:1717` adresine gidin. + +## Desteklenen AI Sağlayıcıları + +Easy Dataset, aşağıdakiler dahil olmak üzere birden fazla AI sağlayıcısını destekler: + +- **OpenAI**: GPT-4, GPT-3.5-turbo ve diğer modeller +- **Ollama**: Yerel model çalıştırma +- **智谱AI (GLM)**: Çince modeller +- **OpenRouter**: Çoklu model aggregatör +- **Özel API Uç Noktaları**: OpenAI formatını takip eden herhangi bir API + +## Proje Yapısı + +``` +easy-dataset/ +├── app/ # Next.js uygulama yönlendiricisi +│ ├── api/ # API rotaları +│ ├── projects/ # Proje sayfaları +│ └── dataset-square/ # Veri seti galerisi +├── components/ # React bileşenleri +├── lib/ # Temel kütüphaneler +│ ├── llm/ # LLM entegrasyonu +│ ├── db/ # Veritabanı erişimi +│ ├── file/ # Dosya işleme +│ └── services/ # İş mantığı +├── locales/ # i18n çevirileri +│ ├── en/ # İngilizce +│ ├── zh-CN/ # Basitleştirilmiş Çince +│ └── tr/ # Türkçe +├── prisma/ # Veritabanı şeması +└── electron/ # Electron masaüstü uygulaması +``` + +## Kullanım Rehberi + +### 1. Proje Oluşturma + +İlk olarak, yeni bir proje oluşturun ve proje adını, açıklamasını ve diğer temel bilgileri yapılandırın. + +### 2. Dosya Yükleme + +Alana özgü belgelerinizi yükleyin. Desteklenen formatlar: + +- PDF +- Markdown (.md) +- Microsoft Word (.docx) +- EPUB +- Düz metin (.txt) + +### 3. Metin Bölme + +Dosyalar aşağıdaki yöntemlerle akıllıca bölünebilir: + +- Doğal dil işleme tabanlı semantik bölme +- Özel ayırıcılara dayalı bölme +- Karakter sayısına dayalı sabit boyutlu bölme +- Manuel görsel bölme + +### 4. Alan Etiketleri Oluşturma + +Sistem, belge içeriğine dayalı olarak otomatik olarak hiyerarşik alan etiketleri oluşturabilir ve iki seviyeyi destekler. + +### 5. Soru Üretimi + +Her metin bloğu için sistem: + +- İçeriğe dayalı alakalı sorular oluşturur +- Tür ve hedef kitle perspektifi sorgulamayı destekler +- Soru sayısını özelleştirme seçeneği sunar + +### 6. Cevap Üretimi + +Yapılandırılmış LLM API'si kullanarak: + +- Her soru için kapsamlı cevaplar oluşturur +- Düşünce Zinciri (COT) üretimini destekler +- Farklı cevap şablonları destekler + +### 7. Veri Seti Dışa Aktarma + +Veri setinizi çeşitli formatlarda dışa aktarın: + +- **Alpaca Format**: Basit talimat-takip formatı +- **ShareGPT Format**: Çok turlu konuşma formatı +- **Çok Dilli Düşünme**: COT ile genişletilmiş format +- **Özel Format**: Kendi JSON yapınızı tanımlayın + +Dışa aktarma hedefleri: + +- Yerel dosya sistemi +- Hugging Face Hub +- LLaMA Factory uyumluluğu + +## Gelişmiş Özellikler + +### Veri Damıtma + +Mevcut veri setlerinden yeni eğitim örnekleri oluşturun: + +- Soru damıtma: Mevcut soru-cevap çiftlerinden yeni sorular oluşturun +- Etiket damıtma: Otomatik etiket ve kategorizasyon oluşturma + +### Tür-Hedef Kitle (GA) Çiftleri + +Spesifik içerik stilleri ve hedef kitleler için veri setlerini uyarlayın: + +- Tür: Akademik, teknik, yaratıcı yazma, vb. +- Hedef Kitle: Yeni başlayanlar, uzmanlar, öğrenciler, vb. + +### Toplu İşlemler + +Birden fazla öğeye verimli bir şekilde işlem: + +- Toplu soru üretimi +- Toplu cevap üretimi +- Toplu veri seti dışa aktarma + +### Görev Yönetimi + +Tüm arka plan görevlerini izleyin ve yönetin: + +- Dosya işleme görevleri +- Soru üretim görevleri +- Cevap üretim görevleri +- Dışa aktarma görevleri + +## Yapılandırma + +### LLM API Yapılandırması + +Ayarlar sayfasında LLM API'nizi yapılandırın: + +1. **Sağlayıcı**: OpenAI, Ollama, 智谱AI veya özel seçin +2. **API Anahtarı**: API anahtarınızı girin (gerekirse) +3. **Model**: Kullanılacak modeli seçin +4. **Temel URL**: Özel API'ler için temel URL'yi ayarlayın + +### Görev Ayarları + +Görev yürütme parametrelerini özelleştirin: + +- Soru üretimi için eşzamanlılık +- Cevap üretimi için eşzamanlılık +- Varsayılan soru sayısı +- Varsayılan cevap şablonu + +### Özel İstemler + +Her görev türü için özel sistem istemleri ekleyin: + +- Soru üretim istemi +- Cevap üretim istemi +- Etiket üretim istemi +- Damıtma istemi + +## Katkıda Bulunma + +Katkılara hoş geldiniz! Lütfen şu adımları izleyin: + +1. Repo'yu fork edin +2. Bir özellik dalı oluşturun (`git checkout -b feature/amazing-feature`) +3. Değişikliklerinizi commit edin (`git commit -m 'Add some amazing feature'`) +4. Dala push edin (`git push origin feature/amazing-feature`) +5. Bir Pull Request açın + +## Lisans + +Bu proje AGPL-3.0 Lisansı altında lisanslanmıştır. Detaylar için [LICENSE](./LICENSE) dosyasına bakın. + +## İletişim + +- **GitHub Issues**: [Yeni bir sorun oluşturun](https://github.com/ConardLi/easy-dataset/issues) +- **Email**: lhj19950927@gmail.com +- **WeChat Grubu**: README'deki QR koduna bakın + +## Alıntı + +Bu aracı araştırmanızda kullanırsanız, lütfen şu şekilde alıntı yapın: + +```bibtex +@misc{easy-dataset-2025, + title={Easy Dataset: A Tool for Creating Fine-tuning Datasets for Large Language Models}, + author={Conard Li}, + year={2025}, + publisher={GitHub}, + howpublished={\url{https://github.com/ConardLi/easy-dataset}} +} +``` + +## Teşekkürler + +Bu proje aşağıdaki harika açık kaynak projelerini kullanır: + +- [Next.js](https://nextjs.org/) +- [React](https://reactjs.org/) +- [Material-UI](https://mui.com/) +- [Prisma](https://www.prisma.io/) +- [Electron](https://www.electronjs.org/) + +--- + +
+⭐️ Bu projeyi beğendiyseniz, lütfen bir yıldız verin! ⭐️ +
diff --git a/easy-dataset-main/README.zh-CN.md b/easy-dataset-main/README.zh-CN.md new file mode 100644 index 0000000..e118b62 --- /dev/null +++ b/easy-dataset-main/README.zh-CN.md @@ -0,0 +1,300 @@ +
+ +![](./public//imgs/bg2.png) + +GitHub Repo stars +GitHub Downloads (all assets, all releases) +GitHub Release +AGPL 3.0 License +GitHub contributors +GitHub last commit + + arXiv:2507.04009 + + +ConardLi%2Feasy-dataset | Trendshift + +**一个强大的大型语言模型微调数据集创建工具** + +[简体中文](./README.zh-CN.md) | [English](./README.md) + +[功能特点](#功能特点) • [快速开始](#本地运行) • [使用文档](https://docs.easy-dataset.com/) • [贡献](#贡献) • [许可证](#许可证) + +如果喜欢本项目,请给本项目留下 Star⭐️,或者请作者喝杯咖啡呀 => [打赏作者](./public/imgs/aw.jpg) ❤️! + +
+ +## 概述 + +Easy Dataset 是一个专为创建大型语言模型数据集而设计的应用程序。它提供了直观的界面,内置了强大的文档解析工具、智能分割算法、数据清洗和数据增强能力,可以将各种格式的领域文献转化为高质量结构化数据集,可用于模型微调、RAG、模型效果评估等场景。 + +![Easy Dataset 产品架构图](./public/imgs/arc3.png) + +## 新闻 + +🎉🎉 Easy Dataset 1.7.0 版本上线全新的评估能力,你可以轻松将领域文献转换为评估数据集(测试集),并且可以自动执行多维度评估任务,另外还配备人工盲测系统,可以轻松助你完成垂直领域模型评估、模型微调后效果评估、RAG 召回率评估等需求,使用教程: [https://www.bilibili.com/video/BV1CRrVB7Eb4/](https://www.bilibili.com/video/BV1CRrVB7Eb4/) + +## 功能特点 + +### 📄 文档处理与数据生成 + +- **智能文档处理**:支持 PDF、Markdown、DOCX、TXT、EPUB 等多种格式智能识别和处理 +- **智能文本分割**:支持多种智能文本分割算法(Markdown 结构、递归分隔符、固定长度、代码智能分块等),支持自定义可视化分段 +- **智能问题生成**:从每个文本片段中自动提取相关问题,支持问题模板和批量生成 +- **领域标签树**:基于文档目录智能构建全局领域标签树,具备全局理解和自动打标能力 +- **答案生成**:使用 LLM API 为每个问题生成全面的答案和思维链(COT),支持 AI 智能优化 +- **数据清洗**:智能清洗文本块内容,去除噪音数据,提升数据质量 + +### 🔄 多种数据集类型 + +- **单轮问答数据集**:标准的问答对格式,适合基础微调 +- **多轮对话数据集**:支持自定义角色和场景的多轮对话格式 +- **图片问答数据集**:基于图片生成视觉问答数据,支持多种导入方式(目录、PDF、压缩包) +- **数据蒸馏**:无需上传文档,直接从领域主题自动生成标签树和问题 + +### 📊 模型评估体系 + +- **评估数据集**:支持生成判断题、单选题、多选题、简答题、开放题等多种题型的评估测试集 +- **模型自动评估**:使用教师模型(Judge Model)自动评估模型回答质量,支持自定义评分规则 +- **人工盲测 (Arena)**:双盲对比两个模型的回答质量,消除偏见进行公正评判 +- **AI 质量评估**:对生成的数据集进行自动质量评分和筛选 + +### 🛠️ 高级功能 + +- **自定义提示词**:项目级自定义各类提示词模板(问题生成、答案生成、数据清洗等) +- **GA 组合生成**:文体-受众对生成,丰富数据多样性 +- **任务管理中心**:后台批量任务处理,支持任务监控和中断 +- **资源监控看板**:Token 消耗统计、调用次数追踪、模型性能分析 +- **模型测试 Playground**:支持最多 3 个模型同时对比测试 + +### 📤 导出与集成 + +- **多种导出格式**:支持 Alpaca、ShareGPT、Multilingual-Thinking 等格式,JSON/JSONL 文件类型 +- **平衡导出**:按标签配置导出数量,实现数据集均衡 +- **LLaMA Factory 集成**:一键生成 LLaMA Factory 配置文件 +- **Hugging Face 上传**:直接将数据集上传至 Hugging Face Hub + +### 🤖 模型支持 + +- **广泛的模型兼容**:兼容所有遵循 OpenAI 格式的 LLM API +- **多提供商支持**:OpenAI、Ollama(本地模型)、智谱 AI、阿里百炼、OpenRouter 等 +- **视觉模型**:支持 Gemini、Claude 等视觉模型用于 PDF 解析和图片问答 + +### 🌐 用户体验 + +- **用户友好界面**:为技术和非技术用户设计的现代化直观 UI +- **多语言支持**:完整的中英文界面支持 +- **数据集广场**:发现和探索各种公开数据集资源 +- **桌面客户端**:提供 Windows、macOS、Linux 桌面应用 + +## 快速演示 + +https://github.com/user-attachments/assets/6ddb1225-3d1b-4695-90cd-aa4cb01376a8 + +## 本地运行 + +### 下载客户端 + + + + + + + + + + + + + +
+ Windows + + MacOS + + Linux +
+ + +
+ Setup.exe +
+
+ + +
+ Intel +
+
+ + +
+ M +
+
+ + +
+ AppImage +
+
+ +### 使用 NPM 安装 + +1. 克隆仓库: + +```bash + git clone https://github.com/ConardLi/easy-dataset.git + cd easy-dataset +``` + +2. 安装依赖: + +```bash + npm install +``` + +3. 启动开发服务器: + +```bash + npm run build + + npm run start +``` + +4. 打开浏览器并访问 `http://localhost:1717` + +### 使用官方 Docker 镜像 + +1. 克隆仓库: + +```bash +git clone https://github.com/ConardLi/easy-dataset.git +cd easy-dataset +``` + +2. 更改 `docker-compose.yml` 文件: + +```yml +services: + easy-dataset: + image: ghcr.io/conardli/easy-dataset + container_name: easy-dataset + ports: + - '1717:1717' + volumes: + - ./local-db:/app/local-db + - ./prisma:/app/prisma + restart: unless-stopped +``` + +> **注意:** 建议直接使用当前代码仓库目录下的 `local-db` 和 `prisma` 文件夹作为挂载路径,这样可以和 NPM 启动时的数据库路径保持一致。 + +> **注意:** 数据库文件会在首次启动时自动初始化,无需手动执行 `npm run db:push`。 + +3. 使用 docker-compose 启动 + +```bash +docker-compose up -d +``` + +4. 打开浏览器并访问 `http://localhost:1717` + +### 使用本地 Dockerfile 构建 + +如果你想自行构建镜像,可以使用项目根目录中的 Dockerfile: + +1. 克隆仓库: + +```bash +git clone https://github.com/ConardLi/easy-dataset.git +cd easy-dataset +``` + +2. 构建 Docker 镜像: + +```bash +docker build -t easy-dataset . +``` + +3. 运行容器: + +```bash +docker run -d \ + -p 1717:1717 \ + -v ./local-db:/app/local-db \ + -v ./prisma:/app/prisma \ + --name easy-dataset \ + easy-dataset +``` + +> **注意:** 建议直接使用当前代码仓库目录下的 `local-db` 和 `prisma` 文件夹作为挂载路径,这样可以和 NPM 启动时的数据库路径保持一致。 + +> **注意:** 数据库文件会在首次启动时自动初始化,无需手动执行 `npm run db:push`。 + +4. 打开浏览器,访问 `http://localhost:1717` + +## 文档 + +- 有关所有功能和 API 的详细文档,请访问我们的 [文档站点](https://docs.easy-dataset.com/) +- 查看本项目的演示视频:[Easy Dataset 演示视频](https://www.bilibili.com/video/BV1y8QpYGE57/) +- 查看本项目的论文:[Easy Dataset: A Unified and Extensible Framework for Synthesizing LLM Fine-Tuning Data from Unstructured Documents](https://arxiv.org/abs/2507.04009v1) + +## 社区教程 + +- [使用 Easy Dataset 完成测试集生成和模型评估](https://www.bilibili.com/video/BV1CRrVB7Eb4/) +- [Easy Dataset × LLaMA Factory: 让大模型高效学习领域知识](https://buaa-act.feishu.cn/wiki/KY9xwTGs1iqHrRkjXBwcZP9WnL9) +- [Easy Dataset 使用实战: 如何构建高质量数据集?](https://www.bilibili.com/video/BV1MRMnz1EGW) +- [Easy Dataset 1.4 重点功能更新解读](https://www.bilibili.com/video/BV1fyJhzHEb7/) +- [Easy Dataset 1.6 重点功能更新解读](https://www.bilibili.com/video/BV1Rq1hBtEJa/) +- [大模型微调数据集: 基础知识科普](https://docs.easy-dataset.com/zhi-shi-ke-pu) +- [实战案例1:生成汽车图片识别数据集](https://docs.easy-dataset.com/bo-ke/shi-zhan-an-li/an-li-1-sheng-cheng-qi-che-tu-pian-shi-bie-shu-ju-ji) +- [实战案例2:评论情感分类数据集](https://docs.easy-dataset.com/bo-ke/shi-zhan-an-li/an-li-2-ping-lun-qing-gan-fen-lei-shu-ju-ji) +- [实战案例3:物理学多轮对话数据集](https://docs.easy-dataset.com/bo-ke/shi-zhan-an-li/an-li-3-wu-li-xue-duo-lun-dui-hua-shu-ju-ji) +- [实战案例4:AI 智能体安全数据集](https://docs.easy-dataset.com/bo-ke/shi-zhan-an-li/an-li-4ai-zhi-neng-ti-an-quan-shu-ju-ji) +- [实战案例5:从图文 PPT 中提取数据集](https://docs.easy-dataset.com/bo-ke/shi-zhan-an-li/an-li-5-cong-tu-wen-ppt-zhong-ti-qu-shu-ju-ji) + +## 贡献 + +我们欢迎社区的贡献!如果您想为 Easy Dataset 做出贡献,请按照以下步骤操作: + +1. Fork 仓库 +2. 创建新分支(`git checkout -b feature/amazing-feature`) +3. 进行更改 +4. 提交更改(`git commit -m '添加一些惊人的功能'`) +5. 推送到分支(`git push origin feature/amazing-feature`) +6. 打开 Pull Request(提交至 DEV 分支) + +请确保适当更新测试并遵守现有的编码风格。 + +## 加交流群 & 联系作者 + +https://docs.easy-dataset.com/geng-duo/lian-xi-wo-men + +## 许可证 + +本项目采用 AGPL 3.0 许可证 - 有关详细信息,请参阅 [LICENSE](LICENSE) 文件。 + +## 引用 + +如果您觉得此项目有帮助,请考虑以下列格式引用 + +```bibtex +@misc{miao2025easydataset, + title={Easy Dataset: A Unified and Extensible Framework for Synthesizing LLM Fine-Tuning Data from Unstructured Documents}, + author={Ziyang Miao and Qiyu Sun and Jingyuan Wang and Yuchen Gong and Yaowei Zheng and Shiqi Li and Richong Zhang}, + year={2025}, + eprint={2507.04009}, + archivePrefix={arXiv}, + primaryClass={cs.CL}, + url={https://arxiv.org/abs/2507.04009} +} +``` + +## Star History + +[![Star History Chart](https://api.star-history.com/svg?repos=ConardLi/easy-dataset&type=Date)](https://www.star-history.com/#ConardLi/easy-dataset&Date) + +
+ ConardLi 用 ❤️ 构建 • 关注我:公众号B站掘金知乎Youtube +
diff --git a/easy-dataset-main/app/api/check-update/route.js b/easy-dataset-main/app/api/check-update/route.js new file mode 100644 index 0000000..891733d --- /dev/null +++ b/easy-dataset-main/app/api/check-update/route.js @@ -0,0 +1,86 @@ +import { NextResponse } from 'next/server'; +import path from 'path'; +import fs from 'fs'; + +// Get current version +function getCurrentVersion() { + try { + const packageJsonPath = path.join(process.cwd(), 'package.json'); + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + return packageJson.version; + } catch (error) { + console.error('Failed to read version from package.json:', String(error)); + return '1.0.0'; + } +} + +// Get latest version from GitHub +async function getLatestVersion() { + try { + const owner = 'ConardLi'; + const repo = 'easy-dataset'; + const response = await fetch(`https://api.github.com/repos/${owner}/${repo}/releases/latest`); + + if (!response.ok) { + throw new Error(`GitHub API request failed: ${response.status}`); + } + + const data = await response.json(); + return data.tag_name.replace('v', ''); + } catch (error) { + console.error('Failed to fetch latest version:', String(error)); + return null; + } +} + +// Check for updates +export async function GET() { + try { + const currentVersion = getCurrentVersion(); + const latestVersion = await getLatestVersion(); + + if (!latestVersion) { + return NextResponse.json({ + hasUpdate: false, + currentVersion, + latestVersion: null, + error: 'Failed to fetch latest version' + }); + } + + // Simple semver-like comparison + const hasUpdate = compareVersions(latestVersion, currentVersion) > 0; + + return NextResponse.json({ + hasUpdate, + currentVersion, + latestVersion, + releaseUrl: hasUpdate ? `https://github.com/ConardLi/easy-dataset/releases/tag/v${latestVersion}` : null + }); + } catch (error) { + console.error('Failed to check for updates:', String(error)); + return NextResponse.json( + { + hasUpdate: false, + error: 'Failed to check for updates' + }, + { status: 500 } + ); + } +} + +// Simple version comparison +function compareVersions(a, b) { + const partsA = a.split('.').map(Number); + const partsB = b.split('.').map(Number); + + for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) { + const numA = i < partsA.length ? partsA[i] : 0; + const numB = i < partsB.length ? partsB[i] : 0; + + if (numA > numB) return 1; + if (numA < numB) return -1; + } + + return 0; +} diff --git a/easy-dataset-main/app/api/llm/fetch-models/route.js b/easy-dataset-main/app/api/llm/fetch-models/route.js new file mode 100644 index 0000000..48addb2 --- /dev/null +++ b/easy-dataset-main/app/api/llm/fetch-models/route.js @@ -0,0 +1,75 @@ +import { NextResponse } from 'next/server'; +import axios from 'axios'; + +// Fetch model list from provider +export async function POST(request) { + try { + const { endpoint, providerId, apiKey } = await request.json(); + + if (!endpoint) { + return NextResponse.json({ error: 'Missing required parameter: endpoint' }, { status: 400 }); + } + + let url = endpoint.replace(/\/$/, ''); // Remove trailing slash + + // Handle Ollama endpoint + if (providerId === 'ollama') { + // Remove possible /v1 or other version suffix + url = url.replace(/\/v\d+$/, ''); + + // Append /api if missing + if (!url.includes('/api')) { + url += '/api'; + } + url += '/tags'; + } else { + url += '/models'; + } + + const headers = {}; + if (apiKey) { + headers.Authorization = `Bearer ${apiKey}`; + } + + const response = await axios.get(url, { headers }); + + // Format response per provider + let formattedModels = []; + if (providerId === 'ollama') { + // Ollama /api/tags format: { models: [{ name: 'model-name', ... }] } + if (response.data.models && Array.isArray(response.data.models)) { + formattedModels = response.data.models.map(item => ({ + modelId: item.name, + modelName: item.name, + providerId + })); + } + } else { + // Default handling (OpenAI-compatible) + if (response.data.data && Array.isArray(response.data.data)) { + formattedModels = response.data.data.map(item => ({ + modelId: item.id, + modelName: item.id, + providerId + })); + } + } + + return NextResponse.json(formattedModels); + } catch (error) { + console.error('Failed to fetch model list:', String(error)); + + // Handle known error shapes + if (error.response) { + if (error.response.status === 401) { + return NextResponse.json({ error: 'Invalid API key' }, { status: 401 }); + } + return NextResponse.json( + { error: `Failed to fetch model list: ${error.response.statusText}` }, + { status: error.response.status } + ); + } + + return NextResponse.json({ error: `Failed to fetch model list: ${error.message}` }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/llm/model/route.js b/easy-dataset-main/app/api/llm/model/route.js new file mode 100644 index 0000000..4ce5f9d --- /dev/null +++ b/easy-dataset-main/app/api/llm/model/route.js @@ -0,0 +1,39 @@ +import { NextResponse } from 'next/server'; +import { getLlmModelsByProviderId } from '@/lib/db/llm-models'; + +// Get LLM models +export async function GET(request) { + try { + const searchParams = request.nextUrl.searchParams; + let providerId = searchParams.get('providerId'); + if (!providerId) { + return NextResponse.json({ error: 'Invalid parameters' }, { status: 400 }); + } + const models = await getLlmModelsByProviderId(providerId); + if (!models) { + return NextResponse.json({ error: 'LLM provider not found' }, { status: 404 }); + } + return NextResponse.json(models); + } catch (error) { + console.error('Database query error:', String(error)); + return NextResponse.json({ error: 'Database query failed' }, { status: 500 }); + } +} + +// Sync latest model list +export async function POST(request) { + try { + const { newModels, providerId } = await request.json(); + const models = await getLlmModelsByProviderId(providerId); + const existingModelIds = models.map(model => model.modelId); + const diffModels = newModels.filter(item => !existingModelIds.includes(item.modelId)); + if (diffModels.length > 0) { + // return NextResponse.json(await createLlmModels(diffModels)); + return NextResponse.json({ message: 'No new models to insert' }, { status: 200 }); + } else { + return NextResponse.json({ message: 'No new models to insert' }, { status: 200 }); + } + } catch (error) { + return NextResponse.json({ error: 'Database insert failed' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/llm/ollama/models/route.js b/easy-dataset-main/app/api/llm/ollama/models/route.js new file mode 100644 index 0000000..348f971 --- /dev/null +++ b/easy-dataset-main/app/api/llm/ollama/models/route.js @@ -0,0 +1,26 @@ +import { NextResponse } from 'next/server'; + +const OllamaClient = require('@/lib/llm/core/providers/ollama'); + +// Force dynamic route to prevent static generation +export const dynamic = 'force-dynamic'; + +export async function GET(request) { + try { + // Read host and port from query params + const { searchParams } = new URL(request.url); + const host = searchParams.get('host') || '127.0.0.1'; + const port = searchParams.get('port') || '11434'; + + // Create Ollama API client + const ollama = new OllamaClient({ + endpoint: `http://${host}:${port}/api` + }); + // Fetch model list + const models = await ollama.getModels(); + return NextResponse.json(models); + } catch (error) { + // console.error('fetch Ollama models error:', error); + return NextResponse.json({ error: 'fetch Models failed' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/llm/providers/route.js b/easy-dataset-main/app/api/llm/providers/route.js new file mode 100644 index 0000000..6dddead --- /dev/null +++ b/easy-dataset-main/app/api/llm/providers/route.js @@ -0,0 +1,14 @@ +import { NextResponse } from 'next/server'; +import { getLlmProviders } from '@/lib/db/llm-providers'; +import { sortProvidersByPriority } from '@/lib/util/providerLogo'; + +// Get LLM provider data +export async function GET() { + try { + const result = await getLlmProviders(); + return NextResponse.json(sortProvidersByPriority(result, item => item.id)); + } catch (error) { + console.error('Database query error:', String(error)); + return NextResponse.json({ error: 'Database query failed' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/monitoring/logs/route.js b/easy-dataset-main/app/api/monitoring/logs/route.js new file mode 100644 index 0000000..660bfc5 --- /dev/null +++ b/easy-dataset-main/app/api/monitoring/logs/route.js @@ -0,0 +1,107 @@ +import { NextResponse } from 'next/server'; +import { db } from '@/lib/db'; + +export const dynamic = 'force-dynamic'; + +export async function GET(request) { + try { + const { searchParams } = new URL(request.url); + const timeRange = searchParams.get('timeRange') || '7d'; + const projectId = searchParams.get('projectId'); + const provider = searchParams.get('provider'); + const status = searchParams.get('status'); + const page = parseInt(searchParams.get('page') || '1', 10); + const pageSize = parseInt(searchParams.get('pageSize') || '10', 10); + const searchTerm = searchParams.get('search') || ''; + + let startDate = new Date(); + + if (timeRange === '24h') { + startDate.setHours(startDate.getHours() - 24); + } else if (timeRange === '30d') { + startDate.setDate(startDate.getDate() - 30); + } else { + startDate.setDate(startDate.getDate() - 7); + } + + const where = { + createAt: { + gte: startDate + } + }; + + if (projectId && projectId !== 'all') { + where.projectId = projectId; + } + if (provider && provider !== 'all') { + where.provider = provider; + } + if (status && status !== 'all') { + where.status = status; + } + + if (searchTerm) { + where.OR = [{ model: { contains: searchTerm } }, { errorMessage: { contains: searchTerm } }]; + } + + const total = await db.llmUsageLogs.count({ where }); + const logs = await db.llmUsageLogs.findMany({ + where, + select: { + id: true, + projectId: true, + provider: true, + model: true, + inputTokens: true, + outputTokens: true, + totalTokens: true, + latency: true, + status: true, + errorMessage: true, + createAt: true + }, + orderBy: { + createAt: 'desc' + }, + skip: (page - 1) * pageSize, + take: pageSize + }); + + const projectIds = [...new Set(logs.map(log => log.projectId))]; + const projects = await db.projects.findMany({ + where: { id: { in: projectIds } }, + select: { id: true, name: true } + }); + const projectMap = projects.reduce((acc, p) => { + acc[p.id] = p.name; + return acc; + }, {}); + + const details = logs.map(log => ({ + id: log.id, + projectId: log.projectId, + projectName: projectMap[log.projectId] || 'Unknown Project', + provider: log.provider, + model: log.model, + status: log.status, + failureReason: log.errorMessage, + inputTokens: log.inputTokens, + outputTokens: log.outputTokens, + totalTokens: log.totalTokens, + calls: 1, // Single record + avgLatency: log.status === 'SUCCESS' ? (log.latency / 1000).toFixed(2) + 's' : '-', + createAt: log.createAt + })); + + return NextResponse.json({ + details, + total, + page, + pageSize, + totalPages: Math.ceil(total / pageSize) + }); + } catch (error) { + console.error('Failed to fetch monitoring logs:', error); + return NextResponse.json({ error: error.message }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/monitoring/stats/route.js b/easy-dataset-main/app/api/monitoring/stats/route.js new file mode 100644 index 0000000..f8d4619 --- /dev/null +++ b/easy-dataset-main/app/api/monitoring/stats/route.js @@ -0,0 +1,188 @@ +import { NextResponse } from 'next/server'; +import { db } from '@/lib/db'; + +export const dynamic = 'force-dynamic'; + +export async function GET(request) { + try { + const { searchParams } = new URL(request.url); + const timeRange = searchParams.get('timeRange') || '7d'; // 24h, 7d, 30d + const projectId = searchParams.get('projectId'); + const provider = searchParams.get('provider'); + const status = searchParams.get('status'); + + let startDate = new Date(); + + if (timeRange === '24h') { + startDate.setHours(startDate.getHours() - 24); + } else if (timeRange === '30d') { + startDate.setDate(startDate.getDate() - 30); + } else { + startDate.setDate(startDate.getDate() - 7); + } + + const where = { + createAt: { + gte: startDate + } + }; + + if (projectId && projectId !== 'all') { + where.projectId = projectId; + } + if (provider && provider !== 'all') { + where.provider = provider; + } + if (status && status !== 'all') { + where.status = status; + } + + // 1. Fetch data for aggregation + // Note: Prisma aggregation can be slow on very large datasets. If needed, optimize with pre-aggregated tables. + const logs = await db.llmUsageLogs.findMany({ + where, + select: { + id: true, + projectId: true, + provider: true, + model: true, + inputTokens: true, + outputTokens: true, + totalTokens: true, + latency: true, + status: true, + errorMessage: true, + createAt: true, + dateString: true + }, + orderBy: { + createAt: 'desc' + } + }); + + // Build project name map + const projects = await db.projects.findMany({ + select: { id: true, name: true } + }); + const projectMap = projects.reduce((acc, p) => { + acc[p.id] = p.name; + return acc; + }, {}); + + // 2. Process and aggregate + const summary = { + totalTokens: 0, + inputTokens: 0, + outputTokens: 0, + totalCalls: logs.length, + successCalls: 0, + failedCalls: 0, + totalLatency: 0, + avgLatency: 0 + }; + + const trendMap = {}; + const modelStats = {}; + const detailedStatsMap = {}; // Key: projectId-model-status-errorMessage + + logs.forEach(log => { + // Summary + summary.totalTokens += log.totalTokens; + summary.inputTokens += log.inputTokens; + summary.outputTokens += log.outputTokens; + + if (log.status === 'SUCCESS') { + summary.successCalls++; + summary.totalLatency += log.latency; + } else { + summary.failedCalls++; + } + + // Trend (by day or hour) + let timeKey; + if (timeRange === '24h') { + const date = new Date(log.createAt); + timeKey = `${String(date.getHours()).padStart(2, '0')}:00`; + } else { + timeKey = log.dateString.slice(5); // MM-DD + } + + if (!trendMap[timeKey]) { + trendMap[timeKey] = { name: timeKey, input: 0, output: 0 }; + } + trendMap[timeKey].input += log.inputTokens; + trendMap[timeKey].output += log.outputTokens; + + // Model Distribution + const modelKey = log.model; + if (!modelStats[modelKey]) { + modelStats[modelKey] = { name: modelKey, value: 0 }; + } + modelStats[modelKey].value += log.totalTokens; + + // Detailed Table Aggregation + // Key: projectId + model + status + (errorMessage || '') + const errorKey = log.errorMessage || ''; + const detailKey = `${log.projectId}|${log.model}|${log.status}|${errorKey}`; + + if (!detailedStatsMap[detailKey]) { + detailedStatsMap[detailKey] = { + projectId: log.projectId, + projectName: projectMap[log.projectId] || 'Unknown Project', + provider: log.provider, + model: log.model, + status: log.status, + failureReason: log.errorMessage, + inputTokens: 0, + outputTokens: 0, + totalTokens: 0, + calls: 0, + totalLatency: 0 + }; + } + const detailItem = detailedStatsMap[detailKey]; + detailItem.inputTokens += log.inputTokens; + detailItem.outputTokens += log.outputTokens; + detailItem.totalTokens += log.totalTokens; + detailItem.calls += 1; + if (log.status === 'SUCCESS') { + detailItem.totalLatency += log.latency; + } + }); + + // Calculate averages + if (summary.successCalls > 0) { + summary.avgLatency = Math.round(summary.totalLatency / summary.successCalls); + } + summary.avgTokensPerCall = summary.totalCalls > 0 ? Math.round(summary.totalTokens / summary.totalCalls) : 0; + summary.failureRate = summary.totalCalls > 0 ? summary.failedCalls / summary.totalCalls : 0; + + // Format chart data + const trend = Object.values(trendMap).sort((a, b) => { + // Simple sorting; for production use, consider stricter time ordering. + return a.name.localeCompare(b.name); + }); + + const modelDistribution = Object.values(modelStats).sort((a, b) => b.value - a.value); + + // Format detailed table data + const details = Object.values(detailedStatsMap) + .map(item => ({ + ...item, + avgLatency: + item.status === 'SUCCESS' && item.calls > 0 ? (item.totalLatency / item.calls / 1000).toFixed(2) + 's' : '-' + })) + .sort((a, b) => b.totalTokens - a.totalTokens); // Default sorting by token usage + + return NextResponse.json({ + summary, + trend, + modelDistribution, + details, + projects + }); + } catch (error) { + console.error('Failed to fetch monitoring stats:', error); + return NextResponse.json({ error: error.message }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/monitoring/summary/route.js b/easy-dataset-main/app/api/monitoring/summary/route.js new file mode 100644 index 0000000..2987743 --- /dev/null +++ b/easy-dataset-main/app/api/monitoring/summary/route.js @@ -0,0 +1,132 @@ +import { NextResponse } from 'next/server'; +import { db } from '@/lib/db'; + +export const dynamic = 'force-dynamic'; + +export async function GET(request) { + try { + const { searchParams } = new URL(request.url); + const timeRange = searchParams.get('timeRange') || '7d'; + const projectId = searchParams.get('projectId'); + const provider = searchParams.get('provider'); + const status = searchParams.get('status'); + + let startDate = new Date(); + + if (timeRange === '24h') { + startDate.setHours(startDate.getHours() - 24); + } else if (timeRange === '30d') { + startDate.setDate(startDate.getDate() - 30); + } else { + startDate.setDate(startDate.getDate() - 7); + } + + const where = { + createAt: { + gte: startDate + } + }; + + if (projectId && projectId !== 'all') { + where.projectId = projectId; + } + if (provider && provider !== 'all') { + where.provider = provider; + } + if (status && status !== 'all') { + where.status = status; + } + + const logs = await db.llmUsageLogs.findMany({ + where, + select: { + inputTokens: true, + outputTokens: true, + totalTokens: true, + latency: true, + status: true, + createAt: true, + dateString: true, + model: true + } + }); + + const summary = { + totalTokens: 0, + inputTokens: 0, + outputTokens: 0, + totalCalls: logs.length, + successCalls: 0, + failedCalls: 0, + totalLatency: 0, + avgLatency: 0 + }; + + const trendMap = {}; + const modelStats = {}; + + logs.forEach(log => { + summary.totalTokens += log.totalTokens; + summary.inputTokens += log.inputTokens; + summary.outputTokens += log.outputTokens; + + if (log.status === 'SUCCESS') { + summary.successCalls++; + summary.totalLatency += log.latency; + } else { + summary.failedCalls++; + } + + let timeKey; + if (timeRange === '24h') { + const date = new Date(log.createAt); + timeKey = `${String(date.getHours()).padStart(2, '0')}:00`; + } else { + timeKey = log.dateString.slice(5); + } + + if (!trendMap[timeKey]) { + trendMap[timeKey] = { name: timeKey, input: 0, output: 0 }; + } + trendMap[timeKey].input += log.inputTokens; + trendMap[timeKey].output += log.outputTokens; + + const modelKey = log.model; + if (!modelStats[modelKey]) { + modelStats[modelKey] = { name: modelKey, value: 0 }; + } + modelStats[modelKey].value += log.totalTokens; + }); + + if (summary.successCalls > 0) { + summary.avgLatency = Math.round(summary.totalLatency / summary.successCalls); + } + summary.avgTokensPerCall = summary.totalCalls > 0 ? Math.round(summary.totalTokens / summary.totalCalls) : 0; + summary.failureRate = summary.totalCalls > 0 ? summary.failedCalls / summary.totalCalls : 0; + + const trend = Object.values(trendMap).sort((a, b) => a.name.localeCompare(b.name)); + const modelDistribution = Object.values(modelStats).sort((a, b) => b.value - a.value); + + const projects = await db.projects.findMany({ + select: { id: true, name: true }, + orderBy: { createAt: 'desc' } + }); + + const allLogs = await db.llmUsageLogs.findMany({ + select: { provider: true }, + distinct: ['provider'] + }); + const providers = allLogs.map(log => log.provider).filter(Boolean); + + return NextResponse.json({ + summary, + trend, + modelDistribution, + projects, + providers + }); + } catch (error) { + console.error('Failed to fetch monitoring summary:', error); + return NextResponse.json({ error: error.message }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/batch-add-manual-ga/route.js b/easy-dataset-main/app/api/projects/[projectId]/batch-add-manual-ga/route.js new file mode 100644 index 0000000..1b945a9 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/batch-add-manual-ga/route.js @@ -0,0 +1,176 @@ +import { NextResponse } from 'next/server'; +import { getUploadFileInfoById } from '@/lib/db/upload-files'; +import { createGaPairs, getGaPairsByFileId } from '@/lib/db/ga-pairs'; + +/** + * 批量手动添加 GA 对到多个文件 + */ +export async function POST(request, { params }) { + try { + const { projectId } = params; + const body = await request.json(); + + if (!projectId) { + return NextResponse.json({ error: 'Project ID is required' }, { status: 400 }); + } + + const { fileIds, gaPair, appendMode = false } = body; + + if (!fileIds || !Array.isArray(fileIds) || fileIds.length === 0) { + return NextResponse.json({ error: 'File IDs array is required' }, { status: 400 }); + } + + if (!gaPair || !gaPair.genreTitle || !gaPair.audienceTitle) { + return NextResponse.json({ error: 'GA pair with genreTitle and audienceTitle is required' }, { status: 400 }); + } + + console.log('开始处理批量手动添加GA对请求'); + console.log('项目ID:', projectId); + console.log('请求的文件IDs:', fileIds); + console.log('GA对:', gaPair); + + // 使用 getUploadFileInfoById 逐个验证文件 + const validFiles = []; + const invalidFileIds = []; + + for (const fileId of fileIds) { + try { + console.log(`正在验证文件: ${fileId}`); + const fileInfo = await getUploadFileInfoById(fileId); + + if (fileInfo && fileInfo.projectId === projectId) { + console.log(`文件验证成功: ${fileInfo.fileName}`); + validFiles.push(fileInfo); + } else if (fileInfo) { + console.log(`文件属于其他项目: ${fileInfo.projectId} != ${projectId}`); + invalidFileIds.push(fileId); + } else { + console.log(`文件不存在: ${fileId}`); + invalidFileIds.push(fileId); + } + } catch (error) { + console.error(`验证文件 ${fileId} 时出错:`, String(error)); + invalidFileIds.push(fileId); + } + } + + console.log(`文件验证完成: 有效${validFiles.length}个, 无效${invalidFileIds.length}个`); + + if (validFiles.length === 0) { + return NextResponse.json( + { + error: 'No valid files found', + debug: { + projectId, + requestedIds: fileIds, + invalidIds: invalidFileIds, + message: 'None of the requested files belong to this project or exist in the database' + } + }, + { status: 404 } + ); + } + + // 批量手动添加 GA 对 + console.log('开始批量手动添加GA对...'); + console.log('追加模式:', appendMode); + const results = []; + + for (const file of validFiles) { + try { + console.log(`处理文件: ${file.fileName}`); + + // 检查是否已存在 GA 对 + const existingPairs = await getGaPairsByFileId(file.id); + + let pairNumber = 1; + if (appendMode && existingPairs && existingPairs.length > 0) { + // 追加模式:在现有 GA 对后面添加 + pairNumber = existingPairs.length + 1; + } else if (!appendMode && existingPairs && existingPairs.length > 0) { + // 非追加模式:如果已存在 GA 对则跳过 + console.log(`文件 ${file.fileName} 已存在GA对,跳过`); + results.push({ + fileId: file.id, + fileName: file.fileName, + success: true, + skipped: true, + message: 'GA pairs already exist' + }); + continue; + } + + // 创建 GA 对数据 + const gaPairData = [ + { + projectId, + fileId: file.id, + pairNumber, + genreTitle: gaPair.genreTitle.trim(), + genreDesc: gaPair.genreDesc?.trim() || '', + audienceTitle: gaPair.audienceTitle.trim(), + audienceDesc: gaPair.audienceDesc?.trim() || '', + isActive: true + } + ]; + + // 保存 GA 对 + if (appendMode) { + // 追加模式:只创建新的 GA 对 + await createGaPairs(gaPairData); + } else { + // 非追加模式:使用 saveGaPairs 替换现有的 + const { saveGaPairs } = await import('@/lib/db/ga-pairs'); + await saveGaPairs(projectId, file.id, [ + { + genre: { title: gaPair.genreTitle.trim(), description: gaPair.genreDesc?.trim() || '' }, + audience: { title: gaPair.audienceTitle.trim(), description: gaPair.audienceDesc?.trim() || '' } + } + ]); + } + + results.push({ + fileId: file.id, + fileName: file.fileName, + success: true, + skipped: false, + message: 'GA pair added successfully' + }); + + console.log(`成功为文件 ${file.fileName} 添加GA对`); + } catch (error) { + console.error(`为文件 ${file.fileName} 添加GA对失败:`, error); + results.push({ + fileId: file.id, + fileName: file.fileName, + success: false, + skipped: false, + error: error.message, + message: `Failed: ${error.message}` + }); + } + } + + // 统计结果 + const successCount = results.filter(r => r.success).length; + const failureCount = results.filter(r => !r.success).length; + + console.log(`批量手动添加完成: 成功${successCount}个, 失败${failureCount}个`); + + return NextResponse.json({ + success: true, + data: results, + summary: { + total: results.length, + success: successCount, + failure: failureCount, + processed: validFiles.length, + skipped: invalidFileIds.length + }, + message: `Added GA pairs to ${successCount} files, ${failureCount} failed, ${invalidFileIds.length} files not found` + }); + } catch (error) { + console.error('Error batch adding manual GA pairs:', String(error)); + return NextResponse.json({ error: String(error) || 'Failed to batch add manual GA pairs' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/batch-delete-files/route.js b/easy-dataset-main/app/api/projects/[projectId]/batch-delete-files/route.js new file mode 100644 index 0000000..989a094 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/batch-delete-files/route.js @@ -0,0 +1,196 @@ +import { NextResponse } from 'next/server'; +import { getUploadFileInfoById, delUploadFileInfoById } from '@/lib/db/upload-files'; +import { getProject } from '@/lib/db/projects'; +import { getProjectChunks, getProjectTocByName } from '@/lib/file/text-splitter'; +import { batchSaveTags } from '@/lib/db/tags'; +import { handleDomainTree } from '@/lib/util/domain-tree'; +import path from 'path'; +import { getProjectRoot } from '@/lib/db/base'; +import { promises as fs } from 'fs'; + +/** + * 批量删除文件 + * 复用单个文件删除的完整逻辑,包括领域树修订 + */ +export async function POST(request, { params }) { + try { + const { projectId } = params; + const body = await request.json(); + + if (!projectId) { + return NextResponse.json({ error: 'Project ID is required' }, { status: 400 }); + } + + const { fileIds, domainTreeAction = 'keep', model, language = '中文' } = body; + + if (!fileIds || !Array.isArray(fileIds) || fileIds.length === 0) { + return NextResponse.json({ error: 'File IDs array is required' }, { status: 400 }); + } + + console.log('开始处理批量删除文件请求'); + console.log('项目ID:', projectId); + console.log('请求的文件IDs:', fileIds); + console.log('领域树操作:', domainTreeAction); + + // 获取项目信息 + const project = await getProject(projectId); + if (!project) { + return NextResponse.json({ error: 'The project does not exist' }, { status: 404 }); + } + + // 验证文件并删除 + const results = []; + const deletedTocs = []; + let deletedCount = 0; + let failedCount = 0; + let totalStats = { + deletedChunks: 0, + deletedQuestions: 0, + deletedDatasets: 0 + }; + + for (const fileId of fileIds) { + try { + console.log(`正在验证文件: ${fileId}`); + const fileInfo = await getUploadFileInfoById(fileId); + + if (!fileInfo) { + console.log(`文件不存在: ${fileId}`); + results.push({ + fileId, + success: false, + error: 'File not found' + }); + failedCount++; + continue; + } + + if (fileInfo.projectId !== projectId) { + console.log(`文件属于其他项目: ${fileInfo.projectId} != ${projectId}`); + results.push({ + fileId, + success: false, + error: 'File belongs to another project' + }); + failedCount++; + continue; + } + + // 删除文件及其相关的文本块、问题和数据集 + console.log(`删除文件: ${fileInfo.fileName}`); + const { stats, fileName } = await delUploadFileInfoById(fileId); + + // 累计统计信息 + totalStats.deletedChunks += stats.deletedChunks || 0; + totalStats.deletedQuestions += stats.deletedQuestions || 0; + totalStats.deletedDatasets += stats.deletedDatasets || 0; + + // 获取并保存删除的 TOC 信息 + const deleteToc = await getProjectTocByName(projectId, fileName); + if (deleteToc) { + deletedTocs.push(deleteToc); + } + + // 删除 TOC 文件 + try { + const projectRoot = await getProjectRoot(); + const projectPath = path.join(projectRoot, projectId); + const tocDir = path.join(projectPath, 'toc'); + const baseName = path.basename(fileInfo.fileName, path.extname(fileInfo.fileName)); + const tocPath = path.join(tocDir, `${baseName}-toc.json`); + await fs.unlink(tocPath); + console.log(`成功删除 TOC 文件: ${tocPath}`); + } catch (error) { + console.error(`删除 TOC 文件失败:`, String(error)); + } + + results.push({ + fileId, + fileName: fileInfo.fileName, + success: true, + stats + }); + deletedCount++; + + console.log(`成功删除文件: ${fileInfo.fileName}`); + } catch (error) { + console.error(`删除文件 ${fileId} 时出错:`, error); + results.push({ + fileId, + success: false, + error: error.message + }); + failedCount++; + } + } + + console.log(`批量删除完成: 成功${deletedCount}个, 失败${failedCount}个`); + + // 如果选择了保持领域树不变,直接返回删除结果 + if (domainTreeAction === 'keep') { + return NextResponse.json({ + success: true, + deletedCount, + failedCount, + total: fileIds.length, + results, + stats: totalStats, + domainTreeAction: 'keep', + message: `Successfully deleted ${deletedCount} files, ${failedCount} failed` + }); + } + + // 处理领域树更新 + try { + // 获取项目的所有文件 + const { chunks, toc } = await getProjectChunks(projectId); + + // 如果不存在文本块,说明项目已经没有文件了 + if (!chunks || chunks.length === 0) { + // 清空领域树 + await batchSaveTags(projectId, []); + return NextResponse.json({ + success: true, + deletedCount, + failedCount, + total: fileIds.length, + results, + stats: totalStats, + domainTreeAction, + message: `Successfully deleted ${deletedCount} files, domain tree cleared`, + domainTreeCleared: true + }); + } + + // 调用领域树处理模块 + await handleDomainTree({ + projectId, + action: domainTreeAction, + allToc: toc, + model: model, + language, + deleteToc: deletedTocs.length > 0 ? deletedTocs : undefined, + project + }); + + console.log('领域树更新成功'); + } catch (error) { + console.error('Error updating domain tree after batch deletion:', String(error)); + // 即使领域树更新失败,也不影响文件删除的结果 + } + + return NextResponse.json({ + success: true, + deletedCount, + failedCount, + total: fileIds.length, + results, + stats: totalStats, + domainTreeAction, + message: `Successfully deleted ${deletedCount} files, ${failedCount} failed` + }); + } catch (error) { + console.error('Error batch deleting files:', String(error)); + return NextResponse.json({ error: String(error) || 'Failed to batch delete files' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/batch-generateGA/route.js b/easy-dataset-main/app/api/projects/[projectId]/batch-generateGA/route.js new file mode 100644 index 0000000..8577157 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/batch-generateGA/route.js @@ -0,0 +1,106 @@ +import { NextResponse } from 'next/server'; +import { batchGenerateGaPairs } from '@/lib/services/ga/ga-pairs'; +import { getUploadFileInfoById } from '@/lib/db/upload-files'; // 导入单个文件查询函数 + +/** + * 批量生成多个文件的 GA 对 + */ +export async function POST(request, { params }) { + try { + const { projectId } = params; + const body = await request.json(); + + if (!projectId) { + return NextResponse.json({ error: 'Project ID is required' }, { status: 400 }); + } + + const { fileIds, modelConfigId, language = '中文', appendMode = false } = body; + + if (!fileIds || !Array.isArray(fileIds) || fileIds.length === 0) { + return NextResponse.json({ error: 'File IDs array is required' }, { status: 400 }); + } + + if (!modelConfigId) { + return NextResponse.json({ error: 'Model configuration ID is required' }, { status: 400 }); + } + + console.log('开始处理批量生成GA对请求'); + console.log('项目ID:', projectId); + console.log('请求的文件IDs:', fileIds); + + // 使用 getUploadFileInfoById 逐个验证文件 + const validFiles = []; + const invalidFileIds = []; + + for (const fileId of fileIds) { + try { + console.log(`正在验证文件: ${fileId}`); + const fileInfo = await getUploadFileInfoById(fileId); + + if (fileInfo && fileInfo.projectId === projectId) { + console.log(`文件验证成功: ${fileInfo.fileName}`); + validFiles.push(fileInfo); + } else if (fileInfo) { + console.log(`文件属于其他项目: ${fileInfo.projectId} != ${projectId}`); + invalidFileIds.push(fileId); + } else { + console.log(`文件不存在: ${fileId}`); + invalidFileIds.push(fileId); + } + } catch (error) { + console.error(`验证文件 ${fileId} 时出错:`, String(error)); + invalidFileIds.push(fileId); + } + } + + console.log(`文件验证完成: 有效${validFiles.length}个, 无效${invalidFileIds.length}个`); + + if (validFiles.length === 0) { + return NextResponse.json( + { + error: 'No valid files found', + debug: { + projectId, + requestedIds: fileIds, + invalidIds: invalidFileIds, + message: 'None of the requested files belong to this project or exist in the database' + } + }, + { status: 404 } + ); + } + + // 批量生成 GA 对 + console.log('开始批量生成GA对...'); + console.log('追加模式:', appendMode); + const results = await batchGenerateGaPairs( + projectId, + validFiles, + modelConfigId, + language, + appendMode // 传递追加模式参数 + ); + + // 统计结果 + const successCount = results.filter(r => r.success).length; + const failureCount = results.filter(r => !r.success).length; + + console.log(`批量生成完成: 成功${successCount}个, 失败${failureCount}个`); + + return NextResponse.json({ + success: true, + data: results, + summary: { + total: results.length, + success: successCount, + failure: failureCount, + processed: validFiles.length, + skipped: invalidFileIds.length + }, + message: `Generated GA pairs for ${successCount} files, ${failureCount} failed, ${invalidFileIds.length} files not found` + }); + } catch (error) { + console.error('Error batch generating GA pairs:', String(error)); + return NextResponse.json({ error: String(error) || 'Failed to batch generate GA pairs' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/blind-test-tasks/[taskId]/current/route.js b/easy-dataset-main/app/api/projects/[projectId]/blind-test-tasks/[taskId]/current/route.js new file mode 100644 index 0000000..6570c50 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/blind-test-tasks/[taskId]/current/route.js @@ -0,0 +1,161 @@ +import { NextResponse } from 'next/server'; +import { db } from '@/lib/db/index'; +import LLMClient from '@/lib/llm/core/index'; +import { getModelConfigById } from '@/lib/db/model-config'; + +/** + * Get current question and generate answers from two models + */ +export async function GET(request, { params }) { + try { + const { projectId, taskId } = params; + + const task = await db.task.findFirst({ + where: { + id: taskId, + projectId, + taskType: 'blind-test' + } + }); + + if (!task) { + return NextResponse.json({ code: 404, error: 'Task not found' }, { status: 404 }); + } + + if (task.status !== 0) { + return NextResponse.json({ code: 400, error: 'Task has ended' }, { status: 400 }); + } + + // Parse task detail + let detail = {}; + let modelInfo = {}; + try { + detail = task.detail ? JSON.parse(task.detail) : {}; + modelInfo = task.modelInfo ? JSON.parse(task.modelInfo) : {}; + } catch (e) { + console.error('Failed to parse task detail:', e); + } + + const questionIds = detail.questionIds || detail.evalDatasetIds || []; + const currentIndex = detail.currentIndex || 0; + + // Check if all questions are completed + if (questionIds.length === 0 || currentIndex >= questionIds.length) { + return NextResponse.json({ + code: 0, + data: { + completed: true, + message: 'All questions completed' + } + }); + } + + // Fetch current question + const currentQuestionId = questionIds[currentIndex]; + const currentQuestion = await db.evalDatasets.findUnique({ + where: { id: currentQuestionId }, + select: { + id: true, + question: true, + questionType: true, + correctAnswer: true, + tags: true + } + }); + + if (!currentQuestion) { + return NextResponse.json({ code: 404, error: 'Question not found' }, { status: 404 }); + } + + // Fetch both model configs + const [modelConfigA, modelConfigB] = await Promise.all([ + getModelConfigById(modelInfo.modelA.providerId), + getModelConfigById(modelInfo.modelB.providerId) + ]); + + if (!modelConfigA || !modelConfigB) { + return NextResponse.json({ code: 400, error: 'Model configuration not found' }, { status: 400 }); + } + + // Build prompts + const systemPrompt = "You are a helpful assistant. Provide detailed and accurate answers to the user's question."; + const userPrompt = currentQuestion.question; + + // Call both models in parallel + const startTimeA = Date.now(); + const startTimeB = Date.now(); + + let answerA = ''; + let answerB = ''; + let errorA = null; + let errorB = null; + let durationA = 0; + let durationB = 0; + + try { + // Call model A + const clientA = new LLMClient(modelConfigA); + + const resultA = await clientA.chat([ + { role: 'system', content: systemPrompt }, + { role: 'user', content: userPrompt } + ]); + + answerA = resultA.text || ''; + durationA = Date.now() - startTimeA; + } catch (err) { + console.error('Model A call failed:', err); + errorA = err.message; + durationA = Date.now() - startTimeA; + } + + try { + // Call model B + const clientB = new LLMClient(modelConfigB); + + const resultB = await clientB.chat([ + { role: 'system', content: systemPrompt }, + { role: 'user', content: userPrompt } + ]); + + answerB = resultB.text || ''; + durationB = Date.now() - startTimeB; + } catch (err) { + console.error('Model B call failed:', err); + errorB = err.message; + durationB = Date.now() - startTimeB; + } + + // Randomly swap positions (core blind-test behavior) + const isSwapped = Math.random() > 0.5; + + return NextResponse.json({ + code: 0, + data: { + completed: false, + currentIndex, + totalCount: evalDatasetIds.length, + question: currentQuestion, + // Blind test: do not reveal which model is which + leftAnswer: { + content: isSwapped ? answerB : answerA, + error: isSwapped ? errorB : errorA, + duration: isSwapped ? durationB : durationA + }, + rightAnswer: { + content: isSwapped ? answerA : answerB, + error: isSwapped ? errorA : errorB, + duration: isSwapped ? durationA : durationB + }, + // Server stores the actual mapping for scoring + _swap: isSwapped + } + }); + } catch (error) { + console.error('Failed to fetch current question:', error); + return NextResponse.json( + { code: 500, error: 'Failed to fetch current question', message: error.message }, + { status: 500 } + ); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/blind-test-tasks/[taskId]/question/route.js b/easy-dataset-main/app/api/projects/[projectId]/blind-test-tasks/[taskId]/question/route.js new file mode 100644 index 0000000..07db8f1 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/blind-test-tasks/[taskId]/question/route.js @@ -0,0 +1,64 @@ +import { NextResponse } from 'next/server'; +import { db } from '@/lib/db/index'; + +/** + * Get current question info (including random swap info) + */ +export async function GET(request, { params }) { + const { projectId, taskId } = params; + + try { + if (!projectId || !taskId) { + return NextResponse.json({ error: 'Missing required parameters' }, { status: 400 }); + } + + // Fetch task + const task = await db.task.findUnique({ + where: { id: taskId } + }); + + if (!task || task.taskType !== 'blind-test') { + return NextResponse.json({ error: 'Task not found' }, { status: 404 }); + } + + // Parse task detail + const detail = JSON.parse(task.detail || '{}'); + // Support both evalDatasetIds and questionIds + const questionIds = detail.questionIds || detail.evalDatasetIds || []; + const currentIndex = detail.currentIndex || 0; + + // Check if task is completed + if (questionIds.length === 0 || currentIndex >= questionIds.length) { + return NextResponse.json({ + completed: true, + currentIndex, + totalQuestions: questionIds.length + }); + } + + // Fetch current question + const currentQuestionId = questionIds[currentIndex]; + const currentQuestion = await db.evalDatasets.findUnique({ + where: { id: currentQuestionId } + }); + + if (!currentQuestion) { + return NextResponse.json({ error: 'Question not found' }, { status: 404 }); + } + + // Randomly decide whether to swap (core blind-test behavior) + const isSwapped = Math.random() > 0.5; + + return NextResponse.json({ + questionId: currentQuestion.id, + question: currentQuestion.question, + answer: currentQuestion.correctAnswer || '', + questionIndex: currentIndex + 1, + totalQuestions: questionIds.length, + isSwapped + }); + } catch (error) { + console.error('Failed to fetch question info:', error); + return NextResponse.json({ error: error.message }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/blind-test-tasks/[taskId]/route.js b/easy-dataset-main/app/api/projects/[projectId]/blind-test-tasks/[taskId]/route.js new file mode 100644 index 0000000..473ed1b --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/blind-test-tasks/[taskId]/route.js @@ -0,0 +1,190 @@ +import { NextResponse } from 'next/server'; +import { db } from '@/lib/db/index'; + +/** + * Get blind-test task details + * Results are fetched from EvalResults table + */ +export async function GET(request, { params }) { + try { + const { projectId, taskId } = params; + + const task = await db.task.findFirst({ + where: { + id: taskId, + projectId, + taskType: 'blind-test' + } + }); + + if (!task) { + return NextResponse.json({ code: 404, error: 'Task not found' }, { status: 404 }); + } + + let detail = {}; + let modelInfo = {}; + try { + detail = task.detail ? JSON.parse(task.detail) : {}; + modelInfo = task.modelInfo ? JSON.parse(task.modelInfo) : {}; + } catch (e) { + console.error('Failed to parse task detail:', e); + } + + // Fetch all related evaluation questions + const evalDatasetIds = detail.evalDatasetIds || []; + const evalDatasets = await db.evalDatasets.findMany({ + where: { + id: { in: evalDatasetIds } + }, + select: { + id: true, + question: true, + questionType: true, + correctAnswer: true, + tags: true + } + }); + + // Sort by evalDatasetIds order + const orderedDatasets = evalDatasetIds.map(id => evalDatasets.find(d => d.id === id)).filter(Boolean); + + // Fetch results from EvalResults table + const evalResults = await db.evalResults.findMany({ + where: { taskId }, + orderBy: { createAt: 'asc' } + }); + + // Parse results into the format expected by frontend + const results = evalResults.map(r => { + let modelAnswer = {}; + let judgeData = {}; + try { + modelAnswer = JSON.parse(r.modelAnswer || '{}'); + judgeData = JSON.parse(r.judgeResponse || '{}'); + } catch (e) { + // Ignore parse errors + } + return { + questionId: r.evalDatasetId, + vote: judgeData.vote, + isSwapped: judgeData.isSwapped, + modelAScore: judgeData.modelAScore || 0, + modelBScore: judgeData.modelBScore || 0, + leftAnswer: modelAnswer.leftAnswer || '', + rightAnswer: modelAnswer.rightAnswer || '', + timestamp: r.createAt + }; + }); + + return NextResponse.json({ + code: 0, + data: { + ...task, + detail: { + ...detail, + results // Include results from EvalResults table + }, + modelInfo, + evalDatasets: orderedDatasets + } + }); + } catch (error) { + console.error('Failed to fetch blind-test task details:', error); + return NextResponse.json( + { code: 500, error: 'Failed to fetch blind-test task details', message: error.message }, + { status: 500 } + ); + } +} + +/** + * Update blind-test task (interrupt/stop) + */ +export async function PUT(request, { params }) { + try { + const { projectId, taskId } = params; + const { action } = await request.json(); + + const task = await db.task.findFirst({ + where: { + id: taskId, + projectId, + taskType: 'blind-test' + } + }); + + if (!task) { + return NextResponse.json({ code: 404, error: 'Task not found' }, { status: 404 }); + } + + if (action === 'interrupt') { + if (task.status !== 0) { + return NextResponse.json({ code: 400, error: 'Only running tasks can be interrupted' }, { status: 400 }); + } + + const updatedTask = await db.task.update({ + where: { id: taskId }, + data: { + status: 3, // Interrupted + endTime: new Date() + } + }); + + return NextResponse.json({ + code: 0, + data: updatedTask, + message: 'Task interrupted' + }); + } + + return NextResponse.json({ code: 400, error: 'Unknown action' }, { status: 400 }); + } catch (error) { + console.error('Failed to update blind-test task:', error); + return NextResponse.json( + { code: 500, error: 'Failed to update blind-test task', message: error.message }, + { status: 500 } + ); + } +} + +/** + * Delete blind-test task and its results + */ +export async function DELETE(request, { params }) { + try { + const { projectId, taskId } = params; + + const task = await db.task.findFirst({ + where: { + id: taskId, + projectId, + taskType: 'blind-test' + } + }); + + if (!task) { + return NextResponse.json({ code: 404, error: 'Task not found' }, { status: 404 }); + } + + // Delete related EvalResults first + await db.evalResults.deleteMany({ + where: { taskId } + }); + + // Then delete the task + await db.task.delete({ + where: { id: taskId } + }); + + return NextResponse.json({ + code: 0, + message: 'Task deleted' + }); + } catch (error) { + console.error('Failed to delete blind-test task:', error); + return NextResponse.json( + { code: 500, error: 'Failed to delete blind-test task', message: error.message }, + { status: 500 } + ); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/blind-test-tasks/[taskId]/stream-model/route.js b/easy-dataset-main/app/api/projects/[projectId]/blind-test-tasks/[taskId]/stream-model/route.js new file mode 100644 index 0000000..beac0f1 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/blind-test-tasks/[taskId]/stream-model/route.js @@ -0,0 +1,92 @@ +import { NextResponse } from 'next/server'; +import { db } from '@/lib/db/index'; +import LLMClient from '@/lib/llm/core/index'; +import { getModelConfigById } from '@/lib/db/model-config'; + +/** + * Stream answer for a specified model + * Query param: model=A or model=B + */ +export async function GET(request, { params }) { + const { projectId, taskId } = params; + const { searchParams } = new URL(request.url); + const modelType = searchParams.get('model'); // 'A' or 'B' + + try { + if (!projectId || !taskId) { + return NextResponse.json({ error: 'Missing required parameters' }, { status: 400 }); + } + + if (!modelType || !['A', 'B'].includes(modelType)) { + return NextResponse.json({ error: 'Model type must be specified (A or B)' }, { status: 400 }); + } + + // Fetch task + const task = await db.task.findUnique({ + where: { id: taskId } + }); + + if (!task || task.taskType !== 'blind-test') { + return NextResponse.json({ error: 'Task not found' }, { status: 404 }); + } + + // Parse task detail + const detail = JSON.parse(task.detail || '{}'); + const modelInfo = JSON.parse(task.modelInfo || '{}'); + // Support both evalDatasetIds and questionIds + const questionIds = detail.questionIds || detail.evalDatasetIds || []; + const currentIndex = detail.currentIndex || 0; + + // Check if task is completed + if (questionIds.length === 0 || currentIndex >= questionIds.length) { + return NextResponse.json({ completed: true }); + } + + // Fetch current question + const currentQuestionId = questionIds[currentIndex]; + const currentQuestion = await db.evalDatasets.findUnique({ + where: { id: currentQuestionId } + }); + + if (!currentQuestion) { + return NextResponse.json({ error: 'Question not found' }, { status: 404 }); + } + + // Resolve model config based on modelType + const modelConfigKey = modelType === 'A' ? 'modelA' : 'modelB'; + const modelConfig = await getModelConfigById(modelInfo[modelConfigKey].id); + + if (!modelConfig) { + return NextResponse.json({ error: 'Model configuration not found' }, { status: 400 }); + } + + // Prepare messages + const messages = [ + { + role: 'system', + content: "You are a helpful assistant. Provide detailed and accurate answers to the user's question." + }, + { role: 'user', content: currentQuestion.question } + ]; + + // Create LLM client + const client = new LLMClient({ + projectId, + ...modelConfig + }); + + // Call streaming API and return response directly + const response = await client.chatStreamAPI(messages); + + return new Response(response.body, { + headers: { + 'Content-Type': 'text/plain; charset=utf-8', + 'Cache-Control': 'no-cache', + Connection: 'keep-alive' + } + }); + } catch (error) { + console.error(`Model ${modelType} streaming call failed:`, error); + return NextResponse.json({ error: error.message }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/blind-test-tasks/[taskId]/stream/route.js b/easy-dataset-main/app/api/projects/[projectId]/blind-test-tasks/[taskId]/stream/route.js new file mode 100644 index 0000000..54f0002 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/blind-test-tasks/[taskId]/stream/route.js @@ -0,0 +1,213 @@ +import { NextResponse } from 'next/server'; +import { db } from '@/lib/db/index'; +import LLMClient from '@/lib/llm/core/index'; +import { getModelConfigById } from '@/lib/db/model-config'; + +/** + * Stream answers from two models for the current question + */ +export async function GET(request, { params }) { + const { projectId, taskId } = params; + + try { + if (!projectId || !taskId) { + return NextResponse.json({ error: 'Missing required parameters' }, { status: 400 }); + } + + // Fetch task + const task = await db.task.findUnique({ + where: { id: taskId } + }); + + if (!task || task.taskType !== 'blind-test') { + return NextResponse.json({ error: 'Task not found' }, { status: 404 }); + } + + // Parse task detail + const detail = JSON.parse(task.detail || '{}'); + const modelInfo = JSON.parse(task.modelInfo || '{}'); + const { questionIds = [], currentIndex = 0 } = detail; + + // Check if task is completed + if (currentIndex >= questionIds.length) { + return NextResponse.json({ completed: true }); + } + + // Fetch current question + const currentQuestionId = questionIds[currentIndex]; + const currentQuestion = await db.evalDatasets.findUnique({ + where: { id: currentQuestionId } + }); + + if (!currentQuestion) { + return NextResponse.json({ error: 'Question not found' }, { status: 404 }); + } + + // Fetch model configs + const [modelConfigA, modelConfigB] = await Promise.all([ + getModelConfigById(modelInfo.modelA.providerId), + getModelConfigById(modelInfo.modelB.providerId) + ]); + + if (!modelConfigA || !modelConfigB) { + return NextResponse.json({ error: 'Model configuration not found' }, { status: 400 }); + } + + // Randomly swap positions (core blind-test behavior) + const isSwapped = Math.random() > 0.5; + + // Create streaming response + const encoder = new TextEncoder(); + const stream = new ReadableStream({ + async start(controller) { + try { + // Send init message + controller.enqueue( + encoder.encode( + JSON.stringify({ + type: 'init', + question: currentQuestion.question, + questionId: currentQuestion.id, + questionIndex: currentIndex + 1, + totalQuestions: questionIds.length, + isSwapped + }) + '\n' + ) + ); + + // Prepare messages + const messages = [ + { + role: 'system', + content: "You are a helpful assistant. Provide detailed and accurate answers to the user's question." + }, + { role: 'user', content: currentQuestion.question } + ]; + + // Create LLM clients + const clientA = new LLMClient({ + projectId, + ...modelConfigA + }); + + const clientB = new LLMClient({ + projectId, + ...modelConfigB + }); + + let answerA = ''; + let answerB = ''; + const startTime = Date.now(); + + // Call both models in parallel (streaming) + await Promise.all([ + (async () => { + try { + const response = await clientA.chatStreamAPI(messages); + const reader = response.body.getReader(); + const decoder = new TextDecoder(); + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + const chunk = decoder.decode(value, { stream: true }); + answerA += chunk; + + // Send chunk update + controller.enqueue( + encoder.encode( + JSON.stringify({ + type: 'chunk', + model: isSwapped ? 'B' : 'A', + content: chunk + }) + '\n' + ) + ); + } + } catch (err) { + console.error('Model A call failed:', err); + controller.enqueue( + encoder.encode( + JSON.stringify({ + type: 'error', + model: isSwapped ? 'B' : 'A', + error: err.message + }) + '\n' + ) + ); + } + })(), + (async () => { + try { + const response = await clientB.chatStreamAPI(messages); + const reader = response.body.getReader(); + const decoder = new TextDecoder(); + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + const chunk = decoder.decode(value, { stream: true }); + answerB += chunk; + + // Send chunk update + controller.enqueue( + encoder.encode( + JSON.stringify({ + type: 'chunk', + model: isSwapped ? 'A' : 'B', + content: chunk + }) + '\n' + ) + ); + } + } catch (err) { + console.error('Model B call failed:', err); + controller.enqueue( + encoder.encode( + JSON.stringify({ + type: 'error', + model: isSwapped ? 'A' : 'B', + error: err.message + }) + '\n' + ) + ); + } + })() + ]); + + const duration = Date.now() - startTime; + + // Send done message + controller.enqueue( + encoder.encode( + JSON.stringify({ + type: 'done', + duration, + answerA: isSwapped ? answerB : answerA, + answerB: isSwapped ? answerA : answerB + }) + '\n' + ) + ); + + controller.close(); + } catch (error) { + console.error('Streaming handler failed:', error); + controller.error(error); + } + } + }); + + return new Response(stream, { + headers: { + 'Content-Type': 'text/plain; charset=utf-8', + 'Cache-Control': 'no-cache', + Connection: 'keep-alive' + } + }); + } catch (error) { + console.error('API error:', error); + return NextResponse.json({ error: error.message }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/blind-test-tasks/[taskId]/vote/route.js b/easy-dataset-main/app/api/projects/[projectId]/blind-test-tasks/[taskId]/vote/route.js new file mode 100644 index 0000000..1e3c264 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/blind-test-tasks/[taskId]/vote/route.js @@ -0,0 +1,154 @@ +import { NextResponse } from 'next/server'; +import { db } from '@/lib/db/index'; + +/** + * Submit vote result + * vote: 'left' | 'right' | 'both_good' | 'both_bad' + * Results are stored in EvalResults table + */ +export async function POST(request, { params }) { + try { + const { projectId, taskId } = params; + const { vote, questionId, isSwapped, leftAnswer, rightAnswer } = await request.json(); + + // Validate vote option + const validVotes = ['left', 'right', 'both_good', 'both_bad']; + if (!validVotes.includes(vote)) { + return NextResponse.json({ code: 400, error: 'Invalid vote option' }, { status: 400 }); + } + + if (!questionId) { + return NextResponse.json({ code: 400, error: 'Question ID is required' }, { status: 400 }); + } + + const task = await db.task.findFirst({ + where: { + id: taskId, + projectId, + taskType: 'blind-test' + } + }); + + if (!task) { + return NextResponse.json({ code: 404, error: 'Task not found' }, { status: 404 }); + } + + if (task.status !== 0) { + return NextResponse.json({ code: 400, error: 'Task has ended' }, { status: 400 }); + } + + // Parse task details + let detail = {}; + try { + detail = task.detail ? JSON.parse(task.detail) : {}; + } catch (e) { + console.error('Failed to parse task detail:', e); + } + + // Calculate scores + // isSwapped: true means left is model B and right is model A + // isSwapped: false means left is model A and right is model B + let modelAScore = 0; + let modelBScore = 0; + + if (vote === 'left') { + if (isSwapped) { + modelBScore = 1; // Left is B + } else { + modelAScore = 1; // Left is A + } + } else if (vote === 'right') { + if (isSwapped) { + modelAScore = 1; // Right is A + } else { + modelBScore = 1; // Right is B + } + } else if (vote === 'both_good') { + modelAScore = 0.5; + modelBScore = 0.5; + } + // both_bad: both scores remain 0 + + // Store result in EvalResults table + const evalResult = await db.evalResults.create({ + data: { + projectId, + taskId, + evalDatasetId: questionId, + modelAnswer: JSON.stringify({ + leftAnswer: leftAnswer || '', + rightAnswer: rightAnswer || '' + }), + score: modelAScore, // Store modelA score for sorting/aggregation + isCorrect: false, // Not applicable for blind-test + judgeResponse: JSON.stringify({ + vote, + isSwapped, + modelAScore, + modelBScore + }), + duration: 0, + status: 0 + } + }); + + // Update task progress + const evalDatasetIds = detail.evalDatasetIds || []; + const newCurrentIndex = (detail.currentIndex || 0) + 1; + const isCompleted = newCurrentIndex >= evalDatasetIds.length; + + const updatedDetail = { + ...detail, + currentIndex: newCurrentIndex + }; + + await db.task.update({ + where: { id: taskId }, + data: { + detail: JSON.stringify(updatedDetail), + completedCount: newCurrentIndex, + status: isCompleted ? 1 : 0, // 1-completed, 0-running + endTime: isCompleted ? new Date() : null + } + }); + + // Calculate current total scores from EvalResults + const allResults = await db.evalResults.findMany({ + where: { taskId }, + select: { judgeResponse: true } + }); + + let totalModelAScore = 0; + let totalModelBScore = 0; + for (const r of allResults) { + try { + const judge = JSON.parse(r.judgeResponse || '{}'); + totalModelAScore += judge.modelAScore || 0; + totalModelBScore += judge.modelBScore || 0; + } catch (e) { + // Ignore parse errors + } + } + + return NextResponse.json({ + code: 0, + data: { + success: true, + isCompleted, + currentIndex: newCurrentIndex, + totalCount: evalDatasetIds.length, + scores: { + modelA: totalModelAScore, + modelB: totalModelBScore + } + }, + message: isCompleted ? 'Blind-test task completed' : 'Vote recorded' + }); + } catch (error) { + console.error('Failed to submit vote result:', error); + return NextResponse.json( + { code: 500, error: 'Failed to submit vote result', message: error.message }, + { status: 500 } + ); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/blind-test-tasks/route.js b/easy-dataset-main/app/api/projects/[projectId]/blind-test-tasks/route.js new file mode 100644 index 0000000..41a8067 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/blind-test-tasks/route.js @@ -0,0 +1,226 @@ +import { NextResponse } from 'next/server'; +import { db } from '@/lib/db/index'; + +/** + * Get all blind-test tasks for a project + */ +export async function GET(request, { params }) { + try { + const { projectId } = params; + const { searchParams } = new URL(request.url); + const page = parseInt(searchParams.get('page') || '1'); + const pageSize = parseInt(searchParams.get('pageSize') || '20'); + + if (!projectId) { + return NextResponse.json({ error: 'Project ID is required' }, { status: 400 }); + } + + const skip = (page - 1) * pageSize; + + // Fetch task list and total count + const [tasks, total] = await Promise.all([ + db.task.findMany({ + where: { + projectId, + taskType: 'blind-test' + }, + orderBy: { createAt: 'desc' }, + skip, + take: pageSize + }), + db.task.count({ + where: { + projectId, + taskType: 'blind-test' + } + }) + ]); + + // Fetch evaluation results for all tasks to calculate scores + const taskIds = tasks.map(t => t.id); + const allEvalResults = await db.evalResults.findMany({ + where: { taskId: { in: taskIds } }, + select: { + taskId: true, + judgeResponse: true + } + }); + + // Group results by taskId and calculate scores + const taskScores = {}; + for (const result of allEvalResults) { + if (!taskScores[result.taskId]) { + taskScores[result.taskId] = { modelAScore: 0, modelBScore: 0 }; + } + try { + const judge = JSON.parse(result.judgeResponse || '{}'); + taskScores[result.taskId].modelAScore += judge.modelAScore || 0; + taskScores[result.taskId].modelBScore += judge.modelBScore || 0; + } catch (e) { + // Ignore parse errors + } + } + + // Parse task detail fields and attach scores + const tasksWithDetails = tasks.map(task => { + let detail = {}; + let modelInfo = {}; + try { + detail = task.detail ? JSON.parse(task.detail) : {}; + modelInfo = task.modelInfo ? JSON.parse(task.modelInfo) : {}; + } catch (e) { + console.error('Failed to parse task detail:', e); + } + + // Attach calculated scores as results array + const scores = taskScores[task.id] || { modelAScore: 0, modelBScore: 0 }; + const results = [ + { + modelAScore: scores.modelAScore, + modelBScore: scores.modelBScore + } + ]; + + return { + ...task, + detail: { + ...detail, + results // Attach results for display in task card + }, + modelInfo + }; + }); + + return NextResponse.json({ + code: 0, + data: { + items: tasksWithDetails, + total, + page, + pageSize, + totalPages: Math.ceil(total / pageSize) + } + }); + } catch (error) { + console.error('Failed to fetch blind-test task list:', error); + return NextResponse.json( + { code: 500, error: 'Failed to fetch blind-test task list', message: error.message }, + { status: 500 } + ); + } +} + +/** + * Create a blind-test task + */ +export async function POST(request, { params }) { + try { + const { projectId } = params; + const data = await request.json(); + + const { modelA, modelB, evalDatasetIds, language = 'zh-CN' } = data; + + if (!modelA || !modelA.modelId || !modelA.providerId) { + return NextResponse.json({ code: 400, error: 'Please select model A' }, { status: 400 }); + } + + if (!modelB || !modelB.modelId || !modelB.providerId) { + return NextResponse.json({ code: 400, error: 'Please select model B' }, { status: 400 }); + } + + if (modelA.modelId === modelB.modelId && modelA.providerId === modelB.providerId) { + return NextResponse.json({ code: 400, error: 'The two models must be different' }, { status: 400 }); + } + + if (!evalDatasetIds || evalDatasetIds.length === 0) { + return NextResponse.json({ code: 400, error: 'Please select questions to evaluate' }, { status: 400 }); + } + + const evalDatasets = await db.evalDatasets.findMany({ + where: { + id: { in: evalDatasetIds }, + projectId + }, + select: { id: true, questionType: true } + }); + + const invalidQuestions = evalDatasets.filter( + q => q.questionType !== 'short_answer' && q.questionType !== 'open_ended' + ); + + if (invalidQuestions.length > 0) { + return NextResponse.json( + { + code: 400, + error: 'Blind-test tasks only support short-answer and open-ended questions' + }, + { status: 400 } + ); + } + + // Fetch model config info + const [modelConfigA, modelConfigB] = await Promise.all([ + db.modelConfig.findFirst({ + where: { projectId, providerId: modelA.providerId, modelId: modelA.modelId } + }), + db.modelConfig.findFirst({ + where: { projectId, providerId: modelB.providerId, modelId: modelB.modelId } + }) + ]); + + // Build model info (two models) + const modelInfo = { + modelA: { + id: modelConfigA?.id, + modelId: modelA.modelId, + modelName: modelConfigA?.modelName || modelA.modelId, + providerId: modelA.providerId, + providerName: modelConfigA?.providerName || modelA.providerId + }, + modelB: { + id: modelConfigB?.id, + modelId: modelB.modelId, + modelName: modelConfigB?.modelName || modelB.modelId, + providerId: modelB.providerId, + providerName: modelConfigB?.providerName || modelB.providerId + } + }; + + // Build task detail (only store evalDatasetIds and currentIndex) + const taskDetail = { + evalDatasetIds, + currentIndex: 0 // Current question index + }; + + // Create task + const newTask = await db.task.create({ + data: { + projectId, + taskType: 'blind-test', + status: 0, // Running + modelInfo: JSON.stringify(modelInfo), + language, + detail: JSON.stringify(taskDetail), + totalCount: evalDatasetIds.length, + completedCount: 0, + note: '' + } + }); + + return NextResponse.json({ + code: 0, + data: { + ...newTask, + detail: taskDetail, + modelInfo + }, + message: 'Blind-test task created' + }); + } catch (error) { + console.error('Failed to create blind-test task:', error); + return NextResponse.json( + { code: 500, error: 'Failed to create blind-test task', message: error.message }, + { status: 500 } + ); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/chunks/[chunkId]/clean/route.js b/easy-dataset-main/app/api/projects/[projectId]/chunks/[chunkId]/clean/route.js new file mode 100644 index 0000000..40872ff --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/chunks/[chunkId]/clean/route.js @@ -0,0 +1,40 @@ +import { NextResponse } from 'next/server'; +import logger from '@/lib/util/logger'; +import cleanService from '@/lib/services/clean'; + +// 为指定文本块进行数据清洗 +export async function POST(request, { params }) { + try { + const { projectId, chunkId } = params; + + // 验证项目ID和文本块ID + if (!projectId || !chunkId) { + return NextResponse.json({ error: 'Project ID or text block ID cannot be empty' }, { status: 400 }); + } + + // 获取请求体 + const { model, language = '中文' } = await request.json(); + + if (!model) { + return NextResponse.json({ error: 'Model cannot be empty' }, { status: 400 }); + } + + // 使用数据清洗服务 + const result = await cleanService.cleanDataForChunk(projectId, chunkId, { + model, + language + }); + + // 返回清洗结果 + return NextResponse.json({ + chunkId, + originalLength: result.originalLength, + cleanedLength: result.cleanedLength, + success: result.success, + message: '数据清洗完成' + }); + } catch (error) { + logger.error('Error cleaning data:', error); + return NextResponse.json({ error: error.message || 'Error cleaning data' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/chunks/[chunkId]/eval-questions/route.js b/easy-dataset-main/app/api/projects/[projectId]/chunks/[chunkId]/eval-questions/route.js new file mode 100644 index 0000000..5b35b6a --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/chunks/[chunkId]/eval-questions/route.js @@ -0,0 +1,35 @@ +import { NextResponse } from 'next/server'; +import { generateEvalQuestionsForChunk } from '@/lib/services/eval'; +import logger from '@/lib/util/logger'; + +/** + * 为指定文本块生成测评题目 + */ +export async function POST(request, { params }) { + try { + const { projectId, chunkId } = params; + + // 验证参数 + if (!projectId || !chunkId) { + return NextResponse.json({ error: 'Project ID and Chunk ID are required' }, { status: 400 }); + } + + // 获取请求体 + const { model, language = 'zh-CN' } = await request.json(); + + if (!model) { + return NextResponse.json({ error: 'Model configuration is required' }, { status: 400 }); + } + + // 调用服务层生成测评题目 + const result = await generateEvalQuestionsForChunk(projectId, chunkId, { + model, + language + }); + + return NextResponse.json(result); + } catch (error) { + logger.error('Error generating eval questions:', error); + return NextResponse.json({ error: error.message || 'Failed to generate eval questions' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/chunks/[chunkId]/questions/route.js b/easy-dataset-main/app/api/projects/[projectId]/chunks/[chunkId]/questions/route.js new file mode 100644 index 0000000..e502b5c --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/chunks/[chunkId]/questions/route.js @@ -0,0 +1,73 @@ +import { NextResponse } from 'next/server'; +import { getQuestionsForChunk } from '@/lib/db/questions'; +import logger from '@/lib/util/logger'; +import questionService from '@/lib/services/questions'; + +// 为指定文本块生成问题 +export async function POST(request, { params }) { + try { + const { projectId, chunkId } = params; + + // 验证项目ID和文本块ID + if (!projectId || !chunkId) { + return NextResponse.json({ error: 'Project ID or text block ID cannot be empty' }, { status: 400 }); + } // 获取请求体 + const { model, language = '中文', number, enableGaExpansion = false } = await request.json(); + + if (!model) { + return NextResponse.json({ error: 'Model cannot be empty' }, { status: 400 }); + } + + // 后续会根据是否有GA对来选择是否启用GA扩展选择服务函数 + const serviceFunc = questionService.generateQuestionsForChunkWithGA; + + // 使用问题生成服务 + const result = await serviceFunc(projectId, chunkId, { + model, + language, + number, + enableGaExpansion + }); + + // 统一返回格式,确保包含GA扩展信息 + const response = { + chunkId, + questions: result.questions || result.labelQuestions || [], + total: result.total || (result.questions || result.labelQuestions || []).length, + gaExpansionUsed: result.gaExpansionUsed || false, + gaPairsCount: result.gaPairsCount || 0, + expectedTotal: result.expectedTotal || result.total + }; + + // 返回生成的问题 + return NextResponse.json(response); + } catch (error) { + logger.error('Error generating questions:', error); + return NextResponse.json({ error: error.message || 'Error generating questions' }, { status: 500 }); + } +} + +// 获取指定文本块的问题 +export async function GET(request, { params }) { + try { + const { projectId, chunkId } = params; + + // 验证项目ID和文本块ID + if (!projectId || !chunkId) { + return NextResponse.json({ error: 'The item ID or text block ID cannot be empty' }, { status: 400 }); + } + + // 获取文本块的问题 + const questions = await getQuestionsForChunk(projectId, chunkId); + + // 返回问题列表 + return NextResponse.json({ + chunkId, + questions, + total: questions.length + }); + } catch (error) { + console.error('Error getting questions:', String(error)); + return NextResponse.json({ error: error.message || 'Error getting questions' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/chunks/[chunkId]/route.js b/easy-dataset-main/app/api/projects/[projectId]/chunks/[chunkId]/route.js new file mode 100644 index 0000000..4b91ee5 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/chunks/[chunkId]/route.js @@ -0,0 +1,73 @@ +import { NextResponse } from 'next/server'; +import { deleteChunkById, getChunkById, updateChunkById } from '@/lib/db/chunks'; + +// 获取文本块内容 +export async function GET(request, { params }) { + try { + const { projectId, chunkId } = params; + // 验证参数 + if (!projectId) { + return NextResponse.json({ error: 'Project ID cannot be empty' }, { status: 400 }); + } + if (!chunkId) { + return NextResponse.json({ error: 'Text block ID cannot be empty' }, { status: 400 }); + } + // 获取文本块内容 + const chunk = await getChunkById(chunkId); + + return NextResponse.json(chunk); + } catch (error) { + console.error('Failed to get text block content:', String(error)); + return NextResponse.json({ error: error.message || 'Failed to get text block content' }, { status: 500 }); + } +} + +// 删除文本块 +export async function DELETE(request, { params }) { + try { + const { projectId, chunkId } = params; + // 验证参数 + if (!projectId) { + return NextResponse.json({ error: 'Project ID cannot be empty' }, { status: 400 }); + } + if (!chunkId) { + return NextResponse.json({ error: 'Text block ID cannot be empty' }, { status: 400 }); + } + await deleteChunkById(chunkId); + + return NextResponse.json({ message: 'Text block deleted successfully' }); + } catch (error) { + console.error('Failed to delete text block:', String(error)); + return NextResponse.json({ error: error.message || 'Failed to delete text block' }, { status: 500 }); + } +} + +// 编辑文本块内容 +export async function PATCH(request, { params }) { + try { + const { projectId, chunkId } = params; + + // 验证参数 + if (!projectId) { + return NextResponse.json({ error: '项目ID不能为空' }, { status: 400 }); + } + + if (!chunkId) { + return NextResponse.json({ error: '文本块ID不能为空' }, { status: 400 }); + } + + // 解析请求体获取新内容 + const requestData = await request.json(); + const { content } = requestData; + + if (!content) { + return NextResponse.json({ error: '内容不能为空' }, { status: 400 }); + } + + let res = await updateChunkById(chunkId, { content }); + return NextResponse.json(res); + } catch (error) { + console.error('编辑文本块失败:', String(error)); + return NextResponse.json({ error: error.message || '编辑文本块失败' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/chunks/batch-content/route.js b/easy-dataset-main/app/api/projects/[projectId]/chunks/batch-content/route.js new file mode 100644 index 0000000..a754548 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/chunks/batch-content/route.js @@ -0,0 +1,20 @@ +import { getChunkContentsByNames } from '@/lib/db/chunks'; +import { NextResponse } from 'next/server'; + +export async function POST(request, { params }) { + try { + const { projectId } = params; + const { chunkNames } = await request.json(); + + if (!chunkNames || !Array.isArray(chunkNames)) { + return NextResponse.json({ error: 'chunkNames 参数必须是数组' }, { status: 400 }); + } + + const chunkContentMap = await getChunkContentsByNames(projectId, chunkNames); + + return NextResponse.json(chunkContentMap); + } catch (error) { + console.error('批量获取文本块内容失败:', error); + return NextResponse.json({ error: '批量获取文本块内容失败' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/chunks/batch-edit/route.js b/easy-dataset-main/app/api/projects/[projectId]/chunks/batch-edit/route.js new file mode 100644 index 0000000..4fb6514 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/chunks/batch-edit/route.js @@ -0,0 +1,102 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { PrismaClient } from '@prisma/client'; + +const prisma = new PrismaClient(); + +/** + * 批量编辑文本块内容 + * POST /api/projects/[projectId]/chunks/batch-edit + */ +export async function POST(request, { params }) { + try { + const { projectId } = params; + const body = await request.json(); + const { position, content, chunkIds } = body; + + // 验证参数 + if (!position || !content || !chunkIds || !Array.isArray(chunkIds) || chunkIds.length === 0) { + return NextResponse.json({ error: 'Missing required parameters: position, content, chunkIds' }, { status: 400 }); + } + + if (!['start', 'end'].includes(position)) { + return NextResponse.json({ error: 'Position must be "start" or "end"' }, { status: 400 }); + } + + // 验证项目权限(获取要编辑的文本块) + const chunksToUpdate = await prisma.chunks.findMany({ + where: { + id: { in: chunkIds }, + projectId: projectId + }, + select: { + id: true, + content: true, + name: true + } + }); + + if (chunksToUpdate.length === 0) { + return NextResponse.json({ error: 'Not found' }, { status: 404 }); + } + + if (chunksToUpdate.length !== chunkIds.length) { + return NextResponse.json({ error: 'Some chunks not found' }, { status: 400 }); + } + + // 准备更新数据 + const updates = chunksToUpdate.map(chunk => { + let newContent; + + if (position === 'start') { + // 在开头添加内容 + newContent = content + '\n\n' + chunk.content; + } else { + // 在结尾添加内容 + newContent = chunk.content + '\n\n' + content; + } + + return { + where: { id: chunk.id }, + data: { + content: newContent, + size: newContent.length, + updateAt: new Date() + } + }; + }); + + async function processBatches(items, batchSize, processFn) { + const results = []; + for (let i = 0; i < items.length; i += batchSize) { + const batch = items.slice(i, i + batchSize); + const batchResults = await Promise.all(batch.map(processFn)); + results.push(...batchResults); + } + return results; + } + + const BATCH_SIZE = 50; // 每批处理 50 个 + await processBatches(updates, BATCH_SIZE, update => prisma.chunks.update(update)); + + // 记录操作日志(可选) + console.log(`Successfully updated ${chunksToUpdate.length} chunks`); + + return NextResponse.json({ + success: true, + updatedCount: chunksToUpdate.length, + message: `Successfully updated ${chunksToUpdate.length} chunks` + }); + } catch (error) { + console.error('批量编辑文本块失败:', error); + + return NextResponse.json( + { + error: 'Batch edit chunks failed', + details: error.message + }, + { status: 500 } + ); + } finally { + await prisma.$disconnect(); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/chunks/name/route.js b/easy-dataset-main/app/api/projects/[projectId]/chunks/name/route.js new file mode 100644 index 0000000..5aa66d9 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/chunks/name/route.js @@ -0,0 +1,35 @@ +import { NextResponse } from 'next/server'; +import { getChunkByName } from '@/lib/db/chunks'; + +/** + * 根据文本块名称获取文本块 + * @param {Request} request 请求对象 + * @param {object} context 上下文,包含路径参数 + * @returns {Promise} 响应对象 + */ +export async function GET(request, { params }) { + try { + const { projectId } = params; + + // 从查询参数中获取 chunkName + const { searchParams } = new URL(request.url); + const chunkName = searchParams.get('chunkName'); + + if (!chunkName) { + return NextResponse.json({ error: '文本块名称不能为空' }, { status: 400 }); + } + + // 根据名称和项目ID查询文本块 + const chunk = await getChunkByName(projectId, chunkName); + + if (!chunk) { + return NextResponse.json({ error: '未找到指定的文本块' }, { status: 404 }); + } + + // 返回文本块信息 + return NextResponse.json(chunk); + } catch (error) { + console.error('根据名称获取文本块失败:', String(error)); + return NextResponse.json({ error: '获取文本块失败: ' + error.message }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/chunks/route.js b/easy-dataset-main/app/api/projects/[projectId]/chunks/route.js new file mode 100644 index 0000000..d944448 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/chunks/route.js @@ -0,0 +1,21 @@ +import { NextResponse } from 'next/server'; +import { deleteChunkById, getChunkByFileIds, getChunkById, getChunksByFileIds, updateChunkById } from '@/lib/db/chunks'; + +// 获取文本块内容 +export async function POST(request, { params }) { + try { + const { projectId } = params; + // 验证参数 + if (!projectId) { + return NextResponse.json({ error: 'Project ID cannot be empty' }, { status: 400 }); + } + const { array } = await request.json(); + // 获取文本块内容 + const chunk = await getChunksByFileIds(array); + + return NextResponse.json(chunk); + } catch (error) { + console.error('Failed to get text block content:', String(error)); + return NextResponse.json({ error: String(error) || 'Failed to get text block content' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/config/route.js b/easy-dataset-main/app/api/projects/[projectId]/config/route.js new file mode 100644 index 0000000..660d83d --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/config/route.js @@ -0,0 +1,36 @@ +import { NextResponse } from 'next/server'; +import { getProject, updateProject, getTaskConfig } from '@/lib/db/projects'; + +// 获取项目配置 +export async function GET(request, { params }) { + try { + const projectId = params.projectId; + const config = await getProject(projectId); + const taskConfig = await getTaskConfig(projectId); + return NextResponse.json({ ...config, ...taskConfig }); + } catch (error) { + console.error('获取项目配置失败:', String(error)); + return NextResponse.json({ error: error.message }, { status: 500 }); + } +} + +// 更新项目配置 +export async function PUT(request, { params }) { + try { + const projectId = params.projectId; + const newConfig = await request.json(); + const currentConfig = await getProject(projectId); + + // 只更新 prompts 部分 + const updatedConfig = { + ...currentConfig, + ...newConfig.prompts + }; + + const config = await updateProject(projectId, updatedConfig); + return NextResponse.json(config); + } catch (error) { + console.error('更新项目配置失败:', String(error)); + return NextResponse.json({ error: error.message }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/custom-prompts/route.js b/easy-dataset-main/app/api/projects/[projectId]/custom-prompts/route.js new file mode 100644 index 0000000..64fc95a --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/custom-prompts/route.js @@ -0,0 +1,105 @@ +import { NextResponse } from 'next/server'; +import { + getCustomPrompts, + getCustomPrompt, + saveCustomPrompt, + deleteCustomPrompt, + batchSaveCustomPrompts, + toggleCustomPrompt, + getPromptTemplates +} from '@/lib/db/custom-prompts'; + +// 获取项目的自定义提示词 +export async function GET(request, { params }) { + try { + const { projectId } = params; + const { searchParams } = new URL(request.url); + const promptType = searchParams.get('promptType'); + const language = searchParams.get('language'); + + if (!projectId) { + return NextResponse.json({ error: 'Project ID is required' }, { status: 400 }); + } + + const customPrompts = await getCustomPrompts(projectId, promptType, language); + const templates = await getPromptTemplates(); + + return NextResponse.json({ + success: true, + customPrompts, + templates + }); + } catch (error) { + console.error('获取自定义提示词失败:', error); + return NextResponse.json({ error: error.message }, { status: 500 }); + } +} + +// 保存自定义提示词 +export async function POST(request, { params }) { + try { + const { projectId } = params; + const body = await request.json(); + + if (!projectId) { + return NextResponse.json({ error: 'Project ID is required' }, { status: 400 }); + } + + // 批量保存 + if (body.prompts && Array.isArray(body.prompts)) { + const results = await batchSaveCustomPrompts(projectId, body.prompts); + return NextResponse.json({ + success: true, + results + }); + } + + // 单个保存 + const { promptType, promptKey, language, content } = body; + if (!promptType || !promptKey || !language || content === undefined) { + return NextResponse.json( + { + error: 'promptType, promptKey, language and content are required' + }, + { status: 400 } + ); + } + + const result = await saveCustomPrompt(projectId, promptType, promptKey, language, content); + return NextResponse.json({ + success: true, + result + }); + } catch (error) { + console.error('保存自定义提示词失败:', error); + return NextResponse.json({ error: error.message }, { status: 500 }); + } +} + +// 删除自定义提示词 +export async function DELETE(request, { params }) { + try { + const { projectId } = params; + const { searchParams } = new URL(request.url); + const promptType = searchParams.get('promptType'); + const promptKey = searchParams.get('promptKey'); + const language = searchParams.get('language'); + + if (!projectId || !promptType || !promptKey || !language) { + return NextResponse.json( + { + error: 'projectId, promptType, promptKey and language are required' + }, + { status: 400 } + ); + } + + const success = await deleteCustomPrompt(projectId, promptType, promptKey, language); + return NextResponse.json({ + success + }); + } catch (error) { + console.error('删除自定义提示词失败:', error); + return NextResponse.json({ error: error.message }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/custom-split/route.js b/easy-dataset-main/app/api/projects/[projectId]/custom-split/route.js new file mode 100644 index 0000000..d587407 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/custom-split/route.js @@ -0,0 +1,116 @@ +import { NextResponse } from 'next/server'; +import { saveChunks, deleteChunksByFileId } from '@/lib/db/chunks'; +import path from 'path'; +import fs from 'fs/promises'; +import { getProjectRoot } from '@/lib/db/base'; + +/** + * 处理自定义分块请求 + * @param {Request} request - 请求对象 + * @param {Object} params - 路由参数 + * @returns {Promise} - 响应对象 + */ +export async function POST(request, { params }) { + try { + const { projectId } = params; + const { fileId, fileName, content, splitPoints } = await request.json(); + + // 参数验证 + if (!projectId || !fileId || !fileName || !content || !splitPoints) { + return NextResponse.json({ error: 'Missing required parameters' }, { status: 400 }); + } + + // 获取项目根目录 + const projectRoot = await getProjectRoot(); + const projectPath = path.join(projectRoot, projectId); + + // 检查项目是否存在 + try { + await fs.access(projectPath); + } catch (error) { + return NextResponse.json({ error: 'Project does not exist' }, { status: 404 }); + } + + // 先删除该文件已有的文本块 + await deleteChunksByFileId(projectId, fileId); + + // 根据分块点将文件内容分割成多个块 + const customChunks = generateCustomChunks(projectId, fileId, fileName, content, splitPoints); + + // 保存新的文本块 + await saveChunks(customChunks); + + return NextResponse.json({ + success: true, + message: 'Custom chunks saved successfully', + totalChunks: customChunks.length + }); + } catch (error) { + console.error('自定义分块处理出错:', String(error)); + return NextResponse.json({ error: error.message || 'Failed to process custom split request' }, { status: 500 }); + } +} + +/** + * 根据分块点生成自定义文本块 + * @param {string} projectId - 项目ID + * @param {string} fileId - 文件ID + * @param {string} fileName - 文件名 + * @param {string} content - 文件内容 + * @param {Array} splitPoints - 分块点数组 + * @returns {Array} - 生成的文本块数组 + */ +function generateCustomChunks(projectId, fileId, fileName, content, splitPoints) { + // 按位置排序分块点 + const sortedPoints = [...splitPoints].sort((a, b) => a.position - b.position); + + // 创建分块 + const chunks = []; + let startPos = 0; + + // 处理每个分块点 + for (let i = 0; i < sortedPoints.length; i++) { + const endPos = sortedPoints[i].position; + + // 提取当前分块内容 + const chunkContent = content.substring(startPos, endPos); + + // 跳过空白分块 + if (chunkContent.trim().length === 0) { + startPos = endPos; + continue; + } + + // 创建分块对象 + const chunk = { + projectId, + name: `${path.basename(fileName, path.extname(fileName))}-part-${i + 1}`, + fileId, + fileName, + content: chunkContent, + summary: `${fileName} 自定义分块 ${i + 1}/${sortedPoints.length + 1}`, + size: chunkContent.length + }; + + chunks.push(chunk); + startPos = endPos; + } + + // 添加最后一个分块(如果有内容) + const lastChunkContent = content.substring(startPos); + if (lastChunkContent.trim().length > 0) { + const lastChunk = { + projectId, + name: `${path.basename(fileName, path.extname(fileName))}-part-${sortedPoints.length + 1}`, + fileId, + fileName, + content: lastChunkContent, + summary: `${fileName} 自定义分块 ${sortedPoints.length + 1}/${sortedPoints.length + 1}`, + size: lastChunkContent.length + }; + + chunks.push(lastChunk); + } + + return chunks; +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/dataset-conversations/[conversationId]/route.js b/easy-dataset-main/app/api/projects/[projectId]/dataset-conversations/[conversationId]/route.js new file mode 100644 index 0000000..065b9d0 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/dataset-conversations/[conversationId]/route.js @@ -0,0 +1,183 @@ +/** + * 单个多轮对话数据集操作API + */ + +import { NextResponse } from 'next/server'; +import { + getDatasetConversationById, + updateDatasetConversation, + deleteDatasetConversation, + getConversationNavigationItems +} from '@/lib/db/dataset-conversations'; + +/** + * 获取单个多轮对话数据集详情 + */ +export async function GET(request, { params }) { + try { + const { projectId, conversationId } = params; + const { searchParams } = new URL(request.url); + const operateType = searchParams.get('operateType'); + + // 如果是导航操作,返回导航项 + if (operateType !== null) { + const data = await getConversationNavigationItems(projectId, conversationId, operateType); + return NextResponse.json(data); + } + + const conversation = await getDatasetConversationById(conversationId); + + if (!conversation) { + return NextResponse.json( + { + success: false, + message: '对话数据集不存在' + }, + { status: 404 } + ); + } + + if (conversation.projectId !== projectId) { + return NextResponse.json( + { + success: false, + message: '对话数据集不属于指定项目' + }, + { status: 403 } + ); + } + + return NextResponse.json(conversation); + } catch (error) { + console.error('获取多轮对话数据集详情失败:', error); + return NextResponse.json( + { + success: false, + message: error.message + }, + { status: 500 } + ); + } +} + +/** + * 更新多轮对话数据集 + */ +export async function PUT(request, { params }) { + try { + const { projectId, conversationId } = params; + const body = await request.json(); + + // 验证对话数据集是否存在且属于项目 + const conversation = await getDatasetConversationById(conversationId); + + if (!conversation) { + return NextResponse.json( + { + success: false, + message: '对话数据集不存在' + }, + { status: 404 } + ); + } + + if (conversation.projectId !== projectId) { + return NextResponse.json( + { + success: false, + message: '对话数据集不属于指定项目' + }, + { status: 403 } + ); + } + + // 只允许更新特定字段 + const allowedFields = ['score', 'tags', 'note', 'confirmed', 'aiEvaluation', 'messages']; + const updateData = {}; + + allowedFields.forEach(field => { + if (body.hasOwnProperty(field)) { + if (field === 'messages') { + // 将messages数组转换为rawMessages字符串存储 + updateData['rawMessages'] = JSON.stringify(body[field]); + } else { + updateData[field] = body[field]; + } + } + }); + + if (Object.keys(updateData).length === 0) { + return NextResponse.json( + { + success: false, + message: '没有有效的更新字段' + }, + { status: 400 } + ); + } + + const updatedConversation = await updateDatasetConversation(conversationId, updateData); + + return NextResponse.json({ + success: true, + data: updatedConversation + }); + } catch (error) { + console.error('更新多轮对话数据集失败:', error); + return NextResponse.json( + { + success: false, + message: error.message + }, + { status: 500 } + ); + } +} + +/** + * 删除多轮对话数据集 + */ +export async function DELETE(request, { params }) { + try { + const { projectId, conversationId } = params; + + // 验证对话数据集是否存在且属于项目 + const conversation = await getDatasetConversationById(conversationId); + + if (!conversation) { + return NextResponse.json( + { + success: false, + message: '对话数据集不存在' + }, + { status: 404 } + ); + } + + if (conversation.projectId !== projectId) { + return NextResponse.json( + { + success: false, + message: '对话数据集不属于指定项目' + }, + { status: 403 } + ); + } + + await deleteDatasetConversation(conversationId); + + return NextResponse.json({ + success: true, + message: '删除成功' + }); + } catch (error) { + console.error('删除多轮对话数据集失败:', error); + return NextResponse.json( + { + success: false, + message: error.message + }, + { status: 500 } + ); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/dataset-conversations/export/route.js b/easy-dataset-main/app/api/projects/[projectId]/dataset-conversations/export/route.js new file mode 100644 index 0000000..ba51d2a --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/dataset-conversations/export/route.js @@ -0,0 +1,68 @@ +/** + * 多轮对话数据集导出API + * 直接导出原始的 ShareGPT 格式数据集 + */ + +import { NextResponse } from 'next/server'; +import { getAllDatasetConversations } from '@/lib/db/dataset-conversations'; + +/** + * 导出多轮对话数据集 + */ +export async function GET(request, { params }) { + try { + const { projectId } = params; + const { searchParams } = new URL(request.url); + + // 筛选条件 + const filters = { + confirmed: searchParams.get('confirmed') + }; + + // 清除空值 + Object.keys(filters).forEach(key => { + if (!filters[key]) delete filters[key]; + }); + + // 获取所有对话数据集 + const conversations = await getAllDatasetConversations(projectId, filters); + + if (conversations.length === 0) { + return NextResponse.json([]); + } + + // 转换为 ShareGPT 格式数组 + const shareGptData = []; + + for (const conversation of conversations) { + try { + // 解析 rawMessages + const messages = JSON.parse(conversation.rawMessages || '[]'); + + if (messages.length > 0) { + // 构建 ShareGPT 格式对象 + const shareGptItem = { + messages: messages + }; + + shareGptData.push(shareGptItem); + } + } catch (error) { + console.error(`解析对话消息失败 ${conversation.id}:`, error); + // 跳过解析失败的对话,继续处理其他对话 + continue; + } + } + + return NextResponse.json(shareGptData); + } catch (error) { + console.error('导出多轮对话数据集失败:', error); + return NextResponse.json( + { + success: false, + message: error.message + }, + { status: 500 } + ); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/dataset-conversations/route.js b/easy-dataset-main/app/api/projects/[projectId]/dataset-conversations/route.js new file mode 100644 index 0000000..76aa359 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/dataset-conversations/route.js @@ -0,0 +1,135 @@ +/** + * 多轮对话数据集管理API + */ + +import { NextResponse } from 'next/server'; +import { + getDatasetConversationsByPagination, + getAllDatasetConversationIds, + createDatasetConversation +} from '@/lib/db/dataset-conversations'; +import { generateMultiTurnConversation } from '@/lib/services/multi-turn/index'; + +/** + * 获取多轮对话数据集列表(支持分页和筛选) + */ +export async function GET(request, { params }) { + try { + const { projectId } = params; + const { searchParams } = new URL(request.url); + + const getAllIds = searchParams.get('getAllIds') === 'true'; // 新增:获取所有对话ID的标志 + + // 筛选条件 + const filters = { + keyword: searchParams.get('keyword'), + roleA: searchParams.get('roleA'), + roleB: searchParams.get('roleB'), + scenario: searchParams.get('scenario'), + scoreMin: searchParams.get('scoreMin'), + scoreMax: searchParams.get('scoreMax'), + confirmed: searchParams.get('confirmed') + }; + + // 清除空值 + Object.keys(filters).forEach(key => { + if (!filters[key]) delete filters[key]; + }); + + // 如果请求获取所有ID + if (getAllIds) { + const allConversationIds = await getAllDatasetConversationIds(projectId, filters); + return NextResponse.json({ allConversationIds }); + } + + // 正常分页查询 + const page = parseInt(searchParams.get('page') || '1'); + const pageSize = parseInt(searchParams.get('pageSize') || '20'); + + const result = await getDatasetConversationsByPagination(projectId, page, pageSize, filters); + + return NextResponse.json({ + success: true, + ...result + }); + } catch (error) { + console.error('获取多轮对话数据集失败:', error); + return NextResponse.json( + { + success: false, + message: error.message + }, + { status: 500 } + ); + } +} + +/** + * 创建多轮对话数据集 + */ +export async function POST(request, { params }) { + try { + const { projectId } = params; + const body = await request.json(); + + const { questionId, systemPrompt, scenario, rounds, roleA, roleB, model, language = '中文' } = body; + + if (!questionId) { + return NextResponse.json( + { + success: false, + message: '问题ID不能为空' + }, + { status: 400 } + ); + } + + if (!model || !model.modelId) { + return NextResponse.json( + { + success: false, + message: '模型配置不能为空' + }, + { status: 400 } + ); + } + + // 构建配置 + const config = { + systemPrompt: systemPrompt || '', + scenario: scenario || '', + rounds: rounds || 3, + roleA: roleA || '用户', + roleB: roleB || '助手', + model, + language + }; + + // 生成多轮对话 + const result = await generateMultiTurnConversation(projectId, questionId, config); + + if (!result.success) { + return NextResponse.json( + { + success: false, + message: result.error + }, + { status: 500 } + ); + } + + return NextResponse.json({ + success: true, + data: result.data + }); + } catch (error) { + console.error('创建多轮对话数据集失败:', error); + return NextResponse.json( + { + success: false, + message: error.message + }, + { status: 500 } + ); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/dataset-conversations/tags/route.js b/easy-dataset-main/app/api/projects/[projectId]/dataset-conversations/tags/route.js new file mode 100644 index 0000000..080e6cc --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/dataset-conversations/tags/route.js @@ -0,0 +1,42 @@ +import { NextResponse } from 'next/server'; +import { getAllDatasetConversations } from '@/lib/db/dataset-conversations'; + +/** + * 获取项目中多轮对话数据集的所有标签 + */ +export async function GET(request, { params }) { + try { + const { projectId } = params; + + if (!projectId) { + return NextResponse.json({ error: '项目ID不能为空' }, { status: 400 }); + } + + // 获取项目所有对话数据集 + const conversations = await getAllDatasetConversations(projectId); + + // 提取所有标签 + const allTags = new Set(); + + conversations.forEach(conversation => { + if (conversation.tags && typeof conversation.tags === 'string') { + const tags = conversation.tags.split(/\s+/).filter(tag => tag.trim().length > 0); + tags.forEach(tag => allTags.add(tag.trim())); + } + }); + + return NextResponse.json({ + success: true, + tags: Array.from(allTags).sort() + }); + } catch (error) { + console.error('获取对话标签失败:', error); + return NextResponse.json( + { + success: false, + message: error.message + }, + { status: 500 } + ); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/datasets/[datasetId]/copy-to-eval/route.js b/easy-dataset-main/app/api/projects/[projectId]/datasets/[datasetId]/copy-to-eval/route.js new file mode 100644 index 0000000..31a8075 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/datasets/[datasetId]/copy-to-eval/route.js @@ -0,0 +1,77 @@ +import { NextResponse } from 'next/server'; +import { db } from '@/lib/db'; + +export async function POST(req, { params }) { + try { + const { projectId, datasetId } = params; + + // 1. 获取数据集详情 + const dataset = await db.datasets.findUnique({ + where: { id: datasetId, projectId } + }); + + if (!dataset) { + return NextResponse.json({ error: 'Dataset not found' }, { status: 404 }); + } + + // 2. 尝试通过 questionId 查找关联的 chunkId + let chunkId = null; + if (dataset.questionId) { + const question = await db.questions.findUnique({ + where: { id: dataset.questionId } + }); + if (question) { + chunkId = question.chunkId; + } + } + + // 3. 创建评估数据集记录 + // 默认使用 open_ended 类型,因为通常数据集是问答对,适合作为评估 + let evalTags = []; + try { + evalTags = JSON.parse(dataset.tags || '[]'); + if (!Array.isArray(evalTags)) evalTags = []; + } catch (e) { + evalTags = []; + } + + // 排除 'Eval' 标签,并将数组转为逗号分隔的字符串 + const evalTagsString = evalTags.filter(tag => tag !== 'Eval').join(','); + + const evalDataset = await db.evalDatasets.create({ + data: { + projectId, + question: dataset.question, + questionType: 'open_ended', + correctAnswer: dataset.answer, + tags: evalTagsString, + note: dataset.note, + chunkId: chunkId, + options: '' // 开放题不需要选项 + } + }); + + // 4. 更新原数据集,添加 'Eval' 标签 + let currentTags = []; + try { + currentTags = JSON.parse(dataset.tags || '[]'); + } catch (e) { + // ignore error + } + + if (!currentTags.includes('Eval')) { + currentTags.push('Eval'); + await db.datasets.update({ + where: { id: datasetId }, + data: { + tags: JSON.stringify(currentTags) + } + }); + } + + return NextResponse.json({ success: true, evalDataset }); + } catch (error) { + console.error('Failed to copy dataset to eval:', error); + return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/datasets/[datasetId]/evaluate/route.js b/easy-dataset-main/app/api/projects/[projectId]/datasets/[datasetId]/evaluate/route.js new file mode 100644 index 0000000..ff8d455 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/datasets/[datasetId]/evaluate/route.js @@ -0,0 +1,36 @@ +import { NextResponse } from 'next/server'; +import { evaluateDataset } from '@/lib/services/datasets/evaluation'; + +/** + * 评估单个数据集的质量 + */ +export async function POST(request, { params }) { + try { + const { projectId, datasetId } = params; + const { model, language = 'zh-CN' } = await request.json(); + + if (!projectId || !datasetId) { + return NextResponse.json({ success: false, message: '项目ID和数据集ID不能为空' }, { status: 400 }); + } + + if (!model) { + return NextResponse.json({ success: false, message: '模型配置不能为空' }, { status: 400 }); + } + + // 使用评估服务进行数据集评估 + const result = await evaluateDataset(projectId, datasetId, model, language); + + if (!result.success) { + return NextResponse.json({ success: false, message: result.error }, { status: 500 }); + } + + return NextResponse.json({ + success: true, + message: '数据集评估完成', + data: result.data + }); + } catch (error) { + console.error('数据集评估失败:', error); + return NextResponse.json({ success: false, message: `评估失败: ${error.message}` }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/datasets/[datasetId]/route.js b/easy-dataset-main/app/api/projects/[projectId]/datasets/[datasetId]/route.js new file mode 100644 index 0000000..4964c9e --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/datasets/[datasetId]/route.js @@ -0,0 +1,82 @@ +import { NextResponse } from 'next/server'; +import { getDatasetsById, getDatasetsCounts, getNavigationItems, updateDatasetMetadata } from '@/lib/db/datasets'; + +/** + * 获取项目的所有数据集 + */ +export async function GET(request, { params }) { + try { + const { projectId, datasetId } = params; + // 验证项目ID + if (!projectId) { + return NextResponse.json({ error: '项目ID不能为空' }, { status: 400 }); + } + if (!datasetId) { + return NextResponse.json({ error: '数据集ID不能为空' }, { status: 400 }); + } + const { searchParams } = new URL(request.url); + const operateType = searchParams.get('operateType'); + if (operateType !== null) { + const data = await getNavigationItems(projectId, datasetId, operateType); + return NextResponse.json(data); + } + const datasets = await getDatasetsById(datasetId); + let counts = await getDatasetsCounts(projectId); + + return NextResponse.json({ datasets, ...counts }); + } catch (error) { + console.error('获取数据集详情失败:', String(error)); + return NextResponse.json( + { + error: error.message || '获取数据集详情失败' + }, + { status: 500 } + ); + } +} + +/** + * 更新数据集元数据(评分、标签、备注) + */ +export async function PATCH(request, { params }) { + try { + const { projectId, datasetId } = params; + + // 验证参数 + if (!projectId) { + return NextResponse.json({ error: '项目ID不能为空' }, { status: 400 }); + } + if (!datasetId) { + return NextResponse.json({ error: '数据集ID不能为空' }, { status: 400 }); + } + + const body = await request.json(); + const { score, tags, note } = body; + + // 验证评分范围 + if (score !== undefined && (score < 0 || score > 5)) { + return NextResponse.json({ error: '评分必须在0-5之间' }, { status: 400 }); + } + + // 验证标签格式 + if (tags !== undefined && !Array.isArray(tags)) { + return NextResponse.json({ error: '标签必须是数组格式' }, { status: 400 }); + } + + // 更新数据集元数据 + const updatedDataset = await updateDatasetMetadata(datasetId, { score, tags, note }); + + return NextResponse.json({ + success: true, + dataset: updatedDataset + }); + } catch (error) { + console.error('更新数据集元数据失败:', String(error)); + return NextResponse.json( + { + error: error.message || '更新数据集元数据失败' + }, + { status: 500 } + ); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/datasets/[datasetId]/token-count/route.js b/easy-dataset-main/app/api/projects/[projectId]/datasets/[datasetId]/token-count/route.js new file mode 100644 index 0000000..37ba645 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/datasets/[datasetId]/token-count/route.js @@ -0,0 +1,52 @@ +import { NextResponse } from 'next/server'; +import { getDatasetsById } from '@/lib/db/datasets'; +import { getEncoding } from '@langchain/core/utils/tiktoken'; + +/** + * 异步计算数据集文本的Token数量 + */ +export async function GET(request, { params }) { + try { + const { projectId, datasetId } = params; + + if (!datasetId) { + return NextResponse.json({ error: '数据集ID不能为空' }, { status: 400 }); + } + + const datasets = await getDatasetsById(datasetId); + const tokenCounts = { + answerTokens: 0, + cotTokens: 0 + }; + + try { + if (datasets.answer || datasets.cot) { + // 使用 cl100k_base 编码,适用于 gpt-3.5-turbo 和 gpt-4 + const encoding = await getEncoding('cl100k_base'); + + if (datasets.answer) { + const tokens = encoding.encode(datasets.answer); + tokenCounts.answerTokens = tokens.length; + } + + if (datasets.cot) { + const tokens = encoding.encode(datasets.cot); + tokenCounts.cotTokens = tokens.length; + } + } + } catch (error) { + console.error('计算Token数量失败:', String(error)); + return NextResponse.json({ error: '计算Token数量失败' }, { status: 500 }); + } + + return NextResponse.json(tokenCounts); + } catch (error) { + console.error('获取Token计数失败:', String(error)); + return NextResponse.json( + { + error: error.message || '获取Token计数失败' + }, + { status: 500 } + ); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/datasets/batch-evaluate/route.js b/easy-dataset-main/app/api/projects/[projectId]/datasets/batch-evaluate/route.js new file mode 100644 index 0000000..5803580 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/datasets/batch-evaluate/route.js @@ -0,0 +1,55 @@ +/** + * 批量数据集评估任务API + * 创建批量评估数据集质量的异步任务 + */ + +import { NextResponse } from 'next/server'; +import { db } from '@/lib/db/index'; +import { processTask } from '@/lib/services/tasks/index'; + +/** + * 创建批量数据集评估任务 + */ +export async function POST(request, { params }) { + try { + const { projectId } = params; + const { model, language = 'zh-CN' } = await request.json(); + + if (!projectId) { + return NextResponse.json({ success: false, message: '项目ID不能为空' }, { status: 400 }); + } + + if (!model || !model.modelId) { + return NextResponse.json({ success: false, message: '模型配置不能为空' }, { status: 400 }); + } + + // 创建批量评估任务 + const newTask = await db.task.create({ + data: { + projectId, + taskType: 'dataset-evaluation', + status: 0, // 初始状态: 处理中 + modelInfo: JSON.stringify(model), + language: language || 'zh-CN', + detail: '', + totalCount: 0, + note: '准备开始批量评估数据集质量...', + completedCount: 0 + } + }); + + // 异步处理任务 + processTask(newTask.id).catch(err => { + console.error(`批量评估任务启动失败: ${newTask.id}`, String(err)); + }); + + return NextResponse.json({ + success: true, + message: '批量评估任务已创建', + data: { taskId: newTask.id } + }); + } catch (error) { + console.error('创建批量评估任务失败:', error); + return NextResponse.json({ success: false, message: `创建任务失败: ${error.message}` }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/datasets/export/route.js b/easy-dataset-main/app/api/projects/[projectId]/datasets/export/route.js new file mode 100644 index 0000000..f7982f6 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/datasets/export/route.js @@ -0,0 +1,128 @@ +import { NextResponse } from 'next/server'; +import { + getDatasets, + getBalancedDatasetsByTags, + getTagsWithDatasetCounts, + getDatasetsBatch, + getBalancedDatasetsByTagsBatch, + getDatasetsByIds, + getDatasetsByIdsBatch +} from '@/lib/db/datasets'; + +/** + * 获取导出数据集 + */ +export async function GET(request, { params }) { + try { + const { projectId } = params; + const { searchParams } = new URL(request.url); + + // 验证项目ID + if (!projectId) { + return NextResponse.json({ error: 'Project ID cannot be empty' }, { status: 400 }); + } + + const confirmedParam = searchParams.get('confirmed'); + const confirmed = confirmedParam === null ? undefined : confirmedParam === 'true'; + + // 获取标签统计信息 + const tagStats = await getTagsWithDatasetCounts(projectId, confirmed); + return NextResponse.json(tagStats); + } catch (error) { + console.error('Failed to get tag statistics:', String(error)); + return NextResponse.json( + { + error: error.message || 'Failed to get tag statistics' + }, + { status: 500 } + ); + } +} + +/** + * 获取标签统计信息 + */ +export async function POST(request, { params }) { + try { + const { projectId } = params; + const body = await request.json(); + + // 验证项目ID + if (!projectId) { + return NextResponse.json({ error: 'Project ID cannot be empty' }, { status: 400 }); + } + + let status = body.status; + let confirmed = undefined; + if (status === 'confirmed') confirmed = true; + if (status === 'unconfirmed') confirmed = false; + + // 检查是否是分批导出模式 + const batchMode = body.batchMode ? 'true' : 'false'; + const offset = body.offset ?? 0; + const batchSize = body.batchSize ?? 1000; + + // 检查是否是平衡导出 + const balanceMode = body.balanceMode ? 'true' : 'false'; + const balanceConfig = body.balanceConfig; + + // 检查是否有选中的数据集 ID + const selectedIds = Array.isArray(body.selectedIds) ? body.selectedIds : null; + + if (batchMode === 'true') { + // 分批导出模式 + if (selectedIds && selectedIds.length > 0) { + // 按选中 ID 分批导出 + const datasets = await getDatasetsByIdsBatch(projectId, selectedIds, offset, batchSize); + const hasMore = datasets.length === batchSize; + return NextResponse.json({ + data: datasets, + hasMore, + offset: offset + datasets.length + }); + } else if (balanceMode === 'true' && balanceConfig) { + // 平衡分批导出 + const parsedConfig = typeof balanceConfig === 'string' ? JSON.parse(balanceConfig) : balanceConfig; + const result = await getBalancedDatasetsByTagsBatch(projectId, parsedConfig, confirmed, offset, batchSize); + return NextResponse.json({ + data: result.data, + hasMore: result.hasMore, + offset: offset + result.data.length + }); + } else { + // 常规分批导出 + const datasets = await getDatasetsBatch(projectId, confirmed, offset, batchSize); + const hasMore = datasets.length === batchSize; + return NextResponse.json({ + data: datasets, + hasMore, + offset: offset + datasets.length + }); + } + } else { + // 传统一次性导出模式(保持向后兼容) + if (selectedIds && selectedIds.length > 0) { + // 按选中 ID 导出 + const datasets = await getDatasetsByIds(projectId, selectedIds); + return NextResponse.json(datasets); + } else if (balanceMode === 'true' && balanceConfig) { + // 平衡导出模式 + const parsedConfig = typeof balanceConfig === 'string' ? JSON.parse(balanceConfig) : balanceConfig; + const datasets = await getBalancedDatasetsByTags(projectId, parsedConfig, confirmed); + return NextResponse.json(datasets); + } else { + // 常规导出模式 + const datasets = await getDatasets(projectId, confirmed); + return NextResponse.json(datasets); + } + } + } catch (error) { + console.error('Failed to get datasets:', String(error)); + return NextResponse.json( + { + error: error.message || 'Failed to get datasets' + }, + { status: 500 } + ); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/datasets/generate-eval-variant/route.js b/easy-dataset-main/app/api/projects/[projectId]/datasets/generate-eval-variant/route.js new file mode 100644 index 0000000..c6458a8 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/datasets/generate-eval-variant/route.js @@ -0,0 +1,44 @@ +import { NextResponse } from 'next/server'; +import { getDatasetsById } from '@/lib/db/datasets'; +import LLMClient from '@/lib/llm/core/index'; +import { getEvalQuestionPrompt } from '@/lib/llm/prompts/evalQuestion'; +import { extractJsonFromLLMOutput } from '@/lib/llm/common/util'; + +export async function POST(request, { params }) { + try { + const { projectId } = params; + const { datasetId, model, language, questionType = 'open_ended', count = 1 } = await request.json(); + + if (!datasetId || !model) { + return NextResponse.json({ error: 'Missing required parameters' }, { status: 400 }); + } + + // 1. 获取原数据集 + const dataset = await getDatasetsById(datasetId); + if (!dataset) { + return NextResponse.json({ error: 'Dataset not found' }, { status: 404 }); + } + + // 2. 构建提示词 + // 将原问题和答案合并作为上下文文本 + const text = `Question: ${dataset.question}\nAnswer: ${dataset.answer}`; + + const prompt = await getEvalQuestionPrompt(language || 'zh-CN', questionType, { text, number: count }, projectId); + + // 3. 调用 LLM + const client = new LLMClient(model); + + const response = await client.getResponse(prompt); + const result = extractJsonFromLLMOutput(response); + + // 结果应该是一个数组 + if (!result || !Array.isArray(result)) { + throw new Error('Failed to parse LLM output or output is not an array'); + } + + return NextResponse.json({ success: true, data: result }); + } catch (error) { + console.error('Generate eval variant failed:', error); + return NextResponse.json({ error: error.message || 'Internal Server Error' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/datasets/import/route.js b/easy-dataset-main/app/api/projects/[projectId]/datasets/import/route.js new file mode 100644 index 0000000..86de7ef --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/datasets/import/route.js @@ -0,0 +1,109 @@ +import { NextResponse } from 'next/server'; +import { createDataset } from '@/lib/db/datasets'; +import { nanoid } from 'nanoid'; + +export async function POST(request, { params }) { + try { + const { projectId } = params; + const { datasets, sourceInfo } = await request.json(); + + if (!datasets || !Array.isArray(datasets)) { + return NextResponse.json({ error: 'Invalid datasets data' }, { status: 400 }); + } + + const results = []; + const errors = []; + let successCount = 0; + let skippedCount = 0; + + for (let i = 0; i < datasets.length; i++) { + try { + const dataset = datasets[i]; + + // 安全获取与清洗字段 + const q = typeof dataset?.question === 'string' ? dataset.question.trim() : ''; + const a = typeof dataset?.answer === 'string' ? dataset.answer.trim() : ''; + + // 验证必填字段:缺失则跳过 + if (!q || !a) { + errors.push(`第 ${i + 1} 条记录缺少必填字段(question/answer),已跳过`); + skippedCount++; + continue; + } + + // 规范化可选字段 + const chunkName = dataset?.chunkName || 'Imported Data'; + const chunkContent = dataset?.chunkContent || 'Imported from external source'; + const model = dataset?.model || 'imported'; + const questionLabel = dataset?.questionLabel || ''; + const cot = typeof dataset?.cot === 'string' ? dataset.cot : ''; + const confirmed = typeof dataset?.confirmed === 'boolean' ? dataset.confirmed : false; + const score = typeof dataset?.score === 'number' ? dataset.score : 0; + // tags: 支持数组/字符串/对象 + let tags = '[]'; + if (Array.isArray(dataset?.tags)) { + try { + tags = JSON.stringify(dataset.tags); + } catch { + tags = '[]'; + } + } else if (typeof dataset?.tags === 'string') { + tags = dataset.tags; + } else if (dataset?.tags && typeof dataset.tags === 'object') { + try { + tags = JSON.stringify(dataset.tags); + } catch { + tags = '[]'; + } + } + // other: 对象或字符串 + let other = '{}'; + if (typeof dataset?.other === 'string') { + other = dataset.other; + } else if (dataset?.other && typeof dataset.other === 'object') { + try { + other = JSON.stringify(dataset.other); + } catch { + other = '{}'; + } + } + const note = typeof dataset?.note === 'string' ? dataset.note : ''; + + // 创建数据集记录 + const newDataset = await createDataset({ + projectId, + questionId: nanoid(), // 生成唯一的问题ID + question: q, + answer: a, + chunkName, + chunkContent, + model, + questionLabel, + cot, + confirmed, + score, + tags, + note, + other + }); + + results.push(newDataset); + successCount++; + } catch (error) { + errors.push(`第 ${i + 1} 条记录: ${error.message}`); + } + } + + return NextResponse.json({ + success: successCount, + total: datasets.length, + failed: errors.length, + skipped: skippedCount, + errors, + sourceInfo + }); + } catch (error) { + console.error('Import datasets error:', error); + return NextResponse.json({ error: error.message }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/datasets/optimize/route.js b/easy-dataset-main/app/api/projects/[projectId]/datasets/optimize/route.js new file mode 100644 index 0000000..7a0067c --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/datasets/optimize/route.js @@ -0,0 +1,89 @@ +import { NextResponse } from 'next/server'; +import { getDatasetsById, updateDataset } from '@/lib/db/datasets'; +import { getQuestionById } from '@/lib/db/questions'; +import { getChunkById } from '@/lib/db/chunks'; +import LLMClient from '@/lib/llm/core/index'; +import { getNewAnswerPrompt } from '@/lib/llm/prompts/newAnswer'; +import { extractJsonFromLLMOutput } from '@/lib/llm/common/util'; + +// 优化数据集答案 +export async function POST(request, { params }) { + try { + const { projectId } = params; + + // 验证项目ID + if (!projectId) { + return NextResponse.json({ error: 'Project ID cannot be empty' }, { status: 400 }); + } + + // 获取请求体 + const { datasetId, model, advice, language } = await request.json(); + + if (!datasetId) { + return NextResponse.json({ error: 'Dataset ID cannot be empty' }, { status: 400 }); + } + + if (!model) { + return NextResponse.json({ error: 'Model cannot be empty' }, { status: 400 }); + } + + if (!advice) { + return NextResponse.json({ error: 'Please provide optimization suggestions' }, { status: 400 }); + } + + // 获取数据集内容 + const dataset = await getDatasetsById(datasetId); + if (!dataset) { + return NextResponse.json({ error: 'Dataset does not exist' }, { status: 404 }); + } + + // 创建LLM客户端 + const llmClient = new LLMClient(model); + + const { question, answer, cot, chunkContent: storedChunkContent, questionId } = dataset; + + let chunkContent = storedChunkContent || ''; + + if (!chunkContent && questionId) { + try { + const questionRecord = await getQuestionById(questionId); + if (questionRecord?.chunkId) { + const chunkRecord = await getChunkById(questionRecord.chunkId); + chunkContent = chunkRecord?.content || ''; + } + } catch (error) { + console.error('Failed to load chunk content by questionId:', error); + } + } + + // 生成优化后的答案和思维链 + const prompt = await getNewAnswerPrompt(language, { question, answer, cot, advice, chunkContent }, projectId); + + const response = await llmClient.getResponse(prompt); + + // 从LLM输出中提取JSON格式的优化结果 + const optimizedResult = extractJsonFromLLMOutput(response); + + if (!optimizedResult || !optimizedResult.answer) { + return NextResponse.json({ error: 'Failed to optimize answer, please try again' }, { status: 500 }); + } + + // 更新数据集 + const updatedDataset = { + ...dataset, + answer: optimizedResult.answer, + cot: cot ? optimizedResult.cot || cot : '' // 如果没有提供思考过程,则不更新 + }; + + await updateDataset(updatedDataset); + + // 返回优化后的数据集 + return NextResponse.json({ + success: true, + dataset: updatedDataset + }); + } catch (error) { + console.error('Failed to optimize answer:', String(error)); + return NextResponse.json({ error: error.message || 'Failed to optimize answer' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/datasets/route.js b/easy-dataset-main/app/api/projects/[projectId]/datasets/route.js new file mode 100644 index 0000000..88e5e17 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/datasets/route.js @@ -0,0 +1,193 @@ +import { NextResponse } from 'next/server'; +import { + deleteDataset, + getDatasetsByPagination, + getDatasetsIds, + getDatasetsById, + updateDataset +} from '@/lib/db/datasets'; +import datasetService from '@/lib/services/datasets'; + +// 优化思维链函数已移至服务层 + +/** + * 生成数据集(为单个问题生成答案) + */ +export async function POST(request, { params }) { + try { + const { projectId } = params; + const { questionId, model, language } = await request.json(); + + // 使用数据集生成服务 + const result = await datasetService.generateDatasetForQuestion(projectId, questionId, { + model, + language + }); + + return NextResponse.json(result); + } catch (error) { + console.error('Failed to generate dataset:', String(error)); + return NextResponse.json( + { + error: error.message || 'Failed to generate dataset' + }, + { status: 500 } + ); + } +} + +/** + * 获取项目的所有数据集 + */ +export async function GET(request, { params }) { + try { + const { projectId } = params; + const { searchParams } = new URL(request.url); + // 验证项目ID + if (!projectId) { + return NextResponse.json({ error: '项目ID不能为空' }, { status: 400 }); + } + const page = parseInt(searchParams.get('page')) || 1; + const size = parseInt(searchParams.get('size')) || 10; + const input = searchParams.get('input'); + const field = searchParams.get('field') || 'question'; + const status = searchParams.get('status'); + const hasCot = searchParams.get('hasCot'); + const isDistill = searchParams.get('isDistill'); + const scoreRange = searchParams.get('scoreRange'); + const customTag = searchParams.get('customTag'); + const noteKeyword = searchParams.get('noteKeyword'); + const chunkName = searchParams.get('chunkName'); + let confirmed = undefined; + if (status === 'confirmed') confirmed = true; + if (status === 'unconfirmed') confirmed = false; + + let selectedAll = searchParams.get('selectedAll'); + + if (selectedAll) { + let data = await getDatasetsIds( + projectId, + confirmed, + input, + field, + hasCot, + isDistill, + scoreRange, + customTag, + noteKeyword, + chunkName + ); + return NextResponse.json(data); + } + + // 获取数据集 + const datasets = await getDatasetsByPagination( + projectId, + page, + size, + confirmed, + input, + field, // 传递搜索字段参数 + hasCot, // 传递思维链筛选参数 + isDistill, // 传递蒸馏数据集筛选参数 + scoreRange, // 传递评分范围筛选参数 + customTag, // 传递自定义标签筛选参数 + noteKeyword, // 传递备注关键字筛选参数 + chunkName // 传递文本块名称筛选参数 + ); + + return NextResponse.json(datasets); + } catch (error) { + console.error('获取数据集失败:', String(error)); + return NextResponse.json( + { + error: error.message || '获取数据集失败' + }, + { status: 500 } + ); + } +} + +/** + * 删除数据集 + */ +export async function DELETE(request) { + try { + const { searchParams } = new URL(request.url); + const datasetId = searchParams.get('id'); + if (!datasetId) { + return NextResponse.json( + { + error: 'Dataset ID cannot be empty' + }, + { status: 400 } + ); + } + + await deleteDataset(datasetId); + + return NextResponse.json({ + success: true, + message: 'Dataset deleted successfully' + }); + } catch (error) { + console.error('Failed to delete dataset:', error); + return NextResponse.json( + { + error: error.message || 'Failed to delete dataset' + }, + { status: 500 } + ); + } +} + +/** + * 编辑数据集 + */ +export async function PATCH(request) { + try { + const { searchParams } = new URL(request.url); + const datasetId = searchParams.get('id'); + const { answer, cot, question, confirmed } = await request.json(); + if (!datasetId) { + return NextResponse.json( + { + error: 'Dataset ID cannot be empty' + }, + { status: 400 } + ); + } + // 获取所有数据集 + let dataset = await getDatasetsById(datasetId); + if (!dataset) { + return NextResponse.json( + { + error: 'Dataset does not exist' + }, + { status: 404 } + ); + } + let data = { id: datasetId }; + if (confirmed !== undefined) data.confirmed = confirmed; + if (answer) data.answer = answer; + if (cot) data.cot = cot; + if (question) data.question = question; + + // 保存更新后的数据集列表 + await updateDataset(data); + + return NextResponse.json({ + success: true, + message: 'Dataset updated successfully', + dataset: dataset + }); + } catch (error) { + console.error('Failed to update dataset:', String(error)); + return NextResponse.json( + { + error: error.message || 'Failed to update dataset' + }, + { status: 500 } + ); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/datasets/tags/route.js b/easy-dataset-main/app/api/projects/[projectId]/datasets/tags/route.js new file mode 100644 index 0000000..cf056a0 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/datasets/tags/route.js @@ -0,0 +1,28 @@ +import { NextResponse } from 'next/server'; +import { getUsedCustomTags } from '@/lib/db/datasets'; + +/** + * 获取项目中使用过的自定义标签 + */ +export async function GET(request, { params }) { + try { + const { projectId } = params; + + // 验证项目ID + if (!projectId) { + return NextResponse.json({ error: '项目ID不能为空' }, { status: 400 }); + } + + const tags = await getUsedCustomTags(projectId); + + return NextResponse.json({ tags }); + } catch (error) { + console.error('获取自定义标签失败:', String(error)); + return NextResponse.json( + { + error: error.message || '获取自定义标签失败' + }, + { status: 500 } + ); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/default-prompts/route.js b/easy-dataset-main/app/api/projects/[projectId]/default-prompts/route.js new file mode 100644 index 0000000..afe0258 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/default-prompts/route.js @@ -0,0 +1,38 @@ +import { NextResponse } from 'next/server'; + +// 获取默认提示词内容 +export async function GET(request, { params }) { + try { + const { searchParams } = new URL(request.url); + const promptType = searchParams.get('promptType'); + const promptKey = searchParams.get('promptKey'); + + if (!promptType || !promptKey) { + return NextResponse.json({ error: 'promptType and promptKey are required' }, { status: 400 }); + } + + // 动态导入对应的提示词模块 + let promptModule; + try { + promptModule = await import(`@/lib/llm/prompts/${promptType}`); + } catch (error) { + return NextResponse.json({ error: `Prompt module ${promptType} not found` }, { status: 404 }); + } + + // 获取指定的提示词常量 + const promptContent = promptModule[promptKey]; + if (!promptContent) { + return NextResponse.json({ error: `Prompt key ${promptKey} not found in module ${promptType}` }, { status: 404 }); + } + + return NextResponse.json({ + success: true, + content: promptContent, + promptType, + promptKey + }); + } catch (error) { + console.error('获取默认提示词失败:', error); + return NextResponse.json({ error: error.message }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/distill/questions/by-tag/route.js b/easy-dataset-main/app/api/projects/[projectId]/distill/questions/by-tag/route.js new file mode 100644 index 0000000..421fca0 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/distill/questions/by-tag/route.js @@ -0,0 +1,67 @@ +import { NextResponse } from 'next/server'; +import { db } from '@/lib/db'; + +/** + * 根据标签ID获取问题列表 + */ +export async function GET(request, { params }) { + try { + const { projectId } = params; + const { searchParams } = new URL(request.url); + const tagId = searchParams.get('tagId'); + + // 验证参数 + if (!projectId) { + return NextResponse.json({ error: '项目ID不能为空' }, { status: 400 }); + } + + if (!tagId) { + return NextResponse.json({ error: '标签ID不能为空' }, { status: 400 }); + } + + // 获取标签信息 + const tag = await db.tags.findUnique({ + where: { id: tagId } + }); + + if (!tag) { + return NextResponse.json({ error: '标签不存在' }, { status: 404 }); + } + + // 获取或创建蒸馏文本块 + let distillChunk = await db.chunks.findFirst({ + where: { + projectId, + name: 'Distilled Content' + } + }); + + if (!distillChunk) { + // 创建一个特殊的蒸馏文本块 + distillChunk = await db.chunks.create({ + data: { + name: 'Distilled Content', + projectId, + fileId: 'distilled', + fileName: 'distilled.md', + content: + 'This text block is used to store questions generated through data distillation and is not related to actual literature.', + summary: 'Questions generated through data distillation', + size: 0 + } + }); + } + const questions = await db.questions.findMany({ + where: { + projectId, + label: tag.label, + chunkId: distillChunk.id + } + }); + + return NextResponse.json(questions); + } catch (error) { + console.error('[distill/questions/by-tag] 获取问题失败:', String(error)); + return NextResponse.json({ error: error.message || '获取问题失败' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/distill/questions/route.js b/easy-dataset-main/app/api/projects/[projectId]/distill/questions/route.js new file mode 100644 index 0000000..00605ed --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/distill/questions/route.js @@ -0,0 +1,101 @@ +import { NextResponse } from 'next/server'; +import { distillQuestionsPrompt } from '@/lib/llm/prompts/distillQuestions'; +import { db } from '@/lib/db'; + +const LLMClient = require('@/lib/llm/core'); + +/** + * 生成问题接口:根据某个标签链路构造指定数量的问题 + */ +export async function POST(request, { params }) { + try { + const { projectId } = params; + + // 验证项目ID + if (!projectId) { + return NextResponse.json({ error: '项目ID不能为空' }, { status: 400 }); + } + + const { tagPath, currentTag, tagId, count = 5, model, language = 'zh' } = await request.json(); + + if (!currentTag || !tagPath) { + const errorMsg = language === 'en' ? 'Tag information cannot be empty' : '标签信息不能为空'; + return NextResponse.json({ error: errorMsg }, { status: 400 }); + } + + // 首先获取或创建蒸馏文本块 + let distillChunk = await db.chunks.findFirst({ + where: { + projectId, + name: 'Distilled Content' + } + }); + + if (!distillChunk) { + // 创建一个特殊的蒸馏文本块 + distillChunk = await db.chunks.create({ + data: { + name: 'Distilled Content', + projectId, + fileId: 'distilled', + fileName: 'distilled.md', + content: + 'This text block is used to store questions generated through data distillation and is not related to actual literature.', + summary: 'Questions generated through data distillation', + size: 0 + } + }); + } + + // 获取已有的问题,避免重复 + const existingQuestions = await db.questions.findMany({ + where: { + projectId, + label: currentTag, + chunkId: distillChunk.id // 使用蒸馏文本块的 ID + }, + select: { question: true } + }); + + const existingQuestionTexts = existingQuestions.map(q => q.question); + + const llmClient = new LLMClient(model); + const prompt = await distillQuestionsPrompt( + language, + { tagPath, currentTag, count, existingQuestionTexts }, + projectId + ); + const { answer } = await llmClient.getResponseWithCOT(prompt); + + let questions = []; + try { + questions = JSON.parse(answer); + } catch (error) { + console.error('解析问题JSON失败:', String(error)); + // 尝试使用正则表达式提取问题 + const matches = answer.match(/"([^"]+)"/g); + if (matches) { + questions = matches.map(match => match.replace(/"/g, '')); + } + } + + // 保存问题到数据库 + const savedQuestions = []; + for (const questionText of questions) { + const question = await db.questions.create({ + data: { + question: questionText, + projectId, + label: currentTag, + chunkId: distillChunk.id + } + }); + savedQuestions.push(question); + } + + return NextResponse.json(savedQuestions); + } catch (error) { + console.error('生成问题失败:', String(error)); + return NextResponse.json({ error: error.message || '生成问题失败' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/distill/tags/[tagId]/route.js b/easy-dataset-main/app/api/projects/[projectId]/distill/tags/[tagId]/route.js new file mode 100644 index 0000000..78dae0a --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/distill/tags/[tagId]/route.js @@ -0,0 +1,61 @@ +import { NextResponse } from 'next/server'; +import { db } from '@/lib/db'; + +/** + * 更新标签接口 + */ +export async function PUT(request, { params }) { + try { + const { projectId, tagId } = params; + + // 验证参数 + if (!projectId || !tagId) { + return NextResponse.json({ error: '项目ID和标签ID不能为空' }, { status: 400 }); + } + + const { label } = await request.json(); + + if (!label || !label.trim()) { + return NextResponse.json({ error: '标签名称不能为空' }, { status: 400 }); + } + + // 检查标签是否存在 + const existingTag = await db.tags.findUnique({ + where: { id: tagId } + }); + + if (!existingTag) { + return NextResponse.json({ error: '标签不存在' }, { status: 404 }); + } + + // 检查项目ID是否匹配 + if (existingTag.projectId !== projectId) { + return NextResponse.json({ error: '无权限编辑此标签' }, { status: 403 }); + } + + // 检查新标签名称是否已存在(同级标签) + const duplicateTag = await db.tags.findFirst({ + where: { + projectId, + label: label.trim(), + parentId: existingTag.parentId, + id: { not: tagId } + } + }); + + if (duplicateTag) { + return NextResponse.json({ error: '同级标签名称已存在' }, { status: 400 }); + } + + // 更新标签 + const updatedTag = await db.tags.update({ + where: { id: tagId }, + data: { label: label.trim() } + }); + + return NextResponse.json(updatedTag); + } catch (error) { + console.error('[标签编辑] 更新标签失败:', String(error)); + return NextResponse.json({ error: error.message || '更新标签失败' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/distill/tags/all/route.js b/easy-dataset-main/app/api/projects/[projectId]/distill/tags/all/route.js new file mode 100644 index 0000000..53de7c1 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/distill/tags/all/route.js @@ -0,0 +1,31 @@ +import { NextResponse } from 'next/server'; +import { db } from '@/lib/db'; + +/** + * 获取项目的所有蒸馏标签 + */ +export async function GET(request, { params }) { + try { + const { projectId } = params; + + // 验证项目ID + if (!projectId) { + return NextResponse.json({ error: '项目ID不能为空' }, { status: 400 }); + } + + // 获取所有标签 + const tags = await db.tags.findMany({ + where: { + projectId + }, + orderBy: { + label: 'asc' + } + }); + + return NextResponse.json(tags); + } catch (error) { + console.error('获取蒸馏标签失败:', String(error)); + return NextResponse.json({ error: error.message || '获取蒸馏标签失败' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/distill/tags/route.js b/easy-dataset-main/app/api/projects/[projectId]/distill/tags/route.js new file mode 100644 index 0000000..dfa8a36 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/distill/tags/route.js @@ -0,0 +1,88 @@ +import { NextResponse } from 'next/server'; +import { distillTagsPrompt } from '@/lib/llm/prompts/distillTags'; +import { db } from '@/lib/db'; +import { getProject } from '@/lib/db/projects'; + +const LLMClient = require('@/lib/llm/core'); + +/** + * 生成标签接口:根据顶级主题、某级标签构造指定数量的子标签 + */ +export async function POST(request, { params }) { + try { + const { projectId } = params; + + // 验证项目ID + if (!projectId) { + return NextResponse.json({ error: '项目ID不能为空' }, { status: 400 }); + } + + const { parentTag, parentTagId, tagPath, count = 10, model, language = 'zh' } = await request.json(); + + if (!parentTag) { + const errorMsg = language === 'en' ? 'Topic tag name cannot be empty' : '主题标签名称不能为空'; + return NextResponse.json({ error: errorMsg }, { status: 400 }); + } + + // 查询现有标签 + const existingTags = await db.tags.findMany({ + where: { + projectId, + parentId: parentTagId || null + } + }); + + const existingTagNames = existingTags.map(tag => tag.label); + + // 创建LLM客户端 + const llmClient = new LLMClient(model); + + // 生成提示词 + const prompt = await distillTagsPrompt( + language, + { tagPath, parentTag, existingTags: existingTagNames, count }, + projectId + ); + + // 调用大模型生成标签 + const { answer } = await llmClient.getResponseWithCOT(prompt); + + // 解析返回的标签 + let tags = []; + + try { + tags = JSON.parse(answer); + } catch (error) { + console.error('解析标签JSON失败:', String(error)); + // 尝试使用正则表达式提取标签 + const matches = answer.match(/"([^"]+)"/g); + if (matches) { + tags = matches.map(match => match.replace(/"/g, '')); + } + } + + // 保存标签到数据库 + const savedTags = []; + for (let i = 0; i < tags.length; i++) { + const tagName = tags[i]; + try { + const tag = await db.tags.create({ + data: { + label: tagName, + projectId, + parentId: parentTagId || null + } + }); + savedTags.push(tag); + } catch (error) { + console.error(`[标签生成] 保存标签 ${tagName} 失败:`, String(error)); + throw error; + } + } + return NextResponse.json(savedTags); + } catch (error) { + console.error('[标签生成] 生成标签失败:', String(error)); + console.error('[标签生成] 错误堆栈:', error.stack); + return NextResponse.json({ error: error.message || '生成标签失败' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/eval-datasets/[evalId]/route.js b/easy-dataset-main/app/api/projects/[projectId]/eval-datasets/[evalId]/route.js new file mode 100644 index 0000000..8a04e3b --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/eval-datasets/[evalId]/route.js @@ -0,0 +1,108 @@ +import { NextResponse } from 'next/server'; +import { getEvalQuestionById, updateEvalQuestion, deleteEvalQuestion } from '@/lib/db/evalDatasets'; +import { db } from '@/lib/db/index'; + +/** + * Get evaluation dataset details by ID + * Supports operateType=prev|next to navigate neighbors + */ +export async function GET(request, { params }) { + try { + const { projectId, evalId } = params; + const { searchParams } = new URL(request.url); + const operateType = searchParams.get('operateType'); + + // Navigation request (prev/next) + if (operateType) { + const current = await db.evalDatasets.findUnique({ + where: { id: evalId }, + select: { createAt: true } + }); + + if (!current) { + return NextResponse.json(null); + } + + let neighbor = null; + + if (operateType === 'prev') { + // Get previous item (newer createAt when list is sorted desc) + neighbor = await db.evalDatasets.findFirst({ + where: { + projectId, + createAt: { gt: current.createAt } + }, + orderBy: { createAt: 'asc' }, + select: { id: true } + }); + } else if (operateType === 'next') { + // Get next item (older createAt) + neighbor = await db.evalDatasets.findFirst({ + where: { + projectId, + createAt: { lt: current.createAt } + }, + orderBy: { createAt: 'desc' }, + select: { id: true } + }); + } + + return NextResponse.json(neighbor || null); + } + + // Regular detail request + const evalQuestion = await getEvalQuestionById(evalId); + + if (!evalQuestion) { + return NextResponse.json({ error: 'Eval question not found' }, { status: 404 }); + } + + return NextResponse.json(evalQuestion); + } catch (error) { + console.error('Failed to get eval question:', error); + return NextResponse.json({ error: error.message || 'Failed to get eval question' }, { status: 500 }); + } +} + +/** + * Update evaluation dataset + */ +export async function PUT(request, { params }) { + try { + const { evalId } = params; + const data = await request.json(); + + // Only allow specific fields + const allowedFields = ['question', 'options', 'correctAnswer', 'tags', 'note']; + const updateData = {}; + + for (const field of allowedFields) { + if (data[field] !== undefined) { + updateData[field] = data[field]; + } + } + + const updated = await updateEvalQuestion(evalId, updateData); + + return NextResponse.json(updated); + } catch (error) { + console.error('Failed to update eval question:', error); + return NextResponse.json({ error: error.message || 'Failed to update eval question' }, { status: 500 }); + } +} + +/** + * Delete evaluation dataset + */ +export async function DELETE(request, { params }) { + try { + const { evalId } = params; + + await deleteEvalQuestion(evalId); + + return NextResponse.json({ success: true }); + } catch (error) { + console.error('Failed to delete eval question:', error); + return NextResponse.json({ error: error.message || 'Failed to delete eval question' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/eval-datasets/count/route.js b/easy-dataset-main/app/api/projects/[projectId]/eval-datasets/count/route.js new file mode 100644 index 0000000..55f5369 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/eval-datasets/count/route.js @@ -0,0 +1,63 @@ +import { NextResponse } from 'next/server'; +import { db } from '@/lib/db'; +import { buildEvalQuestionWhere } from '@/lib/db/evalDatasets'; + +export async function GET(request, { params }) { + try { + const { projectId } = params; + const { searchParams } = new URL(request.url); + + const questionType = searchParams.get('questionType') || ''; + const keyword = searchParams.get('keyword') || ''; + const chunkId = searchParams.get('chunkId') || ''; + + const questionTypes = searchParams.getAll('questionTypes') || []; + + const tags = + searchParams.getAll('tags').length > 0 + ? searchParams.getAll('tags') + : searchParams.get('tag') + ? searchParams.get('tag').split(',') + : []; + + const where = buildEvalQuestionWhere(projectId, { + questionType: questionType || undefined, + questionTypes: questionTypes.length > 0 ? questionTypes : undefined, + keyword: keyword || undefined, + chunkId: chunkId || undefined, + tags: tags.length > 0 ? tags : undefined + }); + + const [total, byTypeRaw] = await Promise.all([ + db.evalDatasets.count({ where }), + db.evalDatasets.groupBy({ + by: ['questionType'], + where, + _count: { id: true } + }) + ]); + + const byType = {}; + byTypeRaw.forEach(item => { + byType[item.questionType] = item._count.id; + }); + + const hasShortAnswer = (byType.short_answer || 0) > 0; + const hasOpenEnded = (byType.open_ended || 0) > 0; + const hasSubjective = hasShortAnswer || hasOpenEnded; + + return NextResponse.json( + { + code: 0, + data: { total, byType, hasSubjective, hasShortAnswer, hasOpenEnded } + }, + { status: 200 } + ); + } catch (error) { + console.error('Failed to count eval datasets:', error); + return NextResponse.json( + { code: 500, error: 'Failed to count eval datasets', message: error.message }, + { status: 500 } + ); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/eval-datasets/export/route.js b/easy-dataset-main/app/api/projects/[projectId]/eval-datasets/export/route.js new file mode 100644 index 0000000..dd0aab7 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/eval-datasets/export/route.js @@ -0,0 +1,231 @@ +import { NextResponse } from 'next/server'; +import { db } from '@/lib/db/index'; +import { buildEvalQuestionWhere } from '@/lib/db/evalDatasets'; + +const BATCH_SIZE = 500; + +/** + * Convert an evaluation item to a CSV row + */ +function convertToCSVRow(item, isHeader = false) { + if (isHeader) { + return ['questionType', 'question', 'options', 'correctAnswer', 'tags'].join(','); + } + + const escapeCSV = str => { + if (str === null || str === undefined) return ''; + const strValue = String(str); + if (strValue.includes(',') || strValue.includes('"') || strValue.includes('\n')) { + return `"${strValue.replace(/"/g, '""')}"`; + } + return strValue; + }; + + return [ + escapeCSV(item.questionType), + escapeCSV(item.question), + escapeCSV(item.options), + escapeCSV(item.correctAnswer), + escapeCSV(item.tags) + ].join(','); +} + +/** + * Convert an evaluation item to export format + */ +function formatExportItem(item) { + return { + questionType: item.questionType, + question: item.question, + options: item.options, + correctAnswer: item.correctAnswer, + tags: item.tags + }; +} + +/** + * Export evaluation datasets + * Supports JSON, JSONL, and CSV + * Uses batched streaming for large datasets + */ +export async function POST(request, { params }) { + try { + const { projectId } = params; + const body = await request.json(); + + const { + format = 'json', // json | jsonl | csv + questionTypes = [], + tags = [], + keyword = '' + } = body; + + // Validate format + if (!['json', 'jsonl', 'csv'].includes(format)) { + return NextResponse.json({ code: 400, error: 'Unsupported export format' }, { status: 400 }); + } + + // Build query conditions + const where = buildEvalQuestionWhere(projectId, { + questionTypes: questionTypes.length > 0 ? questionTypes : undefined, + tags: tags.length > 0 ? tags : undefined, + keyword: keyword || undefined + }); + + // Fetch total count + const total = await db.evalDatasets.count({ where }); + + if (total === 0) { + return NextResponse.json({ code: 400, error: 'No data matches the criteria' }, { status: 400 }); + } + + // Return directly for small datasets + if (total <= 1000) { + const items = await db.evalDatasets.findMany({ + where, + orderBy: { createAt: 'desc' } + }); + + const formattedItems = items.map(formatExportItem); + + if (format === 'json') { + return new Response(JSON.stringify(formattedItems, null, 2), { + headers: { + 'Content-Type': 'application/json', + 'Content-Disposition': `attachment; filename="eval-datasets-${Date.now()}.json"` + } + }); + } + + if (format === 'jsonl') { + const jsonlContent = formattedItems.map(item => JSON.stringify(item)).join('\n'); + return new Response(jsonlContent, { + headers: { + 'Content-Type': 'application/x-ndjson', + 'Content-Disposition': `attachment; filename="eval-datasets-${Date.now()}.jsonl"` + } + }); + } + + if (format === 'csv') { + const csvContent = [convertToCSVRow(null, true), ...items.map(item => convertToCSVRow(item))].join('\n'); + return new Response('\uFEFF' + csvContent, { + headers: { + 'Content-Type': 'text/csv; charset=utf-8', + 'Content-Disposition': `attachment; filename="eval-datasets-${Date.now()}.csv"` + } + }); + } + } + + // Stream export for large datasets + const stream = new ReadableStream({ + async start(controller) { + const encoder = new TextEncoder(); + let isFirst = true; + + // CSV outputs header row first + if (format === 'csv') { + controller.enqueue(encoder.encode('\uFEFF' + convertToCSVRow(null, true) + '\n')); + } + + // JSON outputs opening bracket + if (format === 'json') { + controller.enqueue(encoder.encode('[\n')); + } + + // Fetch data in batches + const totalBatches = Math.ceil(total / BATCH_SIZE); + + for (let batch = 0; batch < totalBatches; batch++) { + const items = await db.evalDatasets.findMany({ + where, + orderBy: { createAt: 'desc' }, + skip: batch * BATCH_SIZE, + take: BATCH_SIZE + }); + + for (const item of items) { + const formattedItem = formatExportItem(item); + + if (format === 'json') { + const prefix = isFirst ? '' : ',\n'; + controller.enqueue(encoder.encode(prefix + JSON.stringify(formattedItem))); + isFirst = false; + } else if (format === 'jsonl') { + controller.enqueue(encoder.encode(JSON.stringify(formattedItem) + '\n')); + } else if (format === 'csv') { + controller.enqueue(encoder.encode(convertToCSVRow(item) + '\n')); + } + } + } + + // JSON outputs closing bracket + if (format === 'json') { + controller.enqueue(encoder.encode('\n]')); + } + + controller.close(); + } + }); + + const contentTypes = { + json: 'application/json', + jsonl: 'application/x-ndjson', + csv: 'text/csv; charset=utf-8' + }; + + const extensions = { + json: 'json', + jsonl: 'jsonl', + csv: 'csv' + }; + + return new Response(stream, { + headers: { + 'Content-Type': contentTypes[format], + 'Content-Disposition': `attachment; filename="eval-datasets-${Date.now()}.${extensions[format]}"`, + 'Transfer-Encoding': 'chunked' + } + }); + } catch (error) { + console.error('Failed to export eval datasets:', error); + return NextResponse.json({ code: 500, error: error.message || 'Export failed' }, { status: 500 }); + } +} + +/** + * Get export preview (count only) + */ +export async function GET(request, { params }) { + try { + const { projectId } = params; + const { searchParams } = new URL(request.url); + + // Parse query params + const questionTypes = searchParams.getAll('questionTypes'); + const tags = searchParams.getAll('tags'); + const keyword = searchParams.get('keyword') || ''; + + // Build query conditions + const where = buildEvalQuestionWhere(projectId, { + questionTypes: questionTypes.length > 0 ? questionTypes : undefined, + tags: tags.length > 0 ? tags : undefined, + keyword: keyword || undefined + }); + + // Count rows + const total = await db.evalDatasets.count({ where }); + + return NextResponse.json({ + code: 0, + data: { + total, + isLargeDataset: total > 1000 + } + }); + } catch (error) { + console.error('Failed to get export preview:', error); + return NextResponse.json({ code: 500, error: error.message || 'Failed to get export preview' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/eval-datasets/import/route.js b/easy-dataset-main/app/api/projects/[projectId]/eval-datasets/import/route.js new file mode 100644 index 0000000..f7557fa --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/eval-datasets/import/route.js @@ -0,0 +1,380 @@ +import { NextResponse } from 'next/server'; +import { db } from '@/lib/db/index'; +import { nanoid } from 'nanoid'; +import * as XLSX from 'xlsx'; + +/** + * Validate true/false item schema + */ +function validateTrueFalse(item, index) { + const errors = []; + if (!item.question || typeof item.question !== 'string') { + errors.push(`Item ${index + 1}: missing or invalid "question"`); + } + if (!item.correctAnswer || (item.correctAnswer !== '✅' && item.correctAnswer !== '❌')) { + errors.push(`Item ${index + 1}: "correctAnswer" must be "✅" or "❌"`); + } + return errors; +} + +/** + * Validate single-choice item schema + */ +function validateSingleChoice(item, index) { + const errors = []; + if (!item.question || typeof item.question !== 'string') { + errors.push(`Item ${index + 1}: missing or invalid "question"`); + } + + // Normalize options + let options = item.options; + if (typeof options === 'string') { + try { + options = JSON.parse(options); + } catch (e) { + errors.push(`Item ${index + 1}: invalid "options" format; unable to parse`); + return errors; + } + } + + if (!options || !Array.isArray(options) || options.length < 2) { + errors.push(`Item ${index + 1}: "options" must be an array with at least 2 items`); + } + if (!item.correctAnswer || !/^[A-Z]$/.test(item.correctAnswer)) { + errors.push(`Item ${index + 1}: "correctAnswer" must be a single uppercase letter (A-Z)`); + } + return errors; +} + +/** + * Validate multiple-choice item schema + */ +function validateMultipleChoice(item, index) { + const errors = []; + if (!item.question || typeof item.question !== 'string') { + errors.push(`Item ${index + 1}: missing or invalid "question"`); + } + + // Normalize options + let options = item.options; + if (typeof options === 'string') { + try { + options = JSON.parse(options); + } catch (e) { + errors.push(`Item ${index + 1}: invalid "options" format; unable to parse`); + return errors; + } + } + + if (!options || !Array.isArray(options) || options.length < 2) { + errors.push(`Item ${index + 1}: "options" must be an array with at least 2 items`); + } + + // Normalize correctAnswer + let correctAnswer = item.correctAnswer; + if (typeof correctAnswer === 'string') { + try { + correctAnswer = JSON.parse(correctAnswer); + } catch (e) { + errors.push(`Item ${index + 1}: invalid "correctAnswer" format; unable to parse`); + return errors; + } + } + + if (!correctAnswer || !Array.isArray(correctAnswer) || correctAnswer.length < 1) { + errors.push(`Item ${index + 1}: "correctAnswer" must be an array with at least 1 item`); + } + // Validate each answer token + if (Array.isArray(correctAnswer)) { + for (const ans of correctAnswer) { + if (!/^[A-Z]$/.test(ans)) { + errors.push(`Item ${index + 1}: "${ans}" is not a valid option letter in "correctAnswer"`); + } + } + } + return errors; +} + +/** + * Validate QA item schema (short_answer and open_ended) + */ +function validateQA(item, index) { + const errors = []; + if (!item.question || typeof item.question !== 'string') { + errors.push(`Item ${index + 1}: missing or invalid "question"`); + } + if (!item.correctAnswer || typeof item.correctAnswer !== 'string') { + errors.push(`Item ${index + 1}: missing or invalid "correctAnswer"`); + } + return errors; +} + +/** + * Validate data by question type + */ +function validateData(data, questionType) { + const allErrors = []; + + for (let i = 0; i < data.length; i++) { + let errors = []; + switch (questionType) { + case 'true_false': + errors = validateTrueFalse(data[i], i); + break; + case 'single_choice': + errors = validateSingleChoice(data[i], i); + break; + case 'multiple_choice': + errors = validateMultipleChoice(data[i], i); + break; + case 'short_answer': + case 'open_ended': + errors = validateQA(data[i], i); + break; + default: + errors = [`Unsupported question type: ${questionType}`]; + } + allErrors.push(...errors); + } + + return allErrors; +} + +/** + * Parse an Excel file + */ +function parseExcel(buffer, questionType) { + const excelHeaders = { + question: '\u9898\u76ee', + correctAnswer: '\u6b63\u786e\u7b54\u6848', + answer: '\u7b54\u6848', + options: '\u9009\u9879' + }; + + const workbook = XLSX.read(buffer, { type: 'buffer' }); + const sheetName = workbook.SheetNames[0]; + const sheet = workbook.Sheets[sheetName]; + const rawData = XLSX.utils.sheet_to_json(sheet, { defval: '' }); + + // Convert to normalized schema + const data = rawData.map(row => { + const item = { + question: row.question || row[excelHeaders.question] || '', + correctAnswer: row.correctAnswer || row[excelHeaders.correctAnswer] || row[excelHeaders.answer] || '' + }; + + // Handle options (choice questions) + if (questionType === 'single_choice' || questionType === 'multiple_choice') { + // Try to parse from options column + if (row.options || row[excelHeaders.options]) { + let optionsStr = (row.options || row[excelHeaders.options]).trim(); + + // Replace single quotes so it becomes valid JSON + if (optionsStr.startsWith('[') && optionsStr.includes("'")) { + optionsStr = optionsStr.replace(/'/g, '"'); + } + + try { + // Try JSON parsing + item.options = JSON.parse(optionsStr); + } catch { + // Fallback: split by separators + item.options = optionsStr + .split(/[,;|,;]/) + .map(o => o.trim()) + .filter(Boolean); + } + } + } + + // Handle multiple-choice correctAnswer + if (questionType === 'multiple_choice') { + if (typeof item.correctAnswer === 'string') { + let answerStr = item.correctAnswer.trim(); + + // Replace single quotes so it becomes valid JSON + if (answerStr.startsWith('[') && answerStr.includes("'")) { + answerStr = answerStr.replace(/'/g, '"'); + } + + // Try JSON parsing + try { + item.correctAnswer = JSON.parse(answerStr); + } catch { + // Split string such as "A,B,C" or "ABC" + if (answerStr.includes(',') || answerStr.includes(',')) { + item.correctAnswer = answerStr.split(/[,,]/).map(a => a.trim().toUpperCase()); + } else { + // Split characters such as "ABC" -> ["A", "B", "C"] + item.correctAnswer = answerStr + .toUpperCase() + .split('') + .filter(c => /[A-Z]/.test(c)); + } + } + } + } + + return item; + }); + + return data; +} + +/** + * Parse a JSON file + */ +function parseJSON(content) { + return JSON.parse(content); +} + +/** + * POST - Import evaluation datasets + */ +export async function POST(request, { params }) { + try { + const { projectId } = params; + const formData = await request.formData(); + + const file = formData.get('file'); + const questionType = formData.get('questionType'); + const tags = formData.get('tags') || ''; + + console.log(`[Import] Start processing. Project: ${projectId}, questionType: ${questionType}, tags: ${tags}`); + + if (!file) { + return NextResponse.json({ code: 400, error: 'Please upload a file' }, { status: 400 }); + } + + if (!questionType) { + return NextResponse.json({ code: 400, error: 'Please select a question type' }, { status: 400 }); + } + + // Validate question type + const validTypes = ['true_false', 'single_choice', 'multiple_choice', 'short_answer', 'open_ended']; + if (!validTypes.includes(questionType)) { + return NextResponse.json({ code: 400, error: `Unsupported question type: ${questionType}` }, { status: 400 }); + } + + // Get file extension + const fileName = file.name; + const fileExt = fileName.split('.').pop().toLowerCase(); + console.log(`[Import] File name: ${fileName}, extension: ${fileExt}`); + + // Validate file type + if (!['json', 'xls', 'xlsx'].includes(fileExt)) { + return NextResponse.json( + { code: 400, error: 'Unsupported file format. Please upload a json, xls, or xlsx file' }, + { status: 400 } + ); + } + + // Read file content + const buffer = await file.arrayBuffer(); + let data = []; + + // Parse file + console.log('[Import] Parsing file...'); + if (fileExt === 'json') { + const content = new TextDecoder().decode(buffer); + data = parseJSON(content); + } else { + data = parseExcel(Buffer.from(buffer), questionType); + } + + console.log(`[Import] Parsing completed. Total items: ${data.length}`); + + if (!Array.isArray(data) || data.length === 0) { + return NextResponse.json({ code: 400, error: 'File is empty or has an invalid format' }, { status: 400 }); + } + + // Validate data + console.log('[Import] Validating data...'); + const errors = validateData(data, questionType); + if (errors.length > 0) { + console.log(`[Import] Validation failed. Error count: ${errors.length}`); + return NextResponse.json( + { + code: 400, + error: 'Data validation failed', + details: errors.slice(0, 10), + totalErrors: errors.length + }, + { status: 400 } + ); + } + + console.log('[Import] Validation passed. Writing to database...'); + + // Prepare data + const now = new Date(); + const evalDatasets = data.map(item => { + // Normalize options + let options = item.options; + if (typeof options === 'string') { + try { + options = JSON.parse(options); + } catch (e) { + // Keep original on parse failure + } + } + + // Normalize correctAnswer + let correctAnswer = item.correctAnswer; + if (typeof correctAnswer === 'string' && questionType === 'multiple_choice') { + try { + correctAnswer = JSON.parse(correctAnswer); + } catch (e) { + // Keep original on parse failure + } + } + + return { + id: nanoid(), + projectId, + question: item.question, + questionType, + options: options ? JSON.stringify(options) : '', + // For multiple_choice, store correctAnswer as JSON array string + correctAnswer: Array.isArray(correctAnswer) ? JSON.stringify(correctAnswer) : correctAnswer, + tags: tags || '', + note: '', + createAt: now, + updateAt: now + }; + }); + + // Batch insert + const batchSize = 100; + let insertedCount = 0; + + for (let i = 0; i < evalDatasets.length; i += batchSize) { + const batch = evalDatasets.slice(i, i + batchSize); + await db.evalDatasets.createMany({ data: batch }); + insertedCount += batch.length; + console.log(`[Import] Inserted ${insertedCount}/${evalDatasets.length} items`); + } + + console.log(`[Import] Import completed. Total inserted: ${insertedCount}`); + + return NextResponse.json({ + code: 0, + data: { + total: insertedCount, + questionType, + tags + }, + message: `Successfully imported ${insertedCount} evaluation items` + }); + } catch (error) { + console.error('[Import] Import failed:', error); + return NextResponse.json( + { + code: 500, + error: 'Import failed', + message: error.message + }, + { status: 500 } + ); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/eval-datasets/route.js b/easy-dataset-main/app/api/projects/[projectId]/eval-datasets/route.js new file mode 100644 index 0000000..69a1ce9 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/eval-datasets/route.js @@ -0,0 +1,164 @@ +import { NextResponse } from 'next/server'; +import { getEvalQuestionsWithPagination, getEvalQuestionsStats, deleteEvalQuestion } from '@/lib/db/evalDatasets'; + +/** + * Get project's evaluation dataset list (paginated) + */ +export async function GET(request, { params }) { + try { + const { projectId } = params; + const { searchParams } = new URL(request.url); + + // Parse query params + const page = parseInt(searchParams.get('page') || '1', 10); + const pageSize = parseInt(searchParams.get('pageSize') || '20', 10); + const questionType = searchParams.get('questionType') || ''; + const questionTypes = searchParams.getAll('questionTypes'); + const keyword = searchParams.get('keyword') || ''; + const chunkId = searchParams.get('chunkId') || ''; + // Support multiple tags params or comma-separated tag + const tags = + searchParams.getAll('tags').length > 0 + ? searchParams.getAll('tags') + : searchParams.get('tag') + ? searchParams.get('tag').split(',') + : []; + + const includeStats = searchParams.get('includeStats') === 'true'; + + const queryOptions = { + page, + pageSize, + questionType: questionType || undefined, + questionTypes: questionTypes.length > 0 ? questionTypes : undefined, + keyword: keyword || undefined, + chunkId: chunkId || undefined, + tags: tags.length > 0 ? tags : undefined + }; + + if (includeStats) { + const [result, stats] = await Promise.all([ + getEvalQuestionsWithPagination(projectId, queryOptions), + getEvalQuestionsStats(projectId) + ]); + result.stats = stats; + return NextResponse.json(result); + } + + const result = await getEvalQuestionsWithPagination(projectId, queryOptions); + return NextResponse.json(result); + } catch (error) { + console.error('Failed to get eval datasets:', error); + return NextResponse.json({ error: error.message || 'Failed to get eval datasets' }, { status: 500 }); + } +} + +/** + * Batch delete evaluation datasets + */ +export async function DELETE(request, { params }) { + try { + const { ids } = await request.json(); + + if (!ids || !Array.isArray(ids) || ids.length === 0) { + return NextResponse.json({ error: 'Invalid request: ids array is required' }, { status: 400 }); + } + + const results = await Promise.all(ids.map(id => deleteEvalQuestion(id).catch(err => ({ error: err.message, id })))); + const deleted = results.filter(r => !r.error).length; + const failed = results.filter(r => r.error).length; + + return NextResponse.json({ + success: true, + deleted, + failed, + message: `Successfully deleted ${deleted} items${failed > 0 ? `, ${failed} failed` : ''}` + }); + } catch (error) { + console.error('Failed to delete eval datasets:', error); + return NextResponse.json({ error: error.message || 'Failed to delete eval datasets' }, { status: 500 }); + } +} + +/** + * Create a new evaluation dataset (or batch create) + */ +export async function POST(request, { params }) { + try { + const { projectId } = params; + const body = await request.json(); + + const { createEvalQuestion, createManyEvalQuestions } = require('@/lib/db/evalDatasets'); + + // Handle batch creation + if (Array.isArray(body) || (body.items && Array.isArray(body.items))) { + const items = Array.isArray(body) ? body : body.items; + + if (items.length === 0) { + return NextResponse.json({ success: true, count: 0 }); + } + + // Validate items + const validItems = items + .map(item => { + // 确保标签格式正确: 数组转为逗号分隔字符串 + let tagsStr = item.tags || ''; + if (Array.isArray(tagsStr)) { + tagsStr = tagsStr.join(','); + } + return { + projectId, + question: item.question, + questionType: item.questionType || 'open_ended', + correctAnswer: + typeof item.correctAnswer === 'object' ? JSON.stringify(item.correctAnswer) : item.correctAnswer, + tags: tagsStr, + note: item.note || '', + chunkId: item.chunkId || null, + options: item.options + ? typeof item.options === 'object' + ? JSON.stringify(item.options) + : item.options + : '' + }; + }) + .filter(item => item.question && item.correctAnswer); + + if (validItems.length === 0) { + return NextResponse.json({ error: 'No valid items to create' }, { status: 400 }); + } + + const result = await createManyEvalQuestions(validItems); + return NextResponse.json({ success: true, count: result.count }); + } + + // Handle single creation + const { question, correctAnswer, questionType = 'open_ended', tags, note, chunkId, options } = body; + + if (!question || !correctAnswer) { + return NextResponse.json({ error: 'Question and Correct Answer are required' }, { status: 400 }); + } + + // 确保标签格式正确: 数组转为逗号分隔字符串 + let tagsStr = tags || ''; + if (Array.isArray(tagsStr)) { + tagsStr = tagsStr.join(','); + } + + const evalDataset = await createEvalQuestion({ + projectId, + question, + questionType, + correctAnswer: typeof correctAnswer === 'object' ? JSON.stringify(correctAnswer) : correctAnswer, + tags: tagsStr, + note: note || '', + chunkId: chunkId || null, + options: options ? (typeof options === 'object' ? JSON.stringify(options) : options) : '' + }); + + return NextResponse.json({ success: true, evalDataset }); + } catch (error) { + console.error('Failed to create eval dataset:', error); + return NextResponse.json({ error: error.message || 'Failed to create eval dataset' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/eval-datasets/sample/route.js b/easy-dataset-main/app/api/projects/[projectId]/eval-datasets/sample/route.js new file mode 100644 index 0000000..74113ad --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/eval-datasets/sample/route.js @@ -0,0 +1,124 @@ +import { NextResponse } from 'next/server'; +import { db } from '@/lib/db'; +import { buildEvalQuestionWhere } from '@/lib/db/evalDatasets'; + +const SMALL_TOTAL_THRESHOLD = 5000; +const HARD_LIMIT = 50000; + +function shuffleArray(arr) { + const result = [...arr]; + for (let i = result.length - 1; i > 0; i -= 1) { + const j = Math.floor(Math.random() * (i + 1)); + [result[i], result[j]] = [result[j], result[i]]; + } + return result; +} + +export async function POST(request, { params }) { + try { + const { projectId } = params; + const body = await request.json(); + + const { + questionType = '', + questionTypes = [], + keyword = '', + chunkId = '', + tags = [], + limit = 0, + strategy = 'random' + } = body || {}; + + const where = buildEvalQuestionWhere(projectId, { + questionType: questionType || undefined, + questionTypes: Array.isArray(questionTypes) && questionTypes.length > 0 ? questionTypes : undefined, + keyword: keyword || undefined, + chunkId: chunkId || undefined, + tags: Array.isArray(tags) && tags.length > 0 ? tags : undefined + }); + + const total = await db.evalDatasets.count({ where }); + + if (total === 0) { + return NextResponse.json( + { + code: 0, + data: { + total: 0, + selectedCount: 0, + ids: [], + strategyUsed: strategy + } + }, + { status: 200 } + ); + } + + let normalizedLimit = typeof limit === 'number' && limit > 0 ? Math.min(limit, HARD_LIMIT) : HARD_LIMIT; + + if (normalizedLimit >= total) { + const items = await db.evalDatasets.findMany({ + where, + select: { id: true }, + orderBy: { createAt: 'desc' } + }); + + const ids = items.map(item => item.id); + + return NextResponse.json( + { + code: 0, + data: { + total, + selectedCount: ids.length, + ids, + strategyUsed: total > HARD_LIMIT ? 'top' : strategy + } + }, + { status: 200 } + ); + } + + let ids = []; + let strategyUsed = strategy; + + if (total <= SMALL_TOTAL_THRESHOLD) { + const items = await db.evalDatasets.findMany({ + where, + select: { id: true }, + orderBy: { createAt: 'desc' } + }); + const shuffled = shuffleArray(items); + ids = shuffled.slice(0, normalizedLimit).map(item => item.id); + strategyUsed = 'random-small'; + } else { + const items = await db.evalDatasets.findMany({ + where, + select: { id: true }, + orderBy: { createAt: 'desc' }, + take: normalizedLimit + }); + ids = items.map(item => item.id); + strategyUsed = 'top-latest'; + } + + return NextResponse.json( + { + code: 0, + data: { + total, + selectedCount: ids.length, + ids, + strategyUsed + } + }, + { status: 200 } + ); + } catch (error) { + console.error('Failed to sample eval datasets:', error); + return NextResponse.json( + { code: 500, error: 'Failed to sample eval datasets', message: error.message }, + { status: 500 } + ); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/eval-datasets/tags/route.js b/easy-dataset-main/app/api/projects/[projectId]/eval-datasets/tags/route.js new file mode 100644 index 0000000..4cef0c5 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/eval-datasets/tags/route.js @@ -0,0 +1,35 @@ +import { NextResponse } from 'next/server'; +import { db } from '@/lib/db/index'; + +/** + * Get all evaluation dataset tags in the project + */ +export async function GET(request, { params }) { + try { + const { projectId } = params; + + // Fetch tags for all datasets in the project + const datasets = await db.evalDatasets.findMany({ + where: { projectId }, + select: { tags: true } + }); + + // Extract and de-duplicate tags + const tagsSet = new Set(); + datasets.forEach(dataset => { + if (dataset.tags) { + // Support both English and Chinese commas + const tags = dataset.tags + .split(/[,,]/) + .map(t => t.trim()) + .filter(Boolean); + tags.forEach(tag => tagsSet.add(tag)); + } + }); + + return NextResponse.json({ tags: Array.from(tagsSet).sort() }); + } catch (error) { + console.error('Failed to get tags:', error); + return NextResponse.json({ error: error.message || 'Failed to get tags' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/eval-tasks/[taskId]/route.js b/easy-dataset-main/app/api/projects/[projectId]/eval-tasks/[taskId]/route.js new file mode 100644 index 0000000..f7ebf7b --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/eval-tasks/[taskId]/route.js @@ -0,0 +1,176 @@ +import { NextResponse } from 'next/server'; +import { db } from '@/lib/db/index'; +import { getEvalResultsByTaskId, getEvalResultsStats } from '@/lib/db/evalResults'; + +/** + * Get evaluation task details and results + */ +export async function GET(request, { params }) { + try { + const { projectId, taskId } = params; + + if (!projectId || !taskId) { + return NextResponse.json({ error: 'Project ID and Task ID are required' }, { status: 400 }); + } + + // Fetch task details + const task = await db.task.findUnique({ + where: { id: taskId } + }); + + if (!task) { + return NextResponse.json({ error: 'Task not found' }, { status: 404 }); + } + + if (task.projectId !== projectId) { + return NextResponse.json({ error: 'Task does not belong to this project' }, { status: 403 }); + } + + // Parse task detail fields + let detail = {}; + let modelInfo = {}; + try { + detail = task.detail ? JSON.parse(task.detail) : {}; + modelInfo = task.modelInfo ? JSON.parse(task.modelInfo) : {}; + } catch (e) { + console.error('Failed to parse task detail:', e); + } + + // Parse query params + const { searchParams } = new URL(request.url); + const page = parseInt(searchParams.get('page') || '1'); + const pageSize = parseInt(searchParams.get('pageSize') || '10'); + const type = searchParams.get('type') || null; + const isCorrectStr = searchParams.get('isCorrect'); + const isCorrect = isCorrectStr === 'true' ? true : isCorrectStr === 'false' ? false : null; + + // Fetch results (supports pagination and filters) + const { items: results, total } = await getEvalResultsByTaskId(taskId, { + page, + pageSize, + type, + isCorrect + }); + + // Fetch stats + const stats = await getEvalResultsStats(taskId); + + return NextResponse.json({ + code: 0, + data: { + task: { + ...task, + detail, + modelInfo + }, + results, + total, + page, + pageSize, + stats + } + }); + } catch (error) { + console.error('Failed to fetch evaluation task details:', error); + return NextResponse.json( + { code: 500, error: 'Failed to fetch evaluation task details', message: error.message }, + { status: 500 } + ); + } +} + +/** + * Delete evaluation task + */ +export async function DELETE(request, { params }) { + try { + const { projectId, taskId } = params; + + if (!projectId || !taskId) { + return NextResponse.json({ error: 'Project ID and Task ID are required' }, { status: 400 }); + } + + // Validate task exists and belongs to this project + const task = await db.task.findUnique({ + where: { id: taskId } + }); + + if (!task) { + return NextResponse.json({ error: 'Task not found' }, { status: 404 }); + } + + if (task.projectId !== projectId) { + return NextResponse.json({ error: 'Task does not belong to this project' }, { status: 403 }); + } + + // Delete evaluation results + await db.evalResults.deleteMany({ + where: { taskId } + }); + + // Delete task + await db.task.delete({ + where: { id: taskId } + }); + + return NextResponse.json({ + code: 0, + message: 'Deleted' + }); + } catch (error) { + console.error('Failed to delete evaluation task:', error); + return NextResponse.json( + { code: 500, error: 'Failed to delete evaluation task', message: error.message }, + { status: 500 } + ); + } +} + +/** + * Interrupt evaluation task + */ +export async function PUT(request, { params }) { + try { + const { projectId, taskId } = params; + const data = await request.json(); + const { action } = data; + + if (!projectId || !taskId) { + return NextResponse.json({ error: 'Project ID and Task ID are required' }, { status: 400 }); + } + + // Validate task exists and belongs to this project + const task = await db.task.findUnique({ + where: { id: taskId } + }); + + if (!task) { + return NextResponse.json({ error: 'Task not found' }, { status: 404 }); + } + + if (task.projectId !== projectId) { + return NextResponse.json({ error: 'Task does not belong to this project' }, { status: 403 }); + } + + if (action === 'interrupt') { + // Interrupt task + await db.task.update({ + where: { id: taskId }, + data: { + status: 3, // Interrupted + endTime: new Date() + } + }); + + return NextResponse.json({ + code: 0, + message: 'Task interrupted' + }); + } + + return NextResponse.json({ error: 'Unknown action' }, { status: 400 }); + } catch (error) { + console.error('Failed to operate evaluation task:', error); + return NextResponse.json({ code: 500, error: 'Operation failed', message: error.message }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/eval-tasks/route.js b/easy-dataset-main/app/api/projects/[projectId]/eval-tasks/route.js new file mode 100644 index 0000000..9984b05 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/eval-tasks/route.js @@ -0,0 +1,207 @@ +import { NextResponse } from 'next/server'; +import { db } from '@/lib/db/index'; +import { processTask } from '@/lib/services/tasks'; + +/** + * Get all evaluation tasks for a project + */ +export async function GET(request, { params }) { + try { + const { projectId } = params; + const { searchParams } = new URL(request.url); + const page = parseInt(searchParams.get('page') || '1'); + const pageSize = parseInt(searchParams.get('pageSize') || '20'); + + if (!projectId) { + return NextResponse.json({ error: 'Project ID is required' }, { status: 400 }); + } + + const skip = (page - 1) * pageSize; + + // Fetch task list and total count + const [tasks, total] = await Promise.all([ + db.task.findMany({ + where: { + projectId, + taskType: 'model-evaluation' + }, + orderBy: { createAt: 'desc' }, + skip, + take: pageSize + }), + db.task.count({ + where: { + projectId, + taskType: 'model-evaluation' + } + }) + ]); + + // Parse task detail fields + const tasksWithDetails = tasks.map(task => { + let detail = {}; + let modelInfo = {}; + try { + detail = task.detail ? JSON.parse(task.detail) : {}; + modelInfo = task.modelInfo ? JSON.parse(task.modelInfo) : {}; + } catch (e) { + console.error('Failed to parse task detail:', e); + } + return { + ...task, + detail, + modelInfo + }; + }); + + return NextResponse.json({ + code: 0, + data: { + items: tasksWithDetails, + total, + page, + pageSize, + totalPages: Math.ceil(total / pageSize) + } + }); + } catch (error) { + console.error('Failed to fetch evaluation task list:', error); + return NextResponse.json( + { code: 500, error: 'Failed to fetch evaluation task list', message: error.message }, + { status: 500 } + ); + } +} + +/** + * Create evaluation tasks + * Supports selecting multiple models and creating one task per model + */ +export async function POST(request, { params }) { + try { + const { projectId } = params; + const data = await request.json(); + + const { + models, // Models to evaluate: [{ modelId, providerId }] + evalDatasetIds, // Evaluation question IDs + judgeModelId, // Judge model ID (for subjective grading) + judgeProviderId, // Judge provider ID + language = 'zh-CN', + filterOptions = {}, // Filter options (for display) + customScoreAnchors = null // Custom score anchors for subjective grading + } = data; + + // Validate required fields + if (!models || models.length === 0) { + return NextResponse.json({ code: 400, error: 'Please select at least one model to evaluate' }, { status: 400 }); + } + + if (!evalDatasetIds || evalDatasetIds.length === 0) { + return NextResponse.json({ code: 400, error: 'Please select questions to evaluate' }, { status: 400 }); + } + + // Check for subjective questions + const evalDatasets = await db.evalDatasets.findMany({ + where: { + id: { in: evalDatasetIds }, + projectId + }, + select: { questionType: true } + }); + + const hasSubjectiveQuestions = evalDatasets.some( + q => q.questionType === 'short_answer' || q.questionType === 'open_ended' + ); + + // If there are subjective questions, a judge model is required + if (hasSubjectiveQuestions && (!judgeModelId || !judgeProviderId)) { + return NextResponse.json( + { code: 400, error: 'Short-answer or open-ended questions found. Please select a judge model for grading' }, + { status: 400 } + ); + } + + // Judge model must not be the same as any test model + if (judgeModelId && judgeProviderId) { + const judgeModel = { modelId: judgeModelId, providerId: judgeProviderId }; + const isJudgeInTestModels = models.some( + m => m.modelId === judgeModel.modelId && m.providerId === judgeModel.providerId + ); + if (isJudgeInTestModels) { + return NextResponse.json( + { code: 400, error: 'Judge model cannot be the same as a test model' }, + { status: 400 } + ); + } + } + + // Create one task per model + const createdTasks = []; + + for (const model of models) { + const { modelId, providerId } = model; + + // Fetch full model config + const modelConfig = await db.modelConfig.findFirst({ + where: { + projectId, + providerId, + modelId + } + }); + + // Keep providerId for lookup, add providerName for display + const modelInfo = { + modelId, + modelName: modelConfig?.modelName || modelId, + providerId: providerId, // Provider ID (DB ID) + providerName: modelConfig?.providerName || providerId // Provider display name + }; + + // Build task detail + const taskDetail = { + evalDatasetIds, + judgeModelId: judgeModelId || null, + judgeProviderId: judgeProviderId || null, + filterOptions, + hasSubjectiveQuestions, + customScoreAnchors: customScoreAnchors || null // Store custom score anchors + }; + + // Create task + const newTask = await db.task.create({ + data: { + projectId, + taskType: 'model-evaluation', + status: 0, // Processing + modelInfo: JSON.stringify(modelInfo), + language, + detail: JSON.stringify(taskDetail), + totalCount: evalDatasetIds.length, + completedCount: 0, + note: '' + } + }); + + createdTasks.push(newTask); + + // Start task processing asynchronously + processTask(newTask.id).catch(err => { + console.error(`Failed to start evaluation task: ${newTask.id}`, err); + }); + } + + return NextResponse.json({ + code: 0, + data: createdTasks, + message: `Successfully created ${createdTasks.length} evaluation tasks` + }); + } catch (error) { + console.error('Failed to create evaluation task:', error); + return NextResponse.json( + { code: 500, error: 'Failed to create evaluation task', message: error.message }, + { status: 500 } + ); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/files/[fileId]/ga-pairs/route.js b/easy-dataset-main/app/api/projects/[projectId]/files/[fileId]/ga-pairs/route.js new file mode 100644 index 0000000..3b6aed5 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/files/[fileId]/ga-pairs/route.js @@ -0,0 +1,313 @@ +import { NextResponse } from 'next/server'; +import { getGaPairsByFileId, toggleGaPairActive, saveGaPairs, createGaPairs } from '@/lib/db/ga-pairs'; +import { getUploadFileInfoById } from '@/lib/db/upload-files'; +import { generateGaPairs } from '@/lib/services/ga/ga-generation'; +import logger from '@/lib/util/logger'; +import { db } from '@/lib/db/index'; + +/** + * 生成文件的 GA 对 + */ +export async function POST(request, { params }) { + try { + const { projectId, fileId } = params; + const { regenerate = false, appendMode = false, language = '中文' } = await request.json(); + + // 验证参数 + if (!projectId || !fileId) { + return NextResponse.json({ error: 'Project ID and File ID are required' }, { status: 400 }); + } + + logger.info(`Starting GA pairs generation for project: ${projectId}, file: ${fileId}, appendMode: ${appendMode}`); + + // 检查文件是否存在 + const file = await getUploadFileInfoById(fileId); + if (!file || file.projectId !== projectId) { + return NextResponse.json({ error: 'File not found or does not belong to the project' }, { status: 404 }); + } + + // 获取现有的GA对 + const existingGaPairs = await getGaPairsByFileId(fileId); + + // 如果是追加模式且已有GA对,或者不是重新生成且已存在GA对 + if (!regenerate && !appendMode && existingGaPairs.length > 0) { + return NextResponse.json({ + success: true, + message: 'GA pairs already exist for this file', + data: existingGaPairs + }); + } + + // 读取文件内容 + const fileContent = await getFileContent(projectId, file.fileName); + if (!fileContent) { + return NextResponse.json({ error: 'Failed to read file content' }, { status: 500 }); + } + + logger.info(`File content loaded successfully, length: ${fileContent.length}`); + + // 检查模型配置 + try { + const { getActiveModel } = await import('@/lib/services/models'); + const activeModel = await getActiveModel(projectId); + + if (!activeModel) { + logger.error('No active model configuration found'); + return NextResponse.json( + { error: 'No active AI model configured. Please configure a model in settings first.' }, + { status: 400 } + ); + } + + logger.info(`Using active model: ${activeModel.provider} - ${activeModel.model}`); + } catch (modelError) { + logger.error('Error checking model configuration:', modelError); + return NextResponse.json( + { error: 'Failed to load model configuration. Please check your AI model settings.' }, + { status: 500 } + ); + } + + // 调用 LLM 生成 GA 对 + logger.info(`Generating GA pairs for file: ${file.fileName}`); + let generatedGaPairs; + + try { + generatedGaPairs = await generateGaPairs(fileContent, projectId, language); + + if (!generatedGaPairs || generatedGaPairs.length === 0) { + logger.warn('No GA pairs generated from LLM'); + return NextResponse.json( + { + error: + 'No GA pairs could be generated from the file content. The content might be too short or not suitable for GA pair generation.' + }, + { status: 400 } + ); + } + + logger.info(`Successfully generated ${generatedGaPairs.length} GA pairs from LLM`); + } catch (generationError) { + logger.error('GA pairs generation failed:', generationError); + + // 现有的错误处理逻辑... + let errorMessage = 'Failed to generate GA pairs'; + if (generationError.message.includes('No active model')) { + errorMessage = 'No active AI model available. Please configure and activate a model in settings.'; + } else if (generationError.message.includes('API key')) { + errorMessage = 'Invalid API key or model configuration. Please check your AI model settings.'; + } else if (generationError.message.includes('rate limit')) { + errorMessage = 'API rate limit exceeded. Please try again later.'; + } else { + errorMessage = `AI model error: ${generationError.message}`; + } + + return NextResponse.json({ error: errorMessage }, { status: 500 }); + } + + // 保存到数据库 + try { + if (appendMode && existingGaPairs.length > 0) { + // 追加模式:只保存新生成的GA对,不删除现有的 + logger.info(`Appending ${generatedGaPairs.length} new GA pairs to existing ${existingGaPairs.length} pairs`); + + // 为新GA对设置正确的pairNumber + const startPairNumber = existingGaPairs.length + 1; + const newGaPairData = generatedGaPairs.map((pair, index) => ({ + projectId, + fileId, + pairNumber: startPairNumber + index, + genreTitle: pair.genre?.title || pair.genreTitle || '', + genreDesc: pair.genre?.description || pair.genreDesc || '', + audienceTitle: pair.audience?.title || pair.audienceTitle || '', + audienceDesc: pair.audience?.description || pair.audienceDesc || '', + isActive: true + })); + + // 只创建新的GA对,不删除现有的 + await createGaPairs(newGaPairData); + logger.info('New GA pairs appended to database successfully'); + } else { + // 覆盖模式:删除现有的,保存新的 + await saveGaPairs(projectId, fileId, generatedGaPairs); + logger.info('GA pairs saved to database successfully'); + } + } catch (saveError) { + logger.error('Failed to save GA pairs to database:', saveError); + return NextResponse.json( + { error: 'Generated GA pairs successfully but failed to save to database' }, + { status: 500 } + ); + } + + // 获取保存后的所有GA对 + const allGaPairs = await getGaPairsByFileId(fileId); + + if (appendMode && existingGaPairs.length > 0) { + // 追加模式:只返回新生成的GA对 + const newGaPairs = allGaPairs.slice(existingGaPairs.length); + logger.info(`Successfully appended ${newGaPairs.length} GA pairs. Total pairs: ${allGaPairs.length}`); + + return NextResponse.json({ + success: true, + message: `${newGaPairs.length} new GA pairs appended successfully`, + data: newGaPairs, + total: allGaPairs.length + }); + } else { + // 覆盖模式:返回所有GA对 + logger.info(`Successfully generated and saved ${allGaPairs.length} GA pairs for file: ${file.fileName}`); + + return NextResponse.json({ + success: true, + message: 'GA pairs generated successfully', + data: allGaPairs + }); + } + } catch (error) { + logger.error('Unexpected error in GA pairs generation:', error); + return NextResponse.json( + { error: error.message || 'Unexpected error occurred during GA pairs generation' }, + { status: 500 } + ); + } +} + +/** + * 获取文件的 GA 对 + */ +export async function GET(request, { params }) { + try { + const { projectId, fileId } = params; + + if (!projectId || !fileId) { + return NextResponse.json({ error: 'Project ID and File ID are required' }, { status: 400 }); + } + + const gaPairs = await getGaPairsByFileId(fileId); + + return NextResponse.json({ + success: true, + data: gaPairs + }); + } catch (error) { + console.error('Error getting GA pairs:', String(error)); + return NextResponse.json({ error: 'Failed to get GA pairs' }, { status: 500 }); + } +} + +/** + * 更新/替换文件的所有 GA 对 + */ +export async function PUT(request, { params }) { + try { + const { projectId, fileId } = params; + const body = await request.json(); + + if (!projectId || !fileId) { + return NextResponse.json({ error: 'Project ID and File ID are required' }, { status: 400 }); + } + + const { updates } = body; + + if (!updates || !Array.isArray(updates)) { + return NextResponse.json({ error: 'Updates array is required' }, { status: 400 }); + } + + logger.info(`Replacing all GA pairs for file ${fileId} with ${updates.length} pairs`); + + // 使用数据库事务确保原子性操作 + const results = await db.$transaction(async tx => { + // 1. 先删除所有现有的GA对 + await tx.gaPairs.deleteMany({ + where: { fileId } + }); + + // 2. 然后创建新的GA对 + if (updates.length > 0) { + const gaPairData = updates.map((pair, index) => ({ + projectId, + fileId, + pairNumber: index + 1, + genreTitle: pair.genreTitle || pair.genre?.title || pair.genre || '', + genreDesc: pair.genreDesc || pair.genre?.description || '', + audienceTitle: pair.audienceTitle || pair.audience?.title || pair.audience || '', + audienceDesc: pair.audienceDesc || pair.audience?.description || '', + isActive: pair.isActive !== undefined ? pair.isActive : true + })); + + // 验证数据 + for (const data of gaPairData) { + if (!data.genreTitle || !data.audienceTitle) { + throw new Error(`Invalid GA pair data: missing genre or audience title`); + } + } + + await tx.gaPairs.createMany({ data: gaPairData }); + } + + // 3. 返回新创建的GA对 + return await tx.gaPairs.findMany({ + where: { fileId }, + orderBy: { pairNumber: 'asc' } + }); + }); + + logger.info(`Successfully replaced GA pairs, new count: ${results.length}`); + + return NextResponse.json({ + success: true, + data: results + }); + } catch (error) { + logger.error('Error updating GA pairs:', error); + return NextResponse.json({ error: error.message || 'Failed to update GA pairs' }, { status: 500 }); + } +} + +/** + * 切换 GA 对激活状态 + */ +export async function PATCH(request, { params }) { + try { + const { projectId, fileId } = params; + const body = await request.json(); + + if (!projectId || !fileId) { + return NextResponse.json({ error: 'Project ID and File ID are required' }, { status: 400 }); + } + + const { gaPairId, isActive } = body; + + if (!gaPairId || typeof isActive !== 'boolean') { + return NextResponse.json({ error: 'GA pair ID and active status are required' }, { status: 400 }); + } + + const updatedPair = await toggleGaPairActive(gaPairId, isActive); + + return NextResponse.json({ + success: true, + data: updatedPair + }); + } catch (error) { + console.error('Error toggling GA pair active status:', String(error)); + return NextResponse.json({ error: 'Failed to toggle GA pair active status' }, { status: 500 }); + } +} + +// Helper function to read file content +async function getFileContent(projectId, fileName) { + try { + const { getProjectRoot } = await import('@/lib/db/base'); + const path = await import('path'); + const fs = await import('fs'); + + const projectRoot = await getProjectRoot(); + const filePath = path.join(projectRoot, projectId, 'files', fileName.replace('.pdf', '.md')); + + return await fs.promises.readFile(filePath, 'utf8'); + } catch (error) { + logger.error('Failed to read file content:', error); + return null; + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/files/route.js b/easy-dataset-main/app/api/projects/[projectId]/files/route.js new file mode 100644 index 0000000..2374196 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/files/route.js @@ -0,0 +1,243 @@ +import { NextResponse } from 'next/server'; +import { getProject } from '@/lib/db/projects'; +import path from 'path'; +import { getProjectRoot, ensureDir } from '@/lib/db/base'; +import { promises as fs } from 'fs'; +import { + checkUploadFileInfoByMD5, + createUploadFileInfo, + delUploadFileInfoById, + getUploadFilesPagination +} from '@/lib/db/upload-files'; +import { getFileMD5 } from '@/lib/util/file'; +import { batchSaveTags } from '@/lib/db/tags'; +import { getProjectChunks, getProjectTocByName } from '@/lib/file/text-splitter'; +import { handleDomainTree } from '@/lib/util/domain-tree'; + +// Replace the deprecated config export with the new export syntax +export const dynamic = 'force-dynamic'; +// This tells Next.js not to parse the request body automatically +export const bodyParser = false; + +// 获取项目文件列表 +export async function GET(request, { params }) { + try { + const { projectId } = params; + + // 验证项目ID + if (!projectId) { + return NextResponse.json({ error: 'The project ID cannot be empty' }, { status: 400 }); + } + const { searchParams } = new URL(request.url); + const page = parseInt(searchParams.get('page')) || 1; + const pageSize = parseInt(searchParams.get('pageSize')) || 10; // 每页10个文件,支持分页 + const fileName = searchParams.get('fileName') || ''; + const getAllIds = searchParams.get('getAllIds') === 'true'; // 新增:获取所有文件ID的标志 + + // 如果请求所有文件ID,直接返回ID列表 + if (getAllIds) { + const allFiles = await getUploadFilesPagination(projectId, 1, 9999, fileName); // 获取所有文件 + const allFileIds = allFiles.data?.map(file => String(file.id)) || []; + return NextResponse.json({ allFileIds }); + } + // 获取文件列表 + const files = await getUploadFilesPagination(projectId, page, pageSize, fileName); + + return NextResponse.json(files); + } catch (error) { + console.error('Error obtaining file list:', String(error)); + return NextResponse.json({ error: error.message || 'Error obtaining file list' }, { status: 500 }); + } +} + +// 删除文件 +export async function DELETE(request, { params }) { + try { + const { projectId } = params; + const { searchParams } = new URL(request.url); + const fileId = searchParams.get('fileId'); + const domainTreeAction = searchParams.get('domainTreeAction') || 'keep'; + + // 从请求体中获取模型信息和语言环境 + const requestData = await request.json(); + const model = requestData.model; + const language = requestData.language || 'en'; + + // 验证项目ID和文件名 + if (!projectId) { + return NextResponse.json({ error: 'The project ID cannot be empty' }, { status: 400 }); + } + + if (!fileId) { + return NextResponse.json({ error: 'The file name cannot be empty' }, { status: 400 }); + } + + // 获取项目信息 + const project = await getProject(projectId); + if (!project) { + return NextResponse.json({ error: 'The project does not exist' }, { status: 404 }); + } + + // 删除文件及其相关的文本块、问题和数据集 + const { stats, fileName, fileInfo } = await delUploadFileInfoById(fileId); + const deleteToc = await getProjectTocByName(projectId, fileName); + try { + const projectRoot = await getProjectRoot(); + const projectPath = path.join(projectRoot, projectId); + const tocDir = path.join(projectPath, 'toc'); + const baseName = path.basename(fileInfo.fileName, path.extname(fileInfo.fileName)); + const tocPath = path.join(tocDir, `${baseName}-toc.json`); + + // 检查文件是否存在再删除 + await fs.unlink(tocPath); + console.log(`成功删除 TOC 文件: ${tocPath}`); + } catch (error) { + console.error(`删除 TOC 文件失败:`, String(error)); + // 即使 TOC 文件删除失败,不影响整体结果 + } + + // 如果选择了保持领域树不变,直接返回删除结果 + if (domainTreeAction === 'keep') { + return NextResponse.json({ + message: '文件删除成功', + stats: stats, + domainTreeAction: 'keep', + cascadeDelete: true + }); + } + + // 处理领域树更新 + try { + // 获取项目的所有文件 + const { chunks, toc } = await getProjectChunks(projectId); + + // 如果不存在文本块,说明项目已经没有文件了 + if (!chunks || chunks.length === 0) { + // 清空领域树 + await batchSaveTags(projectId, []); + return NextResponse.json({ + message: '文件删除成功,领域树已清空', + stats: stats, + domainTreeAction, + cascadeDelete: true + }); + } + + // 调用领域树处理模块 + await handleDomainTree({ + projectId, + action: domainTreeAction, + allToc: toc, + model, + language, + deleteToc, + project + }); + } catch (error) { + console.error('Error updating domain tree after file deletion:', String(error)); + // 即使领域树更新失败,也不影响文件删除的结果 + } + + return NextResponse.json({ + message: '文件删除成功', + stats: stats, + domainTreeAction, + cascadeDelete: true + }); + } catch (error) { + console.error('Error deleting file:', String(error)); + return NextResponse.json({ error: error.message || 'Error deleting file' }, { status: 500 }); + } +} + +// 上传文件 +export async function POST(request, { params }) { + console.log('File upload request processing, parameters:', params); + const { projectId } = params; + + // 验证项目ID + if (!projectId) { + console.log('The project ID cannot be empty, returning 400 error'); + return NextResponse.json({ error: 'The project ID cannot be empty' }, { status: 400 }); + } + + // 获取项目信息 + const project = await getProject(projectId); + if (!project) { + console.log('The project does not exist, returning 404 error'); + return NextResponse.json({ error: 'The project does not exist' }, { status: 404 }); + } + console.log('Project information retrieved successfully:', project.name || project.id); + + try { + console.log('Try using alternate methods for file upload...'); + + // 检查请求头中是否包含文件名 + const encodedFileName = request.headers.get('x-file-name'); + const fileName = encodedFileName ? decodeURIComponent(encodedFileName) : null; + console.log('Get file name from request header:', fileName); + + if (!fileName) { + console.log('The request header does not contain a file name'); + return NextResponse.json( + { error: 'The request header does not contain a file name (x-file-name)' }, + { status: 400 } + ); + } + + // 检查文件类型 + if (!fileName.endsWith('.md') && !fileName.endsWith('.pdf')) { + return NextResponse.json({ error: 'Only Markdown files are supported' }, { status: 400 }); + } + + // 直接从请求体中读取二进制数据 + const fileBuffer = Buffer.from(await request.arrayBuffer()); + + // 保存文件 + const projectRoot = await getProjectRoot(); + const projectPath = path.join(projectRoot, projectId); + const filesDir = path.join(projectPath, 'files'); + + await ensureDir(filesDir); + + const filePath = path.join(filesDir, fileName); + await fs.writeFile(filePath, fileBuffer); + //获取文件大小 + const stats = await fs.stat(filePath); + //获取文件md5 + const md5 = await getFileMD5(filePath); + //获取文件扩展名 + const ext = path.extname(filePath); + + // let res = await checkUploadFileInfoByMD5(projectId, md5); + // if (res) { + // return NextResponse.json({ error: `【${fileName}】该文件已在此项目中存在` }, { status: 400 }); + // } + + let fileInfo = await createUploadFileInfo({ + projectId, + fileName, + size: stats.size, + md5, + fileExt: ext, + path: filesDir + }); + + console.log('The file upload process is complete, and a successful response is returned'); + return NextResponse.json({ + message: 'File uploaded successfully', + fileName, + filePath, + fileId: fileInfo.id + }); + } catch (error) { + console.error('Error processing file upload:', String(error)); + console.error('Error stack:', error.stack); + return NextResponse.json( + { + error: 'File upload failed: ' + (error.message || 'Unknown error') + }, + { status: 500 } + ); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/generate-questions/route.js b/easy-dataset-main/app/api/projects/[projectId]/generate-questions/route.js new file mode 100644 index 0000000..5c8bab4 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/generate-questions/route.js @@ -0,0 +1,126 @@ +import { NextResponse } from 'next/server'; +import { getProjectChunks } from '@/lib/file/text-splitter'; +import { getTaskConfig } from '@/lib/db/projects'; +import { getChunkById } from '@/lib/db/chunks'; +import { generateQuestionsForChunk, generateQuestionsForChunkWithGA } from '@/lib/services/questions'; + +// 批量生成问题 +export async function POST(request, { params }) { + try { + const { projectId } = params; + + // 验证项目ID + if (!projectId) { + return NextResponse.json({ error: 'The project ID cannot be empty' }, { status: 400 }); + } + + // 获取请求体 + const { model, chunkIds, language = '中文', enableGaExpansion = false } = await request.json(); + + if (!model) { + return NextResponse.json({ error: 'The model cannot be empty' }, { status: 400 }); + } + + // 如果没有指定文本块ID,则获取所有文本块 + let chunks = []; + if (!chunkIds || chunkIds.length === 0) { + const result = await getProjectChunks(projectId); + chunks = result.chunks || []; + } else { + // 获取指定的文本块 + chunks = await Promise.all( + chunkIds.map(async chunkId => { + const chunk = await getChunkById(chunkId); + if (chunk) { + return { + id: chunk.id, + content: chunk.content, + length: chunk.content.length + }; + } + return null; + }) + ); + chunks = chunks.filter(Boolean); // 过滤掉不存在的文本块 + } + if (chunks.length === 0) { + return NextResponse.json({ error: 'No valid text blocks found' }, { status: 404 }); + } + + const results = []; + const errors = []; + + // 获取项目 task-config 信息 + const taskConfig = await getTaskConfig(projectId); + const { questionGenerationLength } = taskConfig; + for (const chunk of chunks) { + try { + // 根据文本长度自动计算问题数量 + const questionNumber = Math.floor(chunk.length / questionGenerationLength); + + let result; + if (enableGaExpansion) { + // 使用GA增强的问题生成 + result = await generateQuestionsForChunkWithGA(projectId, chunk.id, { + model, + language, + number: questionNumber + }); + } else { + // 使用标准问题生成 + result = await generateQuestionsForChunk(projectId, chunk.id, { + model, + language, + number: questionNumber + }); + } + + // 统一处理返回结果格式 + if (result && result.questions && Array.isArray(result.questions)) { + // GA增强模式的结果格式 + results.push({ + chunkId: chunk.id, + success: true, + questions: result.questions, + total: result.total, + gaExpansionUsed: result.gaExpansionUsed, + gaPairsCount: result.gaPairsCount + }); + } else if (result && result.labelQuestions && Array.isArray(result.labelQuestions)) { + // 标准模式的结果格式 + results.push({ + chunkId: chunk.id, + success: true, + questions: result.labelQuestions, + total: result.total, + gaExpansionUsed: false, + gaPairsCount: 0 + }); + } else { + errors.push({ + chunkId: chunk.id, + error: 'Failed to parse questions' + }); + } + } catch (error) { + console.error(`Failed to generate questions for text block ${chunk.id}:`, String(error)); + errors.push({ + chunkId: chunk.id, + error: error.message || 'Failed to generate questions' + }); + } + } + + // 返回生成结果 + return NextResponse.json({ + results, + errors, + totalSuccess: results.length, + totalErrors: errors.length, + totalChunks: chunks.length + }); + } catch (error) { + console.error('Failed to generate questions:', String(error)); + return NextResponse.json({ error: error.message || 'Failed to generate questions' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/huggingface/upload/route.js b/easy-dataset-main/app/api/projects/[projectId]/huggingface/upload/route.js new file mode 100644 index 0000000..5e4de6c --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/huggingface/upload/route.js @@ -0,0 +1,310 @@ +import { NextResponse } from 'next/server'; +import { getProject } from '@/lib/db/projects'; +import { getDatasets } from '@/lib/db/datasets'; +import fs from 'fs'; +import path from 'path'; +import os from 'os'; +import { uploadFiles, createRepo, checkRepoAccess } from '@huggingface/hub'; + +// 上传数据集到 HuggingFace +export async function POST(request, { params }) { + try { + const projectId = params.projectId; + const { + token, + datasetName, + isPrivate, + formatType, + systemPrompt, + confirmedOnly, + includeCOT, + fileFormat, + customFields, + reasoningLanguage + } = await request.json(); + + // 获取项目信息 + const project = await getProject(projectId); + if (!project) { + return NextResponse.json({ error: '项目不存在' }, { status: 404 }); + } + + // 获取数据集问题 + const questions = await getDatasets(projectId, confirmedOnly); + if (!questions || questions.length === 0) { + return NextResponse.json({ error: '没有可用的数据集问题' }, { status: 400 }); + } + + // 格式化数据集 + const formattedData = formatDataset(questions, formatType, systemPrompt, includeCOT, customFields); + + // 创建临时目录 + const tempDir = path.join(os.tmpdir(), `hf-upload-${projectId}-${Date.now()}`); + fs.mkdirSync(tempDir, { recursive: true }); + + // 创建数据集文件 + const datasetFilePath = path.join(tempDir, `dataset.${fileFormat}`); + if (fileFormat === 'json') { + fs.writeFileSync(datasetFilePath, JSON.stringify(formattedData, null, 2)); + } else if (fileFormat === 'jsonl') { + const jsonlContent = formattedData.map(item => JSON.stringify(item)).join('\n'); + fs.writeFileSync(datasetFilePath, jsonlContent); + } else if (fileFormat === 'csv') { + const csvContent = convertToCSV(formattedData); + fs.writeFileSync(datasetFilePath, csvContent); + } + + // 创建 README.md 文件 + const readmePath = path.join(tempDir, 'README.md'); + const readmeContent = generateReadme(project.name, project.description, formatType); + fs.writeFileSync(readmePath, readmeContent); + + // 使用 Hugging Face REST API 上传数据集 + const visibility = isPrivate ? 'private' : 'public'; + + try { + // 准备仓库配置 + const repo = { type: 'dataset', name: datasetName }; + + // 检查仓库是否存在 + let repoExists = true; + try { + await checkRepoAccess({ repo, accessToken: token }); + console.log(`Repository ${datasetName} exists, continuing to upload files`); + } catch (error) { + // If error code is 404, the repository does not exist + if (error.statusCode === 404) { + repoExists = false; + console.log(`Repository ${datasetName} does not exist, preparing to create`); + } else { + // Other errors (e.g., permission errors) + throw new Error(`Failed to check repository access: ${error.message}`); + } + } + + // If the repository does not exist, create a new one + if (!repoExists) { + try { + await createRepo({ + repo, + accessToken: token, + private: isPrivate, + license: 'mit', + description: project.description || 'Dataset created with Easy Dataset' + }); + console.log(`Successfully created dataset repository: ${datasetName}`); + } catch (error) { + throw new Error(`Failed to create dataset repository: ${error.message}`); + } + } + + // 2. 上传数据集文件 + await uploadFile(token, datasetName, datasetFilePath, `dataset.${fileFormat}`); + + // 3. 上传 README.md + await uploadFile(token, datasetName, readmePath, 'README.md'); + } catch (error) { + console.error('Upload to HuggingFace Failed:', String(error)); + return NextResponse.json({ error: `Upload Error: ${error.message}` }, { status: 500 }); + } + + // 清理临时目录 + fs.rmSync(tempDir, { recursive: true, force: true }); + + // 返回成功信息 + const datasetUrl = `https://huggingface.co/datasets/${datasetName}`; + return NextResponse.json({ + success: true, + message: 'Upload successfully HuggingFace', + url: datasetUrl + }); + } catch (error) { + console.error('Upload Faile:', String(error)); + return NextResponse.json({ error: error.message }, { status: 500 }); + } +} + +// 格式化数据集 +function formatDataset(questions, formatType, systemPrompt, includeCOT, customFields) { + if (formatType === 'alpaca') { + return questions.map(q => { + const item = { + instruction: q.question, + input: '', + output: includeCOT && q.cot ? `${q.cot}\n\n${q.answer}` : q.answer + }; + + if (systemPrompt) { + item.system = systemPrompt; + } + + return item; + }); + } else if (formatType === 'sharegpt') { + return questions.map(q => { + const messages = []; + + if (systemPrompt) { + messages.push({ + role: 'system', + content: systemPrompt + }); + } + + messages.push({ + role: 'user', + content: q.question + }); + + messages.push({ + role: 'assistant', + content: includeCOT && q.cot ? `${q.cot}\n\n${q.answer}` : q.answer + }); + + return { messages }; + }); + } else if (formatType === 'multilingualthinking') { + return questions.map(q => { + const messages = []; + + // Main message block + const mainMsg = { + reasoning_language: reasoningLanguage ? reasoningLanguage : 'English', + user: q.question, + analysis: includeCOT && q.cot ? `${q.cot}` : null, + final: q.answer + }; + if (systemPrompt) { + mainMsg.developer = systemPrompt; + } + messages.push(mainMsg); + + // Optional system prompt + if (systemPrompt) { + messages.push({ + role: 'system', + content: systemPrompt, + thinking: null + }); + } + + // User message + messages.push({ + role: 'user', + content: q.question, + thinking: null + }); + + // Assistant message + messages.push({ + role: 'assistant', + content: q.answer, + thinking: includeCOT && q.cot ? `${q.cot}` : null + }); + + return { messages }; + }); + } else if (formatType === 'custom' && customFields) { + return questions.map(q => { + const item = { + [customFields.questionField]: q.question, + [customFields.answerField]: q.answer + }; + + if (includeCOT && q.cot) { + item[customFields.cotField] = q.cot; + } + + if (customFields.includeLabels && q.labels) { + item.labels = q.labels; + } + + if (customFields.includeChunk && q.chunkId) { + item.chunkId = q.chunkId; + } + + return item; + }); + } + + // 默认返回 alpaca 格式 + return questions.map(q => ({ + instruction: q.question, + output: includeCOT && q.cot ? `${q.cot}\n\n${q.answer}` : q.answer + })); +} + +// 将数据转换为 CSV 格式 +function convertToCSV(data) { + if (!data || data.length === 0) return ''; + + const headers = Object.keys(data[0]); + const headerRow = headers.join(','); + + const rows = data.map(item => { + return headers + .map(header => { + const value = item[header]; + if (typeof value === 'string') { + // 处理字符串中的逗号和引号 + return `"${value.replace(/"/g, '""')}"`; + } else if (Array.isArray(value)) { + return `"${JSON.stringify(value).replace(/"/g, '""')}"`; + } else if (typeof value === 'object' && value !== null) { + return `"${JSON.stringify(value).replace(/"/g, '""')}"`; + } + return value; + }) + .join(','); + }); + + return [headerRow, ...rows].join('\n'); +} + +// 使用 @huggingface/hub 包上传文件到 HuggingFace +async function uploadFile(token, datasetName, filePath, destFileName) { + try { + // 准备仓库配置 + const repo = { type: 'dataset', name: datasetName }; + + // 创建文件 URL + const fileUrl = new URL(`file://${filePath}`); + + // 使用 @huggingface/hub 包上传文件 + await uploadFiles({ + repo, + accessToken: token, + files: [ + { + path: destFileName, + content: fileUrl + } + ], + commitTitle: `Upload ${destFileName}`, + commitDescription: `Files uploaded using Easy Dataset` + }); + + return { success: true }; + } catch (error) { + console.error(`File ${destFileName} Upload Error:`, String(error)); + throw error; + } +} + +// Generate README.md file +function generateReadme(projectName, projectDescription, formatType) { + return `# ${projectName} + +## Description +${projectDescription || 'This dataset was created using the Easy Dataset tool.'} + +## Format +This dataset is in ${formatType} format. + +## Creation Method +This dataset was created using the [Easy Dataset](https://github.com/ConardLi/easy-dataset) tool. + +> Easy Dataset is a specialized application designed to streamline the creation of fine-tuning datasets for Large Language Models (LLMs). It offers an intuitive interface for uploading domain-specific files, intelligently splitting content, generating questions, and producing high-quality training data for model fine-tuning. + +`; +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/image-datasets/[datasetId]/route.js b/easy-dataset-main/app/api/projects/[projectId]/image-datasets/[datasetId]/route.js new file mode 100644 index 0000000..7e7a9ea --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/image-datasets/[datasetId]/route.js @@ -0,0 +1,109 @@ +import { NextResponse } from 'next/server'; +import { getImageDatasetById, updateImageDataset, deleteImageDataset } from '@/lib/db/imageDatasets'; +import { getProjectPath } from '@/lib/db/base'; +import fs from 'fs/promises'; +import path from 'path'; + +// 获取单个数据集详情 +export async function GET(request, { params }) { + try { + const { projectId, datasetId } = params; + + const dataset = await getImageDatasetById(datasetId); + + if (!dataset || dataset.projectId !== projectId) { + return NextResponse.json({ error: 'Dataset not found' }, { status: 404 }); + } + + // 获取项目路径 + const projectPath = await getProjectPath(projectId); + + // 读取图片 base64 + let base64 = null; + try { + const imagePath = path.join(projectPath, 'images', dataset.imageName); + const imageBuffer = await fs.readFile(imagePath); + const base64Data = imageBuffer.toString('base64'); + const ext = path.extname(dataset.imageName).toLowerCase(); + const mimeType = ext === '.png' ? 'image/png' : ext === '.gif' ? 'image/gif' : 'image/jpeg'; + base64 = `data:${mimeType};base64,${base64Data}`; + } catch (error) { + console.error(`Failed to read image ${dataset.imageName}:`, error); + } + + // 添加图片 base64 + const datasetWithImage = { + ...dataset, + base64 + }; + + return NextResponse.json(datasetWithImage); + } catch (error) { + console.error('Failed to get dataset detail:', error); + return NextResponse.json({ error: error.message || 'Failed to get dataset detail' }, { status: 500 }); + } +} + +// 更新数据集 +export async function PUT(request, { params }) { + try { + const { projectId, datasetId } = params; + const updates = await request.json(); + + // 验证数据集存在且属于该项目 + const dataset = await getImageDatasetById(datasetId); + if (!dataset || dataset.projectId !== projectId) { + return NextResponse.json({ error: 'Dataset not found' }, { status: 404 }); + } + + // 更新数据集 + const updated = await updateImageDataset(datasetId, updates); + + // 获取项目路径 + const projectPath = await getProjectPath(projectId); + + // 读取图片 base64 + let base64 = null; + try { + const imagePath = path.join(projectPath, 'images', updated.imageName); + const imageBuffer = await fs.readFile(imagePath); + const base64Data = imageBuffer.toString('base64'); + const ext = path.extname(updated.imageName).toLowerCase(); + const mimeType = ext === '.png' ? 'image/png' : ext === '.gif' ? 'image/gif' : 'image/jpeg'; + base64 = `data:${mimeType};base64,${base64Data}`; + } catch (error) { + console.error(`Failed to read image ${updated.imageName}:`, error); + } + + // 添加图片 base64 + const updatedWithImage = { + ...updated, + base64 + }; + + return NextResponse.json(updatedWithImage); + } catch (error) { + console.error('Failed to update dataset:', error); + return NextResponse.json({ error: error.message || 'Failed to update dataset' }, { status: 500 }); + } +} + +// 删除数据集 +export async function DELETE(request, { params }) { + try { + const { projectId, datasetId } = params; + + // 验证数据集存在且属于该项目 + const dataset = await getImageDatasetById(datasetId); + if (!dataset || dataset.projectId !== projectId) { + return NextResponse.json({ error: 'Dataset not found' }, { status: 404 }); + } + + await deleteImageDataset(datasetId); + + return NextResponse.json({ success: true }); + } catch (error) { + console.error('Failed to delete dataset:', error); + return NextResponse.json({ error: error.message || 'Failed to delete dataset' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/image-datasets/export-zip/route.js b/easy-dataset-main/app/api/projects/[projectId]/image-datasets/export-zip/route.js new file mode 100644 index 0000000..c1c96f4 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/image-datasets/export-zip/route.js @@ -0,0 +1,85 @@ +import { NextResponse } from 'next/server'; +import { getImageDatasetsForExport } from '@/lib/db/imageDatasets'; +import archiver from 'archiver'; +import { getProjectPath } from '@/lib/db/base'; +import path from 'path'; +import fs from 'fs'; + +/** + * 导出图片文件压缩包 + */ +export async function GET(request, { params }) { + try { + const { projectId } = params; + const { searchParams } = new URL(request.url); + const confirmedOnly = searchParams.get('confirmedOnly') === 'true'; + + // 验证项目ID + if (!projectId) { + return NextResponse.json({ error: 'Project ID cannot be empty' }, { status: 400 }); + } + + // 获取数据集(用于确定需要哪些图片) + const datasets = await getImageDatasetsForExport(projectId, confirmedOnly); + + if (!datasets || datasets.length === 0) { + return NextResponse.json({ error: 'No data to export' }, { status: 404 }); + } + + // 获取所有需要的图片名称 + const imageNames = new Set(datasets.map(d => d.imageName).filter(Boolean)); + + if (imageNames.size === 0) { + return NextResponse.json({ error: 'No images to export' }, { status: 404 }); + } + + // 创建压缩包 + const archive = archiver('zip', { + zlib: { level: 9 } + }); + + // 设置响应头 + const dateStr = new Date().toISOString().slice(0, 10); + const filename = `images-${projectId}-${dateStr}.zip`; + + // 添加图片文件到压缩包 + const projectPath = await getProjectPath(projectId); + const imageDir = path.join(projectPath, 'images'); + + if (!fs.existsSync(imageDir)) { + return NextResponse.json({ error: 'Image directory not found' }, { status: 404 }); + } + + let addedCount = 0; + for (const imageName of imageNames) { + const imagePath = path.join(imageDir, imageName); + if (fs.existsSync(imagePath)) { + archive.file(imagePath, { name: imageName }); + addedCount++; + } + } + + if (addedCount === 0) { + return NextResponse.json({ error: 'No image files found' }, { status: 404 }); + } + + // 完成压缩 + archive.finalize(); + + // 返回流式响应 + return new NextResponse(archive, { + headers: { + 'Content-Type': 'application/zip', + 'Content-Disposition': `attachment; filename="${filename}"` + } + }); + } catch (error) { + console.error('Failed to export images:', String(error)); + return NextResponse.json( + { + error: error.message || 'Failed to export images' + }, + { status: 500 } + ); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/image-datasets/export/route.js b/easy-dataset-main/app/api/projects/[projectId]/image-datasets/export/route.js new file mode 100644 index 0000000..a6f73ca --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/image-datasets/export/route.js @@ -0,0 +1,32 @@ +import { NextResponse } from 'next/server'; +import { getImageDatasetsForExport } from '@/lib/db/imageDatasets'; + +/** + * 导出图像数据集 + */ +export async function POST(request, { params }) { + try { + const { projectId } = params; + const body = await request.json(); + + // 验证项目ID + if (!projectId) { + return NextResponse.json({ error: 'Project ID cannot be empty' }, { status: 400 }); + } + + const confirmedOnly = body.confirmedOnly || false; + + // 获取数据集 + const datasets = await getImageDatasetsForExport(projectId, confirmedOnly); + + return NextResponse.json(datasets); + } catch (error) { + console.error('Failed to export image datasets:', String(error)); + return NextResponse.json( + { + error: error.message || 'Failed to export image datasets' + }, + { status: 500 } + ); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/image-datasets/route.js b/easy-dataset-main/app/api/projects/[projectId]/image-datasets/route.js new file mode 100644 index 0000000..63102dd --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/image-datasets/route.js @@ -0,0 +1,72 @@ +import { NextResponse } from 'next/server'; +import { getImageDatasetsByProject } from '@/lib/db/imageDatasets'; +import { getProjectPath } from '@/lib/db/base'; +import fs from 'fs/promises'; +import path from 'path'; + +// 获取图片数据集列表 +export async function GET(request, { params }) { + try { + const { projectId } = params; + const { searchParams } = new URL(request.url); + + const page = parseInt(searchParams.get('page')) || 1; + const pageSize = parseInt(searchParams.get('pageSize')) || 20; + const search = searchParams.get('search') || ''; + const confirmed = searchParams.get('confirmed'); + const minScore = searchParams.get('minScore'); + const maxScore = searchParams.get('maxScore'); + + // 构建筛选条件 + const filters = {}; + if (search) { + filters.search = search; + } + if (confirmed !== null && confirmed !== undefined) { + filters.confirmed = confirmed === 'true'; + } + if (minScore) { + filters.minScore = parseInt(minScore); + } + if (maxScore) { + filters.maxScore = parseInt(maxScore); + } + + const result = await getImageDatasetsByProject(projectId, page, pageSize, filters); + + // 获取项目路径 + const projectPath = await getProjectPath(projectId); + + // 为每个数据集添加图片 base64 + const datasetsWithImages = await Promise.all( + result.data.map(async dataset => { + try { + const imagePath = path.join(projectPath, 'images', dataset.imageName); + const imageBuffer = await fs.readFile(imagePath); + const base64 = imageBuffer.toString('base64'); + const ext = path.extname(dataset.imageName).toLowerCase(); + const mimeType = ext === '.png' ? 'image/png' : ext === '.gif' ? 'image/gif' : 'image/jpeg'; + + return { + ...dataset, + base64: `data:${mimeType};base64,${base64}` + }; + } catch (error) { + console.error(`Failed to read image ${dataset.imageName}:`, error); + return { + ...dataset, + base64: null + }; + } + }) + ); + + return NextResponse.json({ + data: datasetsWithImages, + total: result.total + }); + } catch (error) { + console.error('Failed to get image datasets:', error); + return NextResponse.json({ error: error.message || 'Failed to get image datasets' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/image-datasets/tags/route.js b/easy-dataset-main/app/api/projects/[projectId]/image-datasets/tags/route.js new file mode 100644 index 0000000..8b7d768 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/image-datasets/tags/route.js @@ -0,0 +1,37 @@ +import { NextResponse } from 'next/server'; +import { getImageDatasetsTagsByProject } from '@/lib/db/imageDatasets'; + +// 获取项目中所有已使用的标签 +export async function GET(request, { params }) { + try { + const { projectId } = params; + + // 获取项目的所有数据集 + const datasets = await getImageDatasetsTagsByProject(projectId); + + console.log('datasets', datasets); + + // 提取所有标签 + const tagsSet = new Set(); + datasets.forEach(dataset => { + if (dataset.tags) { + try { + const tags = JSON.parse(dataset.tags); + if (Array.isArray(tags)) { + tags.forEach(tag => tagsSet.add(tag)); + } + } catch (e) { + // 忽略解析错误 + } + } + }); + + // 转换为数组并排序 + const tags = Array.from(tagsSet).sort(); + + return NextResponse.json({ tags }); + } catch (error) { + console.error('Failed to get tags:', error); + return NextResponse.json({ error: error.message || 'Failed to get tags' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/images/[imageId]/route.js b/easy-dataset-main/app/api/projects/[projectId]/images/[imageId]/route.js new file mode 100644 index 0000000..886a9a5 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/images/[imageId]/route.js @@ -0,0 +1,31 @@ +import { NextResponse } from 'next/server'; +import { getImageDetailWithQuestions } from '@/lib/services/images'; + +// 根据图片ID获取图片详情,包含问题列表和已标注数据 +export async function GET(request, { params }) { + try { + const { projectId, imageId } = params; + + // 调用服务层获取图片详情 + const imageData = await getImageDetailWithQuestions(projectId, imageId); + + return NextResponse.json({ + success: true, + data: imageData + }); + } catch (error) { + console.error('Failed to get image details:', error); + + // 根据错误类型返回不同的状态码 + let statusCode = 500; + if (error.message === '缺少图片ID') { + statusCode = 400; + } else if (error.message === '图片不存在') { + statusCode = 404; + } else if (error.message === '图片不属于指定项目') { + statusCode = 403; + } + + return NextResponse.json({ error: error.message || 'Failed to get image details' }, { status: statusCode }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/images/annotations/route.js b/easy-dataset-main/app/api/projects/[projectId]/images/annotations/route.js new file mode 100644 index 0000000..c60eac4 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/images/annotations/route.js @@ -0,0 +1,89 @@ +import { NextResponse } from 'next/server'; +import { PrismaClient } from '@prisma/client'; +import { getImageById, getImageChunk } from '@/lib/db/images'; +import { createImageDataset } from '@/lib/db/imageDatasets'; + +const prisma = new PrismaClient(); + +// 创建标注 +export async function POST(request, { params }) { + try { + const { projectId } = params; + const { imageId, questionId, question, answerType, answer, note } = await request.json(); + + // 验证必填字段 + if (!imageId || !question || !answerType || answer === undefined || answer === null) { + return NextResponse.json({ error: '缺少必要参数:imageId, question, answerType, answer' }, { status: 400 }); + } + + // 验证图片存在 + const image = await getImageById(imageId); + if (!image || image.projectId !== projectId) { + return NextResponse.json({ error: '图片不存在' }, { status: 404 }); + } + + // 验证答案类型 + if (!['text', 'label', 'custom_format'].includes(answerType)) { + return NextResponse.json({ error: '无效的答案类型' }, { status: 400 }); + } + + // 验证答案内容 + if (answerType === 'text' && typeof answer !== 'string') { + return NextResponse.json({ error: '文本类型答案必须是字符串' }, { status: 400 }); + } + if (answerType === 'label' && !Array.isArray(answer)) { + return NextResponse.json({ error: '标签类型答案必须是数组' }, { status: 400 }); + } + + // 序列化答案 + let answerString = answer; + if (answerType !== 'text' && typeof answerString !== 'string') { + answerString = JSON.stringify(answer, null, 2); + } + + // 1. 获取问题记录(前端传递的 questionId 指向已有的问题) + if (!questionId) { + return NextResponse.json({ error: '缺少必要参数:questionId' }, { status: 400 }); + } + + const questionRecord = await prisma.questions.findUnique({ + where: { id: questionId } + }); + + if (!questionRecord) { + return NextResponse.json({ error: '问题不存在' }, { status: 404 }); + } + + // 验证问题属于该图片 + if (questionRecord.imageId !== imageId) { + return NextResponse.json({ error: '问题不属于该图片' }, { status: 400 }); + } + + // 2. 更新问题为已回答 + await prisma.questions.update({ + where: { id: questionRecord.id }, + data: { answered: true } + }); + + // 3. 创建 ImageDataset 记录 + const dataset = await createImageDataset(projectId, { + imageId: image.id, + imageName: image.imageName, + questionId: questionRecord.id, + question, + answer: answerString, + answerType, + model: 'manual', + note: note || '' + }); + + return NextResponse.json({ + success: true, + dataset, + questionId: questionRecord.id + }); + } catch (error) { + console.error('Failed to create annotation:', error); + return NextResponse.json({ error: error.message || 'Failed to create annotation' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/images/datasets/route.js b/easy-dataset-main/app/api/projects/[projectId]/images/datasets/route.js new file mode 100644 index 0000000..82958a2 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/images/datasets/route.js @@ -0,0 +1,41 @@ +import { NextResponse } from 'next/server'; +import { getImageByName } from '@/lib/db/images'; +import imageService from '@/lib/services/images'; + +// 生成图像数据集 +export async function POST(request, { params }) { + try { + const { projectId } = params; + const { imageName, question, model, language = 'zh', previewOnly = false } = await request.json(); + + if (!imageName || !question) { + return NextResponse.json({ error: '缺少必要参数' }, { status: 400 }); + } + + if (!model) { + return NextResponse.json({ error: '请选择一个视觉模型' }, { status: 400 }); + } + + // 获取图片信息 + const image = await getImageByName(projectId, imageName); + if (!image) { + return NextResponse.json({ error: '图片不存在' }, { status: 404 }); + } + + // 调用图片数据集生成服务 + const result = await imageService.generateDatasetForImage(projectId, image.id, question, { + model, + language, + previewOnly + }); + + return NextResponse.json({ + success: true, + answer: result.answer, + dataset: result.dataset + }); + } catch (error) { + console.error('Failed to generate image dataset:', error); + return NextResponse.json({ error: error.message || 'Failed to generate dataset' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/images/next-unanswered/route.js b/easy-dataset-main/app/api/projects/[projectId]/images/next-unanswered/route.js new file mode 100644 index 0000000..19ad7c6 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/images/next-unanswered/route.js @@ -0,0 +1,41 @@ +import { NextResponse } from 'next/server'; +import { PrismaClient } from '@prisma/client'; +import { getImageDetailWithQuestions } from '@/lib/services/images'; + +const prisma = new PrismaClient(); + +// 获取下一个有未标注问题的图片 +export async function GET(request, { params }) { + try { + const { projectId } = params; + + // 查找第一个有未标注问题的图片 + const unansweredQuestion = await prisma.questions.findFirst({ + where: { + projectId, + imageId: { + not: null + }, + answered: false + } + }); + + if (!unansweredQuestion) { + return NextResponse.json({ + success: true, + data: null + }); + } + + // 调用服务层获取图片详情 + const imageData = await getImageDetailWithQuestions(projectId, unansweredQuestion.imageId); + + return NextResponse.json({ + success: true, + data: imageData + }); + } catch (error) { + console.error('Failed to get next unanswered image:', error); + return NextResponse.json({ error: error.message || 'Failed to get next unanswered image' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/images/pdf-convert/route.js b/easy-dataset-main/app/api/projects/[projectId]/images/pdf-convert/route.js new file mode 100644 index 0000000..b302bad --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/images/pdf-convert/route.js @@ -0,0 +1,98 @@ +import { NextResponse } from 'next/server'; +import { getProjectPath } from '@/lib/db/base'; +import { importImagesFromDirectories } from '@/lib/services/images'; +import fs from 'fs/promises'; +import path from 'path'; +import { savePdfAsImages } from '@/lib/util/file'; + +// PDF 转图片并导入 +export async function POST(request, { params }) { + let tempPdfPath = null; + let tempImagesDir = null; + + try { + const { projectId } = params; + const formData = await request.formData(); + const pdfFile = formData.get('file'); + + if (!pdfFile) { + return NextResponse.json({ error: '请选择 PDF 文件' }, { status: 400 }); + } + + if (!pdfFile.name.toLowerCase().endsWith('.pdf')) { + return NextResponse.json({ error: '只支持 PDF 文件' }, { status: 400 }); + } + + const projectPath = await getProjectPath(projectId); + const tempDir = path.join(projectPath, 'temp'); + await fs.mkdir(tempDir, { recursive: true }); + + // 1. 保存 PDF 到临时目录 + tempPdfPath = path.join(tempDir, `temp_${Date.now()}_${pdfFile.name}`); + const pdfBuffer = Buffer.from(await pdfFile.arrayBuffer()); + await fs.writeFile(tempPdfPath, pdfBuffer); + + // 2. 创建临时图片目录 + tempImagesDir = path.join(tempDir, `pdf_images_${Date.now()}`); + await fs.mkdir(tempImagesDir, { recursive: true }); + + // 3. 调用 pdf2md-js 转换 PDF 为图片 + console.log('开始转换 PDF 为图片...'); + const imagePaths = await savePdfAsImages(tempPdfPath, tempImagesDir, 3); + console.log('PDF 转换完成,生成图片数量:', imagePaths.length); + + if (!imagePaths || imagePaths.length === 0) { + throw new Error('PDF 转换失败,未生成图片'); + } + + // 4. 直接调用服务层导入图片 + const importResult = await importImagesFromDirectories(projectId, [tempImagesDir]); + + // 5. 清理临时文件 + try { + if (tempPdfPath) { + await fs.unlink(tempPdfPath); + } + if (tempImagesDir) { + const tempImages = await fs.readdir(tempImagesDir); + for (const img of tempImages) { + await fs.unlink(path.join(tempImagesDir, img)); + } + await fs.rmdir(tempImagesDir); + } + const tempDirContents = await fs.readdir(tempDir); + if (tempDirContents.length === 0) { + await fs.rmdir(tempDir); + } + } catch (cleanupErr) { + console.warn('清理临时文件失败:', cleanupErr); + } + + return NextResponse.json({ + success: true, + count: importResult.count, + images: importResult.images, + pdfName: pdfFile.name + }); + } catch (error) { + console.error('Failed to convert PDF:', error); + + // 清理临时文件 + try { + if (tempPdfPath) { + await fs.unlink(tempPdfPath).catch(() => {}); + } + if (tempImagesDir) { + const tempImages = await fs.readdir(tempImagesDir).catch(() => []); + for (const img of tempImages) { + await fs.unlink(path.join(tempImagesDir, img)).catch(() => {}); + } + await fs.rmdir(tempImagesDir).catch(() => {}); + } + } catch (cleanupErr) { + console.warn('清理临时文件失败:', cleanupErr); + } + + return NextResponse.json({ error: error.message || 'Failed to convert PDF' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/images/questions/route.js b/easy-dataset-main/app/api/projects/[projectId]/images/questions/route.js new file mode 100644 index 0000000..747c816 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/images/questions/route.js @@ -0,0 +1,40 @@ +import { NextResponse } from 'next/server'; +import { getImageByName } from '@/lib/db/images'; +import imageService from '@/lib/services/images'; + +// 生成图片问题 +export async function POST(request, { params }) { + try { + const { projectId } = params; + const { imageName, count = 3, model, language = 'zh' } = await request.json(); + + if (!imageName) { + return NextResponse.json({ error: '缺少图片名称' }, { status: 400 }); + } + + if (!model) { + return NextResponse.json({ error: '请选择一个视觉模型' }, { status: 400 }); + } + + // 获取图片信息 + const image = await getImageByName(projectId, imageName); + if (!image) { + return NextResponse.json({ error: '图片不存在' }, { status: 404 }); + } + + // 调用图片问题生成服务 + const result = await imageService.generateQuestionsForImage(projectId, image.id, { + model, + language, + count + }); + + return NextResponse.json({ + success: true, + questions: result.questions + }); + } catch (error) { + console.error('Failed to generate image questions:', error); + return NextResponse.json({ error: error.message || 'Failed to generate questions' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/images/route.js b/easy-dataset-main/app/api/projects/[projectId]/images/route.js new file mode 100644 index 0000000..37e303e --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/images/route.js @@ -0,0 +1,92 @@ +import { NextResponse } from 'next/server'; +import { getImages, deleteImage, getImageDetail } from '@/lib/db/images'; +import { getProjectPath } from '@/lib/db/base'; +import { db } from '@/lib/db/index'; +import { importImagesFromDirectories } from '@/lib/services/images'; +import fs from 'fs/promises'; +import path from 'path'; + +// 获取图片列表 +export async function GET(request, { params }) { + try { + const { projectId } = params; + const { searchParams } = new URL(request.url); + + const page = parseInt(searchParams.get('page')) || 1; + const pageSize = parseInt(searchParams.get('pageSize')) || 20; + const imageName = searchParams.get('imageName') || ''; + const hasQuestions = searchParams.get('hasQuestions'); + const hasDatasets = searchParams.get('hasDatasets'); + const simple = searchParams.get('simple'); + + const result = await getImages(projectId, page, pageSize, imageName, hasQuestions, hasDatasets, simple); + + return NextResponse.json(result); + } catch (error) { + console.error('Failed to get images:', error); + return NextResponse.json({ error: error.message || 'Failed to get images' }, { status: 500 }); + } +} + +// 导入图片 +export async function POST(request, { params }) { + try { + const { projectId } = params; + const { directories } = await request.json(); + + // 调用服务层处理图片导入 + const result = await importImagesFromDirectories(projectId, directories); + + return NextResponse.json(result); + } catch (error) { + console.error('Failed to import images:', error); + return NextResponse.json({ error: error.message || 'Failed to import images' }, { status: 500 }); + } +} + +// 删除图片 +export async function DELETE(request, { params }) { + try { + const { projectId } = params; + const { searchParams } = new URL(request.url); + const imageId = searchParams.get('imageId'); + + if (!imageId) { + return NextResponse.json({ error: '缺少图片ID' }, { status: 400 }); + } + + // 获取图片信息 + const image = await getImageDetail(imageId); + + if (!image) { + return NextResponse.json({ error: '图片不存在' }, { status: 404 }); + } + + // 删除关联的数据集 + await db.imageDatasets.deleteMany({ + where: { imageId } + }); + + // 删除关联的问题 + await db.questions.deleteMany({ + where: { imageId } + }); + + // 删除文件 + const projectPath = await getProjectPath(projectId); + const filePath = path.join(projectPath, 'images', image.imageName); + try { + await fs.unlink(filePath); + } catch (err) { + console.warn('删除文件失败:', err); + } + + // 删除数据库记录 + await deleteImage(imageId); + + return NextResponse.json({ success: true }); + } catch (error) { + console.error('Failed to delete image:', error); + return NextResponse.json({ error: error.message || 'Failed to delete image' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/images/zip-import/route.js b/easy-dataset-main/app/api/projects/[projectId]/images/zip-import/route.js new file mode 100644 index 0000000..dd4786d --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/images/zip-import/route.js @@ -0,0 +1,127 @@ +import { NextResponse } from 'next/server'; +import { getProjectPath } from '@/lib/db/base'; +import { importImagesFromDirectories } from '@/lib/services/images'; +import fs from 'fs/promises'; +import path from 'path'; +import AdmZip from 'adm-zip'; + +// 压缩包解压并导入图片 +export async function POST(request, { params }) { + let tempZipPath = null; + let tempExtractDir = null; + + try { + const { projectId } = params; + const formData = await request.formData(); + const zipFile = formData.get('file'); + + if (!zipFile) { + return NextResponse.json({ error: '请选择压缩包文件' }, { status: 400 }); + } + + if (!zipFile.name.toLowerCase().endsWith('.zip')) { + return NextResponse.json({ error: '只支持 ZIP 格式的压缩包' }, { status: 400 }); + } + + const projectPath = await getProjectPath(projectId); + const tempDir = path.join(projectPath, 'temp'); + await fs.mkdir(tempDir, { recursive: true }); + + // 1. 保存压缩包到临时目录 + tempZipPath = path.join(tempDir, `temp_${Date.now()}_${zipFile.name}`); + const zipBuffer = Buffer.from(await zipFile.arrayBuffer()); + await fs.writeFile(tempZipPath, zipBuffer); + + // 2. 创建临时解压目录 + tempExtractDir = path.join(tempDir, `zip_extract_${Date.now()}`); + await fs.mkdir(tempExtractDir, { recursive: true }); + + // 3. 使用 adm-zip 解压文件 + console.log('开始解压压缩包...'); + const zip = new AdmZip(tempZipPath); + const zipEntries = zip.getEntries(); + + // 支持的图片扩展名 + const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.svg']; + let extractedCount = 0; + + // 遍历压缩包中的所有文件 + for (const entry of zipEntries) { + // 跳过目录和隐藏文件 + if ( + entry.isDirectory || + entry.entryName.startsWith('__MACOSX') || + path.basename(entry.entryName).startsWith('.') + ) { + continue; + } + + const ext = path.extname(entry.entryName).toLowerCase(); + if (imageExtensions.includes(ext)) { + // 提取文件名(不包含路径) + const fileName = path.basename(entry.entryName); + const targetPath = path.join(tempExtractDir, fileName); + + // 解压文件 + zip.extractEntryTo(entry, tempExtractDir, false, true, false, fileName); + extractedCount++; + } + } + + console.log(`压缩包解压完成,提取图片数量: ${extractedCount}`); + + if (extractedCount === 0) { + throw new Error('压缩包中没有找到支持的图片文件'); + } + + // 4. 调用服务层导入图片 + const importResult = await importImagesFromDirectories(projectId, [tempExtractDir]); + + // 5. 清理临时文件 + try { + if (tempZipPath) { + await fs.unlink(tempZipPath); + } + if (tempExtractDir) { + const tempImages = await fs.readdir(tempExtractDir); + for (const img of tempImages) { + await fs.unlink(path.join(tempExtractDir, img)); + } + await fs.rmdir(tempExtractDir); + } + const tempDirContents = await fs.readdir(tempDir); + if (tempDirContents.length === 0) { + await fs.rmdir(tempDir); + } + } catch (cleanupErr) { + console.warn('清理临时文件失败:', cleanupErr); + } + + return NextResponse.json({ + success: true, + count: importResult.count, + images: importResult.images, + zipName: zipFile.name + }); + } catch (error) { + console.error('Failed to import ZIP:', error); + + // 清理临时文件 + try { + if (tempZipPath) { + await fs.unlink(tempZipPath).catch(() => {}); + } + if (tempExtractDir) { + const tempImages = await fs.readdir(tempExtractDir).catch(() => []); + for (const img of tempImages) { + await fs.unlink(path.join(tempExtractDir, img)).catch(() => {}); + } + await fs.rmdir(tempExtractDir).catch(() => {}); + } + } catch (cleanupErr) { + console.warn('清理临时文件失败:', cleanupErr); + } + + return NextResponse.json({ error: error.message || 'Failed to import ZIP' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/llamaFactory/checkConfig/route.js b/easy-dataset-main/app/api/projects/[projectId]/llamaFactory/checkConfig/route.js new file mode 100644 index 0000000..7049298 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/llamaFactory/checkConfig/route.js @@ -0,0 +1,27 @@ +import { NextResponse } from 'next/server'; +import path from 'path'; +import fs from 'fs'; +import { getProjectRoot } from '@/lib/db/base'; + +export async function GET(request, { params }) { + try { + const { projectId } = params; + if (!projectId) { + return NextResponse.json({ error: 'The project ID cannot be empty' }, { status: 400 }); + } + + const projectRoot = await getProjectRoot(); + const projectPath = path.join(projectRoot, projectId); + const configPath = path.join(projectPath, 'dataset_info.json'); + + const exists = fs.existsSync(configPath); + + return NextResponse.json({ + exists, + configPath: exists ? configPath : null + }); + } catch (error) { + console.error('Error checking Llama Factory config:', String(error)); + return NextResponse.json({ error: error.message }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/llamaFactory/generate/route.js b/easy-dataset-main/app/api/projects/[projectId]/llamaFactory/generate/route.js new file mode 100644 index 0000000..41d7275 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/llamaFactory/generate/route.js @@ -0,0 +1,141 @@ +import { NextResponse } from 'next/server'; +import path from 'path'; +import fs from 'fs'; +import { getProjectRoot } from '@/lib/db/base'; +import { getDatasets } from '@/lib/db/datasets'; + +export async function POST(request, { params }) { + try { + const { projectId } = params; + const { formatType, systemPrompt, confirmedOnly, includeCOT, reasoningLanguage } = await request.json(); + + if (!projectId) { + return NextResponse.json({ error: 'The project ID cannot be empty' }, { status: 400 }); + } + + // 获取项目根目录 + const projectRoot = await getProjectRoot(); + const projectPath = path.join(projectRoot, projectId); + const configPath = path.join(projectPath, 'dataset_info.json'); + const alpacaPath = path.join(projectPath, 'alpaca.json'); + const sharegptPath = path.join(projectPath, 'sharegpt.json'); + const multilingualThinkingPath = path.join(projectPath, 'multilingual-thinking.json'); + + // 获取数据集 + let datasets = await getDatasets(projectId, !!confirmedOnly); + + // 创建 dataset_info.json 配置 + const config = { + [`[Easy Dataset] [${projectId}] Alpaca`]: { + file_name: 'alpaca.json', + columns: { + prompt: 'instruction', + query: 'input', + response: 'output', + system: 'system' + } + }, + [`[Easy Dataset] [${projectId}] ShareGPT`]: { + file_name: 'sharegpt.json', + formatting: 'sharegpt', + columns: { + messages: 'messages' + }, + tags: { + role_tag: 'role', + content_tag: 'content', + user_tag: 'user', + assistant_tag: 'assistant', + system_tag: 'system' + } + }, + [`[Easy Dataset] [${projectId}] multilingual-thinking`]: { + file_name: 'multilingual-thinking.json', + formatting: 'multilingual-thinking', + columns: { + messages: 'messages' + }, + tags: { + role_tag: 'role', + content_tag: 'content', + user_tag: 'user', + assistant_tag: 'assistant', + system_tag: 'system' + } + } + }; + + // 生成数据文件 + const alpacaData = datasets.map(({ question, answer, cot }) => ({ + instruction: question, + input: '', + output: cot && includeCOT ? `${cot}\n${answer}` : answer, + system: systemPrompt || '' + })); + + const sharegptData = datasets.map(({ question, answer, cot }) => { + const messages = []; + if (systemPrompt) { + messages.push({ + role: 'system', + content: systemPrompt + }); + } + messages.push({ + role: 'user', + content: question + }); + messages.push({ + role: 'assistant', + content: cot && includeCOT ? `${cot}\n${answer}` : answer + }); + return { messages }; + }); + const multilingualThinkingData = datasets.map(({ question, answer, cot }) => ({ + reasoning_language: reasoningLanguage ? reasoningLanguage : 'English', + developer: systemPrompt ? systemPrompt : '', // system prompt (may be empty) + user: question, + analysis: includeCOT && cot ? cot : null, // null if no COT + final: answer, + messages: [ + { + content: systemPrompt ? systemPrompt : '', + role: 'system', + thinking: null + }, + { + content: question, + role: 'user', + thinking: null + }, + { + content: answer, + role: 'assistant', + thinking: includeCOT && cot ? cot : null + } + ] + })); + + const multilingualThinkingLines = multilingualThinkingData.map(item => JSON.stringify(item, null, 2)).join('\n'); + + await fs.promises.writeFile(multilingualThinkingPath, multilingualThinkingLines, 'utf8'); + + // 写入文件 + await fs.promises.writeFile(configPath, JSON.stringify(config, null, 2)); + await fs.promises.writeFile(alpacaPath, JSON.stringify(alpacaData, null, 2)); + await fs.promises.writeFile(sharegptPath, JSON.stringify(sharegptData, null, 2)); + + return NextResponse.json({ + success: true, + configPath, + files: [ + { path: alpacaPath, format: 'alpaca' }, + { path: sharegptPath, format: 'sharegpt' }, + { path: multilingualThinkingPath, format: 'multilingual-thinking' } + ] + }); + } catch (error) { + console.error('Error generating Llama Factory config:', String(error)); + return NextResponse.json({ error: error.message }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/model-config/[modelConfigId]/route.js b/easy-dataset-main/app/api/projects/[projectId]/model-config/[modelConfigId]/route.js new file mode 100644 index 0000000..35e0d9c --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/model-config/[modelConfigId]/route.js @@ -0,0 +1,18 @@ +import { NextResponse } from 'next/server'; +import { deleteModelConfigById } from '@/lib/db/model-config'; + +// 删除模型配置 +export async function DELETE(request, { params }) { + try { + const { projectId, modelConfigId } = params; + // 验证项目 ID + if (!projectId) { + return NextResponse.json({ error: 'The project ID cannot be empty' }, { status: 400 }); + } + await deleteModelConfigById(modelConfigId); + return NextResponse.json(true); + } catch (error) { + console.error('Error obtaining model configuration:', String(error)); + return NextResponse.json({ error: 'Failed to obtain model configuration' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/model-config/route.js b/easy-dataset-main/app/api/projects/[projectId]/model-config/route.js new file mode 100644 index 0000000..d954c80 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/model-config/route.js @@ -0,0 +1,103 @@ +import { NextResponse } from 'next/server'; +import { createInitModelConfig, getModelConfigByProjectId, saveModelConfig } from '@/lib/db/model-config'; +import { DEFAULT_MODEL_SETTINGS, MODEL_PROVIDERS } from '@/constant/model'; +import { getProject } from '@/lib/db/projects'; +import { sortProvidersByPriority } from '@/lib/util/providerLogo'; + +function normalizeModelEndpoint(endpoint = '') { + let normalizedEndpoint = String(endpoint).trim(); + if (!normalizedEndpoint) { + return ''; + } + if (normalizedEndpoint.includes('/chat/completions')) { + normalizedEndpoint = normalizedEndpoint.replace('/chat/completions', ''); + } + return normalizedEndpoint; +} + +// 获取模型配置列表 +export async function GET(request, { params }) { + try { + const { projectId } = params; + // 验证项目 ID + if (!projectId) { + return NextResponse.json({ error: 'The project ID cannot be empty' }, { status: 400 }); + } + let modelConfigList = await getModelConfigByProjectId(projectId); + if (!modelConfigList || modelConfigList.length === 0) { + let insertModelConfigList = []; + const sortedProviders = sortProvidersByPriority(MODEL_PROVIDERS, item => item.id); + sortedProviders.forEach(item => { + let data = { + projectId: projectId, + providerId: item.id, + providerName: item.name, + endpoint: item.defaultEndpoint, + apiKey: '', + modelId: '', + modelName: '', + type: 'text', + temperature: DEFAULT_MODEL_SETTINGS.temperature, + maxTokens: DEFAULT_MODEL_SETTINGS.maxTokens, + topK: 0, + topP: DEFAULT_MODEL_SETTINGS.topP, + status: 1 + }; + insertModelConfigList.push(data); + }); + modelConfigList = await createInitModelConfig(insertModelConfigList); + } + modelConfigList = sortProvidersByPriority(modelConfigList, item => item.providerId); + let project = await getProject(projectId); + return NextResponse.json({ data: modelConfigList, defaultModelConfigId: project.defaultModelConfigId }); + } catch (error) { + console.error('Error obtaining model configuration:', String(error)); + return NextResponse.json({ error: 'Failed to obtain model configuration' }, { status: 500 }); + } +} + +// 保存模型配置 +export async function POST(request, { params }) { + try { + const { projectId } = params; + + // 验证项目 ID + if (!projectId) { + return NextResponse.json({ error: 'The project ID cannot be empty' }, { status: 400 }); + } + // 获取请求体 + const modelConfig = await request.json(); + + // 验证请求体 + if (!modelConfig) { + return NextResponse.json({ error: 'The model configuration cannot be empty ' }, { status: 400 }); + } + modelConfig.projectId = projectId; + modelConfig.endpoint = normalizeModelEndpoint(modelConfig.endpoint); + // 如果没有 modelId,使用 modelName 补齐(兼容旧逻辑) + if (!modelConfig.modelId && modelConfig.modelName) { + modelConfig.modelId = modelConfig.modelName; + } + // 如果没有 modelName,使用 modelId 补齐 + if (!modelConfig.modelName && modelConfig.modelId) { + modelConfig.modelName = modelConfig.modelId; + } + if (!modelConfig.topK) { + modelConfig.topK = 0; + } + if (!modelConfig.status) { + modelConfig.status = 1; + } + const parsedMaxTokens = Number(modelConfig.maxTokens ?? DEFAULT_MODEL_SETTINGS.maxTokens); + if (!Number.isInteger(parsedMaxTokens) || parsedMaxTokens < 1) { + return NextResponse.json({ error: 'maxTokens must be a positive integer' }, { status: 400 }); + } + modelConfig.maxTokens = parsedMaxTokens; + const res = await saveModelConfig(modelConfig); + + return NextResponse.json(res); + } catch (error) { + console.error('Error updating model configuration:', String(error)); + return NextResponse.json({ error: 'Failed to update model configuration' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/models/[modelId]/route.js b/easy-dataset-main/app/api/projects/[projectId]/models/[modelId]/route.js new file mode 100644 index 0000000..047d953 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/models/[modelId]/route.js @@ -0,0 +1,173 @@ +import { NextResponse } from 'next/server'; +import { getProjectRoot } from '@/lib/db/base'; +import path from 'path'; +import fs from 'fs/promises'; + +export async function GET(request, { params }) { + try { + const { projectId, modelId } = params; + + // 验证项目ID和模型ID + if (!projectId || !modelId) { + return NextResponse.json({ error: 'The project ID and model ID cannot be empty' }, { status: 400 }); + } + + // 获取项目根目录 + const projectRoot = await getProjectRoot(); + const projectPath = path.join(projectRoot, projectId); + + // 检查项目是否存在 + try { + await fs.access(projectPath); + } catch (error) { + return NextResponse.json({ error: 'The project does not exist' }, { status: 404 }); + } + + // 获取模型配置文件路径 + const modelConfigPath = path.join(projectPath, 'model-config.json'); + + // 检查模型配置文件是否存在 + try { + await fs.access(modelConfigPath); + } catch (error) { + return NextResponse.json({ error: 'The model configuration does not exist' }, { status: 404 }); + } + + // 读取模型配置文件 + const modelConfigData = await fs.readFile(modelConfigPath, 'utf-8'); + const modelConfig = JSON.parse(modelConfigData); + + // 查找指定ID的模型 + const model = modelConfig.find(model => model.id === modelId); + + if (!model) { + return NextResponse.json({ error: 'The model does not exist' }, { status: 404 }); + } + + return NextResponse.json(model); + } catch (error) { + console.error('Error getting model:', String(error)); + return NextResponse.json({ error: 'Failed to get model' }, { status: 500 }); + } +} + +export async function PUT(request, { params }) { + try { + const { projectId, modelId } = params; + + // 验证项目ID和模型ID + if (!projectId || !modelId) { + return NextResponse.json({ error: 'The project ID and model ID cannot be empty' }, { status: 400 }); + } + + // 获取请求体 + const modelData = await request.json(); + + // 验证请求体 + if (!modelData || !modelData.provider || !modelData.name) { + return NextResponse.json({ error: 'The model data is incomplete' }, { status: 400 }); + } + + // 获取项目根目录 + const projectRoot = await getProjectRoot(); + const projectPath = path.join(projectRoot, projectId); + + // 检查项目是否存在 + try { + await fs.access(projectPath); + } catch (error) { + return NextResponse.json({ error: 'The project does not exist' }, { status: 404 }); + } + + // 获取模型配置文件路径 + const modelConfigPath = path.join(projectPath, 'model-config.json'); + + // 读取模型配置文件 + let modelConfig = []; + try { + const modelConfigData = await fs.readFile(modelConfigPath, 'utf-8'); + modelConfig = JSON.parse(modelConfigData); + } catch (error) { + // 如果文件不存在,创建一个空数组 + } + + // 更新模型数据 + const modelIndex = modelConfig.findIndex(model => model.id === modelId); + + if (modelIndex >= 0) { + // 更新现有模型 + modelConfig[modelIndex] = { + ...modelConfig[modelIndex], + ...modelData, + id: modelId // 确保ID不变 + }; + } else { + // 添加新模型 + modelConfig.push({ + ...modelData, + id: modelId + }); + } + + // 写入模型配置文件 + await fs.writeFile(modelConfigPath, JSON.stringify(modelConfig, null, 2), 'utf-8'); + + return NextResponse.json({ message: 'Model configuration updated successfully' }); + } catch (error) { + console.error('Error updating model configuration:', String(error)); + return NextResponse.json({ error: 'Failed to update model configuration' }, { status: 500 }); + } +} + +export async function DELETE(request, { params }) { + try { + const { projectId, modelId } = params; + + // 验证项目ID和模型ID + if (!projectId || !modelId) { + return NextResponse.json({ error: 'The project ID and model ID cannot be empty' }, { status: 400 }); + } + + // 获取项目根目录 + const projectRoot = await getProjectRoot(); + const projectPath = path.join(projectRoot, projectId); + + // 检查项目是否存在 + try { + await fs.access(projectPath); + } catch (error) { + return NextResponse.json({ error: 'The project does not exist' }, { status: 404 }); + } + + // 获取模型配置文件路径 + const modelConfigPath = path.join(projectPath, 'model-config.json'); + + // 检查模型配置文件是否存在 + try { + await fs.access(modelConfigPath); + } catch (error) { + return NextResponse.json({ error: 'The model configuration does not exist' }, { status: 404 }); + } + + // 读取模型配置文件 + const modelConfigData = await fs.readFile(modelConfigPath, 'utf-8'); + let modelConfig = JSON.parse(modelConfigData); + + // 过滤掉要删除的模型 + const initialLength = modelConfig.length; + modelConfig = modelConfig.filter(model => model.id !== modelId); + + // 检查是否找到并删除了模型 + if (modelConfig.length === initialLength) { + return NextResponse.json({ error: 'The model does not exist' }, { status: 404 }); + } + + // 写入模型配置文件 + await fs.writeFile(modelConfigPath, JSON.stringify(modelConfig, null, 2), 'utf-8'); + + return NextResponse.json({ message: 'Model deleted successfully' }); + } catch (error) { + console.error('Error deleting model:', String(error)); + return NextResponse.json({ error: 'Failed to delete model' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/models/route.js b/easy-dataset-main/app/api/projects/[projectId]/models/route.js new file mode 100644 index 0000000..94b7590 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/models/route.js @@ -0,0 +1,89 @@ +import { NextResponse } from 'next/server'; +import path from 'path'; +import fs from 'fs/promises'; +import { getProjectRoot } from '@/lib/db/base'; + +// 获取模型配置 +export async function GET(request, { params }) { + try { + const { projectId } = params; + + // 验证项目 ID + if (!projectId) { + return NextResponse.json({ error: 'The project ID cannot be empty' }, { status: 400 }); + } + + // 获取项目根目录 + const projectRoot = await getProjectRoot(); + const projectPath = path.join(projectRoot, projectId); + + // 检查项目是否存在 + try { + await fs.access(projectPath); + } catch (error) { + return NextResponse.json({ error: 'The project does not exist' }, { status: 404 }); + } + + // 获取模型配置文件路径 + const modelConfigPath = path.join(projectPath, 'model-config.json'); + + // 检查模型配置文件是否存在 + try { + await fs.access(modelConfigPath); + } catch (error) { + // 如果配置文件不存在,返回默认配置 + return NextResponse.json([]); + } + + // 读取模型配置文件 + const modelConfigData = await fs.readFile(modelConfigPath, 'utf-8'); + const modelConfig = JSON.parse(modelConfigData); + + return NextResponse.json(modelConfig); + } catch (error) { + console.error('Error obtaining model configuration:', String(error)); + return NextResponse.json({ error: 'Failed to obtain model configuration' }, { status: 500 }); + } +} + +// 更新模型配置 +export async function PUT(request, { params }) { + try { + const { projectId } = params; + + // 验证项目 ID + if (!projectId) { + return NextResponse.json({ error: 'The project ID cannot be empty' }, { status: 400 }); + } + + // 获取请求体 + const modelConfig = await request.json(); + + // 验证请求体 + if (!modelConfig || !Array.isArray(modelConfig)) { + return NextResponse.json({ error: 'The model configuration must be an array' }, { status: 400 }); + } + + // 获取项目根目录 + const projectRoot = await getProjectRoot(); + const projectPath = path.join(projectRoot, projectId); + + // 检查项目是否存在 + try { + await fs.access(projectPath); + } catch (error) { + return NextResponse.json({ error: 'The project does not exist' }, { status: 404 }); + } + + // 获取模型配置文件路径 + const modelConfigPath = path.join(projectPath, 'model-config.json'); + + // 写入模型配置文件 + await fs.writeFile(modelConfigPath, JSON.stringify(modelConfig, null, 2), 'utf-8'); + + return NextResponse.json({ message: 'Model configuration updated successfully' }); + } catch (error) { + console.error('Error updating model configuration:', String(error)); + return NextResponse.json({ error: 'Failed to update model configuration' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/playground/chat/route.js b/easy-dataset-main/app/api/projects/[projectId]/playground/chat/route.js new file mode 100644 index 0000000..6e0cdf7 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/playground/chat/route.js @@ -0,0 +1,99 @@ +import { NextResponse } from 'next/server'; +import LLMClient from '@/lib/llm/core/index'; +import { getModelConfigById } from '@/lib/db/model-config'; + +async function resolveLatestModelConfig(projectId, incomingModel = {}) { + const modelId = incomingModel?.id; + if (!modelId) { + return incomingModel; + } + + try { + const latestModelConfig = await getModelConfigById(modelId); + if (!latestModelConfig) { + return incomingModel; + } + if (String(latestModelConfig.projectId) !== String(projectId)) { + return incomingModel; + } + + // Keep transient client-only fields, but force endpoint/auth/model fields to latest DB values. + return { + ...incomingModel, + ...latestModelConfig + }; + } catch (error) { + console.error('Failed to resolve latest model config:', String(error)); + return incomingModel; + } +} + +export async function POST(request, { params }) { + try { + const { projectId } = params; + + // Validate project ID. + if (!projectId) { + return NextResponse.json({ error: 'The project ID cannot be empty' }, { status: 400 }); + } + + // Read request payload. + const { model, messages } = await request.json(); + const resolvedModel = await resolveLatestModelConfig(projectId, model); + + // Validate request parameters. + if (!resolvedModel) { + return NextResponse.json({ error: 'The model parameters cannot be empty' }, { status: 400 }); + } + + if (!Array.isArray(messages) || messages.length === 0) { + return NextResponse.json({ error: 'The message list cannot be empty' }, { status: 400 }); + } + + // Use custom LLM client. + const llmClient = new LLMClient(resolvedModel); + + // Normalize message payload for text + vision models. + const formattedMessages = messages.map(msg => { + // Plain text message. + if (typeof msg.content === 'string') { + return { + role: msg.role, + content: msg.content + }; + } + // Multimodal message (e.g. image parts). + if (Array.isArray(msg.content)) { + return { + role: msg.role, + content: msg.content + }; + } + // Fallback. + return { + role: msg.role, + content: msg.content + }; + }); + + // Call LLM API. + let response = ''; + try { + const { answer, cot } = await llmClient.getResponseWithCOT(formattedMessages.filter(f => f.role !== 'error')); + response = `${cot}${answer}`; + } catch (error) { + console.error('Failed to call LLM API:', String(error)); + return NextResponse.json( + { + error: `Failed to call ${resolvedModel.modelId || resolvedModel.modelName || 'unknown'} model: ${error.message}` + }, + { status: 500 } + ); + } + + return NextResponse.json({ response }); + } catch (error) { + console.error('Failed to process chat request:', String(error)); + return NextResponse.json({ error: `Failed to process chat request: ${error.message}` }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/playground/chat/stream/route.js b/easy-dataset-main/app/api/projects/[projectId]/playground/chat/stream/route.js new file mode 100644 index 0000000..30c2308 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/playground/chat/stream/route.js @@ -0,0 +1,89 @@ +import { NextResponse } from 'next/server'; +import LLMClient from '@/lib/llm/core/index'; +import { getModelConfigById } from '@/lib/db/model-config'; + +async function resolveLatestModelConfig(projectId, incomingModel = {}) { + const modelId = incomingModel?.id; + if (!modelId) { + return incomingModel; + } + + try { + const latestModelConfig = await getModelConfigById(modelId); + if (!latestModelConfig) { + return incomingModel; + } + if (String(latestModelConfig.projectId) !== String(projectId)) { + return incomingModel; + } + + return { + ...incomingModel, + ...latestModelConfig + }; + } catch (error) { + console.error('Failed to resolve latest model config:', String(error)); + return incomingModel; + } +} + +/** + * Streaming chat endpoint. + */ +export async function POST(request, { params }) { + const { projectId } = params; + + try { + const body = await request.json(); + const { model, messages } = body; + const resolvedModel = await resolveLatestModelConfig(projectId, model); + + if (!resolvedModel || !messages) { + return NextResponse.json({ error: 'Missing necessary parameters' }, { status: 400 }); + } + + // Use custom LLM client. + const llmClient = new LLMClient(resolvedModel); + + // Normalize message payload for text + vision models. + const formattedMessages = messages.map(msg => { + // Plain text message. + if (typeof msg.content === 'string') { + return { + role: msg.role, + content: msg.content + }; + } + // Multimodal message (e.g. image parts). + if (Array.isArray(msg.content)) { + return { + role: msg.role, + content: msg.content + }; + } + // Fallback. + return { + role: msg.role, + content: msg.content + }; + }); + + try { + // Stream response from provider. + const response = await llmClient.chatStreamAPI(formattedMessages.filter(f => f.role !== 'error')); + // Return native streaming response. + return response; + } catch (error) { + console.error('Failed to call LLM API:', error); + return NextResponse.json( + { + error: `Failed to call ${resolvedModel.modelId || resolvedModel.modelName || 'unknown'} model: ${error.message}` + }, + { status: 500 } + ); + } + } catch (error) { + console.error('Failed to process stream chat request:', String(error)); + return NextResponse.json({ error: `Failed to process stream chat request: ${error.message}` }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/preview/[fileId]/route.js b/easy-dataset-main/app/api/projects/[projectId]/preview/[fileId]/route.js new file mode 100644 index 0000000..c666dc5 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/preview/[fileId]/route.js @@ -0,0 +1,42 @@ +import { NextResponse } from 'next/server'; +import fs from 'fs'; +import path from 'path'; +import { getProjectRoot } from '@/lib/db/base'; +import { getUploadFileInfoById } from '@/lib/db/upload-files'; + +// 获取文件内容 +export async function GET(request, { params }) { + try { + const { projectId, fileId } = params; + + // 验证参数 + if (!projectId) { + return NextResponse.json({ error: 'Project ID cannot be empty' }, { status: 400 }); + } + + // 获取项目根目录 + let fileInfo = await getUploadFileInfoById(fileId); + if (!fileInfo) { + return NextResponse.json({ error: 'file does not exist' }, { status: 400 }); + } + + // 获取文件路径 + let filePath = path.join(fileInfo.path, fileInfo.fileName); + if (fileInfo.fileExt !== '.md') { + filePath = path.join(fileInfo.path, fileInfo.fileName.replace(/\.[^/.]+$/, '.md')); + } + //获取文件 + const buffer = fs.readFileSync(filePath); + + const text = buffer.toString('utf-8'); + + return NextResponse.json({ + fileId: fileId, + fileName: fileInfo.fileName, + content: text + }); + } catch (error) { + console.error('Failed to get text block content:', String(error)); + return NextResponse.json({ error: error.message || 'Failed to get text block content' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/questions/[questionId]/route.js b/easy-dataset-main/app/api/projects/[projectId]/questions/[questionId]/route.js new file mode 100644 index 0000000..a8285ca --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/questions/[questionId]/route.js @@ -0,0 +1,26 @@ +import { NextResponse } from 'next/server'; +import { deleteQuestion } from '@/lib/db/questions'; + +// 删除单个问题 +export async function DELETE(request, { params }) { + try { + const { projectId, questionId } = params; + + // 验证参数 + if (!projectId) { + return NextResponse.json({ error: 'Project ID is required' }, { status: 400 }); + } + + if (!questionId) { + return NextResponse.json({ error: 'Question ID is required' }, { status: 400 }); + } + + // 删除问题 + await deleteQuestion(questionId); + + return NextResponse.json({ success: true, message: 'Delete successful' }); + } catch (error) { + console.error('Delete failed:', String(error)); + return NextResponse.json({ error: error.message || 'Delete failed' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/questions/batch-delete/route.js b/easy-dataset-main/app/api/projects/[projectId]/questions/batch-delete/route.js new file mode 100644 index 0000000..dcc33ef --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/questions/batch-delete/route.js @@ -0,0 +1,23 @@ +import { NextResponse } from 'next/server'; +import { batchDeleteQuestions } from '@/lib/db/questions'; + +// 批量删除问题 +export async function DELETE(request) { + try { + const body = await request.json(); + const { questionIds } = body; + + // 验证参数 + if (questionIds.length === 0) { + return NextResponse.json({ error: 'Question ID is required' }, { status: 400 }); + } + + // 删除问题 + await batchDeleteQuestions(questionIds); + + return NextResponse.json({ success: true, message: 'Delete successful' }); + } catch (error) { + console.error('Delete failed:', String(error)); + return NextResponse.json({ error: error.message || 'Delete failed' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/questions/export/route.js b/easy-dataset-main/app/api/projects/[projectId]/questions/export/route.js new file mode 100644 index 0000000..c1f002f --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/questions/export/route.js @@ -0,0 +1,100 @@ +import { NextResponse } from 'next/server'; + +export async function POST(request, { params }) { + try { + const { projectId } = params; + const body = await request.json(); + const { format, selectedIds, filters } = body; + + let questions; + + // 如果有选中的问题 ID,按 ID 获取 + if (selectedIds && selectedIds.length > 0) { + questions = await getQuestionsByIds(projectId, selectedIds); + } else { + // 否则获取全部问题(不限分页) + questions = await getAllQuestions( + projectId, + filters?.searchTerm || '', + filters?.chunkName || '', + filters?.sourceType || 'all' + ); + } + + // 固定导出字段:问题内容、文本块名称、问题标签 + const filteredQuestions = questions.map(q => ({ + question: q.question, + chunkName: q.chunk?.name || q.chunkName || '', + questionLabel: q.questionLabel || '' + })); + + return NextResponse.json(filteredQuestions); + } catch (error) { + console.error('Failed to export questions:', error); + return NextResponse.json({ error: error.message }, { status: 500 }); + } +} + +// 获取全部问题(不限分页) +async function getAllQuestions(projectId, searchTerm = '', chunkName = '', sourceType = 'all') { + const { db } = await import('@/lib/db/index'); + + const whereClause = { + projectId + }; + + // 搜索条件 + if (searchTerm) { + whereClause.OR = [{ question: { contains: searchTerm } }, { questionLabel: { contains: searchTerm } }]; + } + + // 文本块名称筛选 + if (chunkName) { + whereClause.chunk = { + name: { contains: chunkName } + }; + } + + // 数据源类型筛选 + if (sourceType === 'text') { + whereClause.imageName = null; + } else if (sourceType === 'image') { + whereClause.imageName = { not: null }; + } + + return await db.questions.findMany({ + where: whereClause, + include: { + chunk: { + select: { + name: true + } + } + }, + orderBy: { + createAt: 'desc' + } + }); +} + +// 根据 ID 列表获取问题 +async function getQuestionsByIds(projectId, questionIds) { + const { db } = await import('@/lib/db/index'); + + return await db.questions.findMany({ + where: { + projectId, + id: { in: questionIds } + }, + include: { + chunk: { + select: { + name: true + } + } + }, + orderBy: { + createAt: 'desc' + } + }); +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/questions/route.js b/easy-dataset-main/app/api/projects/[projectId]/questions/route.js new file mode 100644 index 0000000..fd7810c --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/questions/route.js @@ -0,0 +1,110 @@ +import { NextResponse } from 'next/server'; +import { + getAllQuestionsByProjectId, + getQuestions, + getQuestionsIds, + saveQuestions, + updateQuestion +} from '@/lib/db/questions'; +import { getImageById, getImageChunk } from '@/lib/db/images'; + +// 获取项目的所有问题 +export async function GET(request, { params }) { + try { + const { projectId } = params; + // 验证项目ID + if (!projectId) { + return NextResponse.json({ error: 'Missing project ID' }, { status: 400 }); + } + const { searchParams } = new URL(request.url); + let status = searchParams.get('status'); + let answered = undefined; + if (status === 'answered') answered = true; + if (status === 'unanswered') answered = false; + const chunkName = searchParams.get('chunkName'); + const sourceType = searchParams.get('sourceType') || 'all'; // 'all', 'text', 'image' + const searchMatchMode = searchParams.get('searchMatchMode') || 'match'; // 'match', 'notMatch' + let selectedAll = searchParams.get('selectedAll'); + if (selectedAll) { + let data = await getQuestionsIds( + projectId, + answered, + searchParams.get('input'), + chunkName, + sourceType, + searchMatchMode + ); + return NextResponse.json(data); + } + let all = searchParams.get('all'); + if (all) { + let data = await getAllQuestionsByProjectId(projectId); + return NextResponse.json(data); + } + // 获取问题列表 + const questions = await getQuestions( + projectId, + parseInt(searchParams.get('page')), + parseInt(searchParams.get('size')), + answered, + searchParams.get('input'), + chunkName, + sourceType, + searchMatchMode + ); + + return NextResponse.json(questions); + } catch (error) { + console.error('Failed to get questions:', String(error)); + return NextResponse.json({ error: error.message || 'Failed to get questions' }, { status: 500 }); + } +} + +// 新增问题 +export async function POST(request, { params }) { + try { + const { projectId } = params; + const body = await request.json(); + const { question, chunkId, label } = body; + + // 验证必要参数 + if (!projectId || !question) { + return NextResponse.json({ error: 'Missing necessary parameters' }, { status: 400 }); + } + + if (!body.chunkId && body.imageId) { + const chunk = await getImageChunk(projectId); + body.chunkId = chunk.id; + body.label = 'image'; + } + + // 添加新问题 + let questions = [body]; + // 保存更新后的数据 + let data = await saveQuestions(projectId, questions); + + // 返回成功响应 + return NextResponse.json(data); + } catch (error) { + console.error('Failed to create question:', String(error)); + return NextResponse.json({ error: error.message || 'Failed to create question' }, { status: 500 }); + } +} + +// 更新问题 +export async function PUT(request) { + try { + const body = await request.json(); + // 保存更新后的数据 + const { imageId } = body; + if (imageId) { + body.imageName = (await getImageById(imageId))?.imageName; + } + let data = await updateQuestion(body); + // 返回更新后的问题数据 + return NextResponse.json(data); + } catch (error) { + console.error('更新问题失败:', String(error)); + return NextResponse.json({ error: error.message || '更新问题失败' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/questions/templates/[templateId]/route.js b/easy-dataset-main/app/api/projects/[projectId]/questions/templates/[templateId]/route.js new file mode 100644 index 0000000..556d427 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/questions/templates/[templateId]/route.js @@ -0,0 +1,110 @@ +import { NextResponse } from 'next/server'; +import templateDb from '@/lib/db/questionTemplates'; +import { generateQuestionsFromTemplateEdit } from '@/lib/services/questions/template'; + +// 获取单个模板 +export async function GET(request, { params }) { + try { + const { templateId } = params; + + const template = await templateDb.getTemplateById(templateId); + + if (!template) { + return NextResponse.json({ error: '模板不存在' }, { status: 404 }); + } + + // 获取使用统计 + const usageCount = await templateDb.getTemplateUsageCount(templateId); + + return NextResponse.json({ + success: true, + template: { + ...template, + usageCount + } + }); + } catch (error) { + console.error('Failed to get template:', error); + return NextResponse.json({ error: error.message || 'Failed to get template' }, { status: 500 }); + } +} + +// 更新问题模板 +export async function PUT(request, { params }) { + try { + const { projectId, templateId } = params; + const data = await request.json(); + + const { question, sourceType, answerType, description, labels, customFormat, order, autoGenerate } = data; + + // 验证数据源类型 + if (sourceType && !['image', 'text'].includes(sourceType)) { + return NextResponse.json({ error: '无效的数据源类型' }, { status: 400 }); + } + + // 验证答案类型 + if (answerType && !['text', 'label', 'custom_format'].includes(answerType)) { + return NextResponse.json({ error: '无效的答案类型' }, { status: 400 }); + } + + const updateData = {}; + if (question !== undefined) updateData.question = question; + if (sourceType !== undefined) updateData.sourceType = sourceType; + if (answerType !== undefined) updateData.answerType = answerType; + if (description !== undefined) updateData.description = description; + if (labels !== undefined) updateData.labels = labels; + if (customFormat !== undefined) updateData.customFormat = customFormat; + if (order !== undefined) updateData.order = order; + + const template = await templateDb.updateTemplate(templateId, updateData); + + let generationResult = null; + + // 如果启用自动生成,则为还未创建此模板问题的数据源创建问题 + if (autoGenerate) { + try { + generationResult = await generateQuestionsFromTemplateEdit(projectId, template); + } catch (error) { + console.error('编辑模式自动生成问题失败:', error); + generationResult = { + success: false, + successCount: 0, + failCount: 0, + message: '自动生成问题时发生错误' + }; + } + } + + return NextResponse.json({ + success: true, + template, + generation: generationResult + }); + } catch (error) { + console.error('Failed to update template:', error); + return NextResponse.json({ error: error.message || 'Failed to update template' }, { status: 500 }); + } +} + +// 删除问题模板 +export async function DELETE(request, { params }) { + try { + const { templateId } = params; + + // 检查是否有关联的问题 + const usageCount = await templateDb.getTemplateUsageCount(templateId); + if (usageCount > 0) { + return NextResponse.json({ error: `此模板已被 ${usageCount} 个问题使用,无法删除` }, { status: 400 }); + } + + await templateDb.deleteTemplate(templateId); + + return NextResponse.json({ + success: true, + message: '模板删除成功' + }); + } catch (error) { + console.error('Failed to delete template:', error); + return NextResponse.json({ error: error.message || 'Failed to delete template' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/questions/templates/route.js b/easy-dataset-main/app/api/projects/[projectId]/questions/templates/route.js new file mode 100644 index 0000000..4c3c795 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/questions/templates/route.js @@ -0,0 +1,116 @@ +import { NextResponse } from 'next/server'; +import templateDb from '@/lib/db/questionTemplates'; +import { generateQuestionsFromTemplate, checkTemplateGenerationAvailability } from '@/lib/services/questions/template'; + +// 获取问题模板列表 +export async function GET(request, { params }) { + try { + const { projectId } = params; + const { searchParams } = new URL(request.url); + const sourceType = searchParams.get('sourceType'); + const search = searchParams.get('search'); + + const templates = await templateDb.getTemplates(projectId, { sourceType, search }); + + // 获取使用统计 + const templateIds = templates.map(t => t.id); + const usageCounts = await templateDb.getTemplatesUsageCount(templateIds); + + // 添加使用统计到模板数据 + const templatesWithUsage = templates.map(template => ({ + ...template, + usageCount: usageCounts[template.id] || 0 + })); + + return NextResponse.json({ + success: true, + templates: templatesWithUsage + }); + } catch (error) { + console.error('Failed to get templates:', error); + return NextResponse.json({ error: error.message || 'Failed to get templates' }, { status: 500 }); + } +} + +// 创建问题模板 +export async function POST(request, { params }) { + try { + const { projectId } = params; + const data = await request.json(); + + const { question, sourceType, answerType, description, labels, customFormat, order, autoGenerate } = data; + + // 验证必填字段 + if (!question || !sourceType || !answerType) { + return NextResponse.json({ error: '缺少必要参数:question, sourceType, answerType' }, { status: 400 }); + } + + // 验证数据源类型 + if (!['image', 'text'].includes(sourceType)) { + return NextResponse.json({ error: '无效的数据源类型' }, { status: 400 }); + } + + // 验证答案类型 + if (!['text', 'label', 'custom_format'].includes(answerType)) { + return NextResponse.json({ error: '无效的答案类型' }, { status: 400 }); + } + + // 如果是标签类型,验证 labels + if (answerType === 'label' && (!labels || !Array.isArray(labels) || labels.length === 0)) { + return NextResponse.json({ error: '标签类型问题必须提供标签列表' }, { status: 400 }); + } + + // 如果是自定义格式,验证 customFormat + if (answerType === 'custom_format' && !customFormat) { + return NextResponse.json({ error: '自定义格式问题必须提供格式定义' }, { status: 400 }); + } + + const template = await templateDb.createTemplate(projectId, { + question, + sourceType, + answerType, + description, + labels: answerType === 'label' ? labels : [], + customFormat: answerType === 'custom_format' ? customFormat : null, + order: order || 0 + }); + + let generationResult = null; + + // 如果启用自动生成,则为所有相关数据源创建问题 + if (autoGenerate) { + try { + // 先检查是否有可用的数据源 + const availability = await checkTemplateGenerationAvailability(projectId, sourceType); + + if (availability.available) { + generationResult = await generateQuestionsFromTemplate(projectId, template); + } else { + generationResult = { + success: false, + successCount: 0, + failCount: 0, + message: availability.message + }; + } + } catch (error) { + console.error('自动生成问题失败:', error); + generationResult = { + success: false, + successCount: 0, + failCount: 0, + message: '自动生成问题时发生错误' + }; + } + } + + return NextResponse.json({ + success: true, + template, + generation: generationResult + }); + } catch (error) { + console.error('Failed to create template:', error); + return NextResponse.json({ error: error.message || 'Failed to create template' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/questions/tree/route.js b/easy-dataset-main/app/api/projects/[projectId]/questions/tree/route.js new file mode 100644 index 0000000..8ee67db --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/questions/tree/route.js @@ -0,0 +1,44 @@ +import { NextResponse } from 'next/server'; +import { getQuestionsForTree, getQuestionsByTag } from '@/lib/db/questions'; + +/** + * 获取项目的问题树形视图数据 + * @param {Request} request - 请求对象 + * @param {Object} params - 路由参数 + * @returns {Promise} - 包含问题数据的响应 + */ +export async function GET(request, { params }) { + try { + const { projectId } = params; + + // 验证项目ID + if (!projectId) { + return NextResponse.json({ error: '项目ID不能为空' }, { status: 400 }); + } + + const { searchParams } = new URL(request.url); + const tag = searchParams.get('tag'); + const input = searchParams.get('input'); + const tagsOnly = searchParams.get('tagsOnly') === 'true'; + const isDistill = searchParams.get('isDistill') === 'true'; + // 默认排除图片问题(label='image'),可通过 excludeImage=false 参数改变 + const excludeImage = searchParams.get('excludeImage') !== 'false'; + + if (tag) { + // 获取指定标签的问题数据(包含完整字段) + const questions = await getQuestionsByTag(projectId, tag, input, isDistill, excludeImage); + return NextResponse.json(questions); + } else if (tagsOnly) { + // 只获取标签信息(仅包含 id 和 label 字段) + const treeData = await getQuestionsForTree(projectId, input, isDistill, excludeImage); + return NextResponse.json(treeData); + } else { + // 兼容原有请求,获取树形视图数据(仅包含 id 和 label 字段) + const treeData = await getQuestionsForTree(projectId, null, isDistill, excludeImage); + return NextResponse.json(treeData); + } + } catch (error) { + console.error('获取问题树形数据失败:', String(error)); + return NextResponse.json({ error: error.message || '获取问题树形数据失败' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/route.js b/easy-dataset-main/app/api/projects/[projectId]/route.js new file mode 100644 index 0000000..c863428 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/route.js @@ -0,0 +1,65 @@ +// 获取项目详情 +import { deleteProject, getProject, updateProject, getTaskConfig } from '@/lib/db/projects'; + +export async function GET(request, { params }) { + try { + const { projectId } = params; + const project = await getProject(projectId); + const taskConfig = await getTaskConfig(projectId); + if (!project) { + return Response.json({ error: '项目不存在' }, { status: 404 }); + } + return Response.json({ ...project, taskConfig }); + } catch (error) { + console.error('获取项目详情出错:', String(error)); + return Response.json({ error: String(error) }, { status: 500 }); + } +} + +// 更新项目 +export async function PUT(request, { params }) { + try { + const { projectId } = params; + const projectData = await request.json(); + + const hasNameField = Object.prototype.hasOwnProperty.call(projectData, 'name'); + const hasDefaultModelField = Object.prototype.hasOwnProperty.call(projectData, 'defaultModelConfigId'); + + // 至少允许更新名称或默认模型(defaultModelConfigId 可显式为 null) + if (!hasNameField && !hasDefaultModelField) { + return Response.json({ error: '项目名称不能为空' }, { status: 400 }); + } + + if (hasNameField && !projectData.name && !hasDefaultModelField) { + return Response.json({ error: '项目名称不能为空' }, { status: 400 }); + } + + const updatedProject = await updateProject(projectId, projectData); + + if (!updatedProject) { + return Response.json({ error: '项目不存在' }, { status: 404 }); + } + + return Response.json(updatedProject); + } catch (error) { + console.error('更新项目出错:', String(error)); + return Response.json({ error: String(error) }, { status: 500 }); + } +} + +// 删除项目 +export async function DELETE(request, { params }) { + try { + const { projectId } = params; + const success = await deleteProject(projectId); + + if (!success) { + return Response.json({ error: '项目不存在' }, { status: 404 }); + } + + return Response.json({ success: true }); + } catch (error) { + console.error('删除项目出错:', error); + return Response.json({ error: error.message }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/split/route.js b/easy-dataset-main/app/api/projects/[projectId]/split/route.js new file mode 100644 index 0000000..7ab799f --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/split/route.js @@ -0,0 +1,88 @@ +import { NextResponse } from 'next/server'; +import { splitProjectFile, getProjectChunks } from '@/lib/file/text-splitter'; +import { getProject, updateProject } from '@/lib/db/projects'; +import { getTags } from '@/lib/db/tags'; +import { handleDomainTree } from '@/lib/util/domain-tree'; + +// 处理文本分割请求 +export async function POST(request, { params }) { + try { + const { projectId } = params; + + // 获取请求体 + const { fileNames, model, language, domainTreeAction = 'rebuild' } = await request.json(); + + if (!model) { + return NextResponse.json({ error: 'Please Select Model' }, { status: 400 }); + } + + const project = await getProject(projectId); + + let result = { + totalChunks: 0, + chunks: [], + toc: '' + }; + for (let i = 0; i < fileNames.length; i++) { + const fileName = fileNames[i]; + // 分割文本 + const { toc, chunks, totalChunks } = await splitProjectFile(projectId, fileName); + result.toc += toc; + result.chunks.push(...chunks); + result.totalChunks += totalChunks; + console.log(projectId, fileName, `Text split completed, ${domainTreeAction} domain tree`); + } + + // 调用领域树处理模块 + const tags = await handleDomainTree({ + projectId, + action: domainTreeAction, + newToc: result.toc, + model, + language, + fileNames, + project + }); + + if (!tags && domainTreeAction !== 'keep') { + await updateProject(projectId, { ...project }); + return NextResponse.json( + { error: 'AI analysis failed, please check model configuration, delete file and retry!' }, + { status: 400 } + ); + } + + return NextResponse.json({ ...result, tags }); + } catch (error) { + console.error('Text split error:', String(error)); + return NextResponse.json({ error: error.message || 'Text split failed' }, { status: 500 }); + } +} + +// 获取项目中的所有文本块 +export async function GET(request, { params }) { + try { + const { projectId } = params; + const { searchParams } = new URL(request.url); + const filter = searchParams.get('filter'); + // 验证项目ID + if (!projectId) { + return NextResponse.json({ error: 'The project ID cannot be empty' }, { status: 400 }); + } + + // 获取文本块详细信息 + const result = await getProjectChunks(projectId, filter); + + const tags = await getTags(projectId); + + // 返回详细的文本块信息和文件结果(单个文件) + return NextResponse.json({ + chunks: result.chunks, + ...result.fileResult, // 单个文件结果,而不是数组 + tags + }); + } catch (error) { + console.error('Failed to get text chunks:', String(error)); + return NextResponse.json({ error: error.message || 'Failed to get text chunks' }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/tags/route.js b/easy-dataset-main/app/api/projects/[projectId]/tags/route.js new file mode 100644 index 0000000..e5b1392 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/tags/route.js @@ -0,0 +1,102 @@ +import { NextResponse } from 'next/server'; +import { getTags, createTag, updateTag, deleteTag } from '@/lib/db/tags'; +import { getQuestionsByTagName } from '@/lib/db/questions'; + +// 获取项目的标签树 +export async function GET(request, { params }) { + try { + const { projectId } = params; + + // 验证项目ID + if (!projectId) { + return NextResponse.json({ error: 'Project ID is required' }, { status: 400 }); + } + + // 获取标签树 + const tags = await getTags(projectId); + + return NextResponse.json({ tags }); + } catch (error) { + console.error('Failed to obtain the label tree:', String(error)); + return NextResponse.json({ error: error.message || 'Failed to obtain the label tree' }, { status: 500 }); + } +} + +// 更新项目的标签树 +export async function PUT(request, { params }) { + try { + const { projectId } = params; + + // 验证项目ID + if (!projectId) { + return NextResponse.json({ error: 'Project ID is required' }, { status: 400 }); + } + + // 获取请求体 + const { tags } = await request.json(); + if (tags.id === undefined || tags.id === null || tags.id === '') { + console.log('createTag', tags); + let res = await createTag(projectId, tags.label, tags.parentId); + return NextResponse.json({ tags: res }); + } else { + let res = await updateTag(tags.label, tags.id); + return NextResponse.json({ tags: res }); + } + } catch (error) { + console.error('Failed to update tags:', String(error)); + return NextResponse.json({ error: error.message || 'Failed to update tags' }, { status: 500 }); + } +} + +export async function POST(request, { params }) { + try { + const { projectId } = params; + + // 验证项目ID + if (!projectId) { + return NextResponse.json({ error: 'Project ID is required' }, { status: 400 }); + } + const { tagName } = await request.json(); + console.log('tagName', tagName); + let data = await getQuestionsByTagName(projectId, tagName); + + return NextResponse.json(data); + } catch (error) { + console.error('Failed to obtain the label tree:', String(error)); + return NextResponse.json({ error: error.message || 'Failed to obtain the label tree' }, { status: 500 }); + } +} + +export async function DELETE(request, { params }) { + try { + const { projectId } = params; + + // 验证项目ID + if (!projectId) { + return NextResponse.json({ error: 'Project ID is required' }, { status: 400 }); + } + + // 获取要删除的标签ID + const { searchParams } = new URL(request.url); + const id = searchParams.get('id'); + + if (!id) { + return NextResponse.json({ error: '标签 ID 是必需的' }, { status: 400 }); + } + + console.log(`正在删除标签: ${id}`); + const result = await deleteTag(id); + console.log(`删除标签成功: ${id}`); + + return NextResponse.json({ success: true, message: '删除标签成功', data: result }); + } catch (error) { + console.error('删除标签失败:', String(error)); + return NextResponse.json( + { + error: error.message || '删除标签失败', + success: false + }, + { status: 500 } + ); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/tasks/[taskId]/route.js b/easy-dataset-main/app/api/projects/[projectId]/tasks/[taskId]/route.js new file mode 100644 index 0000000..6f13f95 --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/tasks/[taskId]/route.js @@ -0,0 +1,171 @@ +import { NextResponse } from 'next/server'; +import { PrismaClient } from '@prisma/client'; + +const prisma = new PrismaClient(); + +// 获取任务详情 +export async function GET(request, { params }) { + try { + const { projectId, taskId } = params; + + // 验证必填参数 + if (!projectId || !taskId) { + return NextResponse.json( + { + code: 400, + error: '缺少必要参数' + }, + { status: 400 } + ); + } + + // 查询任务详情 + const task = await prisma.task.findUnique({ + where: { + id: taskId, + projectId + } + }); + + if (!task) { + return NextResponse.json( + { + code: 404, + error: '任务不存在' + }, + { status: 404 } + ); + } + + return NextResponse.json({ + code: 0, + data: task, + message: '获取任务详情成功' + }); + } catch (error) { + console.error('获取任务详情失败:', String(error)); + return NextResponse.json( + { + code: 500, + error: '获取任务详情失败', + message: error.message + }, + { status: 500 } + ); + } +} + +// 更新任务状态 +export async function PATCH(request, { params }) { + try { + const { projectId, taskId } = params; + const data = await request.json(); + + // 验证必填参数 + if (!projectId || !taskId) { + return NextResponse.json( + { + code: 400, + error: '缺少必要参数' + }, + { status: 400 } + ); + } + + // 获取要更新的字段 + const { status, completedCount, totalCount, detail, note, endTime } = data; + + // 构建更新数据 + const updateData = {}; + + if (status !== undefined) { + updateData.status = status; + } + + if (completedCount !== undefined) { + updateData.completedCount = completedCount; + } + + if (totalCount !== undefined) { + updateData.totalCount = totalCount; + } + + if (detail !== undefined) { + updateData.detail = detail; + } + + if (note !== undefined) { + updateData.note = note; + } + + // 如果状态变为已完成、失败或已中断,自动添加结束时间 + if (status === 1 || status === 2 || status === 3) { + updateData.endTime = endTime || new Date(); + } + + // 更新任务 + const updatedTask = await prisma.task.update({ + where: { + id: taskId + }, + data: updateData + }); + + return NextResponse.json({ + code: 0, + data: updatedTask, + message: '更新任务状态成功' + }); + } catch (error) { + console.error('更新任务状态失败:', String(error)); + return NextResponse.json( + { + code: 500, + error: '更新任务状态失败', + message: error.message + }, + { status: 500 } + ); + } +} + +// 删除任务 +export async function DELETE(request, { params }) { + try { + const { projectId, taskId } = params; + + // 验证必填参数 + if (!projectId || !taskId) { + return NextResponse.json( + { + code: 400, + error: '缺少必要参数' + }, + { status: 400 } + ); + } + + // 删除任务 + await prisma.task.delete({ + where: { + id: taskId, + projectId + } + }); + + return NextResponse.json({ + code: 0, + message: '删除任务成功' + }); + } catch (error) { + console.error('删除任务失败:', String(error)); + return NextResponse.json( + { + code: 500, + error: '删除任务失败', + message: error.message + }, + { status: 500 } + ); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/tasks/list/route.js b/easy-dataset-main/app/api/projects/[projectId]/tasks/list/route.js new file mode 100644 index 0000000..1c6044c --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/tasks/list/route.js @@ -0,0 +1,63 @@ +import { NextResponse } from 'next/server'; +import { PrismaClient } from '@prisma/client'; + +const prisma = new PrismaClient(); + +// 获取项目的所有任务列表 +export async function GET(request, { params }) { + try { + const { projectId } = params; + const { searchParams } = new URL(request.url); + + // 可选参数: 任务类型和任务状态 + const taskType = searchParams.get('taskType'); + const statusStr = searchParams.get('status'); + + // 分页参数 + const page = parseInt(searchParams.get('page') || '0'); + const limit = parseInt(searchParams.get('limit') || '10'); + + // 构建查询条件 + const where = { projectId }; + + if (taskType) { + where.taskType = taskType; + } + + if (statusStr && !isNaN(parseInt(statusStr))) { + where.status = parseInt(statusStr); + } + + // 获取任务总数 + const total = await prisma.task.count({ where }); + + // 获取任务列表,按创建时间降序排序,并应用分页 + const tasks = await prisma.task.findMany({ + where, + orderBy: { + createAt: 'desc' + }, + skip: page * limit, + take: limit + }); + + return NextResponse.json({ + code: 0, + data: tasks, + total, + page, + limit, + message: '任务列表获取成功' + }); + } catch (error) { + console.error('获取任务列表失败:', String(error)); + return NextResponse.json( + { + code: 500, + error: '获取任务列表失败', + message: error.message + }, + { status: 500 } + ); + } +} diff --git a/easy-dataset-main/app/api/projects/[projectId]/tasks/route.js b/easy-dataset-main/app/api/projects/[projectId]/tasks/route.js new file mode 100644 index 0000000..c69821a --- /dev/null +++ b/easy-dataset-main/app/api/projects/[projectId]/tasks/route.js @@ -0,0 +1,164 @@ +import { NextResponse } from 'next/server'; +import path from 'path'; +import fs from 'fs/promises'; +import { getProjectRoot } from '@/lib/db/base'; +import { getTaskConfig } from '@/lib/db/projects'; +import { processTask } from '@/lib/services/tasks'; +import { db } from '@/lib/db/index'; + +function normalizeModelEndpoint(endpoint = '') { + let normalizedEndpoint = String(endpoint).trim(); + if (!normalizedEndpoint) { + return ''; + } + if (normalizedEndpoint.includes('/chat/completions')) { + normalizedEndpoint = normalizedEndpoint.replace('/chat/completions', ''); + } + return normalizedEndpoint; +} + +function normalizeTaskModelInfo(modelInfo) { + if (!modelInfo) { + return {}; + } + let parsedModelInfo = modelInfo; + if (typeof modelInfo === 'string') { + try { + parsedModelInfo = JSON.parse(modelInfo); + } catch (error) { + return {}; + } + } + if (parsedModelInfo && typeof parsedModelInfo === 'object' && parsedModelInfo.endpoint) { + parsedModelInfo.endpoint = normalizeModelEndpoint(parsedModelInfo.endpoint); + } + return parsedModelInfo; +} + +// 获取任务配置 +export async function GET(request, { params }) { + try { + const { projectId } = params; + + // 验证项目 ID + if (!projectId) { + return NextResponse.json({ error: 'Project ID is required' }, { status: 400 }); + } + + // 获取项目根目录 + const projectRoot = await getProjectRoot(); + const projectPath = path.join(projectRoot, projectId); + + // 检查项目是否存在 + try { + await fs.access(projectPath); + } catch (error) { + return NextResponse.json({ error: 'Project does not exist' + projectPath }, { status: 404 }); + } + + const taskConfig = await getTaskConfig(projectId); + return NextResponse.json(taskConfig); + } catch (error) { + console.error('Failed to obtain task configuration:', String(error)); + return NextResponse.json({ error: 'Failed to obtain task configuration' }, { status: 500 }); + } +} + +// 更新任务配置 +export async function PUT(request, { params }) { + try { + const { projectId } = params; + + // 验证项目 ID + if (!projectId) { + return NextResponse.json({ error: 'Project ID is required' }, { status: 400 }); + } + + // 获取请求体 + const taskConfig = await request.json(); + + // 验证请求体 + if (!taskConfig) { + return NextResponse.json({ error: 'Task configuration cannot be empty' }, { status: 400 }); + } + + // 获取项目根目录 + const projectRoot = await getProjectRoot(); + const projectPath = path.join(projectRoot, projectId); + + // 检查项目是否存在 + try { + await fs.access(projectPath); + } catch (error) { + return NextResponse.json({ error: 'Project does not exist' }, { status: 404 }); + } + + // 获取任务配置文件路径 + const taskConfigPath = path.join(projectPath, 'task-config.json'); + + // 写入任务配置文件 + await fs.writeFile(taskConfigPath, JSON.stringify(taskConfig, null, 2), 'utf-8'); + + return NextResponse.json({ message: 'Task configuration updated successfully' }); + } catch (error) { + console.error('Failed to update task configuration:', String(error)); + return NextResponse.json({ error: 'Failed to update task configuration' }, { status: 500 }); + } +} + +// 创建新任务 +export async function POST(request, { params }) { + try { + const { projectId } = params; + const data = await request.json(); + + // 验证必填字段 + const { taskType, modelInfo, language, detail = '', totalCount = 0, note } = data; + + if (!taskType) { + return NextResponse.json( + { + code: 400, + error: 'Missing required parameter: taskType' + }, + { status: 400 } + ); + } + + // 创建新任务 + const newTask = await db.task.create({ + data: { + projectId, + taskType, + status: 0, // 初始状态: 处理中 + modelInfo: JSON.stringify(normalizeTaskModelInfo(modelInfo)), + language: language || 'zh-CN', + detail: detail || '', + totalCount, + note: note ? JSON.stringify(note) : '', + completedCount: 0 + } + }); + + // 异步启动任务处理 + processTask(newTask.id).catch(err => { + console.error(`Task startup failed: ${newTask.id}`, String(err)); + }); + + return NextResponse.json({ + code: 0, + data: newTask, + message: 'Task created successfully' + }); + } catch (error) { + console.error('Failed to create task:', String(error)); + return NextResponse.json( + { + code: 500, + error: 'Failed to create task', + message: error.message + }, + { status: 500 } + ); + } +} diff --git a/easy-dataset-main/app/api/projects/delete-directory/route.js b/easy-dataset-main/app/api/projects/delete-directory/route.js new file mode 100644 index 0000000..bde0bbf --- /dev/null +++ b/easy-dataset-main/app/api/projects/delete-directory/route.js @@ -0,0 +1,59 @@ +import { getProjectRoot } from '@/lib/db/base'; +import { NextResponse } from 'next/server'; +import path from 'path'; +import fs from 'fs'; +import { promisify } from 'util'; + +const rmdir = promisify(fs.rm); + +/** + * Delete project directory + * @returns {Promise} Operation result response + */ +export async function POST(request) { + try { + const { projectId } = await request.json(); + + if (!projectId) { + return NextResponse.json( + { + success: false, + error: 'Project ID is required' + }, + { status: 400 } + ); + } + + // Get project root directory + const projectRoot = await getProjectRoot(); + const projectPath = path.join(projectRoot, projectId); + + // Check if directory exists + if (!fs.existsSync(projectPath)) { + return NextResponse.json( + { + success: false, + error: 'Project directory not found' + }, + { status: 404 } + ); + } + + // Recursively remove directory + await rmdir(projectPath, { recursive: true, force: true }); + + return NextResponse.json({ + success: true, + message: 'Project directory deleted' + }); + } catch (error) { + console.error('Failed to delete project directory:', String(error)); + return NextResponse.json( + { + success: false, + error: error.message + }, + { status: 500 } + ); + } +} diff --git a/easy-dataset-main/app/api/projects/migrate/route.js b/easy-dataset-main/app/api/projects/migrate/route.js new file mode 100644 index 0000000..2ca6b1c --- /dev/null +++ b/easy-dataset-main/app/api/projects/migrate/route.js @@ -0,0 +1,169 @@ +import { NextResponse } from 'next/server'; +import { main } from '@/lib/db/fileToDb'; + +// Store migration task states +const migrationTasks = new Map(); + +/** + * Start a migration task + */ +export async function POST() { + try { + // Generate a unique task ID + const taskId = Date.now().toString(); + + // Initialize task state + migrationTasks.set(taskId, { + status: 'running', + progress: 0, + total: 0, + completed: 0, + error: null, + startTime: Date.now() + }); + + // Execute migration asynchronously + executeMigration(taskId); + + // Return task ID + return NextResponse.json({ + success: true, + taskId + }); + } catch (error) { + console.error('Failed to start migration task:', String(error)); + return NextResponse.json( + { + success: false, + error: error.message + }, + { status: 500 } + ); + } +} + +/** + * Get migration task status + */ +export async function GET(request) { + try { + // Get task ID from URL + const { searchParams } = new URL(request.url); + const taskId = searchParams.get('taskId'); + + if (!taskId) { + return NextResponse.json( + { + success: false, + error: 'Missing taskId' + }, + { status: 400 } + ); + } + + // Read task state + const task = migrationTasks.get(taskId); + + if (!task) { + return NextResponse.json( + { + success: false, + error: 'Task not found' + }, + { status: 404 } + ); + } + + // Return task state + return NextResponse.json({ + success: true, + task + }); + } catch (error) { + console.error('Failed to get migration task status:', String(error)); + return NextResponse.json( + { + success: false, + error: error.message + }, + { status: 500 } + ); + } +} + +/** + * Execute migration task asynchronously + * @param {string} taskId Task ID + */ +async function executeMigration(taskId) { + try { + // Read task state + const task = migrationTasks.get(taskId); + + if (!task) { + console.error(`Task not found: ${taskId}`); + return; + } + + // Reset task state to running + task.status = 'running'; + task.progress = 0; + task.completed = 0; + task.total = 0; + task.startTime = Date.now(); + + // Persist task state once per second so clients can poll progress + const statusUpdateInterval = setInterval(() => { + // Only update while still running + if (task.status === 'running') { + migrationTasks.set(taskId, { ...task }); + console.log( + `Migration task status updated: ${taskId}, progress: ${task.progress}%, completed: ${task.completed}/${task.total}` + ); + } else { + // Stop updating when task ends + clearInterval(statusUpdateInterval); + } + }, 1000); + + // Run migration and let main(task) mutate progress fields + const count = await main(task); + + // Clear status update timer + clearInterval(statusUpdateInterval); + + // Mark as completed + task.status = 'completed'; + task.progress = 100; + task.completed = count; + if (task.total === 0) task.total = count; + task.endTime = Date.now(); + + // Persist final task state + migrationTasks.set(taskId, { ...task }); + + // Clean up task state after 30 minutes + setTimeout( + () => { + migrationTasks.delete(taskId); + console.log(`Migration task state cleaned up: ${taskId}`); + }, + 30 * 60 * 1000 + ); + } catch (error) { + console.error(`Failed to execute migration task: ${taskId}`, String(error)); + + // Read task state + const task = migrationTasks.get(taskId); + + if (task) { + // Mark as failed + task.status = 'failed'; + task.error = error.message; + task.endTime = Date.now(); + + // Persist task state + migrationTasks.set(taskId, task); + } + } +} diff --git a/easy-dataset-main/app/api/projects/open-directory/route.js b/easy-dataset-main/app/api/projects/open-directory/route.js new file mode 100644 index 0000000..6d7bfe0 --- /dev/null +++ b/easy-dataset-main/app/api/projects/open-directory/route.js @@ -0,0 +1,62 @@ +import { getProjectRoot } from '@/lib/db/base'; +import { NextResponse } from 'next/server'; +import path from 'path'; +import { exec } from 'child_process'; +import { promisify } from 'util'; + +const execAsync = promisify(exec); + +/** + * Open project directory + * @returns {Promise} Operation result response + */ +export async function POST(request) { + try { + const { projectId } = await request.json(); + + if (!projectId) { + return NextResponse.json( + { + success: false, + error: 'Project ID is required' + }, + { status: 400 } + ); + } + + // Get project root directory + const projectRoot = await getProjectRoot(); + const projectPath = path.join(projectRoot, projectId); + + // Open directory based on OS + const platform = process.platform; + let command; + + if (platform === 'win32') { + // Windows + command = `explorer "${projectPath}"`; + } else if (platform === 'darwin') { + // macOS + command = `open "${projectPath}"`; + } else { + // Linux and others + command = `xdg-open "${projectPath}"`; + } + + await execAsync(command); + + return NextResponse.json({ + success: true, + message: 'Project directory opened' + }); + } catch (error) { + console.error('Failed to open project directory:', String(error)); + return NextResponse.json( + { + success: false, + error: error.message + }, + { status: 500 } + ); + } +} diff --git a/easy-dataset-main/app/api/projects/route.js b/easy-dataset-main/app/api/projects/route.js new file mode 100644 index 0000000..ee11911 --- /dev/null +++ b/easy-dataset-main/app/api/projects/route.js @@ -0,0 +1,42 @@ +import { createProject, getProjects, isExistByName } from '@/lib/db/projects'; +import { createInitModelConfig, getModelConfigByProjectId } from '@/lib/db/model-config'; + +export async function POST(request) { + try { + const projectData = await request.json(); + if (!projectData.name) { + return Response.json({ error: 'Project name is required' }, { status: 400 }); + } + + if (await isExistByName(projectData.name)) { + return Response.json({ error: 'Project name already exists' }, { status: 400 }); + } + const newProject = await createProject(projectData); + if (projectData.reuseConfigFrom) { + let data = await getModelConfigByProjectId(projectData.reuseConfigFrom); + + let newData = data.map(item => { + delete item.id; + return { + ...item, + projectId: newProject.id + }; + }); + await createInitModelConfig(newData); + } + return Response.json(newProject, { status: 201 }); + } catch (error) { + console.error('Failed to create project:', String(error)); + return Response.json({ error: String(error) }, { status: 500 }); + } +} + +export async function GET(request) { + try { + const projects = await getProjects(); + return Response.json(projects); + } catch (error) { + console.error('Failed to get project list:', String(error)); + return Response.json({ error: String(error) }, { status: 500 }); + } +} diff --git a/easy-dataset-main/app/api/projects/unmigrated/route.js b/easy-dataset-main/app/api/projects/unmigrated/route.js new file mode 100644 index 0000000..bf8e426 --- /dev/null +++ b/easy-dataset-main/app/api/projects/unmigrated/route.js @@ -0,0 +1,75 @@ +import { getProjectRoot } from '@/lib/db/base'; +import { db } from '@/lib/db/index'; +import fs from 'fs'; +import path from 'path'; +import { NextResponse } from 'next/server'; + +/** + * Get list of unmigrated projects + * @returns {Promise} Response containing unmigrated project IDs + */ +export async function GET(request) { + // Read query params from request URL + const { searchParams } = new URL(request.url); + // Force a unique value per request + const timestamp = searchParams.get('_t') || Date.now(); + try { + // Get project root directory + const projectRoot = await getProjectRoot(); + + // Read all folders under project root (each folder represents a project) + const files = await fs.promises.readdir(projectRoot, { withFileTypes: true }); + + // Filter directories + const projectDirs = files.filter(file => file.isDirectory()); + + // Return empty list if no project directories exist + if (projectDirs.length === 0) { + return NextResponse.json({ + success: true, + data: [] + }); + } + + // Collect all project IDs + const projectIds = projectDirs.map(dir => dir.name); + + // Batch query migrated projects + const existingProjects = await db.projects.findMany({ + where: { + id: { + in: projectIds + } + }, + select: { + id: true + } + }); + + // Convert to Set for fast lookups + const existingProjectIds = new Set(existingProjects.map(p => p.id)); + + // Filter unmigrated projects + const unmigratedProjectDirs = projectDirs.filter(dir => !existingProjectIds.has(dir.name)); + + // Build unmigrated project ID list + const unmigratedProjects = unmigratedProjectDirs.map(dir => dir.name); + + return NextResponse.json({ + success: true, + data: unmigratedProjects, + projectRoot, + number: Date.now(), + timestamp + }); + } catch (error) { + console.error('Failed to get unmigrated project list:', String(error)); + return NextResponse.json( + { + success: false, + error: error.message + }, + { status: 500 } + ); + } +} diff --git a/easy-dataset-main/app/api/update/route.js b/easy-dataset-main/app/api/update/route.js new file mode 100644 index 0000000..c70dd76 --- /dev/null +++ b/easy-dataset-main/app/api/update/route.js @@ -0,0 +1,70 @@ +import { NextResponse } from 'next/server'; +import { exec } from 'child_process'; +import path from 'path'; +import fs from 'fs'; + +export async function POST() { + try { + const desktopDir = path.join(process.cwd(), 'desktop'); + const updaterPath = path.join(desktopDir, 'scripts', 'updater.js'); + + if (!fs.existsSync(updaterPath)) { + return NextResponse.json( + { + success: false, + message: 'The update feature is only available in the client environment' + }, + { status: 400 } + ); + } + + // Run update script + return new Promise(resolve => { + const updaterProcess = exec(`node "${updaterPath}"`, { cwd: process.cwd() }); + + let output = ''; + + updaterProcess.stdout.on('data', data => { + output += data.toString(); + console.log(`Update output: ${data}`); + }); + + updaterProcess.stderr.on('data', data => { + output += data.toString(); + console.error(`Update error: ${data}`); + }); + + updaterProcess.on('close', code => { + console.log(`Update process exit, exit code: ${code}`); + + if (code === 0) { + resolve( + NextResponse.json({ + success: true, + message: 'Update successful, application will restart' + }) + ); + } else { + resolve( + NextResponse.json( + { + success: false, + message: `Update failed, exit code: ${code}, output: ${output}` + }, + { status: 500 } + ) + ); + } + }); + }); + } catch (error) { + console.error('Failed to execute update:', String(error)); + return NextResponse.json( + { + success: false, + message: `Failed to execute update: ${error.message}` + }, + { status: 500 } + ); + } +} diff --git a/easy-dataset-main/app/dataset-square/page.js b/easy-dataset-main/app/dataset-square/page.js new file mode 100644 index 0000000..2f7f46b --- /dev/null +++ b/easy-dataset-main/app/dataset-square/page.js @@ -0,0 +1,200 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { Box, Container, Typography, Paper, useTheme, alpha } from '@mui/material'; +import StorageIcon from '@mui/icons-material/Storage'; +import Navbar from '@/components/Navbar/index'; +import { DatasetSearchBar } from '@/components/dataset-square/DatasetSearchBar'; +import { DatasetSiteList } from '@/components/dataset-square/DatasetSiteList'; +import { useTranslation } from 'react-i18next'; + +export default function DatasetSquarePage() { + const [projects, setProjects] = useState([]); + const theme = useTheme(); + const { t } = useTranslation(); + + // 获取项目列表和模型列表 + useEffect(() => { + async function fetchData() { + try { + // 获取用户创建的项目详情 + const response = await fetch('/api/projects'); + if (response.ok) { + const projectsData = await response.json(); + setProjects(projectsData); + } + } catch (error) { + console.error('获取数据失败:', error); + } + } + + fetchData(); + }, []); + + return ( +
+ {/* 导航栏 */} + + + {/* 头部区域 */} + + {/* 背景装饰 */} + + + + + + + + + {t('datasetSquare.title')} + + + + {t('datasetSquare.subtitle')} + + + {/* 搜索栏组件 */} + + + + + + + + + {/* 内容区域 */} + + {/* 数据集网站列表组件 */} + + + + +
+ ); +} diff --git a/easy-dataset-main/app/globals.css b/easy-dataset-main/app/globals.css new file mode 100644 index 0000000..8d042df --- /dev/null +++ b/easy-dataset-main/app/globals.css @@ -0,0 +1,122 @@ +* { + box-sizing: border-box; + padding: 0; + margin: 0; +} + +html, +body { + width: 100%; + max-width: 100%; + overflow-x: hidden; + height: 100%; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +/* 避免根滚动条显隐导致页面横向抖动 */ +html { + overflow-y: auto; +} + +a { + color: inherit; + text-decoration: none; +} + +/* 渐变文本样式 */ +.gradient-text { + background: linear-gradient(90deg, #2a5caa 0%, #8b5cf6 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + text-fill-color: transparent; +} + +/* 页面容器下间距 */ +main { + min-height: calc(100vh - 64px); +} + +/* 自定义滚动条 */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: transparent; +} + +::-webkit-scrollbar-thumb { + background-color: rgba(0, 0, 0, 0.2); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background-color: rgba(0, 0, 0, 0.3); +} + +/* 暗色模式滚动条 */ +[data-theme='dark'] ::-webkit-scrollbar-thumb { + background-color: rgba(255, 255, 255, 0.2); +} + +[data-theme='dark'] ::-webkit-scrollbar-thumb:hover { + background-color: rgba(255, 255, 255, 0.3); +} + +/* 方便的间距类 */ +.mt-1 { + margin-top: 8px; +} +.mt-2 { + margin-top: 16px; +} +.mt-3 { + margin-top: 24px; +} +.mt-4 { + margin-top: 32px; +} +.mb-1 { + margin-bottom: 8px; +} +.mb-2 { + margin-bottom: 16px; +} +.mb-3 { + margin-bottom: 24px; +} +.mb-4 { + margin-bottom: 32px; +} + +/* 响应式样式 */ +@media (max-width: 600px) { + .hide-on-mobile { + display: none !important; + } +} + +/* 输入框和选择框边框简化 */ +.plain-select .MuiOutlinedInput-notchedOutline, +.plain-input .MuiOutlinedInput-notchedOutline { + border-color: transparent !important; +} + +/* 卡片悬停效果 */ +.hover-card { + transition: + transform 0.2s ease, + box-shadow 0.2s ease; +} + +.hover-card:hover { + transform: translateY(-4px); + box-shadow: 0 12px 20px rgba(0, 0, 0, 0.1); +} + +[data-theme='dark'] .hover-card:hover { + box-shadow: 0 12px 20px rgba(0, 0, 0, 0.3); +} diff --git a/easy-dataset-main/app/layout.js b/easy-dataset-main/app/layout.js new file mode 100644 index 0000000..d6a4558 --- /dev/null +++ b/easy-dataset-main/app/layout.js @@ -0,0 +1,30 @@ +import './globals.css'; +import ThemeRegistry from '@/components/ThemeRegistry'; +import I18nProvider from '@/components/I18nProvider'; +import { Toaster } from 'sonner'; +import { Provider } from 'jotai'; + +export const metadata = { + title: 'Easy Dataset', + description: '一个强大的 LLM 数据集生成工具', + icons: { + icon: '/imgs/logo.ico' // 更新为正确的文件名 + } +}; + +export default function RootLayout({ children }) { + return ( + + + + + + {children} + + + + + + + ); +} diff --git a/easy-dataset-main/app/monitoring/components/Charts.js b/easy-dataset-main/app/monitoring/components/Charts.js new file mode 100644 index 0000000..8a01418 --- /dev/null +++ b/easy-dataset-main/app/monitoring/components/Charts.js @@ -0,0 +1,188 @@ +import React from 'react'; +import { Card, CardContent, Typography, Box, useTheme } from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import { + AreaChart, + Area, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + ResponsiveContainer, + PieChart, + Pie, + Cell, + Legend +} from 'recharts'; + +export default function Charts({ trendData, modelDistribution }) { + const theme = useTheme(); + const { t } = useTranslation(); + + const COLORS = [ + theme.palette.primary.main, + theme.palette.secondary.main, + theme.palette.success.main, + theme.palette.warning.main, + theme.palette.error.main, + theme.palette.info.main + ]; + + return ( + + {/* 趋势图 */} + + + + + + {t('monitoring.charts.tokenTrend')} + + + + + + {t('monitoring.charts.inputLegend')} + + + + + + {t('monitoring.charts.outputLegend')} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {/* 模型分布图 */} + + + + + {t('monitoring.charts.distributionTitle')} + + + {t('monitoring.charts.distributionSubtitle')} + + + + + + + {modelDistribution.map((entry, index) => ( + + ))} + + t('monitoring.charts.tokensTooltip', { value: (value / 1000).toFixed(1) })} + contentStyle={{ + backgroundColor: theme.palette.background.paper, + border: `1px solid ${theme.palette.divider}`, + borderRadius: 8 + }} + /> + ( + {value} + )} + /> + + + {/* 中间文字 */} + {/* + + {modelDistribution.length} + + + Models + + */} + + + + + + ); +} diff --git a/easy-dataset-main/app/monitoring/components/StatsCards.js b/easy-dataset-main/app/monitoring/components/StatsCards.js new file mode 100644 index 0000000..5b79630 --- /dev/null +++ b/easy-dataset-main/app/monitoring/components/StatsCards.js @@ -0,0 +1,145 @@ +import React from 'react'; +import { Box, Card, CardContent, Grid, Typography, Stack, useTheme, alpha } from '@mui/material'; +import { + Storage as StorageIcon, + Balance as BalanceIcon, + Bolt as BoltIcon, + AccessTime as AccessTimeIcon +} from '@mui/icons-material'; +import { useTranslation } from 'react-i18next'; + +function StatCard({ title, value, subValue, icon: Icon, color }) { + const theme = useTheme(); + + return ( + + + + + + + + {title} + + + + + {value} + + + {subValue && ( + + {subValue} + + )} + + + ); +} + +export default function StatsCards({ data }) { + const theme = useTheme(); + const { t } = useTranslation(); + + // 格式化数字 + const formatNumber = num => { + if (num >= 1000000) return `${(num / 1000000).toFixed(1)}M`; + if (num >= 1000) return `${(num / 1000).toFixed(1)}K`; + return num; + }; + + return ( + + {/* 总 Token 消耗 */} + + + + + {/* 平均 Token 消耗/次 */} + + + + + {/* 总调用次数 */} + + + + {t('monitoring.stats.successCalls', { count: formatNumber(data.successCalls) })} + + + · + + + {t('monitoring.stats.failedCalls', { count: formatNumber(data.failedCalls) })} + + {data.totalCalls > 0 && ( + + ({t('monitoring.stats.failureRate', { rate: ((data.failureRate || 0) * 100).toFixed(1) })}) + + )} + + } + icon={BoltIcon} + color={theme.palette.success.main} + /> + + + {/* 平均响应耗时 */} + + 0 + ? t('monitoring.stats.basedOnSuccessCalls', { count: formatNumber(data.successCalls) }) + : t('monitoring.stats.noSuccessCalls') + } + icon={AccessTimeIcon} + color={theme.palette.warning.main} + /> + + + ); +} diff --git a/easy-dataset-main/app/monitoring/components/UsageTable.js b/easy-dataset-main/app/monitoring/components/UsageTable.js new file mode 100644 index 0000000..9a3e466 --- /dev/null +++ b/easy-dataset-main/app/monitoring/components/UsageTable.js @@ -0,0 +1,215 @@ +import React, { useState } from 'react'; +import { + Box, + Card, + CardContent, + Typography, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Chip, + TextField, + InputAdornment, + TablePagination, + useTheme, + alpha, + Tooltip +} from '@mui/material'; +import SearchIcon from '@mui/icons-material/Search'; +import { useTranslation } from 'react-i18next'; + +const statusColors = { + SUCCESS: 'success', + FAILED: 'error' +}; + +export default function UsageTable({ + data, + total, + page, + pageSize, + onPageChange, + onPageSizeChange, + searchTerm, + onSearchChange +}) { + const theme = useTheme(); + const { t } = useTranslation(); + + const handleChangePage = (event, newPage) => { + onPageChange(newPage + 1); // MUI uses 0-indexed, our API uses 1-indexed + }; + + const handleChangeRowsPerPage = event => { + onPageSizeChange(parseInt(event.target.value, 10)); + }; + + const handleSearchChange = event => { + onSearchChange(event.target.value); + }; + + // 直接使用传入的数据,分页和搜索已在后端完成 + + return ( + + + + + {t('monitoring.table.title')} + + + + + ) + }} + sx={{ width: 300 }} + /> + + + + + + + + {t('monitoring.table.columns.projectName')} + + + {t('monitoring.table.columns.provider')} + + + {t('monitoring.table.columns.model')} + + + {t('monitoring.table.columns.status')} + + + {t('monitoring.table.columns.failureReason')} + + + {t('monitoring.table.columns.inputTokens')} + + + {t('monitoring.table.columns.outputTokens')} + + + {t('monitoring.table.columns.totalTokens')} + + + {t('monitoring.table.columns.calls')} + + + {t('monitoring.table.columns.avgLatency')} + + + + + {data.map((row, index) => ( + + + + {row.projectName} + + + + + + + + {row.model} + + + + + + + {row.failureReason ? ( + + 20 ? row.failureReason.slice(0, 20) + '...' : row.failureReason + } + size="small" + color="error" + variant="soft" + sx={{ + maxWidth: 200, + bgcolor: alpha(theme.palette.error.main, 0.1), + color: theme.palette.error.dark, + cursor: 'pointer' + }} + /> + + ) : ( + '-' + )} + + {row.inputTokens.toLocaleString()} + {row.outputTokens.toLocaleString()} + + {row.totalTokens.toLocaleString()} + + {row.calls} + {row.avgLatency} + + ))} + {data.length === 0 && ( + + + {t('monitoring.table.empty')} + + + )} + +
+
+ +
+
+ ); +} diff --git a/easy-dataset-main/app/monitoring/hooks/useMonitoringData.js b/easy-dataset-main/app/monitoring/hooks/useMonitoringData.js new file mode 100644 index 0000000..6b7f0b4 --- /dev/null +++ b/easy-dataset-main/app/monitoring/hooks/useMonitoringData.js @@ -0,0 +1,128 @@ +import { useState, useEffect, useCallback } from 'react'; +import axios from 'axios'; +import { toast } from 'sonner'; +import { useTranslation } from 'react-i18next'; + +export function useMonitoringData() { + const { t } = useTranslation(); + const [loading, setLoading] = useState(true); + const [summaryData, setSummaryData] = useState({ + summary: { + totalTokens: 0, + inputTokens: 0, + outputTokens: 0, + totalCalls: 0, + successCalls: 0, + failedCalls: 0, + totalLatency: 0, + avgLatency: 0, + avgTokensPerCall: 0, + failureRate: 0 + }, + trend: [], + modelDistribution: [], + projects: [], + providers: [] + }); + + const [logsData, setLogsData] = useState({ + details: [], + total: 0, + page: 1, + pageSize: 10, + totalPages: 0 + }); + + const [filters, setFilters] = useState({ + timeRange: '7d', + projectId: 'all', + provider: 'all', + status: 'all' + }); + + const [pagination, setPagination] = useState({ + page: 1, + pageSize: 10 + }); + + const [searchTerm, setSearchTerm] = useState(''); + + // 获取汇总数据 + const fetchSummary = useCallback(async () => { + try { + const response = await axios.get('/api/monitoring/summary', { + params: filters + }); + setSummaryData(response.data); + } catch (error) { + console.error('Failed to fetch monitoring summary:', error); + toast.error(t('monitoring.errors.fetchSummaryFailed')); + } + }, [filters, t]); + + // 获取日志列表 + const fetchLogs = useCallback(async () => { + try { + const response = await axios.get('/api/monitoring/logs', { + params: { + ...filters, + page: pagination.page, + pageSize: pagination.pageSize, + search: searchTerm + } + }); + setLogsData(response.data); + } catch (error) { + console.error('Failed to fetch monitoring logs:', error); + toast.error(t('monitoring.errors.fetchLogsFailed')); + } + }, [filters, pagination, searchTerm, t]); + + // 初始加载 + useEffect(() => { + const fetchData = async () => { + setLoading(true); + await Promise.all([fetchSummary(), fetchLogs()]); + setLoading(false); + }; + fetchData(); + }, [fetchSummary, fetchLogs]); + + const handleFilterChange = (key, value) => { + setFilters(prev => ({ ...prev, [key]: value })); + setPagination(prev => ({ ...prev, page: 1 })); // 重置到第一页 + }; + + const handlePageChange = newPage => { + setPagination(prev => ({ ...prev, page: newPage })); + }; + + const handlePageSizeChange = newPageSize => { + setPagination({ page: 1, pageSize: newPageSize }); + }; + + const handleSearchChange = term => { + setSearchTerm(term); + setPagination(prev => ({ ...prev, page: 1 })); // 重置到第一页 + }; + + const refresh = useCallback(async () => { + setLoading(true); + await Promise.all([fetchSummary(), fetchLogs()]); + setLoading(false); + }, [fetchSummary, fetchLogs]); + + return { + loading, + summaryData, + logsData, + filters, + pagination, + searchTerm, + handleFilterChange, + handlePageChange, + handlePageSizeChange, + handleSearchChange, + refresh + }; +} diff --git a/easy-dataset-main/app/monitoring/page.js b/easy-dataset-main/app/monitoring/page.js new file mode 100644 index 0000000..9cfb87c --- /dev/null +++ b/easy-dataset-main/app/monitoring/page.js @@ -0,0 +1,244 @@ +'use client'; + +import React, { useState, useEffect } from 'react'; +import { + Box, + Container, + Stack, + Button, + FormControl, + Select, + MenuItem, + ToggleButton, + ToggleButtonGroup, + CircularProgress, + useTheme +} from '@mui/material'; +import { + Download as DownloadIcon, + FilterList as FilterListIcon, + CloudQueue as CloudQueueIcon, + CheckCircle as CheckCircleIcon +} from '@mui/icons-material'; +import { useTranslation } from 'react-i18next'; +import Navbar from '@/components/Navbar/index'; +import StatsCards from './components/StatsCards'; +import Charts from './components/Charts'; +import UsageTable from './components/UsageTable'; +import { useMonitoringData } from './hooks/useMonitoringData'; + +export default function MonitoringPage() { + const theme = useTheme(); + const { t } = useTranslation(); + const [projects, setProjects] = useState([]); + const { + loading, + summaryData, + logsData, + filters, + pagination, + searchTerm, + handleFilterChange, + handlePageChange, + handlePageSizeChange, + handleSearchChange + } = useMonitoringData(); + + // 获取项目列表用于 Navbar + useEffect(() => { + async function fetchProjects() { + try { + const response = await fetch('/api/projects'); + if (response.ok) { + const data = await response.json(); + setProjects(data); + } + } catch (error) { + console.error('Failed to fetch projects:', error); + } + } + fetchProjects(); + }, []); + + const handleTimeRangeChange = (event, newRange) => { + if (newRange !== null) { + handleFilterChange('timeRange', newRange); + } + }; + + const handleExport = () => { + // 简单的导出功能实现,将当前 logsData.details 导出为 CSV + if (!logsData.details || logsData.details.length === 0) return; + + const headers = [ + t('monitoring.table.columns.projectName'), + t('monitoring.table.columns.provider'), + t('monitoring.table.columns.model'), + t('monitoring.table.columns.status'), + t('monitoring.table.columns.failureReason'), + t('monitoring.table.columns.inputTokens'), + t('monitoring.table.columns.outputTokens'), + t('monitoring.table.columns.totalTokens'), + t('monitoring.table.columns.calls'), + t('monitoring.table.columns.avgLatency') + ]; + const csvContent = [ + headers.join(','), + ...logsData.details.map(row => + [ + row.projectName, + row.provider, + row.model, + row.status, + (row.failureReason || '').replace(/,/g, ' '), + row.inputTokens, + row.outputTokens, + row.totalTokens, + row.calls, + row.avgLatency + ].join(',') + ) + ].join('\n'); + + const blob = new Blob([`\uFEFF${csvContent}`], { type: 'text/csv;charset=utf-8;' }); + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = `llm-monitoring-export-${new Date().toISOString().slice(0, 10)}.csv`; + link.click(); + }; + + return ( + <> + + + + {/* Header Area */} + + {/* Time Range Selector */} + + {t('monitoring.timeRange.24h')} + {t('monitoring.timeRange.7d')} + {t('monitoring.timeRange.30d')} + + + {/* Filters & Actions */} + + + + + + + + + + + + + + + + + + {loading ? ( + + + + ) : ( + + {/* 统计卡片 */} + + + {/* 图表区域 */} + + + {/* 详细表格 */} + + + )} + + + + ); +} diff --git a/easy-dataset-main/app/page.js b/easy-dataset-main/app/page.js new file mode 100644 index 0000000..6dcc3c0 --- /dev/null +++ b/easy-dataset-main/app/page.js @@ -0,0 +1,153 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { Container, Box, Typography, CircularProgress, Stack, useTheme } from '@mui/material'; +import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline'; +import Navbar from '@/components/Navbar/index'; +import HeroSection from '@/components/home/HeroSection'; +import ProjectList from '@/components/home/ProjectList'; +import CreateProjectDialog from '@/components/home/CreateProjectDialog'; +import MigrationDialog from '@/components/home/MigrationDialog'; +import { motion } from 'framer-motion'; +import { useTranslation } from 'react-i18next'; + +export default function Home() { + const { t } = useTranslation(); + const [projects, setProjects] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [createDialogOpen, setCreateDialogOpen] = useState(false); + const [unmigratedProjects, setUnmigratedProjects] = useState([]); + const [migrationDialogOpen, setMigrationDialogOpen] = useState(false); + + useEffect(() => { + async function fetchProjects() { + try { + setLoading(true); + // 获取用户创建的项目详情 + const response = await fetch(`/api/projects`); + + if (!response.ok) { + throw new Error(t('projects.fetchFailed')); + } + + const data = await response.json(); + setProjects(data); + + // 检查是否有未迁移的项目 + await checkUnmigratedProjects(); + } catch (error) { + console.error(t('projects.fetchError'), String(error)); + setError(String(error)); + } finally { + setLoading(false); + } + } + + // 检查未迁移的项目 + async function checkUnmigratedProjects() { + try { + const response = await fetch('/api/projects/unmigrated'); + + if (!response.ok) { + console.error('检查未迁移项目失败'); + return; + } + + const { success, data } = await response.json(); + + if (success && Array.isArray(data) && data.length > 0) { + setUnmigratedProjects(data); + setMigrationDialogOpen(true); + } + } catch (error) { + console.error('检查未迁移项目出错', error); + } + } + + fetchProjects(); + }, []); + + const theme = useTheme(); + + return ( +
+ + + setCreateDialogOpen(true)} /> + + + {/* */} + + {loading && ( + + + + {t('projects.loading')} + + + )} + + {error && !loading && ( + + + + + {t('projects.fetchFailed')}: {error} + + + + )} + + {!loading && ( + + setCreateDialogOpen(true)} /> + + )} + + + setCreateDialogOpen(false)} /> + + {/* 项目迁移对话框 */} + setMigrationDialogOpen(false)} + projectIds={unmigratedProjects} + /> +
+ ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/[taskId]/page.js b/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/[taskId]/page.js new file mode 100644 index 0000000..8af0afd --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/[taskId]/page.js @@ -0,0 +1,159 @@ +'use client'; + +import { useState } from 'react'; +import { useParams, useRouter } from 'next/navigation'; +import { + Box, + Container, + Button, + CircularProgress, + Alert, + Dialog, + DialogTitle, + DialogContent, + DialogContentText, + DialogActions +} from '@mui/material'; +import StopIcon from '@mui/icons-material/Stop'; +import { useTranslation } from 'react-i18next'; + +import useBlindTestDetail from '../hooks/useBlindTestDetail'; +import BlindTestHeader from '../components/BlindTestHeader'; +import ResultSummary from '../components/ResultSummary'; +import ResultDetailList from '../components/ResultDetailList'; +import BlindTestInProgress from '../components/BlindTestInProgress'; + +export default function BlindTestDetailPage() { + const { projectId, taskId } = useParams(); + const router = useRouter(); + const { t } = useTranslation(); + + const { + task, + loading, + error, + setError, + currentQuestion, + leftAnswer, + rightAnswer, + answersLoading, + streamingA, + streamingB, + voting, + completed, + fetchCurrentQuestion, + submitVote, + interruptTask, + getResultStats + } = useBlindTestDetail(projectId, taskId); + + const [interruptDialog, setInterruptDialog] = useState(false); + + const handleBack = () => router.push(`/projects/${projectId}/blind-test-tasks`); + + const handleVote = async vote => { + await submitVote(vote); + }; + + const handleInterrupt = async () => { + await interruptTask(); + setInterruptDialog(false); + }; + + // 加载中 + if (loading) { + return ( + + + + ); + } + + // 任务不存在 + if (!task) { + return ( + + + {t('blindTest.taskNotFound', '任务不存在')} + + ); + } + + const isResultView = completed || task.status !== 0; + const stats = getResultStats(); + + // 结果展示页面(已完成或已中断) + if (isResultView) { + return ( + + + + + + + + + ); + } + + // 盲测进行中页面 + return ( + + } + onClick={() => setInterruptDialog(true)} + size="small" + > + {t('blindTest.interrupt', '中断任务')} + + } + /> + + {error && ( + setError('')}> + {error} + + )} + + + + + + {/* 中断确认对话框 */} + setInterruptDialog(false)}> + {t('blindTest.interruptConfirmTitle', '确认中断')} + + + {t('blindTest.interruptConfirmMessage', '确定要中断这个盲测任务吗?已完成的评判结果将保留。')} + + + + + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/components/BlindTestHeader.js b/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/components/BlindTestHeader.js new file mode 100644 index 0000000..cb30986 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/components/BlindTestHeader.js @@ -0,0 +1,77 @@ +import { Box, Typography, IconButton, Chip, Button } from '@mui/material'; +import ArrowBackIcon from '@mui/icons-material/ArrowBack'; +import CompareArrowsIcon from '@mui/icons-material/CompareArrows'; +import { useTranslation } from 'react-i18next'; +import { useTheme, alpha } from '@mui/material/styles'; +import { blindTestStyles } from '@/styles/blindTest'; + +export default function BlindTestHeader({ title, status, onBack, actions }) { + const { t } = useTranslation(); + const theme = useTheme(); + const styles = blindTestStyles(theme); + + const getStatusConfig = s => { + switch (s) { + case 1: + return { label: 'blindTest.statusCompleted', color: 'success' }; + case 3: + return { label: 'blindTest.statusInterrupted', color: 'warning' }; + default: + return { label: 'blindTest.statusProcessing', color: 'primary' }; + } + }; + + const statusConfig = status !== undefined ? getStatusConfig(status) : null; + + return ( + + + + + + + + + + + {title} + + {statusConfig && ( + + )} + + + {actions} + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/components/BlindTestInProgress.js b/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/components/BlindTestInProgress.js new file mode 100644 index 0000000..cc6d0b8 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/components/BlindTestInProgress.js @@ -0,0 +1,449 @@ +import { useState, useRef, useEffect } from 'react'; +import { + Box, + Paper, + Typography, + Button, + LinearProgress, + CircularProgress, + Alert, + Chip, + Collapse, + IconButton, + Tooltip, + Fade, + Avatar +} from '@mui/material'; +import { useTheme, alpha } from '@mui/material/styles'; +import ThumbUpIcon from '@mui/icons-material/ThumbUp'; +import ThumbDownIcon from '@mui/icons-material/ThumbDown'; +import ThumbsUpDownIcon from '@mui/icons-material/ThumbsUpDown'; +import RefreshIcon from '@mui/icons-material/Refresh'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import ExpandLessIcon from '@mui/icons-material/ExpandLess'; +import PsychologyIcon from '@mui/icons-material/Psychology'; +import AutoFixHighIcon from '@mui/icons-material/AutoFixHigh'; +import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'; +import AssignmentIcon from '@mui/icons-material/Assignment'; +import SmartToyIcon from '@mui/icons-material/SmartToy'; +import { useTranslation } from 'react-i18next'; +import ReactMarkdown from 'react-markdown'; +import 'github-markdown-css/github-markdown-light.css'; +import { blindTestStyles } from '@/styles/blindTest'; + +function AnswerBox({ title, modelLabel, answer, streaming, showThinking, setShowThinking, scrollRef, styles, theme }) { + const { t } = useTranslation(); + const isLeft = modelLabel === 'A'; + const avatarColor = isLeft ? 'primary.main' : 'secondary.main'; + + return ( + + + + + {modelLabel} + + + {title} + + {streaming && } + + {answer?.duration > 0 && !streaming && ( + + )} + + + {answer?.error ? ( + + + {answer.error} + + + ) : ( + + {/* 思维链渲染 */} + {answer?.thinking && ( + + setShowThinking(!showThinking)} + > + + {answer.isThinking ? ( + + ) : ( + + )} + + {t('playground.reasoningProcess', '推理过程')} + + + + {showThinking ? : } + + + + + + {answer.thinking} + + + + + )} + + {answer?.content ? ( +
+ {answer.content} +
+ ) : streaming ? ( + + + {t('blindTest.generatingAnswers', '正在生成回答...')} + + ) : null} +
+ )} +
+ ); +} + +export default function BlindTestInProgress({ + task, + currentQuestion, + leftAnswer, + rightAnswer, + streamingA, + streamingB, + answersLoading, + voting, + onVote, + onReload +}) { + const { t } = useTranslation(); + const theme = useTheme(); + const styles = blindTestStyles(theme); + + const [showThinkingLeft, setShowThinkingLeft] = useState(true); + const [showThinkingRight, setShowThinkingRight] = useState(true); + + // 自动滚动引用 + const leftScrollRef = useRef(null); + const rightScrollRef = useRef(null); + + // 处理自动滚动 + useEffect(() => { + if (streamingA && leftScrollRef.current) { + leftScrollRef.current.scrollTop = leftScrollRef.current.scrollHeight; + } + }, [leftAnswer?.content, leftAnswer?.thinking, streamingA]); + + useEffect(() => { + if (streamingB && rightScrollRef.current) { + rightScrollRef.current.scrollTop = rightScrollRef.current.scrollHeight; + } + }, [rightAnswer?.content, rightAnswer?.thinking, streamingB]); + + const progress = task ? (task.completedCount / task.totalCount) * 100 : 0; + + if (answersLoading && !currentQuestion) { + return ( + + + + {t('blindTest.generatingAnswers', '正在准备题目...')} + + + ); + } + + if (!currentQuestion) { + return ( + + + + ); + } + + return ( + + {/* 顶部进度和问题 */} + + + + + {t('blindTest.progress', '进度')} {task.completedCount + 1}/{task.totalCount} + + + + + + + + + {currentQuestion.question} + + + + + {/* 回答区域 */} + + + + + + {/* 底部投票区域 */} + + + + + + + + {t('blindTest.referenceAnswer', '参考答案')} + + + {currentQuestion.answer} + + + ) : ( + t('blindTest.noReferenceAnswer', '暂无参考答案') + ) + } + arrow + placement="top" + TransitionComponent={Fade} + TransitionProps={{ timeout: 600 }} + componentsProps={{ + tooltip: { + sx: { + bgcolor: theme.palette.mode === 'dark' ? 'grey.900' : 'background.paper', + color: 'text.primary', + boxShadow: theme.shadows[8], + border: `1px solid ${theme.palette.divider}`, + p: 0, + '& .MuiTooltip-arrow': { + color: theme.palette.mode === 'dark' ? 'grey.900' : 'background.paper', + '&::before': { + border: `1px solid ${theme.palette.divider}` + } + } + } + } + }} + > + + + + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/components/BlindTestTaskCard.js b/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/components/BlindTestTaskCard.js new file mode 100644 index 0000000..3fdb8e3 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/components/BlindTestTaskCard.js @@ -0,0 +1,316 @@ +'use client'; + +import { + Box, + Card, + CardContent, + Typography, + Chip, + IconButton, + Menu, + MenuItem, + LinearProgress, + Avatar, + Grid, + Tooltip, + Divider +} from '@mui/material'; +import MoreVertIcon from '@mui/icons-material/MoreVert'; +import PlayArrowIcon from '@mui/icons-material/PlayArrow'; +import DeleteIcon from '@mui/icons-material/Delete'; +import StopIcon from '@mui/icons-material/Stop'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import AccessTimeIcon from '@mui/icons-material/AccessTime'; +import EmojiEventsIcon from '@mui/icons-material/EmojiEvents'; +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { alpha, useTheme } from '@mui/material/styles'; + +const STATUS_MAP = { + 0: { label: 'blindTest.statusProcessing', color: 'primary', bgColor: 'primary.main' }, + 1: { label: 'blindTest.statusCompleted', color: 'success', bgColor: 'success.main' }, + 2: { label: 'blindTest.statusFailed', color: 'error', bgColor: 'error.main' }, + 3: { label: 'blindTest.statusInterrupted', color: 'warning', bgColor: 'warning.main' } +}; + +export default function BlindTestTaskCard({ task, onView, onDelete, onInterrupt, onContinue }) { + const { t } = useTranslation(); + const theme = useTheme(); + const [anchorEl, setAnchorEl] = useState(null); + + const handleMenuOpen = e => { + e.stopPropagation(); + setAnchorEl(e.currentTarget); + }; + + const handleMenuClose = () => { + setAnchorEl(null); + }; + + const handleView = e => { + e?.stopPropagation?.(); + handleMenuClose(); + onView?.(task); + }; + + const handleDelete = e => { + e?.stopPropagation?.(); + handleMenuClose(); + onDelete?.(task); + }; + + const handleInterrupt = e => { + e?.stopPropagation?.(); + handleMenuClose(); + onInterrupt?.(task); + }; + + const handleContinue = e => { + e?.stopPropagation?.(); + handleMenuClose(); + onContinue?.(task); + }; + + const statusConfig = STATUS_MAP[task.status] || STATUS_MAP[0]; + const progress = task.totalCount > 0 ? (task.completedCount / task.totalCount) * 100 : 0; + const isProcessing = task.status === 0; + const isCompleted = task.status === 1; + + // 计算模型得分 + const results = task.detail?.results || []; + const modelAScore = results.reduce((sum, r) => sum + (r.modelAScore || 0), 0); + const modelBScore = results.reduce((sum, r) => sum + (r.modelBScore || 0), 0); + const totalScore = modelAScore + modelBScore; + + // Calculate win percentages for visual bar + const modelAPercent = totalScore > 0 ? (modelAScore / totalScore) * 100 : 50; + const modelBPercent = totalScore > 0 ? (modelBScore / totalScore) * 100 : 50; + + const winner = isCompleted ? (modelAScore > modelBScore ? 'A' : modelBScore > modelAScore ? 'B' : 'Tie') : null; + + return ( + handleView(e)} + > + + + {/* Status & Time */} + + + + + + + {new Date(task.createAt).toLocaleDateString()} + + + + + + {/* Model Comparison Area */} + + + {/* Model A */} + + + A + + + + + {task.modelInfo?.modelA?.modelName || 'Model A'} + + + + {task.modelInfo?.modelA?.providerName} + + + {isCompleted && winner === 'A' && } + + + {/* Center Status/Score */} + + {isCompleted ? ( + + + {modelAScore.toFixed(1)} + : + {modelBScore.toFixed(1)} + + + + + + + ) : ( + + + VS + + + + {Math.round(progress)}% + + + )} + + + {/* Model B */} + + + B + + + + + {task.modelInfo?.modelB?.modelName || 'Model B'} + + + + {task.modelInfo?.modelB?.providerName} + + + {isCompleted && winner === 'B' && } + + + + + {/* Menu */} + + + + + + + + + {/* 菜单 */} + + + + {t('blindTest.viewDetails', '查看详情')} + + {isProcessing && ( + + + {t('blindTest.continue', '继续盲测')} + + )} + {isProcessing && ( + + + {t('blindTest.interrupt', '中断任务')} + + )} + + + + {t('common.delete', '删除')} + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/components/CreateBlindTestDialog.js b/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/components/CreateBlindTestDialog.js new file mode 100644 index 0000000..f134c67 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/components/CreateBlindTestDialog.js @@ -0,0 +1,498 @@ +'use client'; + +import { useState, useEffect, useCallback } from 'react'; +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + Box, + Typography, + FormControl, + InputLabel, + Select, + MenuItem, + Alert, + CircularProgress, + Chip, + Divider, + TextField, + OutlinedInput, + Checkbox, + ListItemText, + Avatar, + Paper +} from '@mui/material'; +import CompareArrowsIcon from '@mui/icons-material/CompareArrows'; +import SmartToyIcon from '@mui/icons-material/SmartToy'; +import { useTranslation } from 'react-i18next'; +import { alpha, useTheme } from '@mui/material/styles'; + +export default function CreateBlindTestDialog({ open, onClose, projectId, onCreate }) { + const { t } = useTranslation(); + const theme = useTheme(); + + // 模型选择 + const [models, setModels] = useState([]); + const [modelsLoading, setModelsLoading] = useState(false); + const [modelA, setModelA] = useState(null); + const [modelB, setModelB] = useState(null); + + // 题目选择 + const [questionTypes, setQuestionTypes] = useState(['short_answer', 'open_ended']); + const [selectedTags, setSelectedTags] = useState([]); + const [availableTags, setAvailableTags] = useState([]); + const [questionCount, setQuestionCount] = useState(0); + const [filteredCount, setFilteredCount] = useState(0); + const [countLoading, setCountLoading] = useState(false); + + // 提交状态 + const [submitting, setSubmitting] = useState(false); + const [error, setError] = useState(''); + + // 加载模型列表 + useEffect(() => { + if (!open || !projectId) return; + + const fetchModels = async () => { + try { + setModelsLoading(true); + const response = await fetch(`/api/projects/${projectId}/model-config`); + const result = await response.json(); + + if (result.data) { + setModels(result.data); + } + } catch (err) { + console.error('加载模型失败:', err); + } finally { + setModelsLoading(false); + } + }; + + fetchModels(); + }, [open, projectId]); + + // 加载标签和题目数量 + useEffect(() => { + if (!open || !projectId) return; + + const fetchStats = async () => { + try { + const response = await fetch(`/api/projects/${projectId}/eval-datasets?page=1&pageSize=1&includeStats=true`); + const result = await response.json(); + + if (result.stats?.byTag) { + setAvailableTags(Object.keys(result.stats.byTag).sort()); + } + } catch (err) { + console.error('加载统计失败:', err); + } + }; + + fetchStats(); + }, [open, projectId]); + + // 获取符合条件的题目数量 + const fetchFilteredCount = useCallback(async () => { + if (!projectId) return; + + try { + setCountLoading(true); + const params = new URLSearchParams(); + + // 只查询主观题 + questionTypes.forEach(t => params.append('questionTypes', t)); + selectedTags.forEach(t => params.append('tags', t)); + + const response = await fetch(`/api/projects/${projectId}/eval-datasets/count?${params.toString()}`); + const result = await response.json(); + + if (result.code === 0) { + setFilteredCount(result.data?.total || 0); + } + } catch (err) { + console.error('获取题目数量失败:', err); + } finally { + setCountLoading(false); + } + }, [projectId, questionTypes, selectedTags]); + + useEffect(() => { + if (open) { + fetchFilteredCount(); + } + }, [open, fetchFilteredCount]); + + // 重置表单 + const resetForm = () => { + setModelA(null); + setModelB(null); + setQuestionTypes(['short_answer', 'open_ended']); + setSelectedTags([]); + setQuestionCount(0); + setError(''); + }; + + // 关闭对话框 + const handleClose = () => { + if (submitting) return; + resetForm(); + onClose(); + }; + + // 提交创建 + const handleSubmit = async () => { + // 验证 + if (!modelA) { + setError(t('blindTest.errorSelectModelA', '请选择模型A')); + return; + } + if (!modelB) { + setError(t('blindTest.errorSelectModelB', '请选择模型B')); + return; + } + if (modelA.id === modelB.id) { + setError(t('blindTest.errorSameModel', '两个模型不能相同')); + return; + } + if (filteredCount === 0) { + setError(t('blindTest.errorNoQuestions', '没有符合条件的题目')); + return; + } + + try { + setSubmitting(true); + setError(''); + + // 获取题目ID列表 + const params = new URLSearchParams(); + questionTypes.forEach(t => params.append('questionTypes', t)); + selectedTags.forEach(t => params.append('tags', t)); + + const pageSize = questionCount > 0 ? questionCount : filteredCount; + params.append('pageSize', pageSize.toString()); + + const response = await fetch(`/api/projects/${projectId}/eval-datasets?${params.toString()}`); + const result = await response.json(); + + if (!result.items || result.items.length === 0) { + setError(t('blindTest.errorNoQuestions', '没有符合条件的题目')); + return; + } + + // 随机选择题目(如果指定了数量) + let selectedIds = result.items.map(item => item.id); + if (questionCount > 0 && questionCount < selectedIds.length) { + // 随机抽取 + selectedIds = selectedIds.sort(() => Math.random() - 0.5).slice(0, questionCount); + } + + // 创建任务 + const createResult = await onCreate({ + modelA: { modelId: modelA.modelId, providerId: modelA.providerId, id: modelA.id }, + modelB: { modelId: modelB.modelId, providerId: modelB.providerId, id: modelB.id }, + evalDatasetIds: selectedIds + }); + + if (createResult.success) { + handleClose(); + } else { + setError(createResult.error || '创建失败'); + } + } catch (err) { + console.error('创建任务失败:', err); + setError('创建任务失败'); + } finally { + setSubmitting(false); + } + }; + + const QUESTION_TYPES = [ + { value: 'short_answer', labelKey: 'eval.questionTypes.short_answer' }, + { value: 'open_ended', labelKey: 'eval.questionTypes.open_ended' } + ]; + + return ( + + + + + + + {t('blindTest.createTitle', '创建盲测任务')} + + + + + {error && ( + setError('')}> + {error} + + )} + + {/* 模型选择 */} + + + + {t('blindTest.selectModels', '选择对比模型')} + + + + + {/* 模型A */} + + + A + + {t('blindTest.modelA', '模型 A')} + + + + + + + + + + VS + + + + {/* 模型B */} + + + B + + {t('blindTest.modelB', '模型 B')} + + + + + + + + + {models.length === 0 && !modelsLoading && ( + + {t('blindTest.noModelsAvailable', '暂无可用模型,请先在设置中配置模型')} + + )} + + + + {/* 题目筛选 */} + + + {t('blindTest.selectQuestions', '选择测试题目')} + + + + {t('blindTest.questionTypeHint', '盲测任务仅支持简答题和开放题')} + + + + + {/* 题型筛选 */} + + {t('blindTest.questionType', '题型')} + + + + {/* 标签筛选 */} + {availableTags.length > 0 && ( + + {t('blindTest.filterByTag', '按标签筛选')} + + + )} + + + {/* 题目数量 */} + + + setQuestionCount(Math.max(0, parseInt(e.target.value) || 0))} + inputProps={{ min: 0, max: filteredCount }} + sx={{ width: 150, bgcolor: 'background.paper' }} + /> + + + + + {countLoading ? ( + + ) : ( + t('blindTest.availableQuestions', '可用题目:{{count}} 道', { count: filteredCount }) + )} + + {filteredCount > 0 && ( + + )} + + + {questionCount === 0 + ? t('blindTest.useAllQuestions', '使用全部筛选结果') + : t('blindTest.randomSample', '将随机抽取 {{count}} 道题目', { count: questionCount })} + + + + + + + + + + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/components/ResultDetailList.js b/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/components/ResultDetailList.js new file mode 100644 index 0000000..5db9093 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/components/ResultDetailList.js @@ -0,0 +1,335 @@ +import { Box, Paper, Typography, Chip, Collapse, IconButton, Avatar, Divider, Grid } from '@mui/material'; +import ReactMarkdown from 'react-markdown'; +import { useTranslation } from 'react-i18next'; +import { useTheme, alpha } from '@mui/material/styles'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import ExpandLessIcon from '@mui/icons-material/ExpandLess'; +import PsychologyIcon from '@mui/icons-material/Psychology'; +import EmojiEventsIcon from '@mui/icons-material/EmojiEvents'; +import CheckCircleIcon from '@mui/icons-material/CheckCircle'; +import CancelIcon from '@mui/icons-material/Cancel'; +import RemoveCircleIcon from '@mui/icons-material/RemoveCircle'; +import HelpIcon from '@mui/icons-material/Help'; +import { useState } from 'react'; +import 'github-markdown-css/github-markdown-light.css'; + +// 解析包含 标签的内容 +const parseAnswerContent = text => { + if (!text) return { thinking: '', content: '' }; + + // 匹配 ... 内容 + const thinkMatch = text.match(/([\s\S]*?)<\/think>/); + + if (thinkMatch) { + return { + thinking: thinkMatch[1].trim(), + content: text.replace(/[\s\S]*?<\/think>/, '').trim() + }; + } + + return { thinking: '', content: text }; +}; + +function ResultAnswerSection({ title, rawContent, isWinner, modelLabel, t, theme }) { + const { thinking, content } = parseAnswerContent(rawContent); + const [showThinking, setShowThinking] = useState(false); + + const isLeft = modelLabel.includes('A') || title.includes('左'); + const avatarColor = isLeft ? 'primary.main' : 'secondary.main'; + + return ( + + + + + {modelLabel} + + + {title} + + + {isWinner && ( + } + label={t('blindTest.winner', '胜出')} + size="small" + color={isLeft ? 'primary' : 'secondary'} + sx={{ fontWeight: 600 }} + /> + )} + + + + {/* 思维链展示 */} + {thinking && ( + + setShowThinking(!showThinking)} + > + + + {t('playground.reasoningProcess', '推理过程')} + + + {showThinking ? ( + + ) : ( + + )} + + + + + + {thinking} + + + + + )} + + {/* 正文内容 */} +
+ {content || '-'} +
+
+
+ ); +} + +function ResultItem({ result, index, task, question }) { + const { t } = useTranslation(); + const theme = useTheme(); + const [expanded, setExpanded] = useState(false); + + // Determine vote icon and color + let VoteIcon = HelpIcon; + let voteColor = 'default'; + let voteLabel = ''; + + switch (result.vote) { + case 'left': + VoteIcon = CheckCircleIcon; + voteColor = 'primary'; + voteLabel = t('blindTest.leftBetter', '左边更好'); + break; + case 'right': + VoteIcon = CheckCircleIcon; + voteColor = 'secondary'; + voteLabel = t('blindTest.rightBetter', '右边更好'); + break; + case 'both_good': + VoteIcon = CheckCircleIcon; + voteColor = 'success'; + voteLabel = t('blindTest.bothGood', '都好'); + break; + case 'both_bad': + VoteIcon = CancelIcon; + voteColor = 'error'; + voteLabel = t('blindTest.bothBad', '都不好'); + break; + default: + VoteIcon = RemoveCircleIcon; + voteLabel = t('blindTest.ties', '平局'); + } + + // Determine Model labels based on swap status + const leftModelName = result.isSwapped ? task.modelInfo?.modelB?.modelName : task.modelInfo?.modelA?.modelName; + const rightModelName = result.isSwapped ? task.modelInfo?.modelA?.modelName : task.modelInfo?.modelB?.modelName; + const leftModelLabel = result.isSwapped ? 'B' : 'A'; + const rightModelLabel = result.isSwapped ? 'A' : 'B'; + + return ( + + {/* 头部摘要 */} + setExpanded(!expanded)} + > + + + #{index + 1} + + + {question?.question || result.questionId} + + + + + } + label={voteLabel} + color={voteColor === 'default' ? 'default' : voteColor} + variant={result.vote === 'both_good' || result.vote === 'both_bad' ? 'outlined' : 'filled'} + sx={{ fontWeight: 600 }} + /> + + + + + + + {/* 展开详情 */} + + + + + + QUESTION + + + {question?.question} + + + + + {/* 左侧详情 */} + + + + + {/* 右侧详情 */} + + + + + + + + ); +} + +export default function ResultDetailList({ task }) { + const { t } = useTranslation(); + + return ( + + + {t('blindTest.detailResults', '详细结果')} + + + {task.detail?.results?.map((result, index) => { + const question = task.evalDatasets?.find(q => q.id === result.questionId); + return ; + })} + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/components/ResultSummary.js b/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/components/ResultSummary.js new file mode 100644 index 0000000..d51fd66 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/components/ResultSummary.js @@ -0,0 +1,257 @@ +import { Box, Paper, Typography, Card, CardContent, Chip, Grid, Avatar } from '@mui/material'; +import { useTheme, alpha } from '@mui/material/styles'; +import EmojiEventsIcon from '@mui/icons-material/EmojiEvents'; +import { useTranslation } from 'react-i18next'; +import { blindTestStyles } from '@/styles/blindTest'; + +export default function ResultSummary({ stats, modelInfo }) { + const { t } = useTranslation(); + const theme = useTheme(); + + if (!stats) return null; + + const totalScore = stats.modelAScore + stats.modelBScore; + const modelAPercent = totalScore > 0 ? (stats.modelAScore / totalScore) * 100 : 50; + const modelBPercent = totalScore > 0 ? (stats.modelBScore / totalScore) * 100 : 50; + + const winner = stats.modelAScore > stats.modelBScore ? 'A' : stats.modelBScore > stats.modelAScore ? 'B' : 'tie'; + + return ( + + + + {t('blindTest.resultSummary', '评测结果汇总')} + + + + {/* Model A */} + + + {winner === 'A' && ( + + WINNER + + )} + + + A + + + {modelInfo?.modelA?.modelName || 'Model A'} + + + + + + {stats.modelAScore.toFixed(1)} + + + {t('blindTest.wins', '胜出')}: {stats.modelAWins} + + + + + + + {/* VS / Progress */} + + + + VS + + + + + + + + + + + {/* Model B */} + + + {winner === 'B' && ( + + WINNER + + )} + + + B + + + {modelInfo?.modelB?.modelName || 'Model B'} + + + + + + {stats.modelBScore.toFixed(1)} + + + {t('blindTest.wins', '胜出')}: {stats.modelBWins} + + + + + + + + {/* 底部统计条 */} + + + + + + {stats.totalQuestions} + + + {t('blindTest.totalQuestions', '总题数')} + + + + + + + {stats.bothGood} + + + {t('blindTest.bothGood', '都好')} + + + + + + + {stats.bothBad} + + + {t('blindTest.bothBad', '都不好')} + + + + + + + {stats.ties} + + + {t('blindTest.ties', '平局')} + + + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/hooks/useBlindTestDetail.js b/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/hooks/useBlindTestDetail.js new file mode 100644 index 0000000..7710183 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/hooks/useBlindTestDetail.js @@ -0,0 +1,405 @@ +'use client'; + +import { useState, useCallback, useEffect, useRef } from 'react'; + +/** + * 盲测任务详情和盲测过程管理 Hook + */ +export default function useBlindTestDetail(projectId, taskId) { + // 任务详情 + const [task, setTask] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(''); + + // 当前题目状态 + const [currentQuestion, setCurrentQuestion] = useState(null); + const [leftAnswer, setLeftAnswer] = useState(null); + const [rightAnswer, setRightAnswer] = useState(null); + const [isSwapped, setIsSwapped] = useState(false); + const [answersLoading, setAnswersLoading] = useState(false); + + // 流式输出状态 + const [streamingA, setStreamingA] = useState(false); + const [streamingB, setStreamingB] = useState(false); + const abortControllerRef = useRef(null); + const hasAutoLoadedRef = useRef(false); + + // 投票状态 + const [voting, setVoting] = useState(false); + const [completed, setCompleted] = useState(false); + + // 加载任务详情 + const loadTask = useCallback( + async (silent = false) => { + if (!projectId || !taskId) return; + + try { + if (!silent) setLoading(true); + setError(''); + // 添加时间戳防止缓存 + const response = await fetch(`/api/projects/${projectId}/blind-test-tasks/${taskId}?t=${Date.now()}`, { + cache: 'no-store', + headers: { + Pragma: 'no-cache', + 'Cache-Control': 'no-cache' + } + }); + const result = await response.json(); + + if (result.code === 0) { + console.log('任务状态更新:', result.data.completedCount, '/', result.data.totalCount); + setTask(result.data); + // 检查任务是否已完成 (0=进行中, 1=已完成, 2=失败, 3=已中断) + if (result.data.status !== 0) { + setCompleted(true); + } + } else { + if (!silent) setError(result.error || '加载任务详情失败'); + } + } catch (err) { + console.error('加载任务详情失败:', err); + if (!silent) setError('加载任务详情失败'); + } finally { + if (!silent) setLoading(false); + } + }, + [projectId, taskId] + ); + + // 流式获取当前题目和模型回答 + const fetchCurrentQuestion = useCallback(async () => { + if (!projectId || !taskId) return; + + // 取消上一次的请求 + if (abortControllerRef.current) { + abortControllerRef.current.abort(); + } + const controller = new AbortController(); + abortControllerRef.current = controller; + + try { + setAnswersLoading(true); + setError(''); + setCurrentQuestion(null); + setLeftAnswer({ fullContent: '', content: '', thinking: '', isThinking: false, duration: 0, error: null }); + setRightAnswer({ fullContent: '', content: '', thinking: '', isThinking: false, duration: 0, error: null }); + + // 1. 先获取题目信息 + const questionRes = await fetch(`/api/projects/${projectId}/blind-test-tasks/${taskId}/question`, { + signal: controller.signal, + cache: 'no-store' + }); + + if (!questionRes.ok) throw new Error('获取题目失败'); + + const questionData = await questionRes.json(); + + if (questionData.completed) { + setCompleted(true); + return; + } + + setCurrentQuestion({ + id: questionData.questionId, + question: questionData.question, + answer: questionData.answer, + index: questionData.questionIndex, + total: questionData.totalQuestions + }); + setIsSwapped(questionData.isSwapped); + setCompleted(false); + + // 2. 并行调用两个模型的流式接口 + setStreamingA(true); + setStreamingB(true); + + const processStream = async (modelType, setAnswer, setStreaming) => { + const modelStartTime = Date.now(); + try { + const streamUrl = `/api/projects/${projectId}/blind-test-tasks/${taskId}/stream-model?model=${modelType}`; + const response = await fetch(streamUrl, { + signal: controller.signal + }); + + if (!response.ok) { + throw new Error(`模型${modelType}调用失败: ${response.status}`); + } + + const reader = response.body.getReader(); + const decoder = new TextDecoder(); + + let fullContent = ''; + let currentContent = ''; + let currentThinking = ''; + let isInThinking = false; + let pendingBuffer = ''; // 用于处理跨 chunk 的标签识别 + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + const chunk = decoder.decode(value, { stream: true }); + pendingBuffer += chunk; + + // 处理缓冲区中的内容 + while (pendingBuffer.length > 0) { + // 如果正在思考中,寻找结束标签 + if (isInThinking) { + const endTagIndex = pendingBuffer.indexOf('
'); + if (endTagIndex !== -1) { + const thinkingPart = pendingBuffer.substring(0, endTagIndex); + currentThinking += thinkingPart; + fullContent += thinkingPart + '
'; + isInThinking = false; + pendingBuffer = pendingBuffer.substring(endTagIndex + 8); + continue; + } else { + // 没有找到结束标签,但可能缓冲区末尾包含了部分结束标签 + // 保留最后 7 个字符("
" 长度为 8)以防被截断 + const safeLength = Math.max(0, pendingBuffer.length - 7); + const processingPart = pendingBuffer.substring(0, safeLength); + currentThinking += processingPart; + fullContent += processingPart; + pendingBuffer = pendingBuffer.substring(safeLength); + break; // 等待下一个 chunk + } + } else { + // 不在思考中,寻找开始标签 + const startTagIndex = pendingBuffer.indexOf(''); + if (startTagIndex !== -1) { + const contentPart = pendingBuffer.substring(0, startTagIndex); + currentContent += contentPart; + fullContent += contentPart + ''; + isInThinking = true; + pendingBuffer = pendingBuffer.substring(startTagIndex + 7); + continue; + } else { + // 没有找到开始标签,保留最后 6 个字符以防开始标签被截断 + const safeLength = Math.max(0, pendingBuffer.length - 6); + const processingPart = pendingBuffer.substring(0, safeLength); + currentContent += processingPart; + fullContent += processingPart; + pendingBuffer = pendingBuffer.substring(safeLength); + break; // 等待下一个 chunk + } + } + } + + setAnswer(prev => ({ + ...prev, + fullContent, + content: currentContent, + thinking: currentThinking, + isThinking: isInThinking + })); + } + + const modelDuration = Date.now() - modelStartTime; + setAnswer(prev => ({ ...prev, duration: modelDuration })); + setStreaming(false); + } catch (err) { + if (err.name === 'AbortError') return; + console.error(`模型${modelType}错误:`, err); + const modelDuration = Date.now() - modelStartTime; + setAnswer(prev => ({ + ...prev, + error: err.message, + duration: modelDuration + })); + setStreaming(false); + } + }; + + // 根据是否交换决定左右对应的模型 + const leftModel = questionData.isSwapped ? 'B' : 'A'; + const rightModel = questionData.isSwapped ? 'A' : 'B'; + + await Promise.all([ + processStream(leftModel, setLeftAnswer, setStreamingA), + processStream(rightModel, setRightAnswer, setStreamingB) + ]); + } catch (err) { + if (err.name === 'AbortError') return; + console.error('获取题目失败:', err); + setError(err.message || '获取当前题目失败'); + setStreamingA(false); + setStreamingB(false); + } finally { + // 只有当前请求未被取消时才重置loading + if (abortControllerRef.current === controller) { + setAnswersLoading(false); + } + } + }, [projectId, taskId]); + + // 提交投票 + const submitVote = useCallback( + async vote => { + if (!projectId || !taskId || !currentQuestion) return { success: false }; + + try { + setVoting(true); + setError(''); + + const response = await fetch(`/api/projects/${projectId}/blind-test-tasks/${taskId}/vote`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + vote, + questionId: currentQuestion.id, + isSwapped, + // 使用 fullContent 提交,包含思考过程 + leftAnswer: leftAnswer?.fullContent || leftAnswer?.content || '', + rightAnswer: rightAnswer?.fullContent || rightAnswer?.content || '' + }) + }); + + const result = await response.json(); + + if (result.code === 0) { + // 等待任务状态更新(进度条) + await loadTask(true); + + if (result.data.isCompleted) { + setCompleted(true); + } else { + // 获取下一题 + await fetchCurrentQuestion(); + } + return { success: true, data: result.data }; + } else { + setError(result.error || '提交投票失败'); + return { success: false, error: result.error }; + } + } catch (err) { + console.error('提交投票失败:', err); + setError('提交投票失败'); + return { success: false, error: '提交投票失败' }; + } finally { + setVoting(false); + } + }, + [projectId, taskId, currentQuestion, isSwapped, leftAnswer, rightAnswer, loadTask, fetchCurrentQuestion] + ); + + // 中断任务 + const interruptTask = useCallback(async () => { + if (!projectId || !taskId) return false; + + try { + const response = await fetch(`/api/projects/${projectId}/blind-test-tasks/${taskId}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ action: 'interrupt' }) + }); + + const result = await response.json(); + + if (result.code === 0) { + setCompleted(true); + loadTask(); + return true; + } else { + setError(result.error || '中断任务失败'); + return false; + } + } catch (err) { + console.error('中断任务失败:', err); + setError('中断任务失败'); + return false; + } + }, [projectId, taskId, loadTask]); + + // 初始加载 + useEffect(() => { + loadTask(); + }, [loadTask]); + + // 任务加载完成后,如果任务进行中,自动获取当前题目(只执行一次) + useEffect(() => { + if (task && task.status === 0 && !completed && !hasAutoLoadedRef.current && projectId && taskId) { + hasAutoLoadedRef.current = true; + fetchCurrentQuestion(); + } + }, [task, completed, projectId, taskId, fetchCurrentQuestion]); + + // 计算结果统计 + const getResultStats = useCallback(() => { + if (!task?.detail?.results) return null; + + const results = task.detail.results; + const totalModelAScore = results.reduce((sum, r) => sum + (r.modelAScore || 0), 0); + const totalModelBScore = results.reduce((sum, r) => sum + (r.modelBScore || 0), 0); + + const leftWins = results.filter(r => r.vote === 'left').length; + const rightWins = results.filter(r => r.vote === 'right').length; + const bothGood = results.filter(r => r.vote === 'both_good').length; + const bothBad = results.filter(r => r.vote === 'both_bad').length; + + // 计算实际模型胜出次数(需要考虑 swap) + const modelAWins = results.filter(r => { + if (r.vote === 'left' && !r.isSwapped) return true; + if (r.vote === 'right' && r.isSwapped) return true; + return false; + }).length; + + const modelBWins = results.filter(r => { + if (r.vote === 'left' && r.isSwapped) return true; + if (r.vote === 'right' && !r.isSwapped) return true; + return false; + }).length; + + return { + totalQuestions: results.length, + modelAScore: totalModelAScore, + modelBScore: totalModelBScore, + modelAWins, + modelBWins, + ties: bothGood + bothBad, + bothGood, + bothBad, + leftWins, + rightWins + }; + }, [task]); + + // 组件卸载时取消请求 + useEffect(() => { + return () => { + if (abortControllerRef.current) { + abortControllerRef.current.abort(); + } + }; + }, []); + + return { + // 任务详情 + task, + loading, + error, + setError, + loadTask, + + // 当前题目状态 + currentQuestion, + leftAnswer, + rightAnswer, + answersLoading, + + // 流式状态 + streamingA, + streamingB, + + // 投票状态 + voting, + completed, + + // 操作 + fetchCurrentQuestion, + submitVote, + interruptTask, + + // 结果统计 + getResultStats + }; +} diff --git a/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/hooks/useBlindTestTasks.js b/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/hooks/useBlindTestTasks.js new file mode 100644 index 0000000..2ca159d --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/hooks/useBlindTestTasks.js @@ -0,0 +1,140 @@ +'use client'; + +import { useState, useCallback, useEffect } from 'react'; + +/** + * 盲测任务列表管理 Hook + */ +export default function useBlindTestTasks(projectId) { + const [tasks, setTasks] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(''); + const [page, setPage] = useState(1); + const [pageSize, setPageSize] = useState(6); + const [total, setTotal] = useState(0); + + // 加载任务列表 + const loadTasks = useCallback( + async (isRefresh = false) => { + if (!projectId) return; + + try { + if (!isRefresh) setLoading(true); + setError(''); + const response = await fetch(`/api/projects/${projectId}/blind-test-tasks?page=${page}&pageSize=${pageSize}`); + const result = await response.json(); + + if (result.code === 0) { + setTasks(result.data.items || []); + setTotal(result.data.total || 0); + } else { + setError(result.error || '加载失败'); + } + } catch (err) { + console.error('加载盲测任务失败:', err); + setError('加载失败'); + } finally { + if (!isRefresh) setLoading(false); + } + }, + [projectId, page, pageSize] + ); + + // 初始加载和分页变化加载 + useEffect(() => { + loadTasks(); + }, [loadTasks]); + + // 删除任务 + const deleteTask = useCallback( + async taskId => { + try { + const response = await fetch(`/api/projects/${projectId}/blind-test-tasks/${taskId}`, { + method: 'DELETE' + }); + const result = await response.json(); + + if (result.code === 0) { + loadTasks(); + return true; + } else { + setError(result.error || '删除失败'); + return false; + } + } catch (err) { + console.error('删除任务失败:', err); + setError('删除失败'); + return false; + } + }, + [projectId, loadTasks] + ); + + // 中断任务 + const interruptTask = useCallback( + async taskId => { + try { + const response = await fetch(`/api/projects/${projectId}/blind-test-tasks/${taskId}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ action: 'interrupt' }) + }); + const result = await response.json(); + + if (result.code === 0) { + loadTasks(); + return true; + } else { + setError(result.error || '中断失败'); + return false; + } + } catch (err) { + console.error('中断任务失败:', err); + setError('中断失败'); + return false; + } + }, + [projectId, loadTasks] + ); + + // 创建任务 + const createTask = useCallback( + async taskData => { + try { + const response = await fetch(`/api/projects/${projectId}/blind-test-tasks`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(taskData) + }); + const result = await response.json(); + + if (result.code === 0) { + loadTasks(); + return { success: true, data: result.data }; + } else { + return { success: false, error: result.error || '创建失败' }; + } + } catch (err) { + console.error('创建任务失败:', err); + return { success: false, error: '创建失败' }; + } + }, + [projectId, loadTasks] + ); + + return { + tasks, + loading, + error, + setError, + loadTasks, + deleteTask, + interruptTask, + createTask, + page, + setPage, + pageSize, + setPageSize, + total + }; +} diff --git a/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/page.js b/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/page.js new file mode 100644 index 0000000..7fa7d9a --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/blind-test-tasks/page.js @@ -0,0 +1,215 @@ +'use client'; + +import { useState } from 'react'; +import { useParams, useRouter } from 'next/navigation'; +import { + Box, + Container, + Typography, + Button, + Grid, + CircularProgress, + Alert, + Dialog, + DialogTitle, + DialogContent, + DialogContentText, + DialogActions, + TablePagination +} from '@mui/material'; +import AddIcon from '@mui/icons-material/Add'; +import CompareArrowsIcon from '@mui/icons-material/CompareArrows'; +import { useTranslation } from 'react-i18next'; + +import useBlindTestTasks from './hooks/useBlindTestTasks'; +import BlindTestTaskCard from './components/BlindTestTaskCard'; +import CreateBlindTestDialog from './components/CreateBlindTestDialog'; + +export default function BlindTestTasksPage() { + const { projectId } = useParams(); + const router = useRouter(); + const { t } = useTranslation(); + + const { + tasks, + loading, + error, + setError, + deleteTask, + interruptTask, + createTask, + page, + setPage, + pageSize, + setPageSize, + total + } = useBlindTestTasks(projectId); + + const [createDialogOpen, setCreateDialogOpen] = useState(false); + const [deleteDialog, setDeleteDialog] = useState({ open: false, task: null }); + const [interruptDialog, setInterruptDialog] = useState({ open: false, task: null }); + + const handleView = task => router.push(`/projects/${projectId}/blind-test-tasks/${task.id}`); + const handleContinue = task => router.push(`/projects/${projectId}/blind-test-tasks/${task.id}`); + const handleDelete = task => setDeleteDialog({ open: true, task }); + const handleInterrupt = task => setInterruptDialog({ open: true, task }); + + const handlePageChange = (event, newPage) => { + setPage(newPage + 1); + }; + + const handlePageSizeChange = event => { + setPageSize(parseInt(event.target.value, 10)); + setPage(1); + }; + + const confirmDelete = async () => { + if (deleteDialog.task) { + await deleteTask(deleteDialog.task.id); + } + setDeleteDialog({ open: false, task: null }); + }; + + const confirmInterrupt = async () => { + if (interruptDialog.task) { + await interruptTask(interruptDialog.task.id); + } + setInterruptDialog({ open: false, task: null }); + }; + + const handleCreate = async taskData => { + const result = await createTask(taskData); + if (result.success) { + // 创建成功后跳转到任务详情页开始盲测 + router.push(`/projects/${projectId}/blind-test-tasks/${result.data.id}`); + } + return result; + }; + + return ( + + {/* 页面标题 */} + + + + + {t('blindTest.title', '人工盲测任务')} + + + + + + {/* 错误提示 */} + {error && ( + setError('')}> + {error} + + )} + + {/* 加载状态 */} + {loading && ( + + + + )} + + {/* 空状态 */} + {!loading && tasks.length === 0 && ( + + + + {t('blindTest.noTasks', '暂无盲测任务')} + + + {t('blindTest.noTasksHint', '创建盲测任务来对比两个模型的回答质量')} + + + + )} + + {/* 任务列表 */} + {!loading && tasks.length > 0 && ( + <> + + {tasks.map(task => ( + + + + ))} + + + + + + )} + + {/* 创建对话框 */} + setCreateDialogOpen(false)} + projectId={projectId} + onCreate={handleCreate} + /> + + {/* 删除确认对话框 */} + setDeleteDialog({ open: false, task: null })}> + {t('blindTest.deleteConfirmTitle', '确认删除')} + + + {t('blindTest.deleteConfirmMessage', '确定要删除这个盲测任务吗?此操作不可撤销。')} + + + + + + + + + {/* 中断确认对话框 */} + setInterruptDialog({ open: false, task: null })}> + {t('blindTest.interruptConfirmTitle', '确认中断')} + + + {t('blindTest.interruptConfirmMessage', '确定要中断这个盲测任务吗?已完成的评判结果将保留。')} + + + + + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/datasets/[datasetId]/page.js b/easy-dataset-main/app/projects/[projectId]/datasets/[datasetId]/page.js new file mode 100644 index 0000000..893becd --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/datasets/[datasetId]/page.js @@ -0,0 +1,202 @@ +'use client'; + +import { Container, Box, Typography, Alert, Snackbar, Paper } from '@mui/material'; +import { useEffect } from 'react'; +import ChunkViewDialog from '@/components/text-split/ChunkViewDialog'; +import DatasetHeader from '@/components/datasets/DatasetHeader'; +import DatasetMetadata from '@/components/datasets/DatasetMetadata'; +import EditableField from '@/components/datasets/EditableField'; +import OptimizeDialog from '@/components/datasets/OptimizeDialog'; +import DatasetRatingSection from '@/components/datasets/DatasetRatingSection'; +import useDatasetDetails from '@/app/projects/[projectId]/datasets/[datasetId]/useDatasetDetails'; +import { useTranslation } from 'react-i18next'; + +/** + * 数据集详情页面 + */ +export default function DatasetDetailsPage({ params }) { + const { projectId, datasetId } = params; + + const { t } = useTranslation(); + // 使用自定义Hook管理状态和逻辑 + const { + currentDataset, + loading, + editingAnswer, + editingCot, + editingQuestion, + answerValue, + cotValue, + questionValue, + snackbar, + confirming, + unconfirming, + optimizeDialog, + viewDialogOpen, + viewChunk, + datasetsAllCount, + datasetsConfirmCount, + answerTokens, + cotTokens, + shortcutsEnabled, + setShortcutsEnabled, + setSnackbar, + setAnswerValue, + setCotValue, + setQuestionValue, + setEditingAnswer, + setEditingCot, + setEditingQuestion, + handleNavigate, + handleConfirm, + handleUnconfirm, + handleSave, + handleDelete, + handleOpenOptimizeDialog, + handleCloseOptimizeDialog, + handleOptimize, + handleViewChunk, + handleCloseViewDialog + } = useDatasetDetails(projectId, datasetId); + + // 加载状态 + if (loading) { + return ( + + + {t('datasets.loadingDataset')} + + + ); + } + + // 无数据状态 + if (!currentDataset) { + return ( + + {t('datasets.datasetNotFound')} + + ); + } + + return ( + + {/* 顶部导航栏 */} + + + {/* 主要布局:左右分栏 */} + + {/* 左侧主要内容区域 */} + + + setEditingQuestion(true)} + onChange={e => setQuestionValue(e.target.value)} + onSave={() => handleSave('question', questionValue)} + dataset={currentDataset} + onCancel={() => { + setEditingQuestion(false); + setQuestionValue(currentDataset.question); + }} + /> + + setEditingAnswer(true)} + onChange={e => setAnswerValue(e.target.value)} + onSave={() => handleSave('answer', answerValue)} + onCancel={() => { + setEditingAnswer(false); + setAnswerValue(currentDataset.answer); + }} + dataset={currentDataset} + onOptimize={handleOpenOptimizeDialog} + tokenCount={answerTokens} + optimizing={optimizeDialog.loading} + /> + + setEditingCot(true)} + onChange={e => setCotValue(e.target.value)} + onSave={() => handleSave('cot', cotValue)} + dataset={currentDataset} + onCancel={() => { + setEditingCot(false); + setCotValue(currentDataset.cot || ''); + }} + tokenCount={cotTokens} + /> + + + + {/* 右侧固定侧边栏 */} + + {/* 数据集元数据信息 */} + + + {/* 评分、标签、备注区域 */} + { + // 更新成功后刷新数据,保持页面状态同步 + // 这里可以调用 useDatasetDetails 的刷新逻辑 + }} + currentDataset={currentDataset} + /> + + + + {/* 消息提示 */} + setSnackbar(prev => ({ ...prev, open: false }))} + anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} + > + setSnackbar(prev => ({ ...prev, open: false }))} + severity={snackbar.severity} + sx={{ width: '100%' }} + > + {snackbar.message} + + + + {/* AI优化对话框 */} + + + {/* 文本块详情对话框 */} + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/datasets/[datasetId]/useDatasetDetails.js b/easy-dataset-main/app/projects/[projectId]/datasets/[datasetId]/useDatasetDetails.js new file mode 100644 index 0000000..1e2ed89 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/datasets/[datasetId]/useDatasetDetails.js @@ -0,0 +1,471 @@ +'use client'; + +import { useState, useEffect, useRef, useCallback } from 'react'; +import { useRouter } from 'next/navigation'; +import { useAtomValue } from 'jotai/index'; +import { selectedModelInfoAtom } from '@/lib/store'; +import axios from 'axios'; +import { toast } from 'sonner'; +import i18n from '@/lib/i18n'; + +/** + * 数据集详情页面业务逻辑 Hook + */ +export default function useDatasetDetails(projectId, datasetId) { + const router = useRouter(); + const [datasets, setDatasets] = useState([]); + const [currentDataset, setCurrentDataset] = useState(null); + const [loading, setLoading] = useState(true); + const [editingAnswer, setEditingAnswer] = useState(false); + const [editingCot, setEditingCot] = useState(false); + const [editingQuestion, setEditingQuestion] = useState(false); + const [answerValue, setAnswerValue] = useState(''); + const [cotValue, setCotValue] = useState(''); + const [questionValue, setQuestionValue] = useState(''); + const [snackbar, setSnackbar] = useState({ + open: false, + message: '', + severity: 'success' + }); + const [confirming, setConfirming] = useState(false); + const [unconfirming, setUnconfirming] = useState(false); + const [optimizeDialog, setOptimizeDialog] = useState({ + open: false, + loading: false + }); + const [viewDialogOpen, setViewDialogOpen] = useState(false); + const [viewChunk, setViewChunk] = useState(null); + const [datasetsAllCount, setDatasetsAllCount] = useState(0); + const [datasetsConfirmCount, setDatasetsConfirmCount] = useState(0); + const [answerTokens, setAnswerTokens] = useState(0); + const [cotTokens, setCotTokens] = useState(0); + const model = useAtomValue(selectedModelInfoAtom); + const [shortcutsEnabled, setShortcutsEnabled] = useState(() => { + const storedValue = localStorage.getItem('shortcutsEnabled'); + return storedValue !== null ? storedValue === 'true' : false; + }); + + // 输入环境判断,避免在输入框/可编辑区域误触快捷键 + const isEditableTarget = el => { + if (!el) return false; + const tag = el.tagName?.toLowerCase(); + if (tag && ['input', 'textarea', 'select'].includes(tag)) return true; + if (el.isContentEditable) return true; + // 兼容嵌套的可编辑区域与常见富文本编辑器 + return !!el.closest?.('[contenteditable="true"], .ProseMirror, .ql-editor'); + }; + + // 简单节流,避免连续触发 + const lastShortcutRef = useRef(0); + + // 异步获取Token数量 + const fetchTokenCount = async () => { + try { + const response = await fetch(`/api/projects/${projectId}/datasets/${datasetId}/token-count`); + if (response.ok) { + const data = await response.json(); + if (data.answerTokens !== undefined) { + setAnswerTokens(data.answerTokens); + } + if (data.cotTokens !== undefined) { + setCotTokens(data.cotTokens); + } + } + } catch (error) { + console.error('获取Token数量失败:', error); + // Token加载失败不阻塞主界面或显示错误提示 + } + }; + + // 获取数据集详情 + const fetchDatasets = async () => { + try { + const response = await fetch(`/api/projects/${projectId}/datasets/${datasetId}`); + if (!response.ok) throw new Error('获取数据集详情失败'); + const data = await response.json(); + setCurrentDataset(data.datasets); + setCotValue(data.datasets?.cot); + setAnswerValue(data.datasets?.answer); + setQuestionValue(data.datasets?.question); + setDatasetsAllCount(data.total); + setDatasetsConfirmCount(data.confirmedCount); + + // 数据加载完成后,异步获取Token数量 + fetchTokenCount(); + } catch (error) { + setSnackbar({ + open: true, + message: error.message, + severity: 'error' + }); + } finally { + setLoading(false); + } + }; + + // 确认并保存数据集 + const handleConfirm = async () => { + try { + setConfirming(true); + const response = await fetch(`/api/projects/${projectId}/datasets?id=${datasetId}`, { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + confirmed: true + }) + }); + + if (!response.ok) { + throw new Error('操作失败'); + } + + setCurrentDataset(prev => ({ ...prev, confirmed: true })); + + setSnackbar({ + open: true, + message: '操作成功', + severity: 'success' + }); + + // 导航到下一个数据集 + handleNavigate('next'); + } catch (error) { + setSnackbar({ + open: true, + message: error.message || '操作失败', + severity: 'error' + }); + } finally { + setConfirming(false); + } + }; + + // 取消确认数据集 + const handleUnconfirm = async () => { + try { + setUnconfirming(true); + const response = await fetch(`/api/projects/${projectId}/datasets?id=${datasetId}`, { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + confirmed: false + }) + }); + + if (!response.ok) { + throw new Error('操作失败'); + } + + setCurrentDataset(prev => ({ ...prev, confirmed: false })); + + setSnackbar({ + open: true, + message: '已取消确认', + severity: 'success' + }); + } catch (error) { + setSnackbar({ + open: true, + message: error.message || '取消确认失败', + severity: 'error' + }); + } finally { + setUnconfirming(false); + } + }; + + // 导航到其他数据集 + const handleNavigate = async direction => { + const response = await axios.get(`/api/projects/${projectId}/datasets/${datasetId}?operateType=${direction}`); + if (response.data) { + router.push(`/projects/${projectId}/datasets/${response.data.id}`); + } else { + toast.warning(`已经是${direction === 'next' ? '最后' : '第'}一条数据了`); + } + }; + + // 保存编辑 + const handleSave = async (field, value) => { + try { + const response = await fetch(`/api/projects/${projectId}/datasets?id=${datasetId}`, { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + [field]: value + }) + }); + + if (!response.ok) { + throw new Error('保存失败'); + } + + const data = await response.json(); + setCurrentDataset(prev => ({ ...prev, [field]: value })); + + setSnackbar({ + open: true, + message: '保存成功', + severity: 'success' + }); + + // 重置编辑状态 + if (field === 'answer') setEditingAnswer(false); + if (field === 'cot') setEditingCot(false); + if (field === 'question') setEditingQuestion(false); + } catch (error) { + setSnackbar({ + open: true, + message: error.message || '保存失败', + severity: 'error' + }); + } + }; + + // 删除数据集 + const handleDelete = async () => { + if (!confirm('确定要删除这条数据吗?此操作不可撤销。')) return; + + try { + // 尝试获取下一个数据集,在删除前先确保有可导航的目标 + const nextResponse = await axios.get(`/api/projects/${projectId}/datasets/${datasetId}?operateType=next`); + const hasNextDataset = !!nextResponse.data; + const nextDatasetId = hasNextDataset ? nextResponse.data.id : null; + + // 删除当前数据集 + const deleteResponse = await fetch(`/api/projects/${projectId}/datasets?id=${datasetId}`, { + method: 'DELETE' + }); + + if (!deleteResponse.ok) { + throw new Error('删除失败'); + } + + // 导航逻辑:有下一个就跳转下一个,没有则返回列表页 + if (hasNextDataset) { + router.push(`/projects/${projectId}/datasets/${nextDatasetId}`); + } else { + // 没有更多数据集,返回列表页面 + router.push(`/projects/${projectId}/datasets`); + } + + toast.success('删除成功'); + } catch (error) { + setSnackbar({ + open: true, + message: error.message || '删除失败', + severity: 'error' + }); + } + }; + + // 优化对话框相关操作 + const handleOpenOptimizeDialog = () => { + setOptimizeDialog({ + open: true, + loading: false + }); + }; + + const handleCloseOptimizeDialog = () => { + setOptimizeDialog(prev => { + // 如果正在优化,不允许关闭 + if (prev.loading) { + return prev; + } + return { + open: false, + loading: false + }; + }); + }; + + // 优化操作 + const handleOptimize = async advice => { + if (!model) { + setSnackbar({ + open: true, + message: '请先选择模型,可以在顶部导航栏选择', + severity: 'error' + }); + return; + } + + // 立即关闭对话框,并设置优化中状态 + setOptimizeDialog(prev => { + const newState = { + open: false, + loading: true + }; + return newState; + }); + + toast.info('已开始优化,请稍候...'); + + // 异步后台处理,不等待结果 + (async () => { + try { + const language = i18n.language === 'zh-CN' ? '中文' : 'en'; + const response = await fetch(`/api/projects/${projectId}/datasets/optimize`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + datasetId, + model, + advice, + language + }) + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.error || '优化失败'); + } + + // 优化成功后,重新查询数据以获取最新状态 + await fetchDatasets(); + // 优化可能改变了文本内容,重新获取Token计数 + fetchTokenCount(); + + toast.success('AI智能优化成功'); + } catch (error) { + toast.error(error.message); + } finally { + setOptimizeDialog({ + open: false, + loading: false + }); + } + })(); + }; + + // 查看文本块详情 + const handleViewChunk = async chunkContent => { + try { + setViewChunk(chunkContent); + setViewDialogOpen(true); + } catch (error) { + console.error('查看文本块出错', error); + setSnackbar({ + open: true, + message: error.message, + severity: 'error' + }); + setViewDialogOpen(false); + } + }; + + // 关闭文本块详情对话框 + const handleCloseViewDialog = () => { + setViewDialogOpen(false); + }; + + // 初始化和快捷键事件 + useEffect(() => { + fetchDatasets(); + }, [projectId, datasetId]); + + // 快捷键状态变化 + useEffect(() => { + localStorage.setItem('shortcutsEnabled', shortcutsEnabled); + }, [shortcutsEnabled]); + + // 监听键盘事件 + useEffect(() => { + const handleKeyDown = event => { + if (!shortcutsEnabled) return; + + // 在输入框或可编辑区域时不触发 + const activeEl = typeof document !== 'undefined' ? document.activeElement : null; + if (isEditableTarget(event.target) || isEditableTarget(activeEl)) { + return; + } + + // 仅要求 Shift 修饰键,降低误触且更简单 + if (!event.shiftKey) return; + + // 简单节流,过滤极短时间内重复触发 + const now = Date.now(); + if (now - (lastShortcutRef.current || 0) < 250) { + return; + } + lastShortcutRef.current = now; + + switch (event.key) { + case 'ArrowLeft': // 上一个(Shift + ArrowLeft) + event.preventDefault(); + handleNavigate('prev'); + break; + case 'ArrowRight': // 下一个(Shift + ArrowRight) + event.preventDefault(); + handleNavigate('next'); + break; + case 'y': // 确认(Shift + Y) + case 'Y': + if (!confirming && currentDataset && !currentDataset.confirmed) { + event.preventDefault(); + handleConfirm(); + } + break; + case 'd': // 删除(Shift + D) + case 'D': + event.preventDefault(); + handleDelete(); + break; + default: + break; + } + }; + window.addEventListener('keydown', handleKeyDown); + return () => { + window.removeEventListener('keydown', handleKeyDown); + }; + }, [shortcutsEnabled, confirming, currentDataset]); + + return { + loading, + currentDataset, + answerValue, + cotValue, + questionValue, + editingAnswer, + editingCot, + editingQuestion, + confirming, + unconfirming, + snackbar, + optimizeDialog, + viewDialogOpen, + viewChunk, + datasetsAllCount, + datasetsConfirmCount, + answerTokens, + cotTokens, + shortcutsEnabled, + setShortcutsEnabled, + setSnackbar, + setAnswerValue, + setCotValue, + setQuestionValue, + setEditingAnswer, + setEditingCot, + setEditingQuestion, + handleNavigate, + handleConfirm, + handleUnconfirm, + handleSave, + handleDelete, + handleOpenOptimizeDialog, + handleCloseOptimizeDialog, + handleOptimize, + handleViewChunk, + handleCloseViewDialog + }; +} diff --git a/easy-dataset-main/app/projects/[projectId]/datasets/components/ActionBar.js b/easy-dataset-main/app/projects/[projectId]/datasets/components/ActionBar.js new file mode 100644 index 0000000..7209eaf --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/datasets/components/ActionBar.js @@ -0,0 +1,33 @@ +'use client'; + +import { Box, Button } from '@mui/material'; +import AssessmentIcon from '@mui/icons-material/Assessment'; +import FileUploadIcon from '@mui/icons-material/FileUpload'; +import FileDownloadIcon from '@mui/icons-material/FileDownload'; +import { useTranslation } from 'react-i18next'; + +const ActionBar = ({ onBatchEvaluate, onImport, onExport, batchEvaluating = false }) => { + const { t } = useTranslation(); + + return ( + + + + + + ); +}; + +export default ActionBar; diff --git a/easy-dataset-main/app/projects/[projectId]/datasets/components/DatasetList.js b/easy-dataset-main/app/projects/[projectId]/datasets/components/DatasetList.js new file mode 100644 index 0000000..915b60d --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/datasets/components/DatasetList.js @@ -0,0 +1,422 @@ +'use client'; + +import { + Box, + Typography, + IconButton, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Chip, + Divider, + useTheme, + alpha, + Tooltip, + Checkbox, + TablePagination, + TextField, + Card, + CircularProgress +} from '@mui/material'; +import DeleteIcon from '@mui/icons-material/Delete'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import AssessmentIcon from '@mui/icons-material/Assessment'; +import StarIcon from '@mui/icons-material/Star'; +import { useTranslation } from 'react-i18next'; +import { getRatingConfigI18n, formatScore } from '@/components/datasets/utils/ratingUtils'; + +// 数据集列表组件 +const DatasetList = ({ + datasets, + onViewDetails, + onDelete, + onEvaluate, + page, + rowsPerPage, + onPageChange, + onRowsPerPageChange, + total, + selectedIds, + onSelectAll, + onSelectItem, + evaluatingIds = [], + loading = false +}) => { + const theme = useTheme(); + const { t } = useTranslation(); + + const bgColor = theme.palette.mode === 'dark' ? theme.palette.primary.dark : theme.palette.primary.light; + const color = + theme.palette.mode === 'dark' + ? theme.palette.getContrastText(theme.palette.primary.main) + : theme.palette.getContrastText(theme.palette.primary.contrastText); + + const RatingChip = ({ score }) => { + const config = getRatingConfigI18n(score, t); + return ( + } + label={`${formatScore(score)} ${config.label}`} + size="small" + sx={{ + backgroundColor: config.backgroundColor, + color: config.color, + fontWeight: 'medium', + '& .MuiChip-icon': { + color: config.color + } + }} + /> + ); + }; + + return ( + + + + + + + + 0 && selectedIds.length < total} + checked={total > 0 && selectedIds.length === total} + onChange={onSelectAll} + /> + + + {t('datasets.question')} + + + {t('datasets.rating', '评分')} + + + {t('datasets.model')} + + + {t('datasets.domainTag')} + + + {t('datasets.createdAt')} + + + {t('common.actions')} + + + + + {datasets.map((dataset, index) => ( + <> + onViewDetails(dataset.id)} + > + + { + e.stopPropagation(); + onSelectItem(dataset.id); + }} + onClick={e => e.stopPropagation()} + /> + + + + + {dataset.question} + + {dataset.confirmed && ( + + )} + + + + + + + + + + {dataset.questionLabel ? ( + + ) : ( + + {t('datasets.noTag')} + + )} + + + + {new Date(dataset.createAt).toLocaleDateString('zh-CN')} + + + + + + { + e.stopPropagation(); + onViewDetails(dataset.id); + }} + sx={{ + color: theme.palette.primary.main, + '&:hover': { backgroundColor: alpha(theme.palette.primary.main, 0.1) } + }} + > + + + + + { + e.stopPropagation(); + onEvaluate && onEvaluate(dataset); + }} + sx={{ + color: theme.palette.secondary.main, + '&:hover': { backgroundColor: alpha(theme.palette.secondary.main, 0.1) } + }} + > + {evaluatingIds.includes(dataset.id) ? ( + + ) : ( + + )} + + + + { + e.stopPropagation(); + onDelete(dataset); + }} + sx={{ + color: theme.palette.error.main, + '&:hover': { backgroundColor: alpha(theme.palette.error.main, 0.1) } + }} + > + + + + + + + + ))} + {datasets.length === 0 && ( + + + + {t('datasets.noData')} + + + + )} + +
+
+ {loading && ( + + + + {t('datasets.loading')} + + + )} +
+ + + t('datasets.pagination', { from, to, count })} + sx={{ + '.MuiTablePagination-selectLabel, .MuiTablePagination-displayedRows': { + fontWeight: 'medium' + }, + border: 'none' + }} + /> + + {t('common.jumpTo')}: + { + if (e.key === 'Enter') { + const pageNum = parseInt(e.target.value, 10); + if (pageNum >= 1 && pageNum <= Math.ceil(total / rowsPerPage)) { + onPageChange(null, pageNum - 1); + e.target.value = ''; + } + } + }} + /> + + +
+ ); +}; + +export default DatasetList; diff --git a/easy-dataset-main/app/projects/[projectId]/datasets/components/DeleteConfirmDialog.js b/easy-dataset-main/app/projects/[projectId]/datasets/components/DeleteConfirmDialog.js new file mode 100644 index 0000000..e392615 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/datasets/components/DeleteConfirmDialog.js @@ -0,0 +1,105 @@ +'use client'; + +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Typography, + Paper, + Box, + LinearProgress, + Button, + useTheme, + alpha +} from '@mui/material'; +import { useTranslation } from 'react-i18next'; + +const DeleteConfirmDialog = ({ open, datasets, onClose, onConfirm, batch, progress, deleting }) => { + const theme = useTheme(); + const { t } = useTranslation(); + const dataset = datasets?.[0]; + + return ( + + + + {t('common.confirmDelete')} + + + + + {batch + ? t('datasets.batchconfirmDeleteMessage', { + count: datasets.length + }) + : t('common.confirmDeleteDataSet')} + + {batch ? ( + '' + ) : ( + + + {t('datasets.question')}: + + {dataset?.question} + + )} + {deleting && progress ? ( + + + + {progress.percentage}% + + + + + + + {t('datasets.deletingProgress', '正在删除 {{completed}}/{{total}} 个数据集...', { + completed: progress.completed, + total: progress.total + })} + + + ) : null} + + + + + + + ); +}; + +export default DeleteConfirmDialog; diff --git a/easy-dataset-main/app/projects/[projectId]/datasets/components/FilterDialog.js b/easy-dataset-main/app/projects/[projectId]/datasets/components/FilterDialog.js new file mode 100644 index 0000000..1158431 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/datasets/components/FilterDialog.js @@ -0,0 +1,198 @@ +'use client'; + +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Box, + Typography, + Select, + MenuItem, + Slider, + TextField, + Button, + InputAdornment +} from '@mui/material'; +import SearchIcon from '@mui/icons-material/Search'; +import { useTranslation } from 'react-i18next'; + +const FilterDialog = ({ + open, + onClose, + filterConfirmed, + filterHasCot, + filterIsDistill, + filterScoreRange, + filterCustomTag, + filterNoteKeyword, + filterChunkName, + availableTags, + onFilterConfirmedChange, + onFilterHasCotChange, + onFilterIsDistillChange, + onFilterScoreRangeChange, + onFilterCustomTagChange, + onFilterNoteKeywordChange, + onFilterChunkNameChange, + onResetFilters, + onApplyFilters +}) => { + const { t } = useTranslation(); + + return ( + + {t('datasets.filtersTitle')} + + + + {t('datasets.filterConfirmationStatus')} + + + + + + + {t('datasets.filterCotStatus')} + + + + + + + {t('datasets.filterDistill')} + + + + + + + {t('datasets.filterScoreRange')} + + + onFilterScoreRangeChange(newValue)} + valueLabelDisplay="auto" + min={0} + max={5} + step={0.5} + marks={[ + { value: 0, label: '0' }, + { value: 2.5, label: '2.5' }, + { value: 5, label: '5' } + ]} + sx={{ mt: 1 }} + /> + + {t('datasets.scoreRange', '{{min}} - {{max}} 分', { + min: filterScoreRange[0], + max: filterScoreRange[1] + })} + + + + + + + {t('datasets.filterCustomTag')} + + + + + + + {t('datasets.filterNoteKeyword')} + + onFilterNoteKeywordChange(e.target.value)} + placeholder={t('datasets.filterNoteKeywordPlaceholder')} + fullWidth + size="small" + sx={{ mt: 1 }} + InputProps={{ + startAdornment: ( + + + + ) + }} + /> + + + + + {t('datasets.filterChunkName')} + + onFilterChunkNameChange(e.target.value)} + placeholder={t('datasets.filterChunkNamePlaceholder')} + fullWidth + size="small" + sx={{ mt: 1 }} + InputProps={{ + startAdornment: ( + + + + ) + }} + /> + + + + + + + + ); +}; + +export default FilterDialog; diff --git a/easy-dataset-main/app/projects/[projectId]/datasets/components/SearchBar.js b/easy-dataset-main/app/projects/[projectId]/datasets/components/SearchBar.js new file mode 100644 index 0000000..b111e94 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/datasets/components/SearchBar.js @@ -0,0 +1,68 @@ +'use client'; + +import { Box, Paper, IconButton, InputBase, Select, MenuItem, Button, Badge } from '@mui/material'; +import SearchIcon from '@mui/icons-material/Search'; +import FilterListIcon from '@mui/icons-material/FilterList'; +import { useTranslation } from 'react-i18next'; + +const SearchBar = ({ + searchQuery, + searchField, + onSearchQueryChange, + onSearchFieldChange, + onMoreFiltersClick, + activeFilterCount = 0 +}) => { + const { t } = useTranslation(); + + return ( + + + + + + onSearchQueryChange(e.target.value)} + endAdornment={ + + } + /> + + + + + + ); +}; + +export default SearchBar; diff --git a/easy-dataset-main/app/projects/[projectId]/datasets/hooks/useDatasetEvaluation.js b/easy-dataset-main/app/projects/[projectId]/datasets/hooks/useDatasetEvaluation.js new file mode 100644 index 0000000..5ae655c --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/datasets/hooks/useDatasetEvaluation.js @@ -0,0 +1,165 @@ +'use client'; + +import { useState } from 'react'; +import { useRouter } from 'next/navigation'; +import { useTranslation } from 'react-i18next'; +import { toast } from 'sonner'; +import { useAtomValue } from 'jotai'; +import { selectedModelInfoAtom } from '@/lib/store'; + +/** + * 数据集评估相关的自定义 Hook + * 封装单个评估和批量评估的逻辑 + */ +const useDatasetEvaluation = (projectId, onEvaluationComplete) => { + const router = useRouter(); + const { t } = useTranslation(); + const model = useAtomValue(selectedModelInfoAtom); + + // 评估状态管理 + const [evaluatingIds, setEvaluatingIds] = useState([]); + const [batchEvaluating, setBatchEvaluating] = useState(false); + + /** + * 检查模型是否已配置 + */ + const checkModelConfiguration = () => { + if (!model || !model.modelName) { + toast.error(t('datasets.selectModelFirst', '请先选择模型')); + return false; + } + return true; + }; + + /** + * 处理单个数据集评估 + * @param {Object} dataset - 要评估的数据集对象 + */ + const handleEvaluateDataset = async dataset => { + // 检查模型配置 + if (!checkModelConfiguration()) { + return; + } + + try { + // 添加到评估中的ID列表 + setEvaluatingIds(prev => [...prev, dataset.id]); + + // 调用评估接口 + const evaluateResponse = await fetch(`/api/projects/${projectId}/datasets/${dataset.id}/evaluate`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + model, + language: 'zh-CN' + }) + }); + + const result = await evaluateResponse.json(); + + if (result.success) { + toast.success( + t('datasets.evaluateSuccess', '评估完成!评分:{{score}}/5', { + score: result.data.score + }) + ); + + // 调用回调函数通知评估完成(通常用于刷新数据列表) + if (onEvaluationComplete) { + await onEvaluationComplete(); + } + } else { + toast.error(result.message || t('datasets.evaluateFailed', '评估失败')); + } + } catch (error) { + console.error('评估失败:', error); + toast.error( + t('datasets.evaluateError', '评估失败: {{error}}', { + error: error.message + }) + ); + } finally { + // 从评估中的ID列表移除 + setEvaluatingIds(prev => prev.filter(id => id !== dataset.id)); + } + }; + + /** + * 处理批量评估 + */ + const handleBatchEvaluate = async () => { + // 检查模型配置 + if (!checkModelConfiguration()) { + return; + } + + try { + setBatchEvaluating(true); + + // 调用批量评估接口 + const response = await fetch(`/api/projects/${projectId}/datasets/batch-evaluate`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + model, + language: 'zh-CN' + }) + }); + + const result = await response.json(); + + if (result.success) { + toast.success(t('datasets.batchEvaluateStarted', '批量评估任务已启动,将在后台进行处理')); + // 跳转到任务页面查看进度 + router.push(`/projects/${projectId}/tasks`); + } else { + toast.error(result.message || t('datasets.batchEvaluateStartFailed', '启动批量评估失败')); + } + } catch (error) { + console.error('批量评估失败:', error); + toast.error( + t('datasets.batchEvaluateFailed', '批量评估失败: {{error}}', { + error: error.message + }) + ); + } finally { + setBatchEvaluating(false); + } + }; + + /** + * 检查指定数据集是否正在评估中 + * @param {string} datasetId - 数据集ID + * @returns {boolean} 是否正在评估中 + */ + const isEvaluating = datasetId => { + return evaluatingIds.includes(datasetId); + }; + + /** + * 获取当前正在评估的数据集数量 + * @returns {number} 正在评估的数据集数量 + */ + const getEvaluatingCount = () => { + return evaluatingIds.length; + }; + + return { + // 状态 + evaluatingIds, + batchEvaluating, + + // 方法 + handleEvaluateDataset, + handleBatchEvaluate, + + // 工具方法 + isEvaluating, + getEvaluatingCount, + + // 模型信息(便于组件使用) + model + }; +}; + +export default useDatasetEvaluation; diff --git a/easy-dataset-main/app/projects/[projectId]/datasets/hooks/useDatasetExport.js b/easy-dataset-main/app/projects/[projectId]/datasets/hooks/useDatasetExport.js new file mode 100644 index 0000000..cdb4874 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/datasets/hooks/useDatasetExport.js @@ -0,0 +1,487 @@ +'use client'; + +import { useTranslation } from 'react-i18next'; +import { toast } from 'sonner'; +import axios from 'axios'; + +const useDatasetExport = projectId => { + const { t } = useTranslation(); + + // 优化的流式导出 - 使用 WritableStream 避免内存溢出 + const exportDatasetsStreaming = async (exportOptions, onProgress) => { + try { + const batchSize = exportOptions.batchSize || 1000; + let offset = 0; + let hasMore = true; + let totalProcessed = 0; + let isFirstBatch = true; + + // 确定文件格式 + const fileFormat = exportOptions.fileFormat || 'json'; + const formatType = exportOptions.formatType || 'alpaca'; + + // 生成文件名 + const formatSuffixMap = { + alpaca: 'alpaca', + multilingualthinking: 'multilingual-thinking', + sharegpt: 'sharegpt', + custom: 'custom' + }; + const formatSuffix = formatSuffixMap[formatType] || formatType || 'export'; + const balanceSuffix = exportOptions.balanceMode ? '-balanced' : ''; + const dateStr = new Date().toISOString().slice(0, 10); + const fileName = `datasets-${projectId}-${formatSuffix}${balanceSuffix}-${dateStr}.${fileFormat}`; + + // 创建可写流 + let fileStream; + let writer; + + try { + // 使用 showSaveFilePicker API(现代浏览器) + if (window.showSaveFilePicker) { + const handle = await window.showSaveFilePicker({ + suggestedName: fileName, + types: [ + { + description: 'Dataset File', + accept: { + 'application/json': [`.${fileFormat}`] + } + } + ] + }); + fileStream = await handle.createWritable(); + } else { + // 降级方案:使用内存缓冲区(但分块处理) + fileStream = null; + } + } catch (err) { + // 用户取消或不支持,使用降级方案 + fileStream = null; + } + + // 如果不支持流式写入,使用分块累积方案 + let chunks = []; + let chunkCount = 0; + const MAX_CHUNKS_IN_MEMORY = 5; // 最多在内存中保留5批数据 + + // 写入文件头(JSON数组开始或CSV表头) + if (fileFormat === 'json') { + if (fileStream) { + await fileStream.write('[\n'); + } else { + chunks.push('[\n'); + } + } else if (fileFormat === 'csv') { + // 写入CSV表头 + const headers = getCSVHeaders(formatType, exportOptions); + const headerLine = headers.join(',') + '\n'; + if (fileStream) { + await fileStream.write(headerLine); + } else { + chunks.push(headerLine); + } + } + + // 分批获取和写入数据 + while (hasMore) { + const apiUrl = `/api/projects/${projectId}/datasets/export`; + const requestBody = { + batchMode: true, + offset: offset, + batchSize: batchSize + }; + + // 如果有选中的数据集 ID,传递 ID 列表 + if (exportOptions.selectedIds && exportOptions.selectedIds.length > 0) { + requestBody.selectedIds = exportOptions.selectedIds; + } else if (exportOptions.confirmedOnly) { + requestBody.status = 'confirmed'; + } + + // 检查是否是平衡导出模式 + if (exportOptions.balanceMode && exportOptions.balanceConfig) { + requestBody.balanceMode = true; + requestBody.balanceConfig = exportOptions.balanceConfig; + } + + const response = await axios.post(apiUrl, requestBody); + const batchResult = response.data; + + // 如果需要包含文本块内容,批量查询并填充 + if (exportOptions.customFields?.includeChunk && batchResult.data.length > 0) { + const chunkNames = batchResult.data.map(item => item.chunkName).filter(name => name); + + if (chunkNames.length > 0) { + try { + const chunkResponse = await axios.post(`/api/projects/${projectId}/chunks/batch-content`, { + chunkNames + }); + const chunkContentMap = chunkResponse.data; + + batchResult.data.forEach(item => { + if (item.chunkName && chunkContentMap[item.chunkName]) { + item.chunkContent = chunkContentMap[item.chunkName]; + } + }); + } catch (chunkError) { + console.error('获取文本块内容失败:', chunkError); + } + } + } + + // 转换当前批次数据 + const formattedBatch = formatDataBatch(batchResult.data, exportOptions); + + // 写入当前批次 + if (fileFormat === 'json') { + // 保持与原逻辑一致:JSON 导出为“格式化后的 JSON 数组”(2空格缩进) + // 每条记录单独 stringify + 缩进,并在数组级别拼接,避免一次性 stringify 全量数据导致内存暴涨 + const batchContent = formattedBatch + .map(item => { + const pretty = JSON.stringify(item, null, 2); + // 将对象的每一行整体再缩进 2 个空格,以符合数组元素缩进 + return ' ' + pretty.replace(/\n/g, '\n '); + }) + .join(',\n'); + + const content = isFirstBatch ? batchContent : ',\n' + batchContent; + + if (fileStream) { + await fileStream.write(content); + } else { + chunks.push(content); + chunkCount++; + } + } else if (fileFormat === 'jsonl') { + const batchContent = formattedBatch.map(item => JSON.stringify(item)).join('\n') + '\n'; + + if (fileStream) { + await fileStream.write(batchContent); + } else { + chunks.push(batchContent); + chunkCount++; + } + } else if (fileFormat === 'csv') { + const batchContent = formatBatchToCSV(formattedBatch, formatType, exportOptions); + + if (fileStream) { + await fileStream.write(batchContent); + } else { + chunks.push(batchContent); + chunkCount++; + } + } + + // 如果使用内存缓冲且累积了足够多的块,触发部分下载 + if (!fileStream && chunkCount >= MAX_CHUNKS_IN_MEMORY) { + // 这里我们仍然需要等到最后才能下载,但至少限制了内存使用 + // 可以考虑使用 Blob 分片 + } + + hasMore = batchResult.hasMore; + offset = batchResult.offset; + totalProcessed += batchResult.data.length; + isFirstBatch = false; + + // 通知进度更新 + if (onProgress) { + onProgress({ + processed: totalProcessed, + currentBatch: batchResult.data.length, + hasMore + }); + } + + // 避免过快请求 + if (hasMore) { + await new Promise(resolve => setTimeout(resolve, 50)); + } + } + + // 写入文件尾 + if (fileFormat === 'json') { + if (fileStream) { + await fileStream.write('\n]\n'); + await fileStream.close(); + } else { + chunks.push('\n]\n'); + } + } else { + if (fileStream) { + await fileStream.close(); + } + } + + // 如果使用内存缓冲方案,现在触发下载 + if (!fileStream) { + downloadFromChunks(chunks, fileName); + } + + toast.success(t('datasets.exportSuccess')); + return true; + } catch (error) { + console.error('Streaming export failed:', error); + toast.error(error.message || t('datasets.exportFailed')); + return false; + } + }; + + // 从内存块下载文件(优化版本,使用 Blob 流) + const downloadFromChunks = (chunks, fileName) => { + // 使用 Blob 构造函数,它会自动处理大数据 + const blob = new Blob(chunks, { type: 'application/octet-stream' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = fileName; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + + // 延迟释放 URL,确保下载开始 + setTimeout(() => URL.revokeObjectURL(url), 1000); + }; + + // 获取CSV表头 + const getCSVHeaders = (formatType, exportOptions) => { + if (formatType === 'alpaca') { + return ['instruction', 'input', 'output', 'system']; + } else if (formatType === 'sharegpt') { + return ['messages']; + } else if (formatType === 'multilingualthinking') { + return ['reasoning_language', 'developer', 'user', 'analysis', 'final', 'messages']; + } else if (formatType === 'custom') { + const { questionField, answerField, cotField, includeLabels, includeChunk, questionOnly } = + exportOptions.customFields; + const headers = [questionField]; + if (!questionOnly) { + headers.push(answerField); + if (exportOptions.includeCOT && cotField) { + headers.push(cotField); + } + } + if (includeLabels) headers.push('label'); + if (includeChunk) headers.push('chunk'); + return headers; + } + return []; + }; + + // 格式化数据批次 + const formatDataBatch = (dataBatch, exportOptions) => { + const formatType = exportOptions.formatType || 'alpaca'; + + if (formatType === 'alpaca') { + if (exportOptions.alpacaFieldType === 'instruction') { + return dataBatch.map(({ question, answer, cot }) => ({ + instruction: question, + input: '', + output: cot && exportOptions.includeCOT ? `${cot}\n${answer}` : answer, + system: exportOptions.systemPrompt || '' + })); + } else { + return dataBatch.map(({ question, answer, cot }) => ({ + instruction: exportOptions.customInstruction || '', + input: question, + output: cot && exportOptions.includeCOT ? `${cot}\n${answer}` : answer, + system: exportOptions.systemPrompt || '' + })); + } + } else if (formatType === 'sharegpt') { + return dataBatch.map(({ question, answer, cot }) => { + const messages = []; + if (exportOptions.systemPrompt) { + messages.push({ role: 'system', content: exportOptions.systemPrompt }); + } + messages.push({ + role: 'user', + content: question + }); + messages.push({ + role: 'assistant', + content: cot && exportOptions.includeCOT ? `${cot}\n${answer}` : answer + }); + return { messages }; + }); + } else if (formatType === 'multilingualthinking') { + return dataBatch.map(({ question, answer, cot }) => ({ + reasoning_language: exportOptions.reasoningLanguage || 'English', + developer: exportOptions.systemPrompt || '', + user: question, + analysis: exportOptions.includeCOT && cot ? cot : null, + final: answer, + messages: [ + { + content: exportOptions.systemPrompt || '', + role: 'system', + thinking: null + }, + { + content: question, + role: 'user', + thinking: null + }, + { + content: answer, + role: 'assistant', + thinking: exportOptions.includeCOT && cot ? cot : null + } + ] + })); + } else if (formatType === 'custom') { + const { questionField, answerField, cotField, includeLabels, includeChunk, questionOnly } = + exportOptions.customFields; + return dataBatch.map(({ question, answer, cot, questionLabel: labels, chunkContent }) => { + const item = { [questionField]: question }; + if (!questionOnly) { + item[answerField] = answer; + if (cot && exportOptions.includeCOT && cotField) { + item[cotField] = cot; + } + } + if (includeLabels && labels && labels.length > 0) { + item.label = labels.split(' ')[1]; + } + if (includeChunk && chunkContent) { + item.chunk = chunkContent; + } + return item; + }); + } + return dataBatch; + }; + + // 将批次格式化为CSV行 + const formatBatchToCSV = (formattedBatch, formatType, exportOptions) => { + const headers = getCSVHeaders(formatType, exportOptions); + return ( + formattedBatch + .map(item => { + return headers + .map(header => { + let field = item[header]?.toString() || ''; + // 对于复杂对象,转换为JSON字符串 + if (typeof item[header] === 'object') { + field = JSON.stringify(item[header]); + } + // CSV转义 + if (field.includes(',') || field.includes('\n') || field.includes('"')) { + field = `"${field.replace(/"/g, '""')}"`; + } + return field; + }) + .join(','); + }) + .join('\n') + '\n' + ); + }; + + // 处理和下载数据的通用函数(保留用于小数据量) + const processAndDownloadData = async (dataToExport, exportOptions) => { + const formattedData = formatDataBatch(dataToExport, exportOptions); + + let content; + let fileExtension; + const fileFormat = exportOptions.fileFormat || 'json'; + + if (fileFormat === 'jsonl') { + content = formattedData.map(item => JSON.stringify(item)).join('\n'); + fileExtension = 'jsonl'; + } else if (fileFormat === 'csv') { + const headers = getCSVHeaders(exportOptions.formatType, exportOptions); + const csvRows = [ + headers.join(','), + ...formattedData.map(item => + headers + .map(header => { + let field = item[header]?.toString() || ''; + if (typeof item[header] === 'object') { + field = JSON.stringify(item[header]); + } + if (field.includes(',') || field.includes('\n') || field.includes('"')) { + field = `"${field.replace(/"/g, '""')}"`; + } + return field; + }) + .join(',') + ) + ]; + content = csvRows.join('\n'); + fileExtension = 'csv'; + } else { + content = JSON.stringify(formattedData, null, 2); + fileExtension = 'json'; + } + + const blob = new Blob([content], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + + const formatSuffixMap = { + alpaca: 'alpaca', + multilingualthinking: 'multilingual-thinking', + sharegpt: 'sharegpt', + custom: 'custom' + }; + const formatSuffix = formatSuffixMap[exportOptions.formatType] || exportOptions.formatType || 'export'; + const balanceSuffix = exportOptions.balanceMode ? '-balanced' : ''; + const dateStr = new Date().toISOString().slice(0, 10); + a.download = `datasets-${projectId}-${formatSuffix}${balanceSuffix}-${dateStr}.${fileExtension}`; + + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + }; + + // 导出数据集(保持向后兼容的原有功能) + const exportDatasets = async exportOptions => { + try { + const apiUrl = `/api/projects/${projectId}/datasets/export`; + const requestBody = {}; + + if (exportOptions.selectedIds && exportOptions.selectedIds.length > 0) { + requestBody.selectedIds = exportOptions.selectedIds; + } else if (exportOptions.confirmedOnly) { + requestBody.status = 'confirmed'; + } + + if (exportOptions.balanceMode && exportOptions.balanceConfig) { + requestBody.balanceMode = true; + requestBody.balanceConfig = exportOptions.balanceConfig; + } + + const response = await axios.post(apiUrl, requestBody); + let dataToExport = response.data; + + await processAndDownloadData(dataToExport, exportOptions); + + toast.success(t('datasets.exportSuccess')); + return true; + } catch (error) { + toast.error(error.message); + return false; + } + }; + + // 导出平衡数据集 + const exportBalancedDataset = async exportOptions => { + const balancedOptions = { + ...exportOptions, + balanceMode: true, + balanceConfig: exportOptions.balanceConfig + }; + return await exportDatasets(balancedOptions); + }; + + return { + exportDatasets, + exportBalancedDataset, + exportDatasetsStreaming + }; +}; + +export default useDatasetExport; +export { useDatasetExport }; diff --git a/easy-dataset-main/app/projects/[projectId]/datasets/hooks/useDatasetFilters.js b/easy-dataset-main/app/projects/[projectId]/datasets/hooks/useDatasetFilters.js new file mode 100644 index 0000000..e114fb7 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/datasets/hooks/useDatasetFilters.js @@ -0,0 +1,171 @@ +'use client'; + +import { useState, useEffect } from 'react'; + +/** + * 数据集筛选条件持久化 Hook + * 负责筛选条件的保存、恢复和管理 + * @param {string} projectId - 项目ID + * @returns {Object} 筛选条件和相关方法 + */ +export function useDatasetFilters(projectId) { + const [filterConfirmed, setFilterConfirmed] = useState('all'); + const [filterHasCot, setFilterHasCot] = useState('all'); + const [filterIsDistill, setFilterIsDistill] = useState('all'); + const [filterScoreRange, setFilterScoreRange] = useState([0, 5]); + const [filterCustomTag, setFilterCustomTag] = useState(''); + const [filterNoteKeyword, setFilterNoteKeyword] = useState(''); + const [filterChunkName, setFilterChunkName] = useState(''); + const [searchQuery, setSearchQuery] = useState(''); + const [searchField, setSearchField] = useState('question'); + const [page, setPage] = useState(1); + const [rowsPerPage, setRowsPerPage] = useState(10); + const [isInitialized, setIsInitialized] = useState(false); + + // 从 localStorage 恢复筛选条件 + useEffect(() => { + if (typeof window !== 'undefined') { + try { + const savedFilters = localStorage.getItem(`datasets-filters-${projectId}`); + if (savedFilters) { + const filters = JSON.parse(savedFilters); + setFilterConfirmed(filters.filterConfirmed || 'all'); + setFilterHasCot(filters.filterHasCot || 'all'); + setFilterIsDistill(filters.filterIsDistill || 'all'); + setFilterScoreRange(filters.filterScoreRange || [0, 5]); + setFilterCustomTag(filters.filterCustomTag || ''); + setFilterNoteKeyword(filters.filterNoteKeyword || ''); + setFilterChunkName(filters.filterChunkName || ''); + setSearchQuery(filters.searchQuery || ''); + setSearchField(filters.searchField || 'question'); + setPage(filters.page || 1); + setRowsPerPage(filters.rowsPerPage || 10); + } + } catch (error) { + console.error('恢复筛选条件失败:', error); + } + setIsInitialized(true); + } + }, [projectId]); + + // 保存筛选条件到 localStorage + useEffect(() => { + if (typeof window !== 'undefined' && isInitialized) { + try { + const filters = { + filterConfirmed, + filterHasCot, + filterIsDistill, + filterScoreRange, + filterCustomTag, + filterNoteKeyword, + filterChunkName, + searchQuery, + searchField, + page, + rowsPerPage + }; + localStorage.setItem(`datasets-filters-${projectId}`, JSON.stringify(filters)); + } catch (error) { + console.error('保存筛选条件失败:', error); + } + } + }, [ + projectId, + filterConfirmed, + filterHasCot, + filterIsDistill, + filterScoreRange, + filterCustomTag, + filterNoteKeyword, + filterChunkName, + searchQuery, + searchField, + page, + rowsPerPage, + isInitialized + ]); + + /** + * 重置所有筛选条件为默认值 + */ + const resetFilters = () => { + setFilterConfirmed('all'); + setFilterHasCot('all'); + setFilterIsDistill('all'); + setFilterScoreRange([0, 5]); + setFilterCustomTag(''); + setFilterNoteKeyword(''); + setFilterChunkName(''); + setSearchQuery(''); + setSearchField('question'); + setPage(1); + setRowsPerPage(10); + }; + + /** + * 清除 localStorage 中的筛选条件 + */ + const clearSavedFilters = () => { + if (typeof window !== 'undefined') { + try { + localStorage.removeItem(`datasets-filters-${projectId}`); + } catch (error) { + console.error('清除筛选条件失败:', error); + } + } + }; + + /** + * 计算当前活跃的筛选条件数量 + * @returns {number} 活跃筛选条件的数量 + */ + const getActiveFilterCount = () => { + let count = 0; + + if (filterConfirmed !== 'all') count++; + if (filterHasCot !== 'all') count++; + if (filterIsDistill !== 'all') count++; + if (filterScoreRange[0] > 0 || filterScoreRange[1] < 5) count++; + if (filterCustomTag) count++; + if (filterNoteKeyword) count++; + if (filterChunkName) count++; + + return count; + }; + + return { + // 筛选条件状态 + filterConfirmed, + setFilterConfirmed, + filterHasCot, + setFilterHasCot, + filterIsDistill, + setFilterIsDistill, + filterScoreRange, + setFilterScoreRange, + filterCustomTag, + setFilterCustomTag, + filterNoteKeyword, + setFilterNoteKeyword, + filterChunkName, + setFilterChunkName, + searchQuery, + setSearchQuery, + searchField, + setSearchField, + // 分页状态 + page, + setPage, + rowsPerPage, + setRowsPerPage, + // 初始化状态 + isInitialized, + // 工具方法 + resetFilters, + clearSavedFilters, + getActiveFilterCount + }; +} + +export default useDatasetFilters; diff --git a/easy-dataset-main/app/projects/[projectId]/datasets/page.js b/easy-dataset-main/app/projects/[projectId]/datasets/page.js new file mode 100644 index 0000000..6504f50 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/datasets/page.js @@ -0,0 +1,596 @@ +'use client'; + +import { useState, useEffect, useCallback } from 'react'; +import { Container, Box, Typography, Button, Card, useTheme, alpha } from '@mui/material'; +import DeleteIcon from '@mui/icons-material/Delete'; +import { useRouter } from 'next/navigation'; +import ExportDatasetDialog from '@/components/ExportDatasetDialog'; +import ExportProgressDialog from '@/components/ExportProgressDialog'; +import ImportDatasetDialog from '@/components/datasets/ImportDatasetDialog'; +import { useTranslation } from 'react-i18next'; +import DatasetList from './components/DatasetList'; +import SearchBar from './components/SearchBar'; +import ActionBar from './components/ActionBar'; +import FilterDialog from './components/FilterDialog'; +import DeleteConfirmDialog from './components/DeleteConfirmDialog'; +import useDatasetExport from './hooks/useDatasetExport'; +import useDatasetEvaluation from './hooks/useDatasetEvaluation'; +import useDatasetFilters from './hooks/useDatasetFilters'; +import { processInParallel } from '@/lib/util/async'; +import axios from 'axios'; +import { useDebounce } from '@/hooks/useDebounce'; +import { toast } from 'sonner'; + +// 主页面组件 +export default function DatasetsPage({ params }) { + const { projectId } = params; + const router = useRouter(); + const theme = useTheme(); + const [datasets, setDatasets] = useState({ data: [], total: 0, confirmedCount: 0 }); + const [loading, setLoading] = useState(true); + const [deleteDialog, setDeleteDialog] = useState({ + open: false, + datasets: null, + batch: false, + deleting: false + }); + const [exportDialog, setExportDialog] = useState({ open: false }); + const [importDialog, setImportDialog] = useState({ open: false }); + const [selectedIds, setselectedIds] = useState([]); + const [availableTags, setAvailableTags] = useState([]); + const [filterDialogOpen, setFilterDialogOpen] = useState(false); + const { t } = useTranslation(); + + // 使用 useDatasetFilters Hook 管理筛选条件 + const { + filterConfirmed, + setFilterConfirmed, + filterHasCot, + setFilterHasCot, + filterIsDistill, + setFilterIsDistill, + filterScoreRange, + setFilterScoreRange, + filterCustomTag, + setFilterCustomTag, + filterNoteKeyword, + setFilterNoteKeyword, + filterChunkName, + setFilterChunkName, + searchQuery, + setSearchQuery, + searchField, + setSearchField, + page, + setPage, + rowsPerPage, + setRowsPerPage, + isInitialized, + getActiveFilterCount + } = useDatasetFilters(projectId); + + const debouncedSearchQuery = useDebounce(searchQuery); + // 删除进度状态 + const [deleteProgress, setDeteleProgress] = useState({ + total: 0, // 总删除问题数量 + completed: 0, // 已删除完成的数量 + percentage: 0 // 进度百分比 + }); + // 导出进度状态 + const [exportProgress, setExportProgress] = useState({ + show: false, // 是否显示进度 + processed: 0, // 已处理数量 + total: 0, // 总数量 + hasMore: true // 是否还有更多数据 + }); + + // 3. 添加打开导出对话框的处理函数 + const handleOpenExportDialog = () => { + setExportDialog({ open: true }); + }; + + // 4. 添加关闭导出对话框的处理函数 + const handleCloseExportDialog = () => { + setExportDialog({ open: false }); + }; + + // 5. 添加打开导入对话框的处理函数 + const handleOpenImportDialog = () => { + setImportDialog({ open: true }); + }; + + // 6. 添加关闭导入对话框的处理函数 + const handleCloseImportDialog = () => { + setImportDialog({ open: false }); + }; + + // 7. 导入成功后的处理函数 + const handleImportSuccess = () => { + // 刷新数据集列表 + getDatasetsList(); + toast.success(t('import.importSuccess', '数据集导入成功')); + }; + + // 获取数据集列表 + const getDatasetsList = useCallback( + async ({ pageOverride } = {}) => { + const effectivePage = pageOverride ?? page; + try { + setLoading(true); + let url = `/api/projects/${projectId}/datasets?page=${effectivePage}&size=${rowsPerPage}`; + + if (filterConfirmed !== 'all') { + url += `&status=${filterConfirmed}`; + } + + if (debouncedSearchQuery) { + url += `&input=${encodeURIComponent(debouncedSearchQuery)}&field=${searchField}`; + } + + if (filterHasCot !== 'all') { + url += `&hasCot=${filterHasCot}`; + } + + if (filterIsDistill !== 'all') { + url += `&isDistill=${filterIsDistill}`; + } + + if (filterScoreRange[0] > 0 || filterScoreRange[1] < 5) { + url += `&scoreRange=${filterScoreRange[0]}-${filterScoreRange[1]}`; + } + + if (filterCustomTag) { + url += `&customTag=${encodeURIComponent(filterCustomTag)}`; + } + + if (filterNoteKeyword) { + url += `¬eKeyword=${encodeURIComponent(filterNoteKeyword)}`; + } + + if (filterChunkName) { + url += `&chunkName=${encodeURIComponent(filterChunkName)}`; + } + + const response = await axios.get(url); + setDatasets(response.data || { data: [], total: 0, confirmedCount: 0 }); + } catch (error) { + toast.error(error.message); + } finally { + setLoading(false); + } + }, + [ + debouncedSearchQuery, + filterConfirmed, + filterCustomTag, + filterHasCot, + filterIsDistill, + filterNoteKeyword, + filterChunkName, + filterScoreRange, + page, + projectId, + rowsPerPage, + searchField + ] + ); + + useEffect(() => { + if (!isInitialized) return; + + getDatasetsList(); + // 获取项目中所有使用过的标签 + const fetchAvailableTags = async () => { + try { + const response = await fetch(`/api/projects/${projectId}/datasets/tags`); + if (response.ok) { + const data = await response.json(); + setAvailableTags(data.tags || []); + } + } catch (error) { + console.error('获取标签失败:', error); + } + }; + fetchAvailableTags(); + }, [projectId, page, rowsPerPage, debouncedSearchQuery, searchField, isInitialized]); + + // 处理页码变化 + const handlePageChange = (_event, newPage) => { + // MUI TablePagination 的页码从 0 开始,而我们的 API 从 1 开始 + setPage(newPage + 1); + }; + + // 处理每页行数变化 + const handleRowsPerPageChange = event => { + setPage(1); + setRowsPerPage(parseInt(event.target.value, 10)); + }; + + // 打开删除确认框 + const handleOpenDeleteDialog = dataset => { + setDeleteDialog({ + open: true, + datasets: [dataset] + }); + }; + + // 关闭删除确认框 + const handleCloseDeleteDialog = () => { + setDeleteDialog({ + open: false, + dataset: null + }); + }; + + const handleBatchDeleteDataset = async () => { + const datasetsArray = selectedIds.map(id => ({ id })); + setDeleteDialog({ + open: true, + datasets: datasetsArray, + batch: true, + count: selectedIds.length + }); + }; + + const resetProgress = () => { + setDeteleProgress({ + total: deleteDialog.count, + completed: 0, + percentage: 0 + }); + }; + + const handleDeleteConfirm = async () => { + if (deleteDialog.batch) { + setDeleteDialog({ + ...deleteDialog, + deleting: true + }); + await handleBatchDelete(); + resetProgress(); + } else { + const [dataset] = deleteDialog.datasets; + if (!dataset) return; + await handleDelete(dataset); + } + setselectedIds([]); + // 刷新数据 + getDatasetsList(); + // 关闭确认框 + handleCloseDeleteDialog(); + }; + + // 批量删除数据集 + const handleBatchDelete = async () => { + try { + await processInParallel( + selectedIds, + async datasetId => { + await fetch(`/api/projects/${projectId}/datasets?id=${datasetId}`, { + method: 'DELETE' + }); + }, + 3, + (cur, total) => { + setDeteleProgress({ + total, + completed: cur, + percentage: Math.floor((cur / total) * 100) + }); + } + ); + + toast.success(t('common.deleteSuccess')); + } catch (error) { + console.error('批量删除失败:', error); + toast.error(error.message || t('common.deleteFailed')); + } + }; + + // 删除数据集 + const handleDelete = async dataset => { + try { + const response = await fetch(`/api/projects/${projectId}/datasets?id=${dataset.id}`, { + method: 'DELETE' + }); + if (!response.ok) throw new Error(t('datasets.deleteFailed')); + + toast.success(t('datasets.deleteSuccess')); + } catch (error) { + toast.error(error.message || t('datasets.deleteFailed')); + } + }; + + // 使用自定义 Hook 处理数据集导出逻辑 + const { exportDatasets, exportDatasetsStreaming } = useDatasetExport(projectId); + + // 使用自定义 Hook 处理数据集评估逻辑 + const { evaluatingIds, batchEvaluating, handleEvaluateDataset, handleBatchEvaluate } = useDatasetEvaluation( + projectId, + getDatasetsList + ); + + // 处理导出数据集 - 智能选择导出方式 + const handleExportDatasets = async exportOptions => { + try { + // 如果是平衡导出,则忽略选中项,按 balanceConfig 导出 + const exportOptionsWithSelection = exportOptions.balanceMode + ? { ...exportOptions } + : { ...exportOptions, ...(selectedIds.length > 0 && { selectedIds }) }; + + // 获取数据总量: + // 平衡导出时,按 balanceConfig 的总量计算; + // 其他情况:如果有选中数据集则使用选中数量,否则使用当前筛选条件下的数据总量 + const balancedTotal = Array.isArray(exportOptions.balanceConfig) + ? exportOptions.balanceConfig.reduce((sum, c) => sum + (parseInt(c.maxCount) || 0), 0) + : 0; + const totalCount = exportOptions.balanceMode + ? balancedTotal + : selectedIds.length > 0 + ? selectedIds.length + : datasets.total || 0; + + // 设置阈值:超过1000条数据使用流式导出 + const STREAMING_THRESHOLD = 1000; + + // 检查是否需要包含文本块内容 + const needsChunkContent = exportOptions.formatType === 'custom' && exportOptions.customFields?.includeChunk; + + let success = false; + + // 如果数据量大于阈值或需要查询文本块内容,使用流式导出 + if (totalCount > STREAMING_THRESHOLD || needsChunkContent) { + // 使用流式导出,显示进度 + setExportProgress({ show: true, processed: 0, total: totalCount }); + + success = await exportDatasetsStreaming(exportOptionsWithSelection, progress => { + setExportProgress(prev => ({ + ...prev, + processed: progress.processed, + hasMore: progress.hasMore + })); + }); + + // 隐藏进度 + setExportProgress({ show: false, processed: 0, total: 0 }); + } else { + // 使用传统导出方式 + success = await exportDatasets(exportOptionsWithSelection); + } + + if (success) { + // 关闭export对话框 + handleCloseExportDialog(); + } + } catch (error) { + console.error('Export failed:', error); + setExportProgress({ show: false, processed: 0, total: 0 }); + } + }; + + // 查看详情 + const handleViewDetails = id => { + router.push(`/projects/${projectId}/datasets/${id}`); + }; + + // 处理全选/取消全选 + const handleSelectAll = async event => { + if (event.target.checked) { + // 获取所有符合当前筛选条件的数据,不受分页限制 + let url = `/api/projects/${projectId}/datasets?selectedAll=1`; + + if (filterConfirmed !== 'all') { + url += `&status=${filterConfirmed}`; + } + + if (debouncedSearchQuery) { + url += `&input=${encodeURIComponent(debouncedSearchQuery)}&field=${searchField}`; + } + + if (filterHasCot !== 'all') { + url += `&hasCot=${filterHasCot}`; + } + + if (filterIsDistill !== 'all') { + url += `&isDistill=${filterIsDistill}`; + } + + if (filterScoreRange[0] > 0 || filterScoreRange[1] < 5) { + url += `&scoreRange=${filterScoreRange[0]}-${filterScoreRange[1]}`; + } + + if (filterCustomTag) { + url += `&customTag=${encodeURIComponent(filterCustomTag)}`; + } + + if (filterNoteKeyword) { + url += `¬eKeyword=${encodeURIComponent(filterNoteKeyword)}`; + } + + const response = await axios.get(url); + setselectedIds(response.data.map(dataset => dataset.id)); + } else { + setselectedIds([]); + } + }; + + // 处理单个选择 + const handleSelectItem = id => { + setselectedIds(prev => { + if (prev.includes(id)) { + return prev.filter(item => item !== id); + } else { + return [...prev, id]; + } + }); + }; + + const handleResetFilters = useCallback(() => { + setFilterConfirmed('all'); + setFilterHasCot('all'); + setFilterIsDistill('all'); + setFilterScoreRange([0, 5]); + setFilterCustomTag(''); + setFilterNoteKeyword(''); + setFilterChunkName(''); + setPage(1); + getDatasetsList({ pageOverride: 1 }); + }, [ + getDatasetsList, + setFilterConfirmed, + setFilterHasCot, + setFilterIsDistill, + setFilterScoreRange, + setFilterCustomTag, + setFilterNoteKeyword, + setFilterChunkName, + setPage + ]); + + const handleApplyFilters = useCallback(() => { + setFilterDialogOpen(false); + setPage(1); + getDatasetsList({ pageOverride: 1 }); + }, [getDatasetsList, setFilterDialogOpen, setPage]); + const handleCloseFilterDialog = useCallback(() => setFilterDialogOpen(false), [setFilterDialogOpen]); + + return ( + + + + { + setSearchQuery(value); + setPage(1); + }} + onSearchFieldChange={value => { + setSearchField(value); + setPage(1); + }} + onMoreFiltersClick={() => setFilterDialogOpen(true)} + activeFilterCount={getActiveFilterCount()} + /> + + + + {selectedIds.length ? ( + + + {t('datasets.selected', { + count: selectedIds.length + })} + + + + ) : ( + '' + )} + + + + + + + + + + + + {/* 导出进度对话框 */} + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/distill/autoDistillService.js b/easy-dataset-main/app/projects/[projectId]/distill/autoDistillService.js new file mode 100644 index 0000000..bb7cdc7 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/distill/autoDistillService.js @@ -0,0 +1,870 @@ +'use client'; + +import axios from 'axios'; + +/** + * 自动蒸馏服务 + */ +class AutoDistillService { + /** + * 执行自动蒸馆任务 + * @param {Object} config - 配置信息 + * @param {string} config.projectId - 项目ID + * @param {string} config.topic - 蒸馆主题 + * @param {number} config.levels - 标签层级 + * @param {number} config.tagsPerLevel - 每层标签数量 + * @param {number} config.questionsPerTag - 每个标签问题数量 + * @param {Object} config.model - 模型信息 + * @param {string} config.language - 语言 + * @param {Function} config.onProgress - 进度回调 + * @param {Function} config.onLog - 日志回调 + * @returns {Promise} + */ + async executeDistillTask(config) { + const { + projectId, + topic, + levels, + tagsPerLevel, + questionsPerTag, + model, + language, + datasetType = 'single-turn', // 新增数据集类型 + concurrencyLimit = 5, + onProgress, + onLog + } = config; + + // 项目名称存储,用于整个流程共享 + this.projectName = ''; + + try { + // 初始化进度信息 + if (onProgress) { + onProgress({ + stage: 'initializing', + tagsTotal: 0, + tagsBuilt: 0, + questionsTotal: 0, + questionsBuilt: 0, + datasetsTotal: 0, + datasetsBuilt: 0 + }); + } + + // 获取项目名称,只需获取一次 + try { + const projectResponse = await axios.get(`/api/projects/${projectId}`); + if (projectResponse && projectResponse.data && projectResponse.data.name) { + this.projectName = projectResponse.data.name; + this.addLog(onLog, `Using project name "${this.projectName}" as the top-level tag`); + } else { + this.projectName = topic; // 如果无法获取项目名称,则使用主题作为默认值 + this.addLog(onLog, `Could not find project name, using topic "${topic}" as the top-level tag`); + } + } catch (error) { + this.projectName = topic; // 出错时使用主题作为默认值 + this.addLog(onLog, `Failed to get project name, using topic "${topic}" instead: ${error.message}`); + } + + // 添加日志 + this.addLog( + onLog, + `Starting to build tag tree for "${topic}", number of levels: ${levels}, tags per level: ${tagsPerLevel}, questions per tag: ${questionsPerTag}` + ); + + // 从根节点开始构建标签树 + await this.buildTagTree({ + projectId, + topic, + levels, + tagsPerLevel, + model, + language, + onProgress, + onLog + }); + + // 所有标签构建完成后,生成问题 + await this.generateQuestionsForTags({ + projectId, + levels, + questionsPerTag, + model, + language, + concurrencyLimit, + onProgress, + onLog + }); + + // 根据数据集类型生成不同类型的数据集 + if (datasetType === 'single-turn') { + // 只生成单轮对话数据集 + await this.generateDatasetsForQuestions({ + projectId, + model, + language, + concurrencyLimit, + onProgress, + onLog + }); + } else if (datasetType === 'multi-turn') { + // 只生成多轮对话数据集 + await this.generateMultiTurnDatasetsForQuestions({ + projectId, + model, + language, + concurrencyLimit, + onProgress, + onLog + }); + } else if (datasetType === 'both') { + // 先生成单轮对话数据集 + await this.generateDatasetsForQuestions({ + projectId, + model, + language, + concurrencyLimit, + onProgress, + onLog + }); + // 再生成多轮对话数据集 + await this.generateMultiTurnDatasetsForQuestions({ + projectId, + model, + language, + concurrencyLimit, + onProgress, + onLog + }); + } + + // 任务完成 + if (onProgress) { + onProgress({ + stage: 'completed' + }); + } + + this.addLog(onLog, 'Auto distillation task completed'); + } catch (error) { + console.error('自动蒸馏任务执行失败:', error); + this.addLog(onLog, `Task execution error: ${error.message || 'Unknown error'}`); + throw error; + } + } + + /** + * 构建标签树 + * @param {Object} config - 配置信息 + * @param {string} config.projectId - 项目ID + * @param {string} config.topic - 蒸馆主题 + * @param {number} config.levels - 标签层级 + * @param {number} config.tagsPerLevel - 每层标签数量 + * @param {Object} config.model - 模型信息 + * @param {string} config.language - 语言 + * @param {Function} config.onProgress - 进度回调 + * @param {Function} config.onLog - 日志回调 + * @returns {Promise} + */ + async buildTagTree(config) { + const { projectId, topic, levels, tagsPerLevel, model, language, onProgress, onLog } = config; + + // 使用已经获取的项目名称,如果未获取到,则使用主题 + const projectName = this.projectName || topic; + + try { + // 设置初始阶段 + if (onProgress) { + onProgress({ + stage: 'level1' + }); + } + + // 获取所有现有标签 + let allTags = []; + try { + const response = await axios.get(`/api/projects/${projectId}/distill/tags/all`); + allTags = response.data; + } catch (error) { + console.error('获取标签失败:', error); + this.addLog(onLog, `Failed to get tags: ${error.message}`); + return; + } + + // 获取叶子节点总数,更新进度条 + const leafTags = Math.pow(tagsPerLevel, levels); + if (onProgress) { + onProgress({ + tagsTotal: leafTags + }); + } + + // 批量构建标签树 + await this.batchBuildTagTree({ + projectId, + topic, + levels, + tagsPerLevel, + model, + language, + projectName, + allTags, + onProgress, + onLog + }); + } catch (error) { + console.error('构建标签树失败:', error); + this.addLog(onLog, `Failed to build tag tree: ${error.message}`); + throw error; + } + } + + /** + * 批量构建标签树 + * @param {Object} config - 配置信息 + * @returns {Promise} + */ + async batchBuildTagTree(config) { + const { + projectId, + topic, + levels, + tagsPerLevel, + model, + language, + projectName, + allTags: initialTags, + onProgress, + onLog + } = config; + + // 创建一个本地标签缓存,避免频繁请求服务器 + let allTags = [...initialTags]; + + // 构建父子关系映射 + const childrenMap = {}; + const parentMap = {}; + allTags.forEach(tag => { + parentMap[tag.id] = tag; + if (tag.parentId) { + if (!childrenMap[tag.parentId]) { + childrenMap[tag.parentId] = []; + } + childrenMap[tag.parentId].push(tag); + } + }); + + // 按层级分组标签,提高查找效率 + const tagsByLevel = {}; + allTags.forEach(tag => { + const depth = this.getTagDepth(tag, parentMap); + if (!tagsByLevel[depth]) { + tagsByLevel[depth] = []; + } + tagsByLevel[depth].push(tag); + }); + + // 批量创建各层级标签 + for (let level = 1; level <= levels; level++) { + // 设置当前阶段 + if (onProgress) { + onProgress({ + stage: `level${level}` + }); + } + + // 确定当前层级的父标签 + let parentTags = []; + if (level === 1) { + // 第一层标签没有父标签 + parentTags = [null]; + } else { + // 获取上一层的标签作为父标签 + parentTags = tagsByLevel[level - 1] || []; + } + + const batch = parentTags; + const creationPromises = []; + + for (const parentTag of batch) { + // 获取当前父标签下的子标签 + let currentLevelTags = []; + if (parentTag) { + currentLevelTags = childrenMap[parentTag.id] || []; + } else { + // 根标签(没有父标签的标签) + currentLevelTags = allTags.filter(tag => !tag.parentId); + } + + // 计算需要创建的标签数量 + const needToCreate = Math.max(0, tagsPerLevel - currentLevelTags.length); + + if (needToCreate > 0) { + // 构建标签路径 + let tagPathWithProjectName; + if (level === 1) { + // 第一层使用项目名称 + tagPathWithProjectName = projectName; + } else { + // 其他层构建完整路径 + const parentTagName = parentTag?.label || ''; + const parentTagPath = this.getTagPath(parentTag, parentMap); + + if (!parentTagPath) { + tagPathWithProjectName = projectName; + } else if (!parentTagPath.startsWith(projectName)) { + tagPathWithProjectName = `${projectName} > ${parentTagPath}`; + } else { + tagPathWithProjectName = parentTagPath; + } + } + + // 创建标签的Promise + const createPromise = axios + .post(`/api/projects/${projectId}/distill/tags`, { + parentTag: level === 1 ? topic : parentTag?.label || '', + parentTagId: parentTag ? parentTag.id : null, + tagPath: tagPathWithProjectName || (level === 1 ? projectName : ''), + count: needToCreate, + model, + language + }) + .then(response => { + // 更新本地标签缓存 + const newTags = response.data; + allTags = [...allTags, ...newTags]; + + // 更新父子关系映射 + if (parentTag) { + if (!childrenMap[parentTag.id]) { + childrenMap[parentTag.id] = []; + } + childrenMap[parentTag.id].push(...newTags); + } + + // 更新父标签映射 + newTags.forEach(tag => { + parentMap[tag.id] = tag; + }); + + // 更新层级分组 + if (!tagsByLevel[level]) { + tagsByLevel[level] = []; + } + tagsByLevel[level].push(...newTags); + + // 更新构建的标签数量 + if (onProgress) { + onProgress({ + tagsBuilt: newTags.length, + updateType: 'increment' + }); + } + + // 添加日志 + this.addLog( + onLog, + `Successfully created ${newTags.length} tags: ${newTags.map(tag => `"${tag.label}"`).join(', ')}` + ); + + return newTags; + }) + .catch(error => { + console.error(`创建${level}级标签失败:`, error); + this.addLog(onLog, `Failed to create ${level} level tags: ${error.message || 'Unknown error'}`); + return []; + }); + + creationPromises.push(createPromise); + } + } + + // 并行执行当前批次的所有创建任务 + await Promise.all(creationPromises); + } + } + + /** + * 为标签生成问题 + * @param {Object} config - 配置信息 + * @param {string} config.projectId - 项目ID + * @param {number} config.levels - 标签层级 + * @param {number} config.questionsPerTag - 每个标签问题数量 + * @param {Object} config.model - 模型信息 + * @param {string} config.language - 语言 + * @param {Function} config.onProgress - 进度回调 + * @param {Function} config.onLog - 日志回调 + * @returns {Promise} + */ + async generateQuestionsForTags(config) { + const { projectId, levels, questionsPerTag, model, language, concurrencyLimit = 5, onProgress, onLog } = config; + + // 设置当前阶段 + if (onProgress) { + onProgress({ + stage: 'questions' + }); + } + + this.addLog(onLog, 'Tag tree built, starting to generate questions for leaf tags...'); + + try { + // 获取所有标签 + const response = await axios.get(`/api/projects/${projectId}/distill/tags/all`); + const allTags = response.data; + + // 找出所有叶子标签(没有子标签的标签) + const leafTags = []; + + // 创建一个映射表,记录每个标签的子标签 + const childrenMap = {}; + const parentMap = {}; + allTags.forEach(tag => { + parentMap[tag.id] = tag; + if (tag.parentId) { + if (!childrenMap[tag.parentId]) { + childrenMap[tag.parentId] = []; + } + childrenMap[tag.parentId].push(tag); + } + }); + + // 找出所有叶子标签 + allTags.forEach(tag => { + // 如果没有子标签,并且深度是最大层级,则为叶子标签 + if (!childrenMap[tag.id] && this.getTagDepth(tag, parentMap) === levels) { + leafTags.push(tag); + } + }); + + this.addLog(onLog, `Found ${leafTags.length} leaf tags, starting to generate questions...`); + + // 获取所有问题 + const questionsResponse = await axios.get(`/api/projects/${projectId}/questions/tree?isDistill=true`); + const allQuestions = questionsResponse.data; + + // 更新总问题数量 + const totalQuestionsToGenerate = leafTags.length * questionsPerTag; + if (onProgress) { + onProgress({ + questionsTotal: totalQuestionsToGenerate + }); + } + + // 准备并发任务 + const generateQuestionTasks = []; + const processedTags = []; + + // 准备所有需要生成问题的叶子标签任务 + for (const tag of leafTags) { + // 获取标签路径 + const tagPath = this.getTagPath(tag, parentMap); + + // 计算已有问题数量 + const existingQuestions = allQuestions.filter(q => q.label === tag.label); + const needToCreate = Math.max(0, questionsPerTag - existingQuestions.length); + + if (needToCreate > 0) { + // 只添加需要生成问题的标签任务 + generateQuestionTasks.push({ + tag, + tagPath, + needToCreate + }); + + this.addLog(onLog, `Preparing to generate ${needToCreate} questions for tag "${tag.label}"...`); + } else { + this.addLog( + onLog, + `Tag "${tag.label}" already has ${existingQuestions.length} questions, no need to generate new questions` + ); + } + } + + // 分批执行生成问题任务,控制并发数 + this.addLog( + onLog, + `Total ${generateQuestionTasks.length} tags need questions, concurrency limit: ${concurrencyLimit}` + ); + + // 使用分组批量处理 + for (let i = 0; i < generateQuestionTasks.length; i += concurrencyLimit) { + const batch = generateQuestionTasks.slice(i, i + concurrencyLimit); + + // 并行处理批次任务 + await Promise.all( + batch.map(async task => { + const { tag, tagPath, needToCreate } = task; + + this.addLog(onLog, `Generating ${needToCreate} questions for tag "${tag.label}"...`); + + try { + const response = await axios.post(`/api/projects/${projectId}/distill/questions`, { + tagPath, + currentTag: tag.label, + tagId: tag.id, + count: needToCreate, + model, + language + }); + + // 更新生成的问题数量 + if (onProgress) { + onProgress({ + questionsBuilt: response.data.length, + updateType: 'increment' + }); + } + this.addLog(onLog, `Successfully generated ${response.data.length} questions for tag "${tag.label}"`); + } catch (error) { + console.error(`为标签 "${tag.label}" 生成问题失败:`, error); + this.addLog( + onLog, + `Failed to generate questions for tag "${tag.label}": ${error.message || 'Unknown error'}` + ); + } + }) + ); + + // 每完成一批,输出一次进度日志 + this.addLog( + onLog, + `Completed batch ${Math.min(i + concurrencyLimit, generateQuestionTasks.length)}/${generateQuestionTasks.length} of question generation` + ); + } + } catch (error) { + console.error('获取标签失败:', error); + this.addLog(onLog, `Failed to get tags: ${error.message || 'Unknown error'}`); + } + } + + /** + * 为问题生成数据集 + * @param {Object} config - 配置信息 + * @param {string} config.projectId - 项目ID + * @param {Object} config.model - 模型信息 + * @param {string} config.language - 语言 + * @param {Function} config.onProgress - 进度回调 + * @param {Function} config.onLog - 日志回调 + * @returns {Promise} + */ + async generateDatasetsForQuestions(config) { + const { projectId, model, language, concurrencyLimit = 5, onProgress, onLog } = config; + + // 设置当前阶段 + if (onProgress) { + onProgress({ + stage: 'datasets' + }); + } + + this.addLog(onLog, 'Question generation completed, starting to generate answers...'); + + try { + // 获取所有问题 + const response = await axios.get(`/api/projects/${projectId}/questions/tree?isDistill=true`); + const allQuestions = response.data; + + // 找出未回答的问题 + const unansweredQuestions = allQuestions.filter(q => !q.answered); + const answeredQuestions = allQuestions.filter(q => q.answered); + + // 更新总数据集数量和已生成数量 + if (onProgress) { + onProgress({ + datasetsTotal: allQuestions.length, // 总数据集数量应为总问题数量 + datasetsBuilt: answeredQuestions.length // 已生成的数据集数量即已回答的问题数量 + }); + } + + this.addLog(onLog, `Found ${unansweredQuestions.length} unanswered questions, preparing to generate answers...`); + this.addLog(onLog, `Dataset generation concurrency limit: ${concurrencyLimit}`); + + // 分批处理未回答的问题,控制并发数 + for (let i = 0; i < unansweredQuestions.length; i += concurrencyLimit) { + const batch = unansweredQuestions.slice(i, i + concurrencyLimit); + + // 并行处理批次任务 + await Promise.all( + batch.map(async question => { + const questionContent = `${question.label} 下的问题ID:${question.id}`; + this.addLog(onLog, `Generating answer for "${questionContent}"...`); + + try { + // 调用生成数据集的函数 + await this.generateSingleDataset({ + projectId, + questionId: question.id, + questionInfo: question, + model, + language + }); + + // 更新生成的数据集数量 + if (onProgress) { + onProgress({ + datasetsBuilt: 1, + updateType: 'increment' + }); + } + + this.addLog(onLog, `Successfully generated answer for question "${questionContent}"`); + } catch (error) { + console.error(`Failed to generate dataset for question "${question.id}":`, error); + this.addLog( + onLog, + `Failed to generate answer for question "${questionContent}": ${error.message || 'Unknown error'}` + ); + } + }) + ); + + // 每完成一批,输出一次进度日志 + this.addLog( + onLog, + `Completed batch ${Math.min(i + concurrencyLimit, unansweredQuestions.length)}/${unansweredQuestions.length} of dataset generation` + ); + } + + this.addLog(onLog, 'Dataset generation completed'); + } catch (error) { + console.error('Dataset generation failed:', error); + this.addLog(onLog, `Dataset generation error: ${error.message}`); + throw error; + } + } + + /** + * 为问题生成多轮对话数据集 + */ + async generateMultiTurnDatasetsForQuestions(config) { + const { projectId, model, language, concurrencyLimit = 2, onProgress, onLog } = config; + + // 设置当前阶段 + if (onProgress) { + onProgress({ + stage: 'multi-turn-datasets' + }); + } + + this.addLog(onLog, 'Question generation completed, starting to generate multi-turn conversations...'); + + try { + // 获取项目的多轮对话配置 + const configResponse = await axios.get(`/api/projects/${projectId}/tasks`); + const taskConfig = configResponse.data; + + const multiTurnConfig = { + systemPrompt: taskConfig.multiTurnSystemPrompt || '', + scenario: taskConfig.multiTurnScenario || '', + rounds: taskConfig.multiTurnRounds || 3, + roleA: taskConfig.multiTurnRoleA || '', + roleB: taskConfig.multiTurnRoleB || '' + }; + + // 检查是否已配置必要的多轮对话设置 + if ( + !multiTurnConfig.scenario || + !multiTurnConfig.roleA || + !multiTurnConfig.roleB || + !multiTurnConfig.rounds || + multiTurnConfig.rounds < 1 + ) { + throw new Error('项目未配置多轮对话参数,请先在项目设置中配置多轮对话相关参数'); + } + + // 获取所有已回答的问题(多轮对话需要基于已有答案的问题) + const response = await axios.get(`/api/projects/${projectId}/questions/tree?isDistill=true`); + const allQuestions = response.data; + const answeredQuestions = allQuestions; + + if (answeredQuestions.length === 0) { + this.addLog(onLog, 'No answered questions found, skipping multi-turn conversation generation'); + return; + } + + // 获取已生成多轮对话的问题ID + const conversationsResponse = await axios.get(`/api/projects/${projectId}/dataset-conversations?pageSize=1000`); + const existingConversationIds = new Set( + (conversationsResponse.data.conversations || []).map(conv => conv.questionId) + ); + + // 筛选未生成多轮对话的问题 + const questionsForMultiTurn = answeredQuestions.filter(q => !existingConversationIds.has(q.id)); + + // 更新多轮对话数据集总数和已生成数量 + if (onProgress) { + onProgress({ + multiTurnDatasetsTotal: answeredQuestions.length, + multiTurnDatasetsBuilt: answeredQuestions.length - questionsForMultiTurn.length + }); + } + + this.addLog( + onLog, + `Found ${questionsForMultiTurn.length} questions ready for multi-turn conversation generation...` + ); + this.addLog(onLog, `Multi-turn generation concurrency limit: ${concurrencyLimit}`); + + // 分批处理未生成多轮对话的问题,控制并发数 + for (let i = 0; i < questionsForMultiTurn.length; i += concurrencyLimit) { + const batch = questionsForMultiTurn.slice(i, i + concurrencyLimit); + + // 并行处理批次任务 + await Promise.all( + batch.map(async question => { + const questionContent = `${question.label} 下的问题ID:${question.id}`; + this.addLog(onLog, `Generating multi-turn conversation for "${questionContent}"...`); + + try { + // 调用生成多轮对话的函数 + await this.generateSingleMultiTurnDataset({ + projectId, + questionId: question.id, + questionInfo: question, + model, + language, + multiTurnConfig + }); + + // 更新进度 + if (onProgress) { + onProgress({ + multiTurnDatasetsBuilt: 1, + updateType: 'increment' + }); + } + + this.addLog(onLog, `Multi-turn conversation generated for "${questionContent}"`); + } catch (error) { + this.addLog( + onLog, + `Failed to generate multi-turn conversation for "${questionContent}": ${error.message}` + ); + } + }) + ); + } + + this.addLog(onLog, 'Multi-turn conversation generation completed'); + } catch (error) { + console.error('Multi-turn dataset generation failed:', error); + this.addLog(onLog, `Multi-turn dataset generation error: ${error.message}`); + throw error; + } + } + + /** + * 生成单个问题的多轮对话数据集 + */ + async generateSingleMultiTurnDataset({ projectId, questionId, questionInfo, model, language, multiTurnConfig }) { + try { + const response = await axios.post(`/api/projects/${projectId}/dataset-conversations`, { + questionId, + ...multiTurnConfig, + model, + language + }); + + return response.data; + } catch (error) { + console.error('Failed to generate multi-turn dataset:', error); + throw new Error(`Failed to generate multi-turn dataset: ${error.message}`); + } + } + + /** + * 生成单个问题的数据集 + */ + async generateSingleDataset({ projectId, questionId, questionInfo, model, language }) { + try { + // 获取问题信息 + let question = questionInfo; + if (!question) { + const response = await axios.get(`/api/projects/${projectId}/questions/${questionId}`); + question = response.data; + } + + // 生成数据集 + const response = await axios.post(`/api/projects/${projectId}/datasets`, { + projectId, + questionId, + model, + language: language || 'zh-CN' + }); + + return response.data; + } catch (error) { + console.error('Failed to generate dataset:', error); + throw new Error(`Failed to generate dataset: ${error.message}`); + } + } + + /** + * 获取标签深度 + * @param {Object} tag - 标签信息 + * @param {Object} parentMap - 父标签映射 + * @returns {number} - 标签深度 + */ + getTagDepth(tag, parentMap) { + if (!tag) return 0; + + let depth = 1; + let currentTag = tag; + + while (currentTag && currentTag.parentId) { + depth++; + currentTag = parentMap[currentTag.parentId]; + } + + return depth; + } + + /** + * 获取标签路径,确保始终以项目名称开头 + * @param {Object|null} tag - 标签对象 + * @param {Object} parentMap - 父标签映射 + * @returns {string} 标签路径 + */ + getTagPath(tag, parentMap) { + if (!tag) return ''; + + // 使用已经获取的项目名称 + const projectName = this.projectName || ''; + + // 构建标签路径 + const path = []; + let currentTag = tag; + + while (currentTag) { + path.unshift(currentTag.label); + if (currentTag.parentId) { + currentTag = parentMap[currentTag.parentId]; + } else { + currentTag = null; + } + } + + // 确保路径以项目名称开头 + if (projectName && path.length > 0 && path[0] !== projectName) { + path.unshift(projectName); + } + + return path.join(' > '); + } + + /** + * 添加日志 + * @param {Function} onLog - 日志回调 + * @param {string} message - 日志消息 + */ + addLog(onLog, message) { + if (onLog && typeof onLog === 'function') { + onLog(message); + } + } +} + +export const autoDistillService = new AutoDistillService(); +export default autoDistillService; diff --git a/easy-dataset-main/app/projects/[projectId]/distill/page.js b/easy-dataset-main/app/projects/[projectId]/distill/page.js new file mode 100644 index 0000000..eb697e8 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/distill/page.js @@ -0,0 +1,528 @@ +'use client'; + +import React, { useState, useEffect, useRef } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useParams } from 'next/navigation'; +import { useAtomValue } from 'jotai'; +import { selectedModelInfoAtom } from '@/lib/store'; +import { Box, Typography, Paper, Container, Button, CircularProgress, Alert, IconButton, Tooltip } from '@mui/material'; +import AddIcon from '@mui/icons-material/Add'; +import AutoFixHighIcon from '@mui/icons-material/AutoFixHigh'; +import DistillTreeView from '@/components/distill/DistillTreeView'; +import TagGenerationDialog from '@/components/distill/TagGenerationDialog'; +import QuestionGenerationDialog from '@/components/distill/QuestionGenerationDialog'; +import AutoDistillDialog from '@/components/distill/AutoDistillDialog'; +import AutoDistillProgress from '@/components/distill/AutoDistillProgress'; +import HelpOutlineIcon from '@mui/icons-material/HelpOutline'; +import { autoDistillService } from './autoDistillService'; +import axios from 'axios'; +import { toast } from 'sonner'; + +export default function DistillPage() { + const { t, i18n } = useTranslation(); + const { projectId } = useParams(); + const selectedModel = useAtomValue(selectedModelInfoAtom); + + const [project, setProject] = useState(null); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(''); + const [tags, setTags] = useState([]); + + // 标签生成对话框相关状态 + const [tagDialogOpen, setTagDialogOpen] = useState(false); + const [questionDialogOpen, setQuestionDialogOpen] = useState(false); + const [selectedTag, setSelectedTag] = useState(null); + const [selectedTagPath, setSelectedTagPath] = useState(''); + + // 自动蒸馏相关状态 + const [autoDistillDialogOpen, setAutoDistillDialogOpen] = useState(false); + const [autoDistillProgressOpen, setAutoDistillProgressOpen] = useState(false); + const [autoDistillRunning, setAutoDistillRunning] = useState(false); + const [distillStats, setDistillStats] = useState({ + tagsCount: 0, + questionsCount: 0, + datasetsCount: 0, + multiTurnDatasetsCount: 0 + }); + const [distillProgress, setDistillProgress] = useState({ + stage: 'initializing', + tagsTotal: 0, + tagsBuilt: 0, + questionsTotal: 0, + questionsBuilt: 0, + datasetsTotal: 0, + datasetsBuilt: 0, + multiTurnDatasetsTotal: 0, // 新增多轮对话数据集总数 + multiTurnDatasetsBuilt: 0, // 新增多轮对话数据集已生成数 + logs: [] + }); + + const treeViewRef = useRef(null); + + // 获取项目信息和标签列表 + useEffect(() => { + if (projectId) { + fetchProject(); + fetchTags(); + fetchDistillStats(); + } + }, [projectId]); + + // 监听多轮对话数据集刷新事件 + useEffect(() => { + const handleRefreshStats = () => { + fetchDistillStats(); + }; + + if (typeof window !== 'undefined') { + window.addEventListener('refreshDistillStats', handleRefreshStats); + + return () => { + window.removeEventListener('refreshDistillStats', handleRefreshStats); + }; + } + }, [projectId]); + + // 获取项目信息 + const fetchProject = async () => { + try { + setLoading(true); + const response = await axios.get(`/api/projects/${projectId}`); + setProject(response.data); + } catch (error) { + console.error('获取项目信息失败:', error); + setError(t('common.fetchError')); + } finally { + setLoading(false); + } + }; + + // 获取标签列表 + const fetchTags = async () => { + try { + setLoading(true); + const response = await axios.get(`/api/projects/${projectId}/distill/tags/all`); + setTags(response.data); + } catch (error) { + console.error('获取标签列表失败:', error); + setError(t('common.fetchError')); + } finally { + setLoading(false); + } + }; + + // 获取蒸馏统计信息 + const fetchDistillStats = async () => { + try { + // 获取标签数量 + const tagsResponse = await axios.get(`/api/projects/${projectId}/distill/tags/all`); + const tagsCount = tagsResponse.data.length; + + // 获取问题数量 + const questionsResponse = await axios.get(`/api/projects/${projectId}/questions/tree?isDistill=true`); + const questionsCount = questionsResponse.data.length; + + // 获取数据集数量 + const datasetsCount = questionsResponse.data.filter(q => q.answered).length; + + // 获取多轮对话数据集数量 + let multiTurnDatasetsCount = 0; + try { + const conversationsResponse = await axios.get( + `/api/projects/${projectId}/dataset-conversations?getAllIds=true` + ); + multiTurnDatasetsCount = (conversationsResponse.data.allConversationIds || []).length; + } catch (error) { + console.log('获取多轮对话数据集统计失败,可能是API不存在:', error.message); + } + + setDistillStats({ + tagsCount, + questionsCount, + datasetsCount, + multiTurnDatasetsCount + }); + } catch (error) { + console.error('获取蒸馏统计信息失败:', error); + } + }; + + // 打开生成标签对话框 + const handleOpenTagDialog = (tag = null, tagPath = '') => { + if (!selectedModel || Object.keys(selectedModel).length === 0) { + setError(t('distill.selectModelFirst')); + return; + } + setSelectedTag(tag); + setSelectedTagPath(tagPath); + setTagDialogOpen(true); + }; + + // 打开生成问题对话框 + const handleOpenQuestionDialog = (tag, tagPath) => { + if (!selectedModel || Object.keys(selectedModel).length === 0) { + setError(t('distill.selectModelFirst')); + return; + } + setSelectedTag(tag); + setSelectedTagPath(tagPath); + setQuestionDialogOpen(true); + }; + + // 处理标签生成完成 + const handleTagGenerated = () => { + fetchTags(); // 重新获取标签列表 + setTagDialogOpen(false); + }; + + // 处理问题生成完成 + const handleQuestionGenerated = () => { + // 关闭对话框 + setQuestionDialogOpen(false); + + // 刷新标签数据 + fetchTags(); + fetchDistillStats(); + + // 如果 treeViewRef 存在且有 fetchQuestionsStats 方法,则调用它刷新问题统计信息 + if (treeViewRef.current && typeof treeViewRef.current.fetchQuestionsStats === 'function') { + treeViewRef.current.fetchQuestionsStats(); + } + }; + + // 打开自动蒸馏对话框 + const handleOpenAutoDistillDialog = () => { + if (!selectedModel || Object.keys(selectedModel).length === 0) { + setError(t('distill.selectModelFirst')); + return; + } + setAutoDistillDialogOpen(true); + }; + + // 开始自动蒸馏任务(前台运行) + const handleStartAutoDistill = async config => { + setAutoDistillDialogOpen(false); + setAutoDistillProgressOpen(true); + setAutoDistillRunning(true); + + // 初始化进度信息 + setDistillProgress({ + stage: 'initializing', + tagsTotal: config.estimatedTags, + tagsBuilt: distillStats.tagsCount || 0, + questionsTotal: config.estimatedQuestions, + questionsBuilt: distillStats.questionsCount || 0, + datasetsTotal: config.estimatedQuestions, // 初步设置数据集总数为问题数,后面会更新 + datasetsBuilt: distillStats.datasetsCount || 0, // 根据当前已生成的数据集数量初始化 + multiTurnDatasetsTotal: + config.datasetType === 'multi-turn' || config.datasetType === 'both' ? config.estimatedQuestions : 0, + multiTurnDatasetsBuilt: distillStats.multiTurnDatasetsCount || 0, + logs: [t('distill.autoDistillStarted', { time: new Date().toLocaleTimeString() })] + }); + + try { + // 检查模型是否存在 + if (!selectedModel || Object.keys(selectedModel).length === 0) { + addLog(t('distill.selectModelFirst')); + setAutoDistillRunning(false); + return; + } + + // 使用 autoDistillService 执行蒸馏任务 + await autoDistillService.executeDistillTask({ + projectId, + topic: config.topic, + levels: config.levels, + tagsPerLevel: config.tagsPerLevel, + questionsPerTag: config.questionsPerTag, + datasetType: config.datasetType, // 新增数据集类型参数 + model: selectedModel, + language: i18n.language, + concurrencyLimit: project?.taskConfig?.concurrencyLimit || 5, // 从项目配置中获取并发限制 + onProgress: updateProgress, + onLog: addLog + }); + + // 更新任务状态 + setAutoDistillRunning(false); + } catch (error) { + console.error('自动蒸馏任务执行失败:', error); + addLog(t('distill.taskExecutionError', { error: error.message || t('common.unknownError') })); + setAutoDistillRunning(false); + } + }; + + // 开始自动蒸馏任务(后台运行) + const handleStartAutoDistillBackground = async config => { + setAutoDistillDialogOpen(false); + + try { + // 检查模型是否存在 + if (!selectedModel || Object.keys(selectedModel).length === 0) { + setError(t('distill.selectModelFirst')); + return; + } + + // 创建后台任务 + const response = await axios.post(`/api/projects/${projectId}/tasks`, { + taskType: 'data-distillation', + modelInfo: selectedModel, + language: i18n.language, + detail: t('distill.autoDistillTaskDetail', { topic: config.topic }), + totalCount: config.estimatedQuestions, + note: { + topic: config.topic, + levels: config.levels, + tagsPerLevel: config.tagsPerLevel, + questionsPerTag: config.questionsPerTag, + datasetType: config.datasetType, + estimatedTags: config.estimatedTags, + estimatedQuestions: config.estimatedQuestions + } + }); + + if (response.data.code === 0) { + toast.success(t('distill.backgroundTaskCreated')); + // 3秒后刷新统计信息 + setTimeout(() => { + fetchDistillStats(); + }, 3000); + } else { + toast.error(response.data.message || t('distill.backgroundTaskFailed')); + } + } catch (error) { + console.error('创建后台蒸馏任务失败:', error); + toast.error(error.message || t('distill.backgroundTaskFailed')); + } + }; + + // 更新进度 + const updateProgress = progressUpdate => { + setDistillProgress(prev => { + const newProgress = { ...prev }; + + // 更新阶段 + if (progressUpdate.stage) { + newProgress.stage = progressUpdate.stage; + } + + // 更新标签总数 + if (progressUpdate.tagsTotal) { + newProgress.tagsTotal = progressUpdate.tagsTotal; + } + + // 更新已构建标签数 + if (progressUpdate.tagsBuilt) { + if (progressUpdate.updateType === 'increment') { + newProgress.tagsBuilt += progressUpdate.tagsBuilt; + } else { + newProgress.tagsBuilt = progressUpdate.tagsBuilt; + } + } + + // 更新问题总数 + if (progressUpdate.questionsTotal) { + newProgress.questionsTotal = progressUpdate.questionsTotal; + } + + // 更新已生成问题数 + if (progressUpdate.questionsBuilt) { + if (progressUpdate.updateType === 'increment') { + newProgress.questionsBuilt += progressUpdate.questionsBuilt; + } else { + newProgress.questionsBuilt = progressUpdate.questionsBuilt; + } + } + + // 更新数据集总数 + if (progressUpdate.datasetsTotal) { + newProgress.datasetsTotal = progressUpdate.datasetsTotal; + } + + // 更新已生成数据集数 + if (progressUpdate.datasetsBuilt) { + if (progressUpdate.updateType === 'increment') { + newProgress.datasetsBuilt += progressUpdate.datasetsBuilt; + } else { + newProgress.datasetsBuilt = progressUpdate.datasetsBuilt; + } + } + + // 更新多轮对话数据集总数 + if (progressUpdate.multiTurnDatasetsTotal) { + newProgress.multiTurnDatasetsTotal = progressUpdate.multiTurnDatasetsTotal; + } + + // 更新已生成多轮对话数据集数 + if (progressUpdate.multiTurnDatasetsBuilt) { + if (progressUpdate.updateType === 'increment') { + newProgress.multiTurnDatasetsBuilt += progressUpdate.multiTurnDatasetsBuilt; + } else { + newProgress.multiTurnDatasetsBuilt = progressUpdate.multiTurnDatasetsBuilt; + } + } + + return newProgress; + }); + }; + + // 添加日志,最多保留200条 + const addLog = message => { + setDistillProgress(prev => { + const newLogs = [...prev.logs, message]; + // 如果日志超过200条,只保留最新的200条 + const limitedLogs = newLogs.length > 200 ? newLogs.slice(-200) : newLogs; + return { + ...prev, + logs: limitedLogs + }; + }); + }; + + // 关闭进度对话框 + const handleCloseProgressDialog = () => { + if (!autoDistillRunning) { + setAutoDistillProgressOpen(false); + // 刷新数据 + fetchTags(); + fetchDistillStats(); + if (treeViewRef.current && typeof treeViewRef.current.fetchQuestionsStats === 'function') { + treeViewRef.current.fetchQuestionsStats(); + } + } else { + // 如果任务还在运行,可以展示一个确认对话框 + // 这里简化处理,直接关闭 + setAutoDistillProgressOpen(false); + } + }; + + if (!projectId) { + return ( + + {t('common.projectIdRequired')} + + ); + } + + return ( + + + + + + {t('distill.title')} + + + { + const helpUrl = + i18n.language === 'en' + ? 'https://docs.easy-dataset.com/ed/en/advanced/images-and-media' + : 'https://docs.easy-dataset.com/jin-jie-shi-yong/images-and-media'; + window.open(helpUrl, '_blank'); + }} + sx={{ color: 'text.secondary' }} + > + + + + + + + + + + + {error && ( + setError('')}> + {error} + + )} + + {loading ? ( + + + + ) : ( + + + + )} + + + {/* 生成标签对话框 */} + {tagDialogOpen && ( + setTagDialogOpen(false)} + onGenerated={handleTagGenerated} + projectId={projectId} + parentTag={selectedTag} + tagPath={selectedTagPath} + model={selectedModel} + /> + )} + + {/* 生成问题对话框 */} + {questionDialogOpen && ( + setQuestionDialogOpen(false)} + onGenerated={handleQuestionGenerated} + projectId={projectId} + tag={selectedTag} + tagPath={selectedTagPath} + model={selectedModel} + /> + )} + + {/* 全自动蒸馏数据集配置对话框 */} + setAutoDistillDialogOpen(false)} + onStart={handleStartAutoDistill} + onStartBackground={handleStartAutoDistillBackground} + projectId={projectId} + project={project} + stats={distillStats} + /> + + {/* 全自动蒸馏进度对话框 */} + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/eval-datasets/[evalId]/page.js b/easy-dataset-main/app/projects/[projectId]/eval-datasets/[evalId]/page.js new file mode 100644 index 0000000..e1695be --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-datasets/[evalId]/page.js @@ -0,0 +1,409 @@ +'use client'; +import { useState, useEffect } from 'react'; +import { useParams } from 'next/navigation'; +import { + Box, + Container, + Grid, + Paper, + Chip, + Typography, + CircularProgress, + Alert, + Card, + CardContent, + Divider, + Stack, + RadioGroup, + FormControlLabel, + Radio, + FormGroup, + Checkbox as MuiCheckbox +} from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import { useTheme, alpha } from '@mui/material/styles'; +import CheckCircleIcon from '@mui/icons-material/CheckCircle'; +import RadioButtonCheckedIcon from '@mui/icons-material/RadioButtonChecked'; +import CheckBoxIcon from '@mui/icons-material/CheckBox'; +import ShortTextIcon from '@mui/icons-material/ShortText'; +import NotesIcon from '@mui/icons-material/Notes'; +import AccessTimeIcon from '@mui/icons-material/AccessTime'; +import TagIcon from '@mui/icons-material/Tag'; +import DescriptionIcon from '@mui/icons-material/Description'; + +import useEvalDatasetDetails from './useEvalDatasetDetails'; +import EvalDatasetHeader from '../components/EvalDatasetHeader'; +import EvalEditableField from '../components/EvalEditableField'; +import TagSelector from '@/components/datasets/TagSelector'; + +// 题型图标和颜色映射 +const QUESTION_TYPE_CONFIG = { + true_false: { + icon: CheckCircleIcon, + color: 'success', + bgColor: 'success.light' + }, + single_choice: { + icon: RadioButtonCheckedIcon, + color: 'primary', + bgColor: 'primary.light' + }, + multiple_choice: { + icon: CheckBoxIcon, + color: 'secondary', + bgColor: 'secondary.light' + }, + short_answer: { + icon: ShortTextIcon, + color: 'warning', + bgColor: 'warning.light' + }, + open_ended: { + icon: NotesIcon, + color: 'info', + bgColor: 'info.light' + } +}; + +export default function EvalDatasetDetailPage() { + const { projectId, evalId } = useParams(); + const { t } = useTranslation(); + const theme = useTheme(); + const [availableTags, setAvailableTags] = useState([]); + + const { data, loading, error, handleNavigate, handleSave, handleDelete } = useEvalDatasetDetails(projectId, evalId); + + // 获取项目中已使用的标签 + useEffect(() => { + const fetchAvailableTags = async () => { + try { + const response = await fetch(`/api/projects/${projectId}/eval-datasets/tags`); + if (response.ok) { + const result = await response.json(); + setAvailableTags(result.tags || []); + } + } catch (error) { + console.error('获取可用标签失败:', error); + } + }; + + if (projectId && !loading) { + fetchAvailableTags(); + } + }, [projectId, loading]); + + if (loading) { + return ( + + + + + + ); + } + + if (error || !data) { + return ( + + {error || t('eval.notFound')} + + ); + } + + const typeConfig = QUESTION_TYPE_CONFIG[data.questionType] || QUESTION_TYPE_CONFIG.short_answer; + const TypeIcon = typeConfig.icon; + + // 解析选项 + let options = []; + try { + options = data.options ? (typeof data.options === 'string' ? JSON.parse(data.options) : data.options) : []; + } catch (e) { + options = []; + } + + // 渲染选项预览 + const renderOptionsPreview = value => { + let opts = []; + try { + opts = value ? (typeof value === 'string' ? JSON.parse(value) : value) : []; + } catch (e) { + return Invalid JSON format; + } + + if (!Array.isArray(opts) || opts.length === 0) { + return {t('common.noData')}; + } + + return ( + + {opts.map((option, index) => { + const optionLabel = String.fromCharCode(65 + index); + const isCorrect = + data.questionType === 'multiple_choice' + ? (Array.isArray(data.correctAnswer) + ? data.correctAnswer + : JSON.parse(data.correctAnswer || '[]') + ).includes(optionLabel) + : data.correctAnswer === optionLabel; + + return ( + + + {optionLabel}. + + + {option} + + {isCorrect && ( + + )} + + ); + })} + + ); + }; + + // 渲染答案编辑组件 + const renderAnswerEditor = (currentValue, onChange) => { + if (data.questionType === 'true_false') { + return ( + onChange(e.target.value)} row> + } label={t('eval.correct')} /> + } label={t('eval.wrong')} /> + + ); + } + + if (data.questionType === 'single_choice') { + return ( + onChange(e.target.value)}> + {options.map((_, index) => { + const label = String.fromCharCode(65 + index); + return ( + } label={`${label}. ${options[index]}`} /> + ); + })} + + ); + } + + if (data.questionType === 'multiple_choice') { + const selected = Array.isArray(currentValue) ? currentValue : JSON.parse(currentValue || '[]'); + const handleChange = label => { + const newSelected = selected.includes(label) ? selected.filter(i => i !== label) : [...selected, label].sort(); + onChange(JSON.stringify(newSelected)); + }; + + return ( + + {options.map((_, index) => { + const label = String.fromCharCode(65 + index); + return ( + handleChange(label)} />} + label={`${label}. ${options[index]}`} + /> + ); + })} + + ); + } + + return null; // 简答题和开放题保持默认文本框 + }; + + return ( + + + + + {/* 左侧主要内容 */} + + + {/* 题型标识 */} + + } + label={t(`eval.questionTypes.${data.questionType}`)} + color={typeConfig.color} + sx={{ + fontWeight: 600, + fontSize: '0.9rem', + py: 0.5, + height: 32 + }} + /> + + + {new Date(data.createAt).toLocaleString()} + + + + {/* 问题 */} + handleSave('question', val)} + placeholder={t('eval.questionPlaceholder')} + /> + + {/* 选项 (仅选择题) */} + {(data.questionType === 'single_choice' || data.questionType === 'multiple_choice') && ( + handleSave('options', val)} + placeholder={'["Option A", "Option B", ...]'} + renderPreview={() => renderOptionsPreview(data.options)} + /> + )} + + {/* 答案 */} + handleSave('correctAnswer', val)} + placeholder={t('eval.answerPlaceholder')} + renderEditor={(val, setVal) => renderAnswerEditor(val, setVal)} + /> + + + + {/* 右侧侧边栏 */} + + + {/* 来源信息 */} + + + + + {t('eval.sourceChunk')} + + {data.chunks ? ( + <> + + {data.chunks.content && ( + + {data.chunks.content} + + )} + + ) : ( + + {t('common.noData')} + + )} + + + + {/* 标签和备注 */} + + + + + + {t('eval.tags')} + + t.trim()) + .filter(Boolean) + : [] + : [] + } + onChange={newTags => handleSave('tags', newTags.join(', '))} + availableTags={availableTags} + placeholder={t('eval.tagsPlaceholder')} + /> + + + + + + + + {t('eval.note')} + + } + value={data.note} + onSave={val => handleSave('note', val)} + placeholder={t('eval.notePlaceholder')} + /> + + + + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/eval-datasets/[evalId]/useEvalDatasetDetails.js b/easy-dataset-main/app/projects/[projectId]/eval-datasets/[evalId]/useEvalDatasetDetails.js new file mode 100644 index 0000000..0131800 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-datasets/[evalId]/useEvalDatasetDetails.js @@ -0,0 +1,149 @@ +'use client'; + +import { useState, useEffect, useCallback, useRef } from 'react'; +import { useRouter } from 'next/navigation'; +import { toast } from 'sonner'; +import axios from 'axios'; + +export default function useEvalDatasetDetails(projectId, evalId) { + const router = useRouter(); + const [data, setData] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + // 编辑状态 + const [editingField, setEditingField] = useState(null); // 'question', 'options', 'correctAnswer', 'note', 'tags' + const [fieldValue, setFieldValue] = useState(''); + // 获取详情 + const fetchData = useCallback(async () => { + try { + setLoading(true); + setError(null); + const response = await fetch(`/api/projects/${projectId}/eval-datasets/${evalId}`); + + if (!response.ok) { + if (response.status === 404) { + throw new Error('未找到该题目'); + } + throw new Error('获取数据失败'); + } + + const result = await response.json(); + setData(result); + } catch (err) { + console.error(err); + setError(err.message); + } finally { + setLoading(false); + } + }, [projectId, evalId]); + + useEffect(() => { + fetchData(); + }, [fetchData]); + + // 导航 + const handleNavigate = async direction => { + try { + const response = await fetch(`/api/projects/${projectId}/eval-datasets/${evalId}?operateType=${direction}`); + if (response.ok) { + const neighbor = await response.json(); + if (neighbor && neighbor.id) { + router.push(`/projects/${projectId}/eval-datasets/${neighbor.id}`); + } else { + toast.warning(`已经是${direction === 'next' ? '最后' : '第'}一条数据了`); + } + } + } catch (err) { + console.error('Navigation error:', err); + } + }; + + // 开始编辑 + const handleStartEdit = (field, value) => { + setEditingField(field); + // 对于 options,如果是数组则转为 JSON 字符串编辑,或者在组件层面处理 + // 这里假设 value 已经是适合编辑的格式 + setFieldValue(value); + }; + + // 取消编辑 + const handleCancelEdit = () => { + setEditingField(null); + setFieldValue(''); + }; + + // 保存编辑 + const handleSave = async (field, value) => { + try { + const response = await fetch(`/api/projects/${projectId}/eval-datasets/${evalId}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ [field]: value }) + }); + + if (!response.ok) throw new Error('保存失败'); + + const updated = await response.json(); + setData(prev => ({ ...prev, ...updated })); // 更新本地数据 + setEditingField(null); + toast.success('保存成功'); + } catch (err) { + toast.error(err.message); + } + }; + + // 删除 + const handleDelete = async () => { + if (!confirm('确定要删除这条数据吗?此操作不可撤销。')) return; + + try { + // 先尝试获取下一条,以便删除后跳转 + const nextResponse = await fetch(`/api/projects/${projectId}/eval-datasets/${evalId}?operateType=next`); + let nextId = null; + if (nextResponse.ok) { + const next = await nextResponse.json(); + if (next && next.id) nextId = next.id; + } + + // 如果没有下一条,尝试获取上一条 + if (!nextId) { + const prevResponse = await fetch(`/api/projects/${projectId}/eval-datasets/${evalId}?operateType=prev`); + if (prevResponse.ok) { + const prev = await prevResponse.json(); + if (prev && prev.id) nextId = prev.id; + } + } + + // 删除 + const deleteResponse = await fetch(`/api/projects/${projectId}/eval-datasets/${evalId}`, { + method: 'DELETE' + }); + + if (!deleteResponse.ok) throw new Error('删除失败'); + + toast.success('删除成功'); + + if (nextId) { + router.replace(`/projects/${projectId}/eval-datasets/${nextId}`); + } else { + router.push(`/projects/${projectId}/eval-datasets`); + } + } catch (err) { + toast.error(err.message); + } + }; + return { + data, + loading, + error, + editingField, + fieldValue, + setFieldValue, + handleNavigate, + handleStartEdit, + handleCancelEdit, + handleSave, + handleDelete + }; +} diff --git a/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/BuiltinDatasetDialog.js b/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/BuiltinDatasetDialog.js new file mode 100644 index 0000000..12dd07d --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/BuiltinDatasetDialog.js @@ -0,0 +1,327 @@ +'use client'; + +import { useState, useMemo } from 'react'; +import { + Dialog, + DialogContent, + DialogActions, + Button, + Box, + Typography, + TextField, + Card, + CardActionArea, + Chip, + IconButton, + Tooltip, + InputAdornment, + CircularProgress, + DialogTitle, + DialogContentText +} from '@mui/material'; +import CloseIcon from '@mui/icons-material/Close'; +import SearchIcon from '@mui/icons-material/Search'; +import StorageIcon from '@mui/icons-material/Storage'; +import { useTranslation } from 'react-i18next'; +import { alpha, useTheme } from '@mui/material/styles'; +import { StyledDialogTitle } from './ImportDialog.styles'; +import { DATA_SETS } from '../constants'; + +export default function BuiltinDatasetDialog({ open, onClose, projectId, onSuccess }) { + const { t, i18n } = useTranslation(); + const theme = useTheme(); + const [keyword, setKeyword] = useState(''); + const [selectedDataset, setSelectedDataset] = useState(null); + const [confirmOpen, setConfirmOpen] = useState(false); + const [downloading, setDownloading] = useState(false); + + const isZh = i18n.language.startsWith('zh'); + + // 过滤数据集 + const filteredDatasets = useMemo(() => { + if (!keyword) return DATA_SETS; + const lowerKeyword = keyword.toLowerCase(); + return DATA_SETS.filter( + ds => + ds.zh.toLowerCase().includes(lowerKeyword) || + ds.en.toLowerCase().includes(lowerKeyword) || + ds.type.toLowerCase().includes(lowerKeyword) + ); + }, [keyword]); + + const handleCardClick = dataset => { + setSelectedDataset(dataset); + setConfirmOpen(true); + }; + + const handleConfirmClose = () => { + setConfirmOpen(false); + setSelectedDataset(null); + }; + + const handleImport = async () => { + if (!selectedDataset) return; + + setDownloading(true); + setConfirmOpen(false); + + try { + const cdnUrl = `https://raw.githubusercontent.com/ConardLi/easy-dataset-eval/main/${selectedDataset.file}`; + const response = await fetch(cdnUrl); + if (!response.ok) { + throw new Error(`Failed to fetch dataset: ${response.statusText}`); + } + const jsonData = await response.blob(); + + const formData = new FormData(); + const file = new File([jsonData], `${selectedDataset.en}.json`, { type: 'application/json' }); + formData.append('file', file); + formData.append('questionType', selectedDataset.type); + const tags = `[${selectedDataset.level}] ${selectedDataset.en}`; + formData.append('tags', tags); + + const importResponse = await fetch(`/api/projects/${projectId}/eval-datasets/import`, { + method: 'POST', + body: formData + }); + + const result = await importResponse.json(); + + if (result.code === 0) { + onSuccess?.(result.data); + handleClose(); + } else { + console.error(result.error); + alert(result.error || t('evalDatasets.import.failed')); + } + } catch (error) { + console.error('Import failed:', error); + alert(error.message || t('evalDatasets.import.failed')); + } finally { + setDownloading(false); + setSelectedDataset(null); + } + }; + + const handleClose = () => { + if (downloading) return; + setKeyword(''); + setSelectedDataset(null); + setConfirmOpen(false); + onClose(); + }; + + return ( + <> + + + + + + {t('evalDatasets.import.builtinTitle', '选择内置数据集')} + + + + + + + + + {/* 搜索栏 */} + + setKeyword(e.target.value)} + InputProps={{ + startAdornment: ( + + + + ), + sx: { borderRadius: 2 } + }} + /> + + + {/* 数据集列表 */} + + {downloading ? ( + + + + {t('evalDatasets.import.downloading', '下载并导入中...')} + + + ) : ( + + {filteredDatasets.map((ds, index) => { + const difficultyColor = ds.level === 'easy' ? 'success.main' : 'warning.main'; + const typeLabel = t(`eval.questionTypes.${ds.type}`, ds.type); + const tooltipTitle = ( + + + + + ); + + return ( + + handleCardClick(ds)} + > + + + {isZh ? ds.zh : ds.en} + + + + + ); + })} + + )} + + + + + + + {t('evalDatasets.import.confirmImportTitle', '确认导入')} + + + + {selectedDataset && + t('evalDatasets.import.confirmImportMessage', { + name: isZh ? selectedDataset.zh : selectedDataset.en + })} + + + + + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/EvalDatasetCard.js b/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/EvalDatasetCard.js new file mode 100644 index 0000000..026fd0b --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/EvalDatasetCard.js @@ -0,0 +1,335 @@ +'use client'; + +import { Card, CardContent, Box, Typography, Chip, Checkbox, IconButton, Tooltip, Divider } from '@mui/material'; +import DeleteIcon from '@mui/icons-material/Delete'; +import EditIcon from '@mui/icons-material/Edit'; +import CheckCircleIcon from '@mui/icons-material/CheckCircle'; +import RadioButtonCheckedIcon from '@mui/icons-material/RadioButtonChecked'; +import CheckBoxIcon from '@mui/icons-material/CheckBox'; +import ShortTextIcon from '@mui/icons-material/ShortText'; +import NotesIcon from '@mui/icons-material/Notes'; +import CheckIcon from '@mui/icons-material/Check'; +import CloseIcon from '@mui/icons-material/Close'; +import { useTheme, alpha } from '@mui/material/styles'; +import { useTranslation } from 'react-i18next'; +import { useRouter } from 'next/navigation'; + +// 题型图标和颜色映射 +const QUESTION_TYPE_CONFIG = { + true_false: { + icon: CheckCircleIcon, + color: 'success', + bgColor: 'success.light' + }, + single_choice: { + icon: RadioButtonCheckedIcon, + color: 'primary', + bgColor: 'primary.light' + }, + multiple_choice: { + icon: CheckBoxIcon, + color: 'secondary', + bgColor: 'secondary.light' + }, + short_answer: { + icon: ShortTextIcon, + color: 'warning', + bgColor: 'warning.light' + }, + open_ended: { + icon: NotesIcon, + color: 'info', + bgColor: 'info.light' + } +}; + +export default function EvalDatasetCard({ item, selected, onSelect, onEdit, onDelete, projectId }) { + const theme = useTheme(); + const { t } = useTranslation(); + const router = useRouter(); + + const typeConfig = QUESTION_TYPE_CONFIG[item.questionType] || QUESTION_TYPE_CONFIG.short_answer; + const TypeIcon = typeConfig.icon; + + // 解析选项 + const options = item.options + ? typeof item.options === 'string' + ? JSON.parse(item.options || '[]') + : item.options + : []; + + // 解析答案 + const correctAnswer = item.correctAnswer; + + const handleCardClick = e => { + // 如果点击的是复选框或按钮,不跳转 + if (e.target.closest('.MuiCheckbox-root') || e.target.closest('.MuiIconButton-root')) { + return; + } + router.push(`/projects/${projectId}/eval-datasets/${item.id}`); + }; + + return ( + + + {/* 头部:题型标签和操作 */} + + + { + e.stopPropagation(); + onSelect(item.id); + }} + sx={{ p: 0.5, ml: -0.5 }} + /> + } + label={t(`eval.questionTypes.${item.questionType}`)} + size="small" + color={typeConfig.color} + variant="outlined" + sx={{ + fontWeight: 600, + borderWidth: '1.5px', + bgcolor: alpha(theme.palette[typeConfig.color].main, 0.05) + }} + /> + + + + { + e.stopPropagation(); + onEdit(item); + }} + sx={{ + color: 'text.secondary', + '&:hover': { color: 'primary.main', bgcolor: alpha(theme.palette.primary.main, 0.1) } + }} + > + + + + + { + e.stopPropagation(); + onDelete(item.id); + }} + sx={{ + color: 'text.secondary', + '&:hover': { color: 'error.main', bgcolor: alpha(theme.palette.error.main, 0.1) } + }} + > + + + + + + + {/* 问题内容 */} + + + {item.questionType === 'true_false' && correctAnswer} {item.question} + + + + {/* 选项列表(仅单选/多选显示) */} + {(item.questionType === 'single_choice' || item.questionType === 'multiple_choice') && options.length > 0 && ( + + {(item.questionType === 'multiple_choice' ? options : options.slice(0, 4)).map((option, index) => { + const optionLabel = String.fromCharCode(65 + index); // A, B, C, D + // 解析多选题答案,支持多种格式:数组、JSON字符串、逗号分隔字符串 + const parseMultipleAnswers = answer => { + if (Array.isArray(answer)) return answer; + if (!answer) return []; + // 尝试解析 JSON 数组 + if (answer.startsWith('[')) { + try { + return JSON.parse(answer); + } catch (e) { + return []; + } + } + // 逗号分隔字符串格式,如 "A,B,D" + return answer.split(',').map(s => s.trim()); + }; + const isCorrect = + item.questionType === 'multiple_choice' + ? parseMultipleAnswers(correctAnswer).includes(optionLabel) + : correctAnswer === optionLabel; + + return ( + + + {optionLabel}. + + + {option} + + + ); + })} + {item.questionType === 'single_choice' && options.length > 4 && ( + + ... +{options.length - 4} {t('eval.moreOptions')} + + )} + + )} + + {/* 非选择题且非判断题答案 */} + {item.questionType !== 'single_choice' && + item.questionType !== 'multiple_choice' && + item.questionType !== 'true_false' && + correctAnswer && ( + + + {t('eval.answer')}: + + + {correctAnswer} + + + )} + + + + {/* 底部元信息 */} + + {item.chunks ? ( + + + + ) : ( + + )} + + {item.tags && ( + + + {item.tags + .split(/[,,]/) + .slice(0, 2) + .map((tag, index) => ( + + ))} + {item.tags.split(/[,,]/).length > 2 && ( + + +{item.tags.split(/[,,]/).length - 2} + + )} + + + )} + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/EvalDatasetHeader.js b/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/EvalDatasetHeader.js new file mode 100644 index 0000000..4fc41f5 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/EvalDatasetHeader.js @@ -0,0 +1,44 @@ +'use client'; + +import { Box, Button, Divider, Typography, IconButton, Paper, Tooltip } from '@mui/material'; +import NavigateBeforeIcon from '@mui/icons-material/NavigateBefore'; +import NavigateNextIcon from '@mui/icons-material/NavigateNext'; +import DeleteIcon from '@mui/icons-material/Delete'; +import { useTranslation } from 'react-i18next'; +import { useRouter } from 'next/navigation'; + +export default function EvalDatasetHeader({ projectId, onNavigate, onDelete }) { + const router = useRouter(); + const { t } = useTranslation(); + + return ( + + + + + + {t('eval.detail')} + + + + onNavigate('prev')} title={t('common.prev')}> + + + onNavigate('next')} title={t('common.next')}> + + + + + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/EvalDatasetList.js b/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/EvalDatasetList.js new file mode 100644 index 0000000..5f4ae23 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/EvalDatasetList.js @@ -0,0 +1,155 @@ +'use client'; + +import { + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Paper, + Checkbox, + IconButton, + Chip, + Typography, + Tooltip, + Box +} from '@mui/material'; +import DeleteIcon from '@mui/icons-material/Delete'; +import EditIcon from '@mui/icons-material/Edit'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import { useTranslation } from 'react-i18next'; + +export default function EvalDatasetList({ items, selectedIds, onSelect, onSelectAll, onEdit, onDelete, onView }) { + const { t } = useTranslation(); + + const isAllSelected = items.length > 0 && selectedIds.length === items.length; + const isIndeterminate = selectedIds.length > 0 && selectedIds.length < items.length; + + // 题型颜色映射 + const getTypeColor = type => { + const colors = { + true_false: 'success', + single_choice: 'primary', + multiple_choice: 'secondary', + short_answer: 'warning', + open_ended: 'info' + }; + return colors[type] || 'default'; + }; + + // 格式化答案显示 + const formatAnswer = item => { + const { questionType, correctAnswer, options } = item; + + if (questionType === 'true_false') { + return correctAnswer; + } + + if (questionType === 'single_choice' || questionType === 'multiple_choice') { + return correctAnswer; + } + + // 非选择题,截断显示 + if (correctAnswer && correctAnswer.length > 50) { + return correctAnswer.substring(0, 50) + '...'; + } + return correctAnswer || '-'; + }; + + return ( + + + + + + + + {t('eval.questionType')} + {t('eval.question')} + {t('eval.answer')} + {t('eval.sourceChunk')} + + {t('common.actions')} + + + + + {items.map(item => ( + + + onSelect(item.id)} /> + + + + + + + {item.question} + + + + + {formatAnswer(item)} + + + + {item.chunks ? ( + + ) : ( + + - + + )} + + + + + onView(item)}> + + + + + onDelete(item.id)}> + + + + + + + ))} + {items.length === 0 && ( + + + {t('common.noData')} + + + )} + +
+
+ ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/EvalEditableField.js b/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/EvalEditableField.js new file mode 100644 index 0000000..4aab2fd --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/EvalEditableField.js @@ -0,0 +1,126 @@ +'use client'; + +import { useState } from 'react'; +import { Box, Typography, Button, TextField, IconButton, Paper } from '@mui/material'; +import EditIcon from '@mui/icons-material/Edit'; +import SaveIcon from '@mui/icons-material/Save'; +import CancelIcon from '@mui/icons-material/Cancel'; +import { useTheme, alpha } from '@mui/material/styles'; +import { useTranslation } from 'react-i18next'; + +export default function EvalEditableField({ + label, + value, + multiline = true, + onSave, + placeholder, + renderPreview, // Optional custom preview renderer + renderEditor // Optional custom editor renderer (currentValue, onChange) => ReactNode +}) { + const { t } = useTranslation(); + const theme = useTheme(); + const [editing, setEditing] = useState(false); + const [editValue, setEditValue] = useState(''); + + const handleStartEdit = () => { + setEditValue(value || ''); + setEditing(true); + }; + + const handleCancel = () => { + setEditing(false); + setEditValue(''); + }; + + const handleSave = async () => { + if (onSave) { + await onSave(editValue); + } + setEditing(false); + }; + + return ( + + + + {label} + + {!editing && ( + + + + )} + + + {editing ? ( + + {renderEditor && renderEditor(editValue, setEditValue) ? ( + {renderEditor(editValue, setEditValue)} + ) : ( + setEditValue(e.target.value)} + placeholder={placeholder} + variant="outlined" + size="small" + sx={{ mb: 2 }} + /> + )} + + + + + + ) : ( + + {renderPreview ? ( + renderPreview(value) + ) : ( + + {value || t('common.noData')} + + )} + + )} + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/EvalToolbar.js b/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/EvalToolbar.js new file mode 100644 index 0000000..404ec97 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/EvalToolbar.js @@ -0,0 +1,260 @@ +'use client'; + +import { + Box, + IconButton, + ToggleButton, + Tooltip, + Divider, + Autocomplete, + TextField, + Menu, + MenuItem +} from '@mui/material'; +import SearchIcon from '@mui/icons-material/Search'; +import ViewModuleIcon from '@mui/icons-material/ViewModule'; +import ViewListIcon from '@mui/icons-material/ViewList'; +import DeleteIcon from '@mui/icons-material/DeleteOutline'; // 使用 Outline 版本更精致 +import CheckCircleIcon from '@mui/icons-material/CheckCircleOutline'; // 统一使用 Outline 风格图标 +import RadioButtonCheckedIcon from '@mui/icons-material/RadioButtonChecked'; +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import CheckBoxIcon from '@mui/icons-material/CheckBoxOutlineBlank'; // 或者 CheckBox +import ShortTextIcon from '@mui/icons-material/ShortText'; +import NotesIcon from '@mui/icons-material/Notes'; +import UploadFileIcon from '@mui/icons-material/UploadFile'; +import StorageIcon from '@mui/icons-material/Storage'; +import FileDownloadIcon from '@mui/icons-material/FileDownload'; +import { useTranslation } from 'react-i18next'; +import { useTheme, alpha } from '@mui/material/styles'; +import { useState } from 'react'; + +import { + ToolbarContainer, + FilterGroup, + FilterButton, + SearchWrapper, + StyledInputBase, + ActionGroup, + ActionButton, + DeleteActionButton, + StyledToggleButtonGroup +} from './EvalToolbar.styles'; + +const STATS_CONFIG = [ + { key: 'true_false', icon: CheckCircleIcon, color: 'success' }, + { key: 'single_choice', icon: RadioButtonCheckedIcon, color: 'primary' }, + { key: 'multiple_choice', icon: CheckBoxIcon, color: 'secondary' }, + { key: 'short_answer', icon: ShortTextIcon, color: 'warning' }, + { key: 'open_ended', icon: NotesIcon, color: 'info' } +]; + +export default function EvalToolbar({ + keyword, + onKeywordChange, + viewMode, + onViewModeChange, + selectedCount, + onDeleteSelected, + stats, + questionType, + onTypeChange, + tags, + onTagsChange, + onImport, + onBuiltinImport, + onExport +}) { + const { t } = useTranslation(); + const theme = useTheme(); + + const [importAnchorEl, setImportAnchorEl] = useState(null); + + const handleImportClick = event => { + setImportAnchorEl(event.currentTarget); + }; + + const handleImportClose = () => { + setImportAnchorEl(null); + }; + + const handleCustomImport = () => { + handleImportClose(); + onImport?.(); + }; + + const handleBuiltinImport = () => { + handleImportClose(); + onBuiltinImport?.(); + }; + + const tagOptions = stats?.byTag + ? Object.keys(stats.byTag).map(tag => ({ + label: tag, + count: stats.byTag[tag] + })) + : []; + + return ( + + {/* 顶部:题型统计筛选 */} + + {stats && + STATS_CONFIG.map(({ key, icon: Icon, color }) => { + const count = stats.byType?.[key] || 0; + const isActive = questionType === key; + + return ( + } + active={isActive} + colorType={color} + onClick={() => onTypeChange(isActive ? '' : key)} + > + {t(`eval.questionTypes.${key}`)} + + ({count}) + + + ); + })} + + + + + {/* 底部:筛选和操作 */} + + {/* 左侧:筛选器组 */} + + {/* 搜索框 */} + + + + + onKeywordChange(e.target.value)} + /> + + + {/* 标签筛选 */} + `${option.label} (${option.count})`} + value={tagOptions.filter(o => tags.includes(o.label))} + onChange={(e, newValue) => onTagsChange(newValue.map(v => v.label))} + renderInput={params => ( + + )} + sx={{ + '& .MuiAutocomplete-tag': { + height: 24, + borderRadius: 1 + } + }} + /> + + + {/* 右侧:操作按钮组 */} + + {/* 导入按钮下拉菜单 */} + } + endIcon={} + onClick={handleImportClick} + > + {t('common.import', '导入')} + + + + + {t('evalDatasets.import.custom', '导入自定义数据集')} + + + + {t('evalDatasets.import.builtin', '导入内置数据集')} + + + + {/* 导出按钮 */} + } onClick={onExport}> + {t('common.export', '导出')} + + + {selectedCount > 0 && ( + } onClick={onDeleteSelected}> + {t('eval.deleteSelectedCount', `删除选中 (${selectedCount})`, { count: selectedCount })} + + )} + + + + value && onViewModeChange(value)} + size="small" + > + + + + + + + + + + + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/EvalToolbar.styles.js b/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/EvalToolbar.styles.js new file mode 100644 index 0000000..4f0cfa3 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/EvalToolbar.styles.js @@ -0,0 +1,151 @@ +import { styled, alpha } from '@mui/material/styles'; +import { Box, Paper, Button, ToggleButton, ToggleButtonGroup, InputBase } from '@mui/material'; + +export const ToolbarContainer = styled(Paper)(({ theme }) => ({ + padding: theme.spacing(2, 2.5), + marginBottom: theme.spacing(3), + borderRadius: theme.shape.borderRadius * 2, + border: `1px solid ${theme.palette.divider}`, + backgroundColor: theme.palette.background.paper, + boxShadow: '0 2px 12px rgba(0,0,0,0.03)', + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(2) +})); + +export const FilterGroup = styled(Box)(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + gap: theme.spacing(1.5), + flexWrap: 'wrap' +})); + +export const FilterButton = styled(Button, { + shouldForwardProp: prop => prop !== 'active' && prop !== 'colorType' +})(({ theme, active, colorType }) => { + const colorMap = { + success: theme.palette.success, + primary: theme.palette.primary, + secondary: theme.palette.secondary, + warning: theme.palette.warning, + info: theme.palette.info + }; + const mainColor = colorMap[colorType] || theme.palette.primary; + + return { + padding: theme.spacing(0.75, 2), + borderRadius: theme.shape.borderRadius * 5, // Pill shape + border: '1px solid', + borderColor: active ? mainColor.main : theme.palette.divider, + backgroundColor: active ? alpha(mainColor.main, 0.1) : 'transparent', + color: active ? mainColor.main : theme.palette.text.secondary, + fontSize: '0.875rem', + fontWeight: active ? 600 : 400, + minWidth: 'auto', + textTransform: 'none', + transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)', + '&:hover': { + backgroundColor: active ? alpha(mainColor.main, 0.15) : alpha(theme.palette.text.primary, 0.04), + borderColor: active ? mainColor.main : theme.palette.text.secondary, + transform: 'translateY(-1px)' + }, + '& .MuiButton-startIcon': { + marginRight: theme.spacing(0.8), + color: active ? mainColor.main : theme.palette.text.disabled, + width: 18, + height: 18 + } + }; +}); + +export const SearchWrapper = styled(Paper)(({ theme }) => ({ + padding: '2px 4px', + display: 'flex', + alignItems: 'center', + width: 280, + height: 42, + borderRadius: theme.shape.borderRadius * 1.5, + border: `1px solid ${theme.palette.divider}`, + backgroundColor: theme.palette.background.paper, + boxShadow: 'none', + transition: 'all 0.2s ease', + '&:hover': { + borderColor: theme.palette.text.secondary, + backgroundColor: alpha(theme.palette.action.hover, 0.05) + }, + '&:focus-within': { + borderColor: theme.palette.primary.main, + boxShadow: `0 0 0 3px ${alpha(theme.palette.primary.main, 0.1)}`, + backgroundColor: theme.palette.background.paper + } +})); + +export const StyledInputBase = styled(InputBase)(({ theme }) => ({ + marginLeft: theme.spacing(1), + flex: 1, + fontSize: '0.875rem', + '& input': { + '&::placeholder': { + color: theme.palette.text.disabled, + opacity: 1 + } + } +})); + +export const ActionGroup = styled(Box)(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + gap: theme.spacing(1.5) +})); + +export const ActionButton = styled(Button)(({ theme }) => ({ + borderRadius: theme.shape.borderRadius * 1.5, + height: 40, + paddingLeft: theme.spacing(3), + paddingRight: theme.spacing(3), + borderColor: theme.palette.divider, + color: theme.palette.text.secondary, + '&:hover': { + borderColor: theme.palette.text.primary, + color: theme.palette.text.primary, + backgroundColor: theme.palette.action.hover + } +})); + +export const DeleteActionButton = styled(Button)(({ theme }) => ({ + borderRadius: theme.shape.borderRadius * 1.5, + height: 40, + paddingLeft: theme.spacing(2), + paddingRight: theme.spacing(2), + backgroundColor: alpha(theme.palette.error.main, 0.1), + color: theme.palette.error.main, + '&:hover': { + backgroundColor: alpha(theme.palette.error.main, 0.2) + } +})); + +export const StyledToggleButtonGroup = styled(ToggleButtonGroup)(({ theme }) => ({ + height: 40, + backgroundColor: theme.palette.action.hover, // Slightly darker than paper + padding: 4, + borderRadius: theme.shape.borderRadius * 1.5, + border: 'none', + gap: 4, + '& .MuiToggleButton-root': { + border: 'none', + borderRadius: theme.shape.borderRadius, + width: 36, + color: theme.palette.text.secondary, + '&.Mui-selected': { + backgroundColor: theme.palette.background.paper, + color: theme.palette.primary.main, + boxShadow: '0 2px 4px rgba(0,0,0,0.05)', + '&:hover': { + backgroundColor: theme.palette.background.paper + } + }, + '&:hover': { + backgroundColor: 'rgba(0,0,0,0.04)' + } + } +})); diff --git a/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/ExportEvalDialog.js b/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/ExportEvalDialog.js new file mode 100644 index 0000000..04d6e9e --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/ExportEvalDialog.js @@ -0,0 +1,259 @@ +'use client'; + +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + Box, + Typography, + TextField, + FormControl, + InputLabel, + Select, + MenuItem, + Chip, + OutlinedInput, + Checkbox, + ListItemText, + Alert, + CircularProgress, + IconButton, + ToggleButton, + ToggleButtonGroup, + Divider +} from '@mui/material'; +import CloseIcon from '@mui/icons-material/Close'; +import FileDownloadIcon from '@mui/icons-material/FileDownload'; +import FilterAltIcon from '@mui/icons-material/FilterAlt'; +import ClearIcon from '@mui/icons-material/Clear'; +import { useTranslation } from 'react-i18next'; + +const QUESTION_TYPES = [ + { value: 'true_false', labelKey: 'eval.questionTypes.true_false' }, + { value: 'single_choice', labelKey: 'eval.questionTypes.single_choice' }, + { value: 'multiple_choice', labelKey: 'eval.questionTypes.multiple_choice' }, + { value: 'short_answer', labelKey: 'eval.questionTypes.short_answer' }, + { value: 'open_ended', labelKey: 'eval.questionTypes.open_ended' } +]; + +const EXPORT_FORMATS = [ + { value: 'json', label: 'JSON', description: 'evalDatasets.export.jsonDesc' }, + { value: 'jsonl', label: 'JSONL', description: 'evalDatasets.export.jsonlDesc' }, + { value: 'csv', label: 'CSV', description: 'evalDatasets.export.csvDesc' } +]; + +export default function ExportEvalDialog({ + open, + onClose, + exporting, + error, + format, + setFormat, + questionTypes, + setQuestionTypes, + selectedTags, + setSelectedTags, + keyword, + setKeyword, + previewTotal, + previewLoading, + availableTags, + resetFilters, + onExport +}) { + const { t } = useTranslation(); + + const hasFilters = questionTypes.length > 0 || selectedTags.length > 0 || keyword; + + return ( + + + + + + {t('evalDatasets.export.title', '导出评估数据集')} + + + + + + + + + {error && ( + {}}> + {error} + + )} + + {/* 导出格式选择 */} + + + {t('evalDatasets.export.formatLabel', '导出格式')} + + newFormat && setFormat(newFormat)} + fullWidth + size="small" + > + {EXPORT_FORMATS.map(f => ( + + + + {f.label} + + + {t(f.description, f.label)} + + + + ))} + + + + + + {/* 筛选条件 */} + + + + + {t('evalDatasets.export.filterLabel', '筛选条件')} + + {hasFilters && ( + + )} + + + + {/* 关键字搜索 */} + setKeyword(e.target.value)} + /> + + {/* 题型和标签筛选 */} + + {/* 题型筛选 */} + + {t('evalTasks.filterByTypeLabel', '题型筛选')} + + + + {/* 标签筛选 */} + + {t('evalTasks.filterByTagLabel', '标签筛选')} + + + + + + + {/* 导出预览 */} + + + {t('evalDatasets.export.previewLabel', '将导出数据:')} + + {previewLoading ? ( + + ) : ( + + {previewTotal} {t('evalDatasets.export.records', '条记录')} + + )} + + + {previewTotal > 1000 && ( + + {t('evalDatasets.export.largeDataHint', '数据量较大,将采用流式导出,请耐心等待')} + + )} + + + + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/ImportDialog.js b/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/ImportDialog.js new file mode 100644 index 0000000..66114d6 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/ImportDialog.js @@ -0,0 +1,319 @@ +'use client'; + +import { useState, useRef } from 'react'; +import { + Dialog, + DialogContent, + DialogActions, + Button, + Box, + Typography, + TextField, + Alert, + LinearProgress, + Chip, + IconButton, + Radio +} from '@mui/material'; +import CloudUploadIcon from '@mui/icons-material/CloudUpload'; +import DownloadIcon from '@mui/icons-material/Download'; +import CloseIcon from '@mui/icons-material/Close'; +import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile'; +import { useTranslation } from 'react-i18next'; +import * as XLSX from 'xlsx'; + +import { + QUESTION_TYPES, + FORMAT_PREVIEW, + getJsonTemplateData, + getExcelTemplateData, + getColumnWidths +} from '../constants'; +import { + StyledDialogTitle, + UploadBox, + PreviewPaper, + CodeBlock, + ErrorContainer, + TypeRadioGroup, + TypeFormControlLabel +} from './ImportDialog.styles'; + +export default function ImportDialog({ open, onClose, projectId, onSuccess }) { + const { t } = useTranslation(); + const fileInputRef = useRef(null); + + const [questionType, setQuestionType] = useState('open_ended'); + const [tags, setTags] = useState(''); + const [file, setFile] = useState(null); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + const [errorDetails, setErrorDetails] = useState([]); + + // 处理文件选择 + const handleFileChange = e => { + const selectedFile = e.target.files[0]; + if (selectedFile) { + const ext = selectedFile.name.split('.').pop().toLowerCase(); + if (!['json', 'xls', 'xlsx'].includes(ext)) { + setError(t('evalDatasets.import.invalidFileType', '不支持的文件格式,请上传 json、xls 或 xlsx 文件')); + return; + } + setFile(selectedFile); + setError(null); + setErrorDetails([]); + } + }; + + // 下载模板 + const handleDownloadTemplate = format => { + if (!questionType) { + setError(t('evalDatasets.import.selectTypeFirst', '请先选择题型')); + return; + } + + if (format === 'json') { + // JSON 模板动态生成并下载 + const templateData = getJsonTemplateData(questionType); + const jsonContent = JSON.stringify(templateData, null, 2); + const blob = new Blob([jsonContent], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = `eval-dataset-template-${questionType}.json`; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); + } else { + // Excel 模板动态生成 + const templateData = getExcelTemplateData(questionType); + const worksheet = XLSX.utils.json_to_sheet(templateData); + const workbook = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(workbook, worksheet, 'Template'); + + // 设置列宽 + const colWidths = getColumnWidths(questionType); + worksheet['!cols'] = colWidths; + + // 下载文件 + XLSX.writeFile(workbook, `eval-dataset-template-${questionType}.xlsx`); + } + }; + + // 提交导入 + const handleSubmit = async () => { + if (!questionType) { + setError(t('evalDatasets.import.selectTypeFirst', '请先选择题型')); + return; + } + if (!file) { + setError(t('evalDatasets.import.selectFile', '请选择要导入的文件')); + return; + } + + setLoading(true); + setError(null); + setErrorDetails([]); + + try { + const formData = new FormData(); + formData.append('file', file); + formData.append('questionType', questionType); + formData.append('tags', tags); + + const response = await fetch(`/api/projects/${projectId}/eval-datasets/import`, { + method: 'POST', + body: formData + }); + + const result = await response.json(); + + if (result.code === 0) { + onSuccess?.(result.data); + handleClose(); + } else { + setError(result.error || result.message); + if (result.details) { + setErrorDetails(result.details); + } + } + } catch (err) { + setError(err.message || t('evalDatasets.import.failed', '导入失败')); + } finally { + setLoading(false); + } + }; + + // 关闭对话框 + const handleClose = () => { + if (loading) return; + setQuestionType('open_ended'); + setTags(''); + setFile(null); + setError(null); + setErrorDetails([]); + onClose(); + }; + + // 获取当前题型的格式预览 + const formatPreview = questionType ? FORMAT_PREVIEW[questionType] : null; + + return ( + + + {t('evalDatasets.import.title', '导入评估数据集')} + + + + + + + {loading && } + + {/* 错误提示 */} + {error && ( + + {error} + {errorDetails.length > 0 && ( + + {errorDetails.map((detail, index) => ( + + {detail} + + ))} + {errorDetails.length < 10 && ( + + {t('evalDatasets.import.showingErrors', '显示前 {{count}} 条错误', { count: errorDetails.length })} + + )} + + )} + + )} + + {/* 题型选择 - 使用封装好的样式组件 */} + + + {t('evalDatasets.import.questionType', '选择题型')} + + setQuestionType(e.target.value)}> + {QUESTION_TYPES.map(type => ( + } + label={t(type.label, type.labelZh)} + /> + ))} + + + + {/* 数据格式预览 */} + {formatPreview && ( + + + {t('evalDatasets.import.formatPreview', '数据格式预览')} + + + {formatPreview.fields.map(field => ( + + ))} + + + {formatPreview.description} + + +
{JSON.stringify(formatPreview.example, null, 2)}
+
+ + {/* 下载模板按钮 */} + + + + +
+ )} + + {/* 文件上传 */} + + + fileInputRef.current?.click()}> + {file ? ( + + + + {file.name} + + + + {t('common.clickToReplace', '点击更换文件')} + + + ) : ( + + + + {t('evalDatasets.import.dropOrClick', '点击或拖拽文件到此处')} + + + {t('evalDatasets.import.supportedFormats', '支持 JSON、XLS、XLSX 格式')} + + + )} + + + + {/* 标签输入 */} + setTags(e.target.value)} + disabled={loading} + helperText={t('evalDatasets.import.tagsHelp', '导入的所有数据将打上这些标签')} + InputProps={{ + startAdornment: tags ? # : null + }} + /> +
+ + + + + +
+ ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/ImportDialog.styles.js b/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/ImportDialog.styles.js new file mode 100644 index 0000000..a34dfc4 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-datasets/components/ImportDialog.styles.js @@ -0,0 +1,133 @@ +import { styled, alpha } from '@mui/material/styles'; +import { Box, Paper, DialogTitle as MuiDialogTitle, RadioGroup, FormControlLabel } from '@mui/material'; + +export const StyledDialogTitle = styled(MuiDialogTitle)(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + padding: theme.spacing(2, 3), + borderBottom: `1px solid ${theme.palette.divider}`, + '& .MuiTypography-root': { + fontWeight: 600, + fontSize: '1.1rem' + } +})); + +export const TypeRadioGroup = styled(RadioGroup)(({ theme }) => ({ + display: 'flex', + flexDirection: 'row', + gap: theme.spacing(2) +})); + +export const TypeFormControlLabel = styled(FormControlLabel, { + shouldForwardProp: prop => prop !== 'checked' +})(({ theme, checked }) => ({ + margin: 0, + padding: '4px 12px', + borderRadius: '8px', + border: '1px solid', + borderColor: checked ? theme.palette.primary.main : theme.palette.divider, + backgroundColor: checked ? alpha(theme.palette.primary.main, 0.05) : 'transparent', + transition: 'all 0.2s', + '&:hover': { + backgroundColor: checked ? alpha(theme.palette.primary.main, 0.08) : theme.palette.action.hover + }, + '& .MuiTypography-root': { + fontSize: '0.875rem', + color: checked ? theme.palette.primary.main : theme.palette.text.primary, + fontWeight: checked ? 600 : 400 + }, + '& .MuiRadio-root': { + padding: '4px', + color: checked ? theme.palette.primary.main : theme.palette.text.secondary + } +})); + +export const UploadBox = styled(Box, { + shouldForwardProp: prop => prop !== 'active' && prop !== 'hasFile' +})(({ theme, active, hasFile }) => ({ + border: '2px dashed', + borderColor: active ? theme.palette.primary.main : theme.palette.grey[300], + borderRadius: theme.shape.borderRadius * 2, + padding: theme.spacing(4), + textAlign: 'center', + cursor: 'pointer', + backgroundColor: active + ? alpha(theme.palette.primary.main, 0.05) + : hasFile + ? alpha(theme.palette.primary.main, 0.05) + : 'transparent', + transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)', + '&:hover': { + borderColor: theme.palette.primary.main, + backgroundColor: alpha(theme.palette.primary.main, 0.02), + transform: 'translateY(-1px)', + boxShadow: '0 4px 12px rgba(0,0,0,0.05)' + }, + '& svg': { + fontSize: 48, + marginBottom: theme.spacing(1), + color: active ? theme.palette.primary.main : theme.palette.grey[400], + transition: 'color 0.3s ease' + } +})); + +export const PreviewPaper = styled(Paper)(({ theme }) => ({ + padding: theme.spacing(2.5), + marginBottom: theme.spacing(3), + backgroundColor: theme.palette.grey[50], + border: `1px solid ${theme.palette.divider}`, + borderRadius: theme.shape.borderRadius * 1.5, + '& .title': { + display: 'flex', + alignItems: 'center', + gap: theme.spacing(1), + marginBottom: theme.spacing(1.5), + color: theme.palette.text.primary, + fontWeight: 600 + } +})); + +export const CodeBlock = styled(Box)(({ theme }) => ({ + backgroundColor: '#1e1e1e', // Dark theme for code + color: '#d4d4d4', + padding: theme.spacing(2), + borderRadius: theme.shape.borderRadius, + fontFamily: '"Fira Code", "Roboto Mono", monospace', + fontSize: '0.85rem', + overflow: 'auto', + maxHeight: 300, + '&::-webkit-scrollbar': { + height: 8, + width: 8 + }, + '&::-webkit-scrollbar-track': { + backgroundColor: '#2d2d2d' + }, + '&::-webkit-scrollbar-thumb': { + backgroundColor: '#555', + borderRadius: 4 + } +})); + +export const ErrorContainer = styled(Box)(({ theme }) => ({ + marginTop: theme.spacing(1), + fontSize: '0.85rem', + maxHeight: 200, + overflowY: 'auto', + '& .item': { + padding: theme.spacing(0.5, 0), + color: theme.palette.error.main, + display: 'flex', + alignItems: 'flex-start', + gap: theme.spacing(1), + '&::before': { + content: '"•"', + fontWeight: 'bold' + } + } +})); + +export const TagInputWrapper = styled(Box)(({ theme }) => ({ + // Custom styles for tag input area if needed +})); diff --git a/easy-dataset-main/app/projects/[projectId]/eval-datasets/constants.js b/easy-dataset-main/app/projects/[projectId]/eval-datasets/constants.js new file mode 100644 index 0000000..5cc087b --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-datasets/constants.js @@ -0,0 +1,679 @@ +export const QUESTION_TYPES = [ + { value: 'true_false', label: 'eval.questionTypes.true_false', labelZh: '判断题' }, + { value: 'single_choice', label: 'eval.questionTypes.single_choice', labelZh: '单选题' }, + { value: 'multiple_choice', label: 'eval.questionTypes.multiple_choice', labelZh: '多选题' }, + { value: 'short_answer', label: 'eval.questionTypes.short_answer', labelZh: '短答案题' }, + { value: 'open_ended', label: 'eval.questionTypes.open_ended', labelZh: '开放式问题' } +]; + +export const FORMAT_PREVIEW = { + true_false: { + fields: ['question', 'correctAnswer'], + example: { + question: 'Artificial Intelligence is a branch of computer science', + correctAnswer: '✅ or ❌' + }, + description: 'correctAnswer must be "✅" (correct) or "❌" (incorrect)' + }, + single_choice: { + fields: ['question', 'options', 'correctAnswer'], + example: { + question: 'Which of the following is a core feature of deep learning?', + options: '["Option A", "Option B", "Option C", "Option D"]', + correctAnswer: 'B' + }, + description: 'options is an array of options, correctAnswer is the letter of the correct option (A/B/C/D)' + }, + multiple_choice: { + fields: ['question', 'options', 'correctAnswer'], + example: { + question: 'Which of the following are commonly used deep learning frameworks?', + options: '["TensorFlow", "PyTorch", "Excel", "Keras"]', + correctAnswer: '["A", "B", "D"]' + }, + description: 'options is an array of options, correctAnswer is an array of correct option letters' + }, + short_answer: { + fields: ['question', 'correctAnswer'], + example: { + question: 'What is the typical model structure used in deep learning?', + correctAnswer: 'Neural Network' + }, + description: 'correctAnswer is a short standard answer' + }, + open_ended: { + fields: ['question', 'correctAnswer'], + example: { + question: 'Analyze the main reasons for the success of deep learning in computer vision.', + correctAnswer: 'Reference answer content...' + }, + description: 'correctAnswer is a reference answer (can be long)' + } +}; + +// 获取 JSON 模板数据 +export const getJsonTemplateData = type => { + switch (type) { + case 'true_false': + return [ + { question: 'Artificial Intelligence is a branch of computer science', correctAnswer: '✅' }, + { question: 'Deep learning does not require large amounts of data for training', correctAnswer: '❌' } + ]; + case 'single_choice': + return [ + { + question: 'What is the core feature of deep learning?', + options: [ + 'Requires manual feature engineering', + 'Automatic feature learning', + 'Only handles structured data', + 'Does not need large amounts of data' + ], + correctAnswer: 'B' + }, + { + question: 'Which of the following is a commonly used deep learning framework?', + options: ['Excel', 'Word', 'TensorFlow', 'PowerPoint'], + correctAnswer: 'C' + } + ]; + case 'multiple_choice': + return [ + { + question: 'Which of the following are commonly used deep learning frameworks?', + options: ['TensorFlow', 'PyTorch', 'Excel', 'Keras', 'Word'], + correctAnswer: ['A', 'B', 'D'] + }, + { + question: 'Which of the following are main types of machine learning?', + options: ['Supervised Learning', 'Unsupervised Learning', 'Reinforcement Learning', 'Manual Learning'], + correctAnswer: ['A', 'B', 'C'] + } + ]; + case 'short_answer': + return [ + { question: 'What is the typical model structure used in deep learning?', correctAnswer: 'Neural Network' }, + { question: 'What is the maximum sample size mentioned in the text?', correctAnswer: '1000' } + ]; + case 'open_ended': + return [ + { + question: 'Analyze the main reasons for the success of deep learning in computer vision.', + correctAnswer: + 'The success of deep learning in computer vision can be explained from three dimensions: models, data, and computing power...' + }, + { + question: 'Explain the overfitting problem in machine learning and its solutions.', + correctAnswer: + 'Overfitting refers to the phenomenon where a model performs well on training data but poorly on new data...' + } + ]; + default: + return []; + } +}; + +// 获取 Excel 模板数据 +export const getExcelTemplateData = type => { + switch (type) { + case 'true_false': + return [ + { question: 'Artificial Intelligence is a branch of computer science', correctAnswer: '✅' }, + { question: 'Deep learning does not require large amounts of data for training', correctAnswer: '❌' } + ]; + case 'single_choice': + return [ + { + question: 'What is the core feature of deep learning?', + options: `["Requires manual feature engineering", "Automatic feature learning", "Only handles structured data", "Does not need large amounts of data"]`, + correctAnswer: 'B' + }, + { + question: 'Which of the following is a commonly used deep learning framework?', + options: `["Excel", "Word", "TensorFlow", "PowerPoint"]`, + correctAnswer: 'C' + } + ]; + case 'multiple_choice': + return [ + { + question: 'Which of the following are commonly used deep learning frameworks?', + options: `["TensorFlow", "PyTorch", "Excel", "Keras", "Word"]`, + correctAnswer: `["A", "B", "D"]` + }, + { + question: 'Which of the following are main types of machine learning?', + options: `["Supervised Learning", "Unsupervised Learning", "Reinforcement Learning", "Manual Learning"]`, + correctAnswer: `["A", "B", "C"]` + } + ]; + case 'short_answer': + return [ + { question: 'What is the typical model structure used in deep learning?', correctAnswer: 'Neural Network' }, + { question: 'What is the maximum sample size mentioned in the text?', correctAnswer: '1000' } + ]; + case 'open_ended': + return [ + { + question: 'Analyze the main reasons for the success of deep learning in computer vision.', + correctAnswer: + 'The success of deep learning in computer vision can be explained from three dimensions: models, data, and computing power...' + }, + { + question: 'Explain the overfitting problem in machine learning and its solutions.', + correctAnswer: + 'Overfitting refers to the phenomenon where a model performs well on training data but poorly on new data...' + } + ]; + default: + return []; + } +}; + +// 获取列宽配置 +export const getColumnWidths = type => { + if (type === 'single_choice' || type === 'multiple_choice') { + return [{ wch: 50 }, { wch: 25 }, { wch: 25 }, { wch: 25 }, { wch: 25 }, { wch: 25 }, { wch: 15 }]; + } + return [{ wch: 60 }, { wch: 40 }]; +}; + +export const DATA_SETS = [ + { + zh: '生物学', + en: 'Biology', + file: 'mmlu-pro/biology.json', + level: 'hard', + type: 'single_choice' + }, + { + zh: '商业', + en: 'Business', + file: 'mmlu-pro/business.json', + level: 'hard', + type: 'single_choice' + }, + { + zh: '化学', + en: 'Chemistry', + file: 'mmlu-pro/chemistry.json', + level: 'hard', + type: 'single_choice' + }, + { + zh: '计算机科学', + en: 'Computer Science', + file: 'mmlu-pro/computer_science.json', + level: 'hard', + type: 'single_choice' + }, + { + zh: '经济学', + en: 'Economics', + file: 'mmlu-pro/economics.json', + level: 'hard', + type: 'single_choice' + }, + { + zh: '工程学', + en: 'Engineering', + file: 'mmlu-pro/engineering.json', + level: 'hard', + type: 'single_choice' + }, + { + zh: '健康科学', + en: 'Health', + file: 'mmlu-pro/health.json', + level: 'hard', + type: 'single_choice' + }, + { + zh: '历史', + en: 'History', + file: 'mmlu-pro/history.json', + level: 'hard', + type: 'single_choice' + }, + { + zh: '法律', + en: 'Law', + file: 'mmlu-pro/law.json', + level: 'hard', + type: 'single_choice' + }, + { + zh: '数学', + en: 'Math', + file: 'mmlu-pro/math.json', + level: 'hard', + type: 'single_choice' + }, + { + zh: '其他', + en: 'Other', + file: 'mmlu-pro/other.json', + level: 'hard', + type: 'single_choice' + }, + { + zh: '哲学', + en: 'Philosophy', + file: 'mmlu-pro/philosophy.json', + level: 'hard', + type: 'single_choice' + }, + { + zh: '物理', + en: 'Physics', + file: 'mmlu-pro/physics.json', + level: 'hard', + type: 'single_choice' + }, + { + zh: '心理学', + en: 'Psychology', + file: 'mmlu-pro/psychology.json', + level: 'hard', + type: 'single_choice' + }, + { + zh: '抽象代数', + en: 'Abstract Algebra', + file: 'mmlu/abstract_algebra_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '解剖学', + en: 'Anatomy', + file: 'mmlu/anatomy_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '天文学', + en: 'Astronomy', + file: 'mmlu/astronomy_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '商业伦理', + en: 'Business Ethics', + file: 'mmlu/business_ethics_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '临床知识', + en: 'Clinical Knowledge', + file: 'mmlu/clinical_knowledge_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '大学生物', + en: 'College Biology', + file: 'mmlu/college_biology_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '大学化学', + en: 'College Chemistry', + file: 'mmlu/college_chemistry_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '大学计算机科学', + en: 'College Computer Science', + file: 'mmlu/college_computer_science_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '大学数学', + en: 'College Mathematics', + file: 'mmlu/college_mathematics_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '大学医学', + en: 'College Medicine', + file: 'mmlu/college_medicine_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '大学物理', + en: 'College Physics', + file: 'mmlu/college_physics_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '计算机安全', + en: 'Computer Security', + file: 'mmlu/computer_security_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '概念物理', + en: 'Conceptual Physics', + file: 'mmlu/conceptual_physics_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '计量经济学', + en: 'Econometrics', + file: 'mmlu/econometrics_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '电气工程', + en: 'Electrical Engineering', + file: 'mmlu/electrical_engineering_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '初等数学', + en: 'Elementary Mathematics', + file: 'mmlu/elementary_mathematics_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '形式逻辑', + en: 'Formal Logic', + file: 'mmlu/formal_logic_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '全球事实', + en: 'Global Facts', + file: 'mmlu/global_facts_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '高中生物', + en: 'High School Biology', + file: 'mmlu/high_school_biology_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '高中化学', + en: 'High School Chemistry', + file: 'mmlu/high_school_chemistry_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '高中计算机科学', + en: 'High School Computer Science', + file: 'mmlu/high_school_computer_science_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '高中欧洲历史', + en: 'High School European History', + file: 'mmlu/high_school_european_history_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '高中地理', + en: 'High School Geography', + file: 'mmlu/high_school_geography_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '高中政府与政治', + en: 'High School Government And Politics', + file: 'mmlu/high_school_government_and_politics_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '高中宏观经济学', + en: 'High School Macroeconomics', + file: 'mmlu/high_school_macroeconomics_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '高中数学', + en: 'High School Mathematics', + file: 'mmlu/high_school_mathematics_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '高中微观经济学', + en: 'High School Microeconomics', + file: 'mmlu/high_school_microeconomics_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '高中物理', + en: 'High School Physics', + file: 'mmlu/high_school_physics_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '高中心理学', + en: 'High School Psychology', + file: 'mmlu/high_school_psychology_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '高中统计学', + en: 'High School Statistics', + file: 'mmlu/high_school_statistics_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '高中美国历史', + en: 'High School Us History', + file: 'mmlu/high_school_us_history_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '高中世界历史', + en: 'High School World History', + file: 'mmlu/high_school_world_history_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '人类衰老', + en: 'Human Aging', + file: 'mmlu/human_aging_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '人类性学', + en: 'Human Sexuality', + file: 'mmlu/human_sexuality_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '国际法', + en: 'International Law', + file: 'mmlu/international_law_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '法理学', + en: 'Jurisprudence', + file: 'mmlu/jurisprudence_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '逻辑谬误', + en: 'Logical Fallacies', + file: 'mmlu/logical_fallacies_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '机器学习', + en: 'Machine Learning', + file: 'mmlu/machine_learning_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '管理学', + en: 'Management', + file: 'mmlu/management_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '市场营销', + en: 'Marketing', + file: 'mmlu/marketing_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '医学遗传学', + en: 'Medical Genetics', + file: 'mmlu/medical_genetics_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '杂项/综合', + en: 'Miscellaneous', + file: 'mmlu/miscellaneous_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '道德争议', + en: 'Moral Disputes', + file: 'mmlu/moral_disputes_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '道德场景', + en: 'Moral Scenarios', + file: 'mmlu/moral_scenarios_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '营养学', + en: 'Nutrition', + file: 'mmlu/nutrition_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '哲学', + en: 'Philosophy', + file: 'mmlu/philosophy_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '史前史', + en: 'Prehistory', + file: 'mmlu/prehistory_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '专业会计', + en: 'Professional Accounting', + file: 'mmlu/professional_accounting_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '专业法律', + en: 'Professional Law', + file: 'mmlu/professional_law_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '专业医学', + en: 'Professional Medicine', + file: 'mmlu/professional_medicine_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '专业心理学', + en: 'Professional Psychology', + file: 'mmlu/professional_psychology_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '公共关系', + en: 'Public Relations', + file: 'mmlu/public_relations_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '安全研究', + en: 'Security Studies', + file: 'mmlu/security_studies_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '社会学', + en: 'Sociology', + file: 'mmlu/sociology_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '美国外交政策', + en: 'Us Foreign Policy', + file: 'mmlu/us_foreign_policy_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '病毒学', + en: 'Virology', + file: 'mmlu/virology_test.json', + level: 'easy', + type: 'single_choice' + }, + { + zh: '世界宗教测试', + en: 'World Religions', + file: 'mmlu/world_religions_test.json', + level: 'easy', + type: 'single_choice' + } +]; diff --git a/easy-dataset-main/app/projects/[projectId]/eval-datasets/hooks/useEvalDatasets.js b/easy-dataset-main/app/projects/[projectId]/eval-datasets/hooks/useEvalDatasets.js new file mode 100644 index 0000000..401b743 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-datasets/hooks/useEvalDatasets.js @@ -0,0 +1,220 @@ +'use client'; + +import { useState, useCallback, useEffect, useRef } from 'react'; + +/** + * Eval datasets list hook + * @param {string} projectId + */ +export default function useEvalDatasets(projectId) { + const [data, setData] = useState({ items: [], total: 0, stats: null, totalPages: 1 }); + const [loading, setLoading] = useState(true); + const [searching, setSearching] = useState(false); + const [error, setError] = useState(null); + const isInitialMount = useRef(true); + const abortRef = useRef(null); + + const [page, setPage] = useState(1); + const [pageSize, setPageSize] = useState(20); + + const [questionType, setQuestionType] = useState(''); + const [keyword, setKeyword] = useState(''); + const [debouncedKeyword, setDebouncedKeyword] = useState(''); + const [chunkId, setChunkId] = useState(''); + const [tags, setTags] = useState([]); + + const setQuestionTypeWithReset = useCallback(value => { + setQuestionType(value); + setPage(1); + }, []); + + const setKeywordWithReset = useCallback(value => { + setKeyword(value); + }, []); + + const setChunkIdWithReset = useCallback(value => { + setChunkId(value); + setPage(1); + }, []); + + const setTagsWithReset = useCallback(value => { + setTags(value); + setPage(1); + }, []); + + const [viewMode, setViewMode] = useState('card'); + const [selectedIds, setSelectedIds] = useState([]); + + useEffect(() => { + const timer = setTimeout(() => { + setDebouncedKeyword(keyword); + if (keyword !== debouncedKeyword) { + setPage(1); + } + }, 500); + + return () => clearTimeout(timer); + }, [keyword]); + + const fetchDataRef = useRef(null); + fetchDataRef.current = async (showLoading = true, options = {}) => { + if (!projectId) return; + + const includeStats = options.forceStats || showLoading; + + if (abortRef.current) { + abortRef.current.abort(); + } + const controller = new AbortController(); + abortRef.current = controller; + + if (showLoading) { + setLoading(true); + } else { + setSearching(true); + } + setError(null); + + try { + const params = new URLSearchParams({ + page: String(page), + pageSize: String(pageSize), + includeStats: includeStats ? 'true' : 'false' + }); + + if (questionType) params.append('questionType', questionType); + if (debouncedKeyword) params.append('keyword', debouncedKeyword); + if (chunkId) params.append('chunkId', chunkId); + if (tags.length > 0) { + tags.forEach(tag => params.append('tags', tag)); + } + + const response = await fetch(`/api/projects/${projectId}/eval-datasets?${params}`, { + signal: controller.signal + }); + + if (!response.ok) { + throw new Error('Failed to fetch eval datasets'); + } + + const result = await response.json(); + setData(prev => ({ + ...result, + stats: result.stats ?? prev.stats + })); + } catch (err) { + if (err?.name === 'AbortError') return; + setError(err.message); + } finally { + if (abortRef.current === controller) { + abortRef.current = null; + } + + if (showLoading) { + setLoading(false); + } else { + setSearching(false); + } + } + }; + + const fetchData = useCallback((showLoading = true, options = {}) => { + return fetchDataRef.current?.(showLoading, options); + }, []); + + useEffect(() => { + if (isInitialMount.current) { + isInitialMount.current = false; + fetchDataRef.current?.(true, { forceStats: true }); + } else { + fetchDataRef.current?.(false, { forceStats: false }); + } + }, [projectId, page, pageSize, questionType, debouncedKeyword, chunkId, tags]); + + useEffect(() => { + return () => { + if (abortRef.current) { + abortRef.current.abort(); + } + }; + }, []); + + const deleteItems = useCallback( + async ids => { + if (!ids || ids.length === 0) return; + + const response = await fetch(`/api/projects/${projectId}/eval-datasets`, { + method: 'DELETE', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ ids }) + }); + + if (!response.ok) { + throw new Error('Failed to delete items'); + } + + await fetchData(true, { forceStats: true }); + setSelectedIds([]); + + return await response.json(); + }, + [projectId, fetchData] + ); + + const resetFilters = useCallback(() => { + setQuestionType(''); + setKeyword(''); + setChunkId(''); + setTags([]); + setPage(1); + }, []); + + const toggleSelect = useCallback(id => { + setSelectedIds(prev => (prev.includes(id) ? prev.filter(i => i !== id) : [...prev, id])); + }, []); + + const toggleSelectAll = useCallback(() => { + if (selectedIds.length === data.items.length) { + setSelectedIds([]); + } else { + setSelectedIds(data.items.map(item => item.id)); + } + }, [selectedIds, data.items]); + + return { + items: data.items, + total: data.total, + stats: data.stats, + totalPages: data.totalPages || 1, + + loading, + searching, + error, + + page, + pageSize, + setPage, + setPageSize, + + questionType, + keyword, + chunkId, + tags, + setQuestionType: setQuestionTypeWithReset, + setKeyword: setKeywordWithReset, + setChunkId: setChunkIdWithReset, + setTags: setTagsWithReset, + resetFilters, + + viewMode, + setViewMode, + + selectedIds, + toggleSelect, + toggleSelectAll, + setSelectedIds, + + fetchData, + deleteItems + }; +} diff --git a/easy-dataset-main/app/projects/[projectId]/eval-datasets/hooks/useExportEvalDatasets.js b/easy-dataset-main/app/projects/[projectId]/eval-datasets/hooks/useExportEvalDatasets.js new file mode 100644 index 0000000..70d7f4f --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-datasets/hooks/useExportEvalDatasets.js @@ -0,0 +1,189 @@ +'use client'; + +import { useState, useCallback, useEffect } from 'react'; + +/** + * 评估数据集导出 Hook + * 管理导出对话框状态、筛选条件和导出逻辑 + */ +export default function useExportEvalDatasets(projectId, stats = {}) { + // 对话框状态 + const [dialogOpen, setDialogOpen] = useState(false); + const [exporting, setExporting] = useState(false); + const [error, setError] = useState(''); + + // 导出配置 + const [format, setFormat] = useState('json'); + const [questionTypes, setQuestionTypes] = useState([]); + const [selectedTags, setSelectedTags] = useState([]); + const [keyword, setKeyword] = useState(''); + + // 预览数据 + const [previewTotal, setPreviewTotal] = useState(0); + const [previewLoading, setPreviewLoading] = useState(false); + + // 从 stats 中获取可用的标签列表 + const availableTags = stats?.byTag ? Object.keys(stats.byTag).sort() : []; + + // 当筛选条件变化时,获取预览数量 + useEffect(() => { + if (!dialogOpen || !projectId) return; + + const controller = new AbortController(); + + const fetchPreview = async () => { + try { + setPreviewLoading(true); + const params = new URLSearchParams(); + + if (questionTypes.length > 0) { + questionTypes.forEach(t => params.append('questionTypes', t)); + } + if (selectedTags.length > 0) { + selectedTags.forEach(t => params.append('tags', t)); + } + if (keyword.trim()) { + params.append('keyword', keyword.trim()); + } + + const response = await fetch(`/api/projects/${projectId}/eval-datasets/export?${params.toString()}`, { + signal: controller.signal + }); + + if (response.ok) { + const result = await response.json(); + setPreviewTotal(result?.data?.total ?? 0); + } + } catch (err) { + if (err.name !== 'AbortError') { + console.error('获取导出预览失败:', err); + } + } finally { + setPreviewLoading(false); + } + }; + + fetchPreview(); + + return () => { + controller.abort(); + }; + }, [dialogOpen, projectId, questionTypes, selectedTags, keyword]); + + // 打开对话框 + const openDialog = useCallback(() => { + setDialogOpen(true); + setError(''); + }, []); + + // 关闭对话框 + const closeDialog = useCallback(() => { + if (exporting) return; + setDialogOpen(false); + // 重置状态 + setFormat('json'); + setQuestionTypes([]); + setSelectedTags([]); + setKeyword(''); + setError(''); + }, [exporting]); + + // 重置筛选条件 + const resetFilters = useCallback(() => { + setQuestionTypes([]); + setSelectedTags([]); + setKeyword(''); + }, []); + + // 执行导出 + const handleExport = useCallback(async () => { + if (previewTotal === 0) { + setError('没有符合条件的数据可导出'); + return; + } + + try { + setExporting(true); + setError(''); + + const response = await fetch(`/api/projects/${projectId}/eval-datasets/export`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + format, + questionTypes, + tags: selectedTags, + keyword: keyword.trim() + }) + }); + + if (!response.ok) { + const result = await response.json(); + throw new Error(result.error || '导出失败'); + } + + // 获取文件名 + const contentDisposition = response.headers.get('Content-Disposition'); + let filename = `eval-datasets-${Date.now()}.${format}`; + if (contentDisposition) { + const match = contentDisposition.match(/filename="?([^"]+)"?/); + if (match) { + filename = match[1]; + } + } + + // 下载文件 + const blob = await response.blob(); + const url = window.URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = filename; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + window.URL.revokeObjectURL(url); + + // 导出成功,关闭对话框 + closeDialog(); + + return true; + } catch (err) { + console.error('导出失败:', err); + setError(err.message || '导出失败'); + return false; + } finally { + setExporting(false); + } + }, [projectId, format, questionTypes, selectedTags, keyword, previewTotal, closeDialog]); + + return { + // 对话框状态 + dialogOpen, + openDialog, + closeDialog, + + // 导出状态 + exporting, + error, + setError, + + // 导出配置 + format, + setFormat, + questionTypes, + setQuestionTypes, + selectedTags, + setSelectedTags, + keyword, + setKeyword, + + // 预览数据 + previewTotal, + previewLoading, + availableTags, + + // 操作 + resetFilters, + handleExport + }; +} diff --git a/easy-dataset-main/app/projects/[projectId]/eval-datasets/page.js b/easy-dataset-main/app/projects/[projectId]/eval-datasets/page.js new file mode 100644 index 0000000..4ee328b --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-datasets/page.js @@ -0,0 +1,322 @@ +'use client'; + +import { useState } from 'react'; +import { useParams, useRouter } from 'next/navigation'; +import { + Box, + Container, + Typography, + Grid, + Pagination, + CircularProgress, + Alert, + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + Snackbar +} from '@mui/material'; +import { Masonry } from '@mui/lab'; +import { useTranslation } from 'react-i18next'; + +import useEvalDatasets from './hooks/useEvalDatasets'; +import useExportEvalDatasets from './hooks/useExportEvalDatasets'; +import EvalToolbar from './components/EvalToolbar'; +import EvalDatasetCard from './components/EvalDatasetCard'; +import EvalDatasetList from './components/EvalDatasetList'; +import ImportDialog from './components/ImportDialog'; +import BuiltinDatasetDialog from './components/BuiltinDatasetDialog'; +import ExportEvalDialog from './components/ExportEvalDialog'; + +export default function EvalDatasetsPage() { + const { projectId } = useParams(); + const router = useRouter(); + const { t } = useTranslation(); + + const { + items, + total, + stats, + totalPages, + loading, + searching, + error, + page, + setPage, + questionType, + setQuestionType, + tags, + setTags, + keyword, + setKeyword, + viewMode, + setViewMode, + selectedIds, + toggleSelect, + toggleSelectAll, + fetchData, + deleteItems + } = useEvalDatasets(projectId); + + // 导出 Hook + const { + dialogOpen: exportDialogOpen, + openDialog: openExportDialog, + closeDialog: closeExportDialog, + exporting, + error: exportError, + format: exportFormat, + setFormat: setExportFormat, + questionTypes: exportQuestionTypes, + setQuestionTypes: setExportQuestionTypes, + selectedTags: exportSelectedTags, + setSelectedTags: setExportSelectedTags, + keyword: exportKeyword, + setKeyword: setExportKeyword, + previewTotal, + previewLoading, + availableTags: exportAvailableTags, + resetFilters: resetExportFilters, + handleExport + } = useExportEvalDatasets(projectId, stats); + + // 删除确认对话框 + const [deleteDialog, setDeleteDialog] = useState({ open: false, ids: [] }); + + // 导入对话框 + const [importDialogOpen, setImportDialogOpen] = useState(false); + const [builtinImportOpen, setBuiltinImportOpen] = useState(false); + + // Toast 提示 + const [toast, setToast] = useState({ open: false, message: '', severity: 'success' }); + + // 处理导入成功 + const handleImportSuccess = result => { + setToast({ + open: true, + message: t('evalDatasets.import.successMessage', { count: result.total }), + severity: 'success' + }); + fetchData(); // 刷新数据 + }; + + // 处理删除 + const handleDelete = async ids => { + setDeleteDialog({ open: true, ids: Array.isArray(ids) ? ids : [ids] }); + }; + + const confirmDelete = async () => { + try { + await deleteItems(deleteDialog.ids); + setDeleteDialog({ open: false, ids: [] }); + } catch (err) { + console.error('Delete failed:', err); + } + }; + + // 处理编辑 + const handleEdit = item => { + router.push(`/projects/${projectId}/eval-datasets/${item.id}`); + }; + + // 处理查看 + const handleView = item => { + router.push(`/projects/${projectId}/eval-datasets/${item.id}`); + }; + + return ( + + {/* 错误提示 */} + {error && ( + + {error} + + )} + + {/* 工具栏(包含统计筛选) */} + handleDelete(selectedIds)} + stats={stats} + questionType={questionType} + onTypeChange={setQuestionType} + tags={tags} + onTagsChange={setTags} + onRefresh={fetchData} + loading={loading} + onImport={() => setImportDialogOpen(true)} + onBuiltinImport={() => setBuiltinImportOpen(true)} + onExport={openExportDialog} + /> + + {/* 加载状态 */} + {loading && ( + + + + )} + + {/* 内容区域 */} + {!loading && ( + + {/* 搜索加载遮罩 */} + {searching && ( + + + + )} + + {viewMode === 'card' ? ( + + + {items.map(item => ( + + ))} + + + ) : ( + + + + )} + + {/* 空状态 */} + {items.length === 0 && ( + + + {t('eval.noData')} + + + {t('eval.noDataHint')} + + + )} + + {/* 分页 */} + {totalPages > 1 && ( + + setPage(value)} + color="primary" + showFirstButton + showLastButton + /> + + )} + + )} + + {/* 删除确认对话框 */} + setDeleteDialog({ open: false, ids: [] })}> + {t('eval.deleteConfirmTitle')} + + {t('eval.deleteConfirmMessage', { count: deleteDialog.ids.length })} + + + + + + + + {/* 导入对话框 */} + setImportDialogOpen(false)} + projectId={projectId} + onSuccess={handleImportSuccess} + /> + + {/* 内置数据集导入对话框 */} + setBuiltinImportOpen(false)} + projectId={projectId} + onSuccess={handleImportSuccess} + /> + + {/* 导出对话框 */} + + + {/* Toast 提示 */} + setToast({ ...toast, open: false })} + anchorOrigin={{ vertical: 'top', horizontal: 'center' }} + > + setToast({ ...toast, open: false })}> + {toast.message} + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/eval-tasks/[taskId]/components/EvalHeader.js b/easy-dataset-main/app/projects/[projectId]/eval-tasks/[taskId]/components/EvalHeader.js new file mode 100644 index 0000000..904810f --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-tasks/[taskId]/components/EvalHeader.js @@ -0,0 +1,140 @@ +'use client'; + +import { Box, Paper, Typography, Chip, Grid, Divider } from '@mui/material'; +import SmartToyIcon from '@mui/icons-material/SmartToy'; +import AccessTimeIcon from '@mui/icons-material/AccessTime'; +import { detailStyles } from '../detailStyles'; +import { useTranslation } from 'react-i18next'; +import { getModelIcon } from '@/lib/util/modelIcon'; + +export default function EvalHeader({ task, stats, filterCorrect, onFilterCorrectSelect }) { + const { t } = useTranslation(); + + if (!task) return null; + + const { modelInfo, createAt, status, detail } = task; + const score = detail?.finalScore || 0; + const isPass = score >= 60; + const totalTime = task.endTime ? Math.floor((new Date(task.endTime) - new Date(task.createAt)) / 1000) : 0; + + const incorrectCount = (stats?.totalQuestions || 0) - (stats?.correctCount || 0); + + // 获取教师模型信息 + const judgeModelId = detail?.judgeModelId; + const judgeProviderId = detail?.judgeProviderId; + const hasJudgeModel = judgeModelId && judgeProviderId; + + return ( + + + {/* 左侧:模型信息 */} + + + {modelInfo?.modelId + + + + {modelInfo?.providerName || modelInfo?.providerId} / {modelInfo?.modelName || modelInfo?.modelId} + + + {hasJudgeModel && ( + + )} + + + {new Date(createAt).toLocaleString()} + {totalTime > 0 && ` ${t('evalTasks.durationFormat', { time: totalTime })}`} + + + + + + {/* 中间:统计概览 (增加点击筛选) */} + + onFilterCorrectSelect(null)} + sx={{ + ...detailStyles.statBox, + cursor: 'pointer', + bgcolor: filterCorrect === null ? 'rgba(25, 118, 210, 0.08)' : 'background.default', + border: filterCorrect === null ? '1px solid' : '1px solid transparent', + borderColor: 'primary.main', + transition: 'all 0.2s' + }} + > + + {stats?.totalQuestions || 0} + + + {t('evalTasks.totalQuestionsLabel')} + + + onFilterCorrectSelect(true)} + sx={{ + ...detailStyles.statBox, + cursor: 'pointer', + bgcolor: filterCorrect === true ? 'rgba(46, 125, 50, 0.08)' : 'background.default', + border: filterCorrect === true ? '1px solid' : '1px solid transparent', + borderColor: 'success.main', + transition: 'all 0.2s' + }} + > + + {stats?.correctCount || 0} + + + {t('evalTasks.correctLabel')} + + + onFilterCorrectSelect(false)} + sx={{ + ...detailStyles.statBox, + cursor: 'pointer', + bgcolor: filterCorrect === false ? 'rgba(211, 47, 47, 0.08)' : 'background.default', + border: filterCorrect === false ? '1px solid' : '1px solid transparent', + borderColor: 'error.main', + transition: 'all 0.2s' + }} + > + + {incorrectCount} + + + {t('evalTasks.incorrectLabel')} + + + + + {/* 右侧:分数印章 */} + + {score.toFixed(1)} + SCORE + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/eval-tasks/[taskId]/components/EvalStats.js b/easy-dataset-main/app/projects/[projectId]/eval-tasks/[taskId]/components/EvalStats.js new file mode 100644 index 0000000..ce7ed08 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-tasks/[taskId]/components/EvalStats.js @@ -0,0 +1,95 @@ +'use client'; + +import { Box, Grid, Typography, LinearProgress } from '@mui/material'; +import { detailStyles } from '../detailStyles'; +import { useTranslation } from 'react-i18next'; + +const QUESTION_TYPE_LABELS = { + true_false: 'eval.questionTypes.true_false', + single_choice: 'eval.questionTypes.single_choice', + multiple_choice: 'eval.questionTypes.multiple_choice', + short_answer: 'eval.questionTypes.short_answer', + open_ended: 'eval.questionTypes.open_ended' +}; + +export default function EvalStats({ stats, currentFilter, onFilterSelect }) { + const { t } = useTranslation(); + + if (!stats?.byType || Object.keys(stats.byType).length === 0) return null; + + return ( + + + {Object.entries(stats.byType).map(([type, typeStats]) => { + const accuracy = typeStats.total > 0 ? (typeStats.correct / typeStats.total) * 100 : 0; + + const isSelected = currentFilter === type; + + return ( + + onFilterSelect(isSelected ? null : type)} + sx={{ + ...detailStyles.typeStatsItem, + cursor: 'pointer', + transition: 'all 0.2s', + bgcolor: isSelected ? 'primary.light' : '#fff', + borderColor: isSelected ? 'primary.main' : '#eee', + '&:hover': { + transform: 'translateY(-2px)', + boxShadow: '0 4px 12px rgba(0,0,0,0.1)', + borderColor: 'primary.main' + }, + '& *': { + color: isSelected ? 'primary.contrastText' : undefined + } + }} + > + + {t(QUESTION_TYPE_LABELS[type] || type)} + + + {typeStats.correct} / {typeStats.total} + + + = 60 ? 'success' : 'error'} + /> + + {accuracy.toFixed(0)}% + + + + + ); + })} + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/eval-tasks/[taskId]/components/QuestionCard.js b/easy-dataset-main/app/projects/[projectId]/eval-tasks/[taskId]/components/QuestionCard.js new file mode 100644 index 0000000..5d1f9e4 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-tasks/[taskId]/components/QuestionCard.js @@ -0,0 +1,362 @@ +'use client'; + +import { useState, useRef, useEffect } from 'react'; +import { Box, Typography, Chip, Paper, Button } from '@mui/material'; +import CheckIcon from '@mui/icons-material/Check'; +import CloseIcon from '@mui/icons-material/Close'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import ExpandLessIcon from '@mui/icons-material/ExpandLess'; +import AccessTimeIcon from '@mui/icons-material/AccessTime'; +import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline'; +import ReactMarkdown from 'react-markdown'; +import { detailStyles } from '../detailStyles'; +import { useTranslation } from 'react-i18next'; +import 'github-markdown-css/github-markdown-light.css'; + +// 答题状态常量 +const EVAL_STATUS = { + SUCCESS: 0, + FORMAT_ERROR: 1, + API_ERROR: 2 +}; + +// 状态标签配置 +const STATUS_CONFIG = { + [EVAL_STATUS.SUCCESS]: { label: 'evalTasks.statusSuccess', color: 'success' }, + [EVAL_STATUS.FORMAT_ERROR]: { label: 'evalTasks.statusFormatError', color: 'warning' }, + [EVAL_STATUS.API_ERROR]: { label: 'evalTasks.statusApiError', color: 'error' } +}; + +export default function QuestionCard({ result, index, task }) { + const { t } = useTranslation(); + const { + evalDataset, + modelAnswer, + isCorrect, + score, + judgeResponse, + duration = 0, + status = 0, + errorMessage = '' + } = result; + const { question, questionType, options, correctAnswer } = evalDataset; + + const [isExpanded, setIsExpanded] = useState(false); + const [shouldShowExpand, setShouldShowExpand] = useState(false); + const contentRef = useRef(null); + + const [isCorrectExpanded, setIsCorrectExpanded] = useState(false); + const [shouldShowCorrectExpand, setShouldShowCorrectExpand] = useState(false); + const correctContentRef = useRef(null); + + // 检查内容是否超过高度限制 + useEffect(() => { + if (contentRef.current) { + const hasOverflow = contentRef.current.scrollHeight > 200; + setShouldShowExpand(hasOverflow); + } + }, [modelAnswer]); + + useEffect(() => { + if (correctContentRef.current) { + const hasOverflow = correctContentRef.current.scrollHeight > 200; + setShouldShowCorrectExpand(hasOverflow); + } + }, [correctAnswer]); + + // 解析选项 + let parsedOptions = []; + if (questionType === 'single_choice' || questionType === 'multiple_choice') { + try { + parsedOptions = JSON.parse(options); + } catch (e) { + parsedOptions = options ? [options] : []; + } + } else if (questionType === 'true_false') { + parsedOptions = ['True', 'False']; + } + + // 格式化答案显示 + const formatAnswer = ans => { + if (!ans) return '-'; + return String(ans); + }; + + // 判断选项状态 + const getOptionStatus = (optionText, idx) => { + const letter = String.fromCharCode(65 + idx); + const normModelAns = String(modelAnswer).trim(); + const normCorrectAns = String(correctAnswer).trim(); + + let isSelected = false; + let isCorrectOption = false; + + if (questionType === 'true_false') { + // 判断题:A 对应 ✅/True,B 对应 ❌/False + const isTrueOption = idx === 0; + const isFalseOption = idx === 1; + + isSelected = + (isTrueOption && (normModelAns === '✅' || normModelAns.toUpperCase() === 'TRUE')) || + (isFalseOption && (normModelAns === '❌' || normModelAns.toUpperCase() === 'FALSE')); + + isCorrectOption = + (isTrueOption && (normCorrectAns === '✅' || normCorrectAns.toUpperCase() === 'TRUE')) || + (isFalseOption && (normCorrectAns === '❌' || normCorrectAns.toUpperCase() === 'FALSE')); + } else { + // 选择题逻辑 + const normModelAnsUpper = normModelAns.toUpperCase(); + const normCorrectAnsUpper = normCorrectAns.toUpperCase(); + const normOptionText = String(optionText).toUpperCase(); + + isSelected = normModelAnsUpper.includes(letter) || normModelAnsUpper.includes(normOptionText); + isCorrectOption = normCorrectAnsUpper.includes(letter) || normCorrectAnsUpper.includes(normOptionText); + } + + return { isSelected, isCorrectOption }; + }; + + // 解析 AI 点评内容 + const getJudgeDisplayContent = content => { + if (!content) return ''; + try { + // 尝试从 markdown 代码块中提取 JSON + const jsonMatch = content.match(/\{[\s\S]*?\}/); + if (jsonMatch) { + const parsed = JSON.parse(jsonMatch[0]); + if (parsed.reason) return parsed.reason; + } + // 尝试直接解析 + const parsed = JSON.parse(content); + if (parsed.reason) return parsed.reason; + } catch (e) { + // 解析失败,返回原内容 + } + return content; + }; + + return ( + + {/* 判卷标记 (红勾/红叉) - 绝对定位 */} + + {isCorrect ? : } + + + {/* 题号与类型标签 */} + + + {index + 1} + + + + {/* 答题耗时 */} + {duration > 0 && ( + } + label={duration >= 1000 ? `${(duration / 1000).toFixed(1)}s` : `${duration}ms`} + size="small" + variant="outlined" + sx={{ height: 24, '& .MuiChip-label': { px: 0.75, fontSize: '0.75rem' } }} + /> + )} + + {/* 答题状态 */} + {status !== EVAL_STATUS.SUCCESS && ( + } + label={t( + STATUS_CONFIG[status]?.label || 'evalTasks.statusUnknown', + status === EVAL_STATUS.FORMAT_ERROR ? t('evalTasks.statusFormatError') : t('evalTasks.statusApiError') + )} + size="small" + color={STATUS_CONFIG[status]?.color || 'default'} + variant="outlined" + sx={{ height: 24, '& .MuiChip-label': { px: 0.75, fontSize: '0.75rem' } }} + /> + )} + + + {/* 题目内容 */} + + {question} + + + {/* 选项区域 (仅选择题/判断题) */} + {parsedOptions.length > 0 && ( + + {parsedOptions.map((opt, idx) => { + const letter = String.fromCharCode(65 + idx); + const { isSelected, isCorrectOption } = getOptionStatus(opt, idx); + + return ( + + {letter}. + {opt} + + ); + })} + + )} + + {/* 答案对比区域 */} + + + {t('evalTasks.modelAnswer')} + + + + {questionType === 'open_ended' || questionType === 'short_answer' ? ( +
+ {modelAnswer || ''} +
+ ) : ( + + {formatAnswer(modelAnswer)} + + )} + + {/* 展开/收起 遮罩和按钮 */} + {shouldShowExpand && !isExpanded && ( + + + + )} +
+ + {isExpanded && shouldShowExpand && ( + + + + )} + + + + {t('evalTasks.correctAnswer')} + + + {questionType === 'open_ended' || questionType === 'short_answer' ? ( +
+ {correctAnswer || ''} +
+ ) : ( + + {formatAnswer(correctAnswer)} + + )} + + {/* 展开/收起 遮罩和按钮 */} + {shouldShowCorrectExpand && !isCorrectExpanded && ( + + + + )} +
+ + {isCorrectExpanded && shouldShowCorrectExpand && ( + + + + )} +
+
+ + {/* 错误信息显示 */} + {errorMessage && ( + + + {errorMessage} + + + )} + + {/* 教师点评 (气泡样式) */} + {judgeResponse && ( + + + {t('evalTasks.judgeComment')} + + {getJudgeDisplayContent(judgeResponse)} + + {/* 得分显示(如果是主观题) */} + {(questionType === 'short_answer' || questionType === 'open_ended') && ( + + {(score * 100).toFixed(0)} {t('evalTasks.scoreUnit')} + + )} + + + )} +
+ ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/eval-tasks/[taskId]/detailStyles.js b/easy-dataset-main/app/projects/[projectId]/eval-tasks/[taskId]/detailStyles.js new file mode 100644 index 0000000..09c3308 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-tasks/[taskId]/detailStyles.js @@ -0,0 +1,281 @@ +export const detailStyles = { + // 页面背景 + pageContainer: { + py: 4, + minHeight: '100vh', + bgcolor: '#f5f7fa' + }, + + // 头部概览卡片 + headerCard: { + mb: 3, + borderRadius: 3, + overflow: 'hidden', + boxShadow: '0 4px 20px rgba(0,0,0,0.05)', + border: 'none' + }, + + headerContent: { + p: 3, + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + flexWrap: 'wrap', + gap: 2 + }, + + // 分数印章效果 + scoreStamp: (score, isPass) => ({ + width: 110, + height: 110, + borderRadius: '50%', + border: `4px double ${isPass ? '#2e7d32' : '#d32f2f'}`, + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + color: isPass ? '#2e7d32' : '#d32f2f', + transform: 'rotate(-15deg)', + maskImage: + 'url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZmlsdGVyIGlkPSJub2lzZSI+PGZlVHVyYnVsZW5jZSB0eXBlPSJmcmFjdGFsTm9pc2UiIGJhc2VGcmVxdWVuY3k9IjAuNSIgbnVtT2N0YXZlcz0iMyIgc3RpdGNoVGlsZXM9InN0aXRjaCIvPjwvZmlsdGVyPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbHRlcj0idXJsKCNub2lzZSkiIG9wYWNpdHk9IjAuNSIvPjwvc3ZnPg==")', // 简单的噪点遮罩模拟印章纹理(可选) + opacity: 0.9, + boxShadow: 'inset 0 0 10px rgba(0,0,0,0.1)', + flexShrink: 0 + }), + + scoreValue: { + fontSize: '2.2rem', + fontWeight: 900, + lineHeight: 1.1, + fontFamily: '"Comic Sans MS", "Chalkboard SE", sans-serif', + mb: 0.2 + }, + + scoreLabel: { + fontSize: '0.75rem', + fontWeight: 700, + textTransform: 'uppercase', + letterSpacing: 1 + }, + + // 统计卡片 + statBox: { + textAlign: 'center', + p: 2, + borderRadius: 2, + bgcolor: 'background.default', + minWidth: 100 + }, + + // 试卷主体 + paperContainer: { + width: '100%', + mx: 'auto', + bgcolor: '#fff', + boxShadow: '0 8px 30px rgba(0,0,0,0.08)', + borderRadius: 2, + overflow: 'hidden', + position: 'relative', + border: '1px solid #e0e0e0' + }, + + paperHeader: { + p: 4, + borderBottom: '2px solid #000', + textAlign: 'center', + position: 'relative', + bgcolor: '#fff' + }, + + paperTitle: { + fontSize: '1.75rem', + fontWeight: 700, + mb: 1, + fontFamily: '"Songti SC", "SimSun", serif' // 宋体增强试卷感 + }, + + paperSubTitle: { + color: 'text.secondary', + fontSize: '0.9rem' + }, + + // 题目部分 + questionSection: { + p: 0 + }, + + questionCard: isCorrect => ({ + p: 3, + height: '100%', // 确保在Grid中高度撑满 + borderBottom: '1px solid #f0f0f0', // 减淡边框颜色 + position: 'relative', + transition: 'all 0.2s ease', + '&:hover': { + bgcolor: '#fafafa' + } + }), + + questionIndex: { + position: 'absolute', + left: 20, + top: 24, + width: 32, + height: 32, + borderRadius: '50%', // 圆形题号 + border: '1px solid #ddd', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + fontWeight: 'bold', + color: 'text.secondary', + bgcolor: '#fff', + zIndex: 1, + fontSize: '0.875rem' + }, + + // 判卷标记(红勾/红叉) + markIcon: isCorrect => ({ + position: 'absolute', + right: 20, + top: 20, + fontSize: '3rem', + color: isCorrect ? '#2e7d32' : '#d32f2f', + opacity: 0.8, + transform: 'rotate(10deg)', + fontFamily: '"Comic Sans MS", "Chalkboard SE", sans-serif' + }), + + // 题目内容 + questionContent: { + fontSize: '1.1rem', + fontWeight: 500, + lineHeight: 1.6, + mb: 2, + color: '#333' + }, + + // 选项区域 + optionsContainer: { + pl: 2, + mb: 2 + }, + + optionItem: (isSelected, isCorrectOption) => ({ + p: 1, + mb: 0.5, + borderRadius: 1, + bgcolor: isCorrectOption + ? 'rgba(46, 125, 50, 0.1)' // 正确选项显示绿色背景 + : isSelected + ? 'rgba(211, 47, 47, 0.1)' + : 'transparent', // 错误选中显示红色背景 + color: isCorrectOption ? 'success.main' : isSelected ? 'error.main' : 'text.primary', + display: 'flex', + alignItems: 'flex-start', + gap: 1 + }), + + // 答案区域 + answerSection: { + mt: 2, + p: 2, + bgcolor: '#f8f9fa', + borderRadius: 2, + borderLeft: '4px solid #ddd', + position: 'relative' + }, + + // Markdown 展示区域 + markdownContainer: isExpanded => ({ + maxHeight: isExpanded ? 'none' : '200px', + overflow: 'hidden', + position: 'relative', + '& .markdown-body': { + fontSize: '0.9rem', + lineHeight: 1.6, + bgcolor: 'transparent', + color: 'inherit', + padding: 0 + } + }), + + // 展开收起遮罩层(渐变效果) + expandMask: { + position: 'absolute', + bottom: 0, + left: 0, + right: 0, + height: '60px', + background: 'linear-gradient(transparent, #f8f9fa)', + display: 'flex', + alignItems: 'flex-end', + justifyContent: 'center', + pb: 1, + zIndex: 1 + }, + + expandButton: { + fontSize: '0.75rem', + textTransform: 'none', + color: 'primary.main', + bgcolor: 'rgba(255,255,255,0.8)', + '&:hover': { + bgcolor: 'rgba(255,255,255,1)' + }, + boxShadow: '0 2px 8px rgba(0,0,0,0.05)', + borderRadius: '16px', + px: 2 + }, + + // 教师点评样式 + judgeComment: { + mt: 2, + position: 'relative', + fontFamily: '"KaiTi", "KaiTi_GB2312", serif', // 楷体模拟手写点评 + color: '#d32f2f', + padding: '10px 20px', + border: '1px solid #d32f2f', + borderRadius: '20px 20px 20px 4px', // 气泡形状 + maxWidth: 'fit-content', + bgcolor: '#fff5f5' + }, + + judgeLabel: { + fontSize: '0.8rem', + opacity: 0.7, + fontStyle: 'italic', + mb: 0.5 + }, + + // 按题型统计样式 + typeStatsItem: { + textAlign: 'center', + p: 2, + bgcolor: '#fff', + borderRadius: 2, + border: '1px solid #eee', + boxShadow: '0 2px 8px rgba(0,0,0,0.03)', + height: '100%', + display: 'flex', + flexDirection: 'column', + justifyContent: 'center' + }, + + typeStatsLabel: { + fontSize: '0.85rem', + color: 'text.secondary', + mb: 1 + }, + + typeStatsScore: { + fontWeight: 700, + fontSize: '1.25rem', + color: 'text.primary' + }, + + typeStatsPercent: { + fontSize: '0.75rem', + color: 'text.secondary', + fontWeight: 500 + } +}; diff --git a/easy-dataset-main/app/projects/[projectId]/eval-tasks/[taskId]/page.js b/easy-dataset-main/app/projects/[projectId]/eval-tasks/[taskId]/page.js new file mode 100644 index 0000000..b9550db --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-tasks/[taskId]/page.js @@ -0,0 +1,214 @@ +'use client'; + +import { useParams, useRouter } from 'next/navigation'; +import { + Container, + Box, + Button, + CircularProgress, + Alert, + Typography, + LinearProgress, + Paper, + Grid, + Pagination +} from '@mui/material'; +import ArrowBackIcon from '@mui/icons-material/ArrowBack'; +import RefreshIcon from '@mui/icons-material/Refresh'; +import { useTranslation } from 'react-i18next'; +import useEvalTaskDetail from '../hooks/useEvalTaskDetail'; +import { detailStyles } from './detailStyles'; +import EvalHeader from './components/EvalHeader'; +import EvalStats from './components/EvalStats'; +import QuestionCard from './components/QuestionCard'; + +export default function EvalTaskDetailPage() { + const { projectId, taskId } = useParams(); + const router = useRouter(); + const { t } = useTranslation(); + + const { + task, + results, + stats, + total, + page, + setPage, + pageSize, + filterType, + setFilterType, + filterCorrect, + setFilterCorrect, + loading, + error, + setError, + loadData + } = useEvalTaskDetail(projectId, taskId); + + const handleFilterSelect = type => { + setFilterType(type); + setPage(1); // 切换筛选时重置到第一页 + }; + + const handleFilterCorrectSelect = isCorrect => { + setFilterCorrect(isCorrect); + setPage(1); // 切换筛选时重置到第一页 + }; + + const handlePageChange = (event, value) => { + setPage(value); + // 滚动到试卷顶部 + document.getElementById('paper-top')?.scrollIntoView({ behavior: 'smooth' }); + }; + + if (loading && !task) { + return ( + + + + ); + } + + return ( + + + {/* 顶部导航栏 */} + + + + + + {/* 错误提示 */} + {error && ( + setError('')}> + {error} + + )} + + {/* 任务进度(仅进行中时显示) */} + {task?.status === 0 && ( + + + + {t('evalTasks.statusProcessing')}... + + + {task.completedCount}/{task.totalCount} + + + 0 ? (task.completedCount / task.totalCount) * 100 : 0} + sx={{ height: 10, borderRadius: 5 }} + /> + + )} + + {/* 核心内容区 */} + {task && ( + <> + {/* 头部概览 */} + + + {/* 统计图表 & 筛选 */} + + + {/* 试卷主体 */} + + {/* 试卷抬头 */} + + {t('evalTasks.reportTitle', '模型能力评估报告')} + + + {t('evalTasks.taskIdLabel', '任务 ID')}: {taskId} + + + {t('evalTasks.pageInfo', '第 {{page}} / {{totalPages}} 页', { + page, + totalPages: Math.ceil(total / pageSize) + })} + + + + + {/* 题目列表 (双列布局) */} + + {loading ? ( + + + + ) : ( + + {results?.map((result, index) => ( + + + + ))} + + )} + + {!loading && results?.length === 0 && ( + + {t('evalTasks.noMatchingResults', '暂无符合条件的评估结果')} + + )} + + + {/* 分页控制 */} + + + + + {/* 试卷底部 */} + + + {t('evalTasks.reportFooter', 'Easy Dataset Evaluation System · Generated by AI')} + + + + + )} + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/eval-tasks/components/CreateEvalTaskDialog.js b/easy-dataset-main/app/projects/[projectId]/eval-tasks/components/CreateEvalTaskDialog.js new file mode 100644 index 0000000..5dc90b7 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-tasks/components/CreateEvalTaskDialog.js @@ -0,0 +1,328 @@ +'use client'; + +import { useState } from 'react'; +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + Box, + Typography, + FormControl, + InputLabel, + Select, + MenuItem, + Alert, + Divider, + CircularProgress, + FormHelperText +} from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import ModelSelector from './ModelSelector'; +import QuestionFilter from './QuestionFilter'; +import ScoreAnchorsForm from './ScoreAnchorsForm'; +import { useEvalTaskForm } from '../hooks/useEvalTaskForm'; + +import { useEffect } from 'react'; + +export default function CreateEvalTaskDialog({ open, onClose, projectId, onSuccess }) { + const { t, i18n } = useTranslation(); + const [submitting, setSubmitting] = useState(false); + + const { + models, + selectedModels, + setSelectedModels, + judgeModel, + setJudgeModel, + evalDatasets, + availableTags, + questionTypes, + setQuestionTypes, + selectedTags, + setSelectedTags, + searchKeyword, + setSearchKeyword, + questionCount, + setQuestionCount, + filteredTotal, + sampledIds, + hasSubjectiveQuestions, + hasShortAnswer, + hasOpenEnded, + shortAnswerScoreAnchors, + setShortAnswerScoreAnchors, + openEndedScoreAnchors, + setOpenEndedScoreAnchors, + initScoreAnchors, + loading, + error, + setError, + setSampledIds, + resetFilters, + resetForm + } = useEvalTaskForm(projectId, open); + + // 当有主观题时,初始化评分规则 + useEffect(() => { + if (hasSubjectiveQuestions && open) { + initScoreAnchors(i18n.language === 'zh-CN' ? 'zh-CN' : 'en'); + } + }, [hasSubjectiveQuestions, open, i18n.language]); + + // 统计各题型数量 + const typeStats = {}; + evalDatasets.forEach(d => { + typeStats[d.questionType] = (typeStats[d.questionType] || 0) + 1; + }); + + const getModelKey = model => `${model.providerId}::${model.modelId}`; + + const handleModelSelectionChange = newSelection => { + setSelectedModels(newSelection); + setError(''); + }; + + const handleSubmit = async () => { + // 先清除之前的错误 + setError(''); + + // 验证 + if (selectedModels.length === 0) { + setError(t('evalTasks.errorNoModels')); + return; + } + + if (filteredTotal === 0) { + setError(t('evalTasks.errorNoQuestions')); + return; + } + + if (hasSubjectiveQuestions && !judgeModel) { + setError(t('evalTasks.errorNoJudgeModel')); + return; + } + + // 验证教师模型不在测试模型中 + if (judgeModel && selectedModels.includes(judgeModel)) { + setError(t('evalTasks.errorJudgeSameAsTest')); + return; + } + + try { + setSubmitting(true); + setError(''); + + // 解析选中的模型 + const models = selectedModels.map(m => { + const [providerId, modelId] = m.split('::'); + return { modelId, providerId }; // 注意顺序:modelId 在前 + }); + + // 解析教师模型 + let judgeModelId = null; + let judgeProviderId = null; + if (judgeModel) { + const [pId, mId] = judgeModel.split('::'); + judgeProviderId = pId; + judgeModelId = mId; + } + + // 调用后端采样接口获取题目 ID + const sampleBody = { + questionTypes: questionTypes, + tags: selectedTags, + keyword: searchKeyword.trim() || '', + limit: questionCount > 0 ? questionCount : undefined + }; + + const sampleResponse = await fetch(`/api/projects/${projectId}/eval-datasets/sample`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(sampleBody) + }); + + const sampleResult = await sampleResponse.json(); + if (!sampleResponse.ok || sampleResult.code !== 0) { + setError(sampleResult.error || t('evalTasks.errorCreateFailed')); + return; + } + + const ids = sampleResult?.data?.ids || []; + if (ids.length === 0) { + setError(t('evalTasks.errorNoQuestions')); + return; + } + + setSampledIds(ids); + + // 构建自定义评分规则对象 + const customScoreAnchors = {}; + if (hasShortAnswer && shortAnswerScoreAnchors.length > 0) { + customScoreAnchors.short_answer = shortAnswerScoreAnchors; + } + if (hasOpenEnded && openEndedScoreAnchors.length > 0) { + customScoreAnchors.open_ended = openEndedScoreAnchors; + } + + // 创建任务 + const response = await fetch(`/api/projects/${projectId}/eval-tasks`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + models, // 后端期望的字段名 + judgeModelId, // 分开传递 + judgeProviderId, // 分开传递 + evalDatasetIds: ids, + language: i18n.language === 'zh-CN' ? 'zh-CN' : 'en', + customScoreAnchors: Object.keys(customScoreAnchors).length > 0 ? customScoreAnchors : undefined + }) + }); + + const result = await response.json(); + + if (result.code === 0) { + onSuccess && onSuccess(result.data); + handleClose(); + } else { + setError(result.error || t('evalTasks.errorCreateFailed')); + } + } catch (err) { + console.error('创建评估任务失败:', err); + setError(t('evalTasks.errorCreateFailed')); + } finally { + setSubmitting(false); + } + }; + + const handleClose = () => { + resetForm(); + onClose(); + }; + + const handleJudgeModelChange = event => { + setJudgeModel(event.target.value); + setError(''); + }; + + return ( + + {t('evalTasks.createTitle')} + + + {error && ( + setError('')}> + {error} + + )} + + {/* 选择测试模型 */} + + + + + {/* 题目筛选 */} + + + {/* 最终题目统计 */} + + + {t('evalTasks.finalSelection')} + {sampledIds.length || (questionCount > 0 ? questionCount : filteredTotal)}{' '} + {t('evalTasks.questionsSuffix')} + + {hasSubjectiveQuestions && ( + + {t('evalTasks.hasSubjectiveHint')} + + )} + + + {/* 选择教师模型(仅当有主观题时显示) */} + {hasSubjectiveQuestions && ( + <> + + {t('evalTasks.selectJudgeModel')} * + + {t('evalTasks.selectJudgeModelHint')} + + + {/* 简答题评分规则 */} + {hasShortAnswer && ( + + )} + + {/* 开放题评分规则 */} + {hasOpenEnded && ( + + )} + + )} + + + + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/eval-tasks/components/EvalTaskCard.js b/easy-dataset-main/app/projects/[projectId]/eval-tasks/components/EvalTaskCard.js new file mode 100644 index 0000000..f395bc9 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-tasks/components/EvalTaskCard.js @@ -0,0 +1,183 @@ +'use client'; + +import { useState } from 'react'; +import { + Card, + CardContent, + Box, + Typography, + Chip, + IconButton, + LinearProgress, + Menu, + MenuItem, + ListItemIcon, + Avatar, + useTheme +} from '@mui/material'; +import MoreVertIcon from '@mui/icons-material/MoreVert'; +import DeleteIcon from '@mui/icons-material/Delete'; +import StopIcon from '@mui/icons-material/Stop'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import CheckCircleIcon from '@mui/icons-material/CheckCircle'; +import ErrorIcon from '@mui/icons-material/Error'; +import PauseCircleIcon from '@mui/icons-material/PauseCircle'; +import HourglassEmptyIcon from '@mui/icons-material/HourglassEmpty'; +import SmartToyIcon from '@mui/icons-material/SmartToy'; +import QuizIcon from '@mui/icons-material/Quiz'; +import { useTranslation } from 'react-i18next'; +import { getModelIcon } from '@/lib/util/modelIcon'; +import styles from '../styles'; + +const STATUS_CONFIG = { + 0: { label: 'evalTasks.statusProcessing', color: 'info', icon: HourglassEmptyIcon }, + 1: { label: 'evalTasks.statusCompleted', color: 'success', icon: CheckCircleIcon }, + 2: { label: 'evalTasks.statusFailed', color: 'error', icon: ErrorIcon }, + 3: { label: 'evalTasks.statusInterrupted', color: 'warning', icon: PauseCircleIcon } +}; + +export default function EvalTaskCard({ task, onView, onDelete, onInterrupt }) { + const { t } = useTranslation(); + const theme = useTheme(); + const [anchorEl, setAnchorEl] = useState(null); + const open = Boolean(anchorEl); + + const { modelInfo, detail, status, completedCount, totalCount, createAt } = task; + const statusConfig = STATUS_CONFIG[status] || STATUS_CONFIG[0]; + const StatusIcon = statusConfig.icon; + const progress = totalCount > 0 ? (completedCount / totalCount) * 100 : 0; + const finalScore = detail?.finalScore; + + const handleMenuClick = e => { + e.stopPropagation(); + setAnchorEl(e.currentTarget); + }; + + const handleMenuClose = () => setAnchorEl(null); + + const handleAction = action => () => { + handleMenuClose(); + action?.(task); + }; + + const getScoreColor = score => { + if (score >= 80) return 'success'; + if (score >= 60) return 'info'; + if (score >= 40) return 'warning'; + return 'error'; + }; + + return ( + + + {/* 头部 */} + + + + {modelInfo?.modelId + + + + {modelInfo?.modelName || modelInfo?.modelId} + + + {modelInfo?.providerName || modelInfo?.providerId} + + + + + + + + + {/* 状态和得分 */} + + } + label={t(statusConfig.label)} + color={statusConfig.color} + size="small" + variant="outlined" + sx={{ height: 24, '& .MuiChip-label': { px: 1, fontSize: '0.7rem' } }} + /> + {finalScore !== undefined && status === 1 && ( + + )} + + + {/* 进度条 */} + {status === 0 && ( + + + + {t('evalTasks.progress')} + + + {completedCount}/{totalCount} + + + + + )} + + {/* 统计信息 */} + + } + label={`${totalCount} ${t('evalTasks.questions')}`} + size="small" + variant="outlined" + sx={{ height: 22, '& .MuiChip-label': { px: 0.75, fontSize: '0.7rem' } }} + /> + {detail?.hasSubjectiveQuestions && ( + + )} + + + {/* 时间 */} + + {new Date(createAt).toLocaleString()} + + + + {/* 菜单 */} + e.stopPropagation()}> + + + + + {t('datasets.viewDetails')} + + {status === 0 && ( + + + + + {t('evalTasks.interrupt')} + + )} + + + + + {t('common.delete')} + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/eval-tasks/components/ModelSelector.js b/easy-dataset-main/app/projects/[projectId]/eval-tasks/components/ModelSelector.js new file mode 100644 index 0000000..cc95d05 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-tasks/components/ModelSelector.js @@ -0,0 +1,87 @@ +'use client'; + +import { + Box, + Typography, + Checkbox, + FormHelperText, + FormControl, + InputLabel, + Select, + MenuItem, + ListItemText, + OutlinedInput, + Chip +} from '@mui/material'; +import { useTranslation } from 'react-i18next'; + +export default function ModelSelector({ models, selectedModels, onSelectionChange, error }) { + const { t } = useTranslation(); + + const getModelKey = model => `${model.providerId}::${model.modelId}`; + + const handleChange = event => { + const { + target: { value } + } = event; + // On autofill we get a stringified value. + onSelectionChange(typeof value === 'string' ? value.split(',') : value); + }; + + const getModelLabel = modelKey => { + const model = models.find(m => getModelKey(m) === modelKey); + if (!model) return modelKey; + return `${model.providerName || model.providerId} / ${model.modelName || model.modelId}`; + }; + + return ( + + + {t('evalTasks.selectModels')} * + + {error || t('evalTasks.selectModelsHint')} + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/eval-tasks/components/QuestionFilter.js b/easy-dataset-main/app/projects/[projectId]/eval-tasks/components/QuestionFilter.js new file mode 100644 index 0000000..02322bd --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-tasks/components/QuestionFilter.js @@ -0,0 +1,160 @@ +'use client'; + +import { + Box, + Typography, + TextField, + FormControl, + InputLabel, + Select, + MenuItem, + Chip, + OutlinedInput, + Checkbox, + ListItemText, + Slider, + Button +} from '@mui/material'; +import FilterAltIcon from '@mui/icons-material/FilterAlt'; +import ClearIcon from '@mui/icons-material/Clear'; +import { useTranslation } from 'react-i18next'; + +const QUESTION_TYPES = [ + { value: 'true_false', labelKey: 'eval.questionTypes.true_false' }, + { value: 'single_choice', labelKey: 'eval.questionTypes.single_choice' }, + { value: 'multiple_choice', labelKey: 'eval.questionTypes.multiple_choice' }, + { value: 'short_answer', labelKey: 'eval.questionTypes.short_answer' }, + { value: 'open_ended', labelKey: 'eval.questionTypes.open_ended' } +]; + +export default function QuestionFilter({ + questionTypes, + selectedTags, + searchKeyword, + questionCount, + availableTags, + filteredCount, + onQuestionTypesChange, + onTagsChange, + onSearchChange, + onQuestionCountChange, + onReset +}) { + const { t } = useTranslation(); + + const hasFilters = questionTypes.length > 0 || selectedTags.length > 0 || searchKeyword || questionCount > 0; + + return ( + + + + + {t('evalTasks.filterTitle')} + + {hasFilters && ( + + )} + + + + {/* 关键字搜索 */} + onSearchChange(e.target.value)} + /> + + {/* 题型和标签筛选 - 并排显示 */} + + {/* 题型筛选 */} + + {t('evalTasks.filterByTypeLabel')} + + + + {/* 标签筛选 */} + {availableTags.length > 0 && ( + + {t('evalTasks.filterByTagLabel')} + + + )} + + + {/* 题目数量选择 - 紧凑布局 */} + + + + {t('evalTasks.questionCountLabel')} + {questionCount === 0 ? t('common.all') : questionCount} / {filteredCount} + + onQuestionCountChange(parseInt(e.target.value) || 0)} + inputProps={{ min: 0, max: filteredCount }} + sx={{ width: 100 }} + /> + + onQuestionCountChange(value)} + min={0} + max={filteredCount} + step={1} + valueLabelDisplay="auto" + /> + + {questionCount === 0 + ? t('evalTasks.useAllQuestions') + : t('evalTasks.randomSampleHint', { filteredCount, questionCount })} + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/eval-tasks/components/ScoreAnchorsForm.js b/easy-dataset-main/app/projects/[projectId]/eval-tasks/components/ScoreAnchorsForm.js new file mode 100644 index 0000000..eb26503 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-tasks/components/ScoreAnchorsForm.js @@ -0,0 +1,143 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { + Box, + Typography, + TextField, + Accordion, + AccordionSummary, + AccordionDetails, + Chip, + IconButton, + Tooltip +} from '@mui/material'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import RestoreIcon from '@mui/icons-material/Restore'; +import { useTranslation } from 'react-i18next'; +import { getDefaultScoreAnchors } from '@/lib/llm/prompts/llmJudge'; + +/** + * 评分规则表单组件 + * 用于自定义简答题和开放题的评分规则 + */ +export default function ScoreAnchorsForm({ + questionType, // 'short_answer' 或 'open_ended' + scoreAnchors, + onChange, + language = 'zh-CN' +}) { + const { t, i18n } = useTranslation(); + const [expanded, setExpanded] = useState(false); + + // 获取当前语言 + const currentLanguage = i18n.language === 'zh-CN' ? 'zh-CN' : 'en'; + + // 初始化评分规则(如果为空) + useEffect(() => { + if (!scoreAnchors || scoreAnchors.length === 0) { + onChange(getDefaultScoreAnchors(questionType, currentLanguage)); + } + }, [questionType, currentLanguage]); + + // 处理单个规则的描述更改 + const handleDescriptionChange = (index, newDescription) => { + const newAnchors = [...scoreAnchors]; + newAnchors[index] = { ...newAnchors[index], description: newDescription }; + onChange(newAnchors); + }; + + // 恢复默认值 + const handleRestore = () => { + onChange(getDefaultScoreAnchors(questionType, currentLanguage)); + }; + + // 获取题型显示名称 + const getQuestionTypeName = () => { + if (questionType === 'short_answer') { + return t('evalTasks.shortAnswer', '简答题'); + } + return t('evalTasks.openEnded', '开放题'); + }; + + // 获取分数区间的颜色 + const getScoreColor = range => { + if (range === '1.0') return 'success'; + if (range.includes('0.8') || range.includes('0.9')) return 'info'; + if (range.includes('0.6') || range.includes('0.7')) return 'warning'; + return 'error'; + }; + + if (!scoreAnchors || scoreAnchors.length === 0) { + return null; + } + + return ( + setExpanded(isExpanded)} + sx={{ + mb: 2, + '&:before': { display: 'none' }, + boxShadow: 1 + }} + > + } + sx={{ + bgcolor: 'action.hover', + '&:hover': { bgcolor: 'action.selected' } + }} + > + + + {t('evalTasks.scoreAnchorsTitle', '{{type}}评分规则', { type: getQuestionTypeName() })} + + + + + + + + {t('evalTasks.scoreAnchorsHint', '自定义评分标准,用于指导LLM评估模型的回答质量')} + + + + + + + + + {scoreAnchors.map((anchor, index) => ( + + + + + {t('evalTasks.scoreRange', '分数区间')} + + + handleDescriptionChange(index, e.target.value)} + placeholder={t('evalTasks.scoreDescriptionPlaceholder', '请输入该分数区间的评分标准描述...')} + sx={{ + '& .MuiOutlinedInput-root': { + fontSize: '0.875rem' + } + }} + /> + + ))} + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/eval-tasks/hooks/useEvalTaskDetail.js b/easy-dataset-main/app/projects/[projectId]/eval-tasks/hooks/useEvalTaskDetail.js new file mode 100644 index 0000000..4854aeb --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-tasks/hooks/useEvalTaskDetail.js @@ -0,0 +1,94 @@ +'use client'; + +import { useState, useEffect, useCallback } from 'react'; + +/** + * 评估任务详情 Hook + */ +export default function useEvalTaskDetail(projectId, taskId) { + const [task, setTask] = useState(null); + const [results, setResults] = useState([]); + const [stats, setStats] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(''); + + // 分页和筛选状态 + const [page, setPage] = useState(1); + const [pageSize, setPageSize] = useState(10); + const [filterType, setFilterType] = useState(null); + const [filterCorrect, setFilterCorrect] = useState(null); // null: all, true: correct, false: incorrect + const [total, setTotal] = useState(0); + + // 加载任务详情 + const loadData = useCallback(async () => { + if (!projectId || !taskId) return; + + try { + setLoading(true); + setError(''); + + // 构建查询参数 + const params = new URLSearchParams({ + page: page.toString(), + pageSize: pageSize.toString() + }); + + if (filterType) { + params.append('type', filterType); + } + + if (filterCorrect !== null) { + params.append('isCorrect', filterCorrect.toString()); + } + + const response = await fetch(`/api/projects/${projectId}/eval-tasks/${taskId}?${params.toString()}`); + const result = await response.json(); + + if (result.code === 0) { + setTask(result.data.task); + setResults(result.data.results || []); + setTotal(result.data.total || 0); + setStats(result.data.stats); + } else { + setError(result.error || '加载失败'); + } + } catch (err) { + console.error('加载任务详情失败:', err); + setError('加载失败'); + } finally { + setLoading(false); + } + }, [projectId, taskId, page, pageSize, filterType, filterCorrect]); + + // 初始加载 + useEffect(() => { + loadData(); + }, [loadData]); + + // 自动刷新进行中的任务 (仅在第一页且无筛选时刷新,避免干扰用户查看历史记录) + useEffect(() => { + if (task?.status !== 0 || page !== 1 || filterType || filterCorrect !== null) return; + + const interval = setInterval(loadData, 3000); + return () => clearInterval(interval); + }, [task?.status, page, filterType, filterCorrect, loadData]); + + return { + task, + results, + stats, + total, + page, + setPage, + pageSize, + setPageSize, + filterType, + setFilterType, + filterCorrect, + setFilterCorrect, + loading, + error, + setError, + loadData + }; +} diff --git a/easy-dataset-main/app/projects/[projectId]/eval-tasks/hooks/useEvalTaskForm.js b/easy-dataset-main/app/projects/[projectId]/eval-tasks/hooks/useEvalTaskForm.js new file mode 100644 index 0000000..1257b18 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-tasks/hooks/useEvalTaskForm.js @@ -0,0 +1,187 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { getDefaultScoreAnchors } from '@/lib/llm/prompts/llmJudge'; + +export function useEvalTaskForm(projectId, open) { + const [models, setModels] = useState([]); + const [selectedModels, setSelectedModels] = useState([]); + const [judgeModel, setJudgeModel] = useState(''); + const [evalDatasets, setEvalDatasets] = useState([]); + const [availableTags, setAvailableTags] = useState([]); + + // 筛选条件 + const [questionTypes, setQuestionTypes] = useState([]); + const [selectedTags, setSelectedTags] = useState([]); + const [searchKeyword, setSearchKeyword] = useState(''); + const [questionCount, setQuestionCount] = useState(0); + + // 后端统计 & 采样结果 + const [filteredTotal, setFilteredTotal] = useState(0); + const [sampledIds, setSampledIds] = useState([]); + const [hasSubjectiveQuestions, setHasSubjectiveQuestions] = useState(false); + // 主观题类型统计(用于确定显示哪个评分规则表单) + const [hasShortAnswer, setHasShortAnswer] = useState(false); + const [hasOpenEnded, setHasOpenEnded] = useState(false); + + // 自定义评分规则 + const [shortAnswerScoreAnchors, setShortAnswerScoreAnchors] = useState([]); + const [openEndedScoreAnchors, setOpenEndedScoreAnchors] = useState([]); + + const [loading, setLoading] = useState(false); + const [error, setError] = useState(''); + + // 加载数据 + useEffect(() => { + if (open && projectId) { + loadModels(); + loadEvalDatasets(); + } + }, [open, projectId]); + + // 当筛选条件变化时,调用后端统计数量 + useEffect(() => { + if (!open || !projectId) return; + + const controller = new AbortController(); + + const fetchCount = async () => { + try { + const params = new URLSearchParams(); + if (questionTypes.length > 0) { + questionTypes.forEach(t => params.append('questionTypes', t)); + } + if (searchKeyword.trim()) { + params.append('keyword', searchKeyword.trim()); + } + if (selectedTags.length > 0) { + selectedTags.forEach(tag => params.append('tags', tag)); + } + + const response = await fetch(`/api/projects/${projectId}/eval-datasets/count?${params.toString()}`, { + signal: controller.signal + }); + if (response.ok) { + const result = await response.json(); + const total = result?.data?.total ?? 0; + const hasSubjective = result?.data?.hasSubjective ?? false; + const hasShort = result?.data?.hasShortAnswer ?? false; + const hasOpen = result?.data?.hasOpenEnded ?? false; + setFilteredTotal(total); + setHasSubjectiveQuestions(hasSubjective); + setHasShortAnswer(hasShort); + setHasOpenEnded(hasOpen); + } + } catch (err) { + if (err.name !== 'AbortError') { + console.error('加载评估题目数量失败:', err); + } + } + }; + + fetchCount(); + + return () => { + controller.abort(); + }; + }, [open, projectId, questionTypes, selectedTags, searchKeyword]); + + const loadModels = async () => { + try { + const response = await fetch(`/api/projects/${projectId}/model-config`); + if (response.ok) { + const result = await response.json(); + const modelList = result?.data || []; + const availableModels = modelList.filter(m => m.apiKey && m.apiKey.trim() !== '' && m.status === 1); + setModels(availableModels); + } + } catch (err) { + console.error('加载模型列表失败:', err); + setModels([]); + } + }; + + const loadEvalDatasets = async () => { + try { + setLoading(true); + // 这里只需要拿到全部可用标签和题型分布,可以复用已有列表接口或标签接口 + const response = await fetch(`/api/projects/${projectId}/eval-datasets?includeStats=true&page=1&pageSize=20`); + if (response.ok) { + const data = await response.json(); + const stats = data.stats || {}; + const byTag = stats.byTag || {}; + const tags = Object.keys(byTag); + setAvailableTags(tags.sort()); + + // 用部分数据来判断是否存在主观题(类型统计更准确) + const byType = stats.byType || {}; + const mockDatasets = Object.entries(byType).map(([type]) => ({ questionType: type })); + setEvalDatasets(mockDatasets); + } + } catch (err) { + console.error('加载评估题目失败:', err); + } finally { + setLoading(false); + } + }; + + const resetFilters = () => { + setQuestionTypes([]); + setSelectedTags([]); + setSearchKeyword(''); + setQuestionCount(0); + setFilteredTotal(0); + setSampledIds([]); + setHasShortAnswer(false); + setHasOpenEnded(false); + }; + + // 初始化评分规则(根据语言环境) + const initScoreAnchors = (language = 'zh-CN') => { + setShortAnswerScoreAnchors(getDefaultScoreAnchors('short_answer', language)); + setOpenEndedScoreAnchors(getDefaultScoreAnchors('open_ended', language)); + }; + + const resetForm = () => { + setSelectedModels([]); + setJudgeModel(''); + resetFilters(); + setError(''); + setShortAnswerScoreAnchors([]); + setOpenEndedScoreAnchors([]); + }; + + return { + models, + selectedModels, + setSelectedModels, + judgeModel, + setJudgeModel, + evalDatasets, + availableTags, + questionTypes, + setQuestionTypes, + selectedTags, + setSelectedTags, + searchKeyword, + setSearchKeyword, + questionCount, + setQuestionCount, + filteredTotal, + sampledIds, + hasSubjectiveQuestions, + hasShortAnswer, + hasOpenEnded, + shortAnswerScoreAnchors, + setShortAnswerScoreAnchors, + openEndedScoreAnchors, + setOpenEndedScoreAnchors, + initScoreAnchors, + loading, + error, + setError, + setSampledIds, + resetFilters, + resetForm + }; +} diff --git a/easy-dataset-main/app/projects/[projectId]/eval-tasks/hooks/useEvalTasks.js b/easy-dataset-main/app/projects/[projectId]/eval-tasks/hooks/useEvalTasks.js new file mode 100644 index 0000000..89f359f --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-tasks/hooks/useEvalTasks.js @@ -0,0 +1,149 @@ +'use client'; + +import { useState, useEffect, useCallback } from 'react'; + +/** + * 评估任务列表 Hook + */ +export default function useEvalTasks(projectId) { + const [tasks, setTasks] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(''); + const [page, setPage] = useState(1); + const [pageSize, setPageSize] = useState(12); + const [total, setTotal] = useState(0); + + // 加载任务列表 + const loadTasks = useCallback( + async (isRefresh = false) => { + if (!projectId) return; + + try { + if (!isRefresh) setLoading(true); + setError(''); + const response = await fetch(`/api/projects/${projectId}/eval-tasks?page=${page}&pageSize=${pageSize}`); + const result = await response.json(); + + if (result.code === 0) { + setTasks(result.data.items || []); + setTotal(result.data.total || 0); + } else { + setError(result.error || '加载失败'); + } + } catch (err) { + console.error('加载评估任务失败:', err); + setError('加载失败'); + } finally { + if (!isRefresh) setLoading(false); + } + }, + [projectId, page, pageSize] + ); + + // 初始加载和分页变化加载 + useEffect(() => { + loadTasks(); + }, [loadTasks]); + + // 自动刷新进行中的任务 + useEffect(() => { + const hasProcessingTasks = tasks.some(t => t.status === 0); + if (!hasProcessingTasks) return; + + const interval = setInterval(() => loadTasks(true), 5000); + return () => clearInterval(interval); + }, [tasks, loadTasks]); + + // 删除任务 + const deleteTask = useCallback( + async taskId => { + try { + const response = await fetch(`/api/projects/${projectId}/eval-tasks/${taskId}`, { + method: 'DELETE' + }); + const result = await response.json(); + + if (result.code === 0) { + loadTasks(); + return true; + } else { + setError(result.error || '删除失败'); + return false; + } + } catch (err) { + console.error('删除任务失败:', err); + setError('删除失败'); + return false; + } + }, + [projectId] + ); + + // 中断任务 + const interruptTask = useCallback( + async taskId => { + try { + const response = await fetch(`/api/projects/${projectId}/eval-tasks/${taskId}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ action: 'interrupt' }) + }); + const result = await response.json(); + + if (result.code === 0) { + loadTasks(); + return true; + } else { + setError(result.error || '中断失败'); + return false; + } + } catch (err) { + console.error('中断任务失败:', err); + setError('中断失败'); + return false; + } + }, + [projectId, loadTasks] + ); + + // 创建任务 + const createTasks = useCallback( + async data => { + try { + const response = await fetch(`/api/projects/${projectId}/eval-tasks`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(data) + }); + const result = await response.json(); + + if (result.code === 0) { + loadTasks(); + return { success: true, data: result.data }; + } else { + return { success: false, error: result.error }; + } + } catch (err) { + console.error('创建任务失败:', err); + return { success: false, error: '创建失败' }; + } + }, + [projectId, loadTasks] + ); + + return { + tasks, + loading, + error, + setError, + loadTasks, + deleteTask, + interruptTask, + createTasks, + page, + setPage, + pageSize, + setPageSize, + total + }; +} diff --git a/easy-dataset-main/app/projects/[projectId]/eval-tasks/page.js b/easy-dataset-main/app/projects/[projectId]/eval-tasks/page.js new file mode 100644 index 0000000..9f0ffb8 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-tasks/page.js @@ -0,0 +1,188 @@ +'use client'; + +import { useState } from 'react'; +import { useParams, useRouter } from 'next/navigation'; +import { + Container, + Typography, + Box, + Paper, + Button, + Grid, + CircularProgress, + Alert, + Dialog, + DialogTitle, + DialogContent, + DialogContentText, + DialogActions, + TablePagination +} from '@mui/material'; +import AddIcon from '@mui/icons-material/Add'; +import RefreshIcon from '@mui/icons-material/Refresh'; +import AssessmentIcon from '@mui/icons-material/Assessment'; +import { useTranslation } from 'react-i18next'; + +import useEvalTasks from './hooks/useEvalTasks'; +import CreateEvalTaskDialog from './components/CreateEvalTaskDialog'; +import EvalTaskCard from './components/EvalTaskCard'; +import styles from './styles'; + +export default function EvalTasksPage() { + const { projectId } = useParams(); + const router = useRouter(); + const { t } = useTranslation(); + + const { + tasks, + loading, + error, + setError, + loadTasks, + deleteTask, + interruptTask, + page, + setPage, + pageSize, + setPageSize, + total + } = useEvalTasks(projectId); + + const [createDialogOpen, setCreateDialogOpen] = useState(false); + const [deleteDialog, setDeleteDialog] = useState({ open: false, task: null }); + const [interruptDialog, setInterruptDialog] = useState({ open: false, task: null }); + + const handleView = task => router.push(`/projects/${projectId}/eval-tasks/${task.id}`); + const handleDelete = task => setDeleteDialog({ open: true, task }); + const handleInterrupt = task => setInterruptDialog({ open: true, task }); + + const handlePageChange = (event, newPage) => { + setPage(newPage + 1); + }; + + const handlePageSizeChange = event => { + setPageSize(parseInt(event.target.value, 10)); + setPage(1); + }; + + const confirmDelete = async () => { + if (deleteDialog.task) { + await deleteTask(deleteDialog.task.id); + } + setDeleteDialog({ open: false, task: null }); + }; + + const confirmInterrupt = async () => { + if (interruptDialog.task) { + await interruptTask(interruptDialog.task.id); + } + setInterruptDialog({ open: false, task: null }); + }; + + return ( + + {/* 标题栏 */} + + + {t('evalTasks.title')} + + + + + + + {/* 错误提示 */} + {error && ( + setError('')}> + {error} + + )} + + {/* 加载状态 */} + {loading && tasks.length === 0 && ( + + + + )} + + {/* 空状态 */} + {!loading && tasks.length === 0 && ( + + + + {t('evalTasks.noTasks')} + + + {t('evalTasks.noTasksHint')} + + + + )} + + {/* 任务列表 */} + {tasks.length > 0 && ( + <> + + {tasks.map(task => ( + + + + ))} + + + + + + )} + + {/* 创建任务对话框 */} + setCreateDialogOpen(false)} + projectId={projectId} + onSuccess={loadTasks} + /> + + {/* 删除确认对话框 */} + setDeleteDialog({ open: false, task: null })}> + {t('evalTasks.deleteConfirmTitle')} + + {t('evalTasks.deleteConfirmMessage')} + + + + + + + + {/* 中断确认对话框 */} + setInterruptDialog({ open: false, task: null })}> + {t('evalTasks.interruptConfirmTitle')} + + {t('evalTasks.interruptConfirmMessage')} + + + + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/eval-tasks/styles.js b/easy-dataset-main/app/projects/[projectId]/eval-tasks/styles.js new file mode 100644 index 0000000..a37380b --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/eval-tasks/styles.js @@ -0,0 +1,280 @@ +/** + * 评估任务页面样式 + */ + +export const evalTasksStyles = { + // 页面容器 + pageContainer: { + py: 3, + minHeight: '100vh' + }, + + // 页头 + header: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + mb: 3 + }, + + headerTitle: { + fontWeight: 600 + }, + + headerActions: { + display: 'flex', + gap: 1 + }, + + // 空状态 + emptyState: { + p: 8, + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + minHeight: 400, + borderRadius: 3, + bgcolor: 'background.paper' + }, + + emptyIcon: { + fontSize: 80, + color: 'text.disabled', + mb: 2 + }, + + emptyTitle: { + mb: 1, + fontWeight: 500 + }, + + emptyHint: { + mb: 4, + textAlign: 'center', + maxWidth: 400 + }, + + // 任务卡片 + taskCard: theme => ({ + height: '100%', + cursor: 'pointer', + transition: 'all 0.2s ease', + borderRadius: 2, + overflow: 'hidden', + border: `1px solid ${theme.palette.divider}`, + '&:hover': { + boxShadow: theme.shadows[6], + transform: 'translateY(-4px)', + borderColor: theme.palette.primary.main + } + }), + + taskCardContent: { + p: 2.5 + }, + + taskCardHeader: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'flex-start', + mb: 2 + }, + + taskCardModel: { + flex: 1, + overflow: 'hidden' + }, + + taskCardModelName: { + fontWeight: 600, + fontSize: '0.95rem', + lineHeight: 1.3 + }, + + taskCardTime: { + mt: 0.5, + fontSize: '0.75rem' + }, + + taskCardStatus: { + display: 'flex', + alignItems: 'center', + gap: 1, + mb: 2 + }, + + taskCardProgress: { + mb: 2 + }, + + progressBar: { + height: 6, + borderRadius: 3 + }, + + taskCardStats: { + display: 'flex', + gap: 1, + flexWrap: 'wrap' + }, + + // 统计卡片 + statsCard: theme => ({ + height: '100%', + borderRadius: 2, + border: `1px solid ${theme.palette.divider}`, + transition: 'all 0.2s ease', + '&:hover': { + boxShadow: theme.shadows[2] + } + }), + + statsCardContent: { + p: 2.5 + }, + + statsLabel: { + fontSize: '0.75rem', + color: 'text.secondary', + mb: 1, + textTransform: 'uppercase', + letterSpacing: 0.5 + }, + + statsValue: { + fontWeight: 700, + fontSize: '1.75rem', + lineHeight: 1.2 + }, + + // 按题型统计 + typeStatsContainer: { + p: 2.5, + mb: 3, + borderRadius: 2 + }, + + typeStatsTitle: { + fontWeight: 600, + mb: 2 + }, + + typeStatsItem: theme => ({ + textAlign: 'center', + p: 1.5, + bgcolor: theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.02)', + borderRadius: 1.5, + border: `1px solid ${theme.palette.divider}` + }), + + typeStatsLabel: { + fontSize: '0.7rem', + color: 'text.secondary', + mb: 0.5 + }, + + typeStatsScore: { + fontWeight: 700, + fontSize: '1.1rem' + }, + + typeStatsPercent: { + fontSize: '0.7rem', + color: 'text.secondary' + }, + + // 结果表格 + resultsTable: { + overflow: 'hidden', + borderRadius: 2 + }, + + resultsTableHeader: { + fontWeight: 600, + p: 2, + borderBottom: 1, + borderColor: 'divider' + }, + + resultsTableContainer: { + maxHeight: 600 + }, + + resultRow: { + cursor: 'pointer', + '&:hover': { + bgcolor: 'action.hover' + } + }, + + resultQuestion: { + maxWidth: 400 + }, + + resultScore: correct => ({ + fontWeight: 'bold', + color: correct ? 'success.main' : 'error.main' + }), + + resultExpandedContent: { + py: 2.5, + px: 1.5 + }, + + resultAnswerBox: isCorrect => theme => ({ + p: 2, + mt: 1, + borderRadius: 1.5, + bgcolor: isCorrect + ? theme.palette.mode === 'dark' + ? 'rgba(46, 125, 50, 0.15)' + : 'rgba(46, 125, 50, 0.08)' + : theme.palette.mode === 'dark' + ? 'rgba(211, 47, 47, 0.15)' + : 'rgba(211, 47, 47, 0.08)', + border: `1px solid ${isCorrect ? theme.palette.success.main : theme.palette.error.main}` + }), + + resultReferenceBox: { + p: 2, + mt: 1, + borderRadius: 1.5, + bgcolor: 'action.hover' + }, + + resultJudgeBox: { + p: 2, + mt: 1, + borderRadius: 1.5, + bgcolor: 'action.hover' + }, + + // 对话框 + dialogContent: { + mt: 1 + }, + + dialogSection: { + mb: 3 + }, + + dialogDivider: { + my: 2 + }, + + dialogInfoBox: theme => ({ + p: 2, + bgcolor: theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.02)', + borderRadius: 1.5, + border: `1px solid ${theme.palette.divider}` + }), + + dialogWarning: { + mt: 1, + color: 'warning.main', + fontWeight: 500 + } +}; + +export default evalTasksStyles; diff --git a/easy-dataset-main/app/projects/[projectId]/image-datasets/[datasetId]/page.js b/easy-dataset-main/app/projects/[projectId]/image-datasets/[datasetId]/page.js new file mode 100644 index 0000000..0e27798 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/image-datasets/[datasetId]/page.js @@ -0,0 +1,82 @@ +'use client'; + +import { Container, Box, CircularProgress, Alert } from '@mui/material'; +import { useParams } from 'next/navigation'; +import { useTranslation } from 'react-i18next'; +import useImageDatasetDetails from '../hooks/useImageDatasetDetails'; +import ImageDatasetHeader from '../components/ImageDatasetHeader'; +import DatasetContent from '../components/DatasetContent'; +import DatasetSidebar from '../components/DatasetSidebar'; + +export default function ImageDatasetDetailPage() { + const { projectId, datasetId } = useParams(); + const { t } = useTranslation(); + + const { + currentDataset, + loading, + confirming, + unconfirming, + datasetsAllCount, + datasetsConfirmCount, + updateDataset, + handleNavigate, + handleConfirm, + handleUnconfirm, + handleDelete + } = useImageDatasetDetails(projectId, datasetId); + + // 加载状态 + if (loading) { + return ( + + + + + + ); + } + + // 无数据状态 + if (!currentDataset) { + return ( + + {t('imageDatasets.notFound', '数据集不存在')} + + ); + } + + return ( + + {/* 顶部导航栏 */} + + + {/* 主要布局:左右分栏 */} + + {/* 左侧主要内容区域 */} + { + // 直接传递答案字符串,DatasetContent 已经处理了格式转换 + await updateDataset({ answer: newAnswer }); + }} + /> + + {/* 右侧固定侧边栏 */} + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/image-datasets/components/DatasetContent.js b/easy-dataset-main/app/projects/[projectId]/image-datasets/components/DatasetContent.js new file mode 100644 index 0000000..dab0c52 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/image-datasets/components/DatasetContent.js @@ -0,0 +1,155 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { Box, Paper, Typography, Button } from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import Image from 'next/image'; +import SaveIcon from '@mui/icons-material/Save'; +import AnswerInput from '../../images/components/annotation/AnswerInput'; + +function handleAnswer(dataset) { + const { answer, answerType } = dataset; + if (answerType === 'label' || answerType === 'custom_format') { + try { + return JSON.parse(answer); + } catch (e) { + return answer; + } + } + return answer; +} + +/** + * 数据集主要内容组件 + */ +export default function DatasetContent({ dataset, projectId, onAnswerChange }) { + const { t } = useTranslation(); + const [currentAnswer, setCurrentAnswer] = useState(() => handleAnswer(dataset)); + const [hasChanges, setHasChanges] = useState(false); + const [saving, setSaving] = useState(false); + + // 当 dataset 变化时,重置状态 + useEffect(() => { + setCurrentAnswer(handleAnswer(dataset)); + setHasChanges(false); + }, [dataset.id, dataset.answer]); + + // 处理答案变化 + const handleAnswerChange = newAnswer => { + setCurrentAnswer(newAnswer); + + // 检测是否有变化 + const originalAnswer = handleAnswer(dataset); + const hasChanged = JSON.stringify(newAnswer) !== JSON.stringify(originalAnswer); + setHasChanges(hasChanged); + }; + + // 保存答案 + const handleSave = async () => { + setSaving(true); + try { + let answerToSave = currentAnswer; + if (typeof answerToSave !== 'string') { + answerToSave = JSON.stringify(answerToSave, null, 2); + } + await onAnswerChange(answerToSave); + setHasChanges(false); + } catch (error) { + console.error('保存失败:', error); + } finally { + setSaving(false); + } + }; + + return ( + + + {/* 问题和保存按钮 */} + + + {dataset.question} + + + {/* 保存按钮 - 只在有变化时显示 */} + {hasChanges && ( + + )} + + + {/* 答案编辑器 */} + + + {/* 图片 */} + + + {dataset.base64 ? ( + {dataset.imageName} + ) : ( + {dataset.imageName} + )} + + + {dataset.imageName} + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/image-datasets/components/DatasetSidebar.js b/easy-dataset-main/app/projects/[projectId]/image-datasets/components/DatasetSidebar.js new file mode 100644 index 0000000..ec8bc85 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/image-datasets/components/DatasetSidebar.js @@ -0,0 +1,28 @@ +'use client'; + +import { Box } from '@mui/material'; +import MetadataInfo from './MetadataInfo'; +import MetadataEditor from './MetadataEditor'; + +/** + * 数据集右侧边栏组件 + */ +export default function DatasetSidebar({ dataset, projectId, onUpdate }) { + return ( + + {/* 元数据信息 - Chip 形式 */} + + + {/* 操作卡片 */} + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/image-datasets/components/EmptyState.js b/easy-dataset-main/app/projects/[projectId]/image-datasets/components/EmptyState.js new file mode 100644 index 0000000..e5a87e1 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/image-datasets/components/EmptyState.js @@ -0,0 +1,24 @@ +'use client'; + +import { Box, Typography } from '@mui/material'; +import ImageSearchIcon from '@mui/icons-material/ImageSearch'; +import { useTranslation } from 'react-i18next'; +import { imageDatasetStyles } from '../styles/imageDatasetStyles'; + +export default function EmptyState() { + const { t } = useTranslation(); + + return ( + + + + + + {t('imageDatasets.noData', { defaultValue: '暂无图片数据集' })} + + + {t('imageDatasets.noDataTip', { defaultValue: '请先在图片管理中生成问答数据集' })} + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/image-datasets/components/ExportImageDatasetDialog.js b/easy-dataset-main/app/projects/[projectId]/image-datasets/components/ExportImageDatasetDialog.js new file mode 100644 index 0000000..188fb90 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/image-datasets/components/ExportImageDatasetDialog.js @@ -0,0 +1,127 @@ +'use client'; + +import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + FormControl, + FormLabel, + RadioGroup, + FormControlLabel, + Radio, + Checkbox, + TextField, + Box, + Typography, + Alert +} from '@mui/material'; + +const ExportImageDatasetDialog = ({ open, onClose, onExport }) => { + const { t } = useTranslation(); + const [formatType, setFormatType] = useState('raw'); + const [exportImages, setExportImages] = useState(false); + const [includeImagePath, setIncludeImagePath] = useState(true); + const [systemPrompt, setSystemPrompt] = useState(''); + const [confirmedOnly, setConfirmedOnly] = useState(false); + + const handleExport = () => { + onExport({ + formatType, + exportImages, + includeImagePath, + systemPrompt, + confirmedOnly + }); + }; + + const handleClose = () => { + onClose(); + }; + + return ( + + {t('imageDatasets.exportTitle', '导出图片数据集')} + + + {/* 导出格式选择 */} + + {t('imageDatasets.exportFormat', '导出格式')} + setFormatType(e.target.value)}> + } label={t('imageDatasets.rawFormat', '原始格式')} /> + } label="ShareGPT (OpenAI)" /> + } label="Alpaca" /> + + + + {/* 图片导出选项 */} + + setExportImages(e.target.checked)} />} + label={t('imageDatasets.exportImagesOption', '导出图片文件')} + /> + + {t('imageDatasets.exportImagesDesc', '将所有图片打包成 ZIP 压缩包一起下载')} + + + + {/* 图片路径选项 */} + + setIncludeImagePath(e.target.checked)} />} + label={t('imageDatasets.includeImagePath', '在数据集中包含图片路径')} + /> + + {t('imageDatasets.includeImagePathDesc', '在问题或答案中添加图片路径(格式:/images/图片名称)')} + + + + {/* 系统提示词 */} + setSystemPrompt(e.target.value)} + placeholder={t('imageDatasets.systemPromptPlaceholder', '输入系统提示词...')} + fullWidth + /> + + {/* 仅导出已确认 */} + setConfirmedOnly(e.target.checked)} />} + label={t('imageDatasets.confirmedOnly', '仅导出已确认的数据集')} + /> + + {/* 提示信息 */} + + {t('imageDatasets.exportTip', '标签格式的答案将自动解析为文本(逗号分隔)')} + + + + + + + + + ); +}; + +export default ExportImageDatasetDialog; diff --git a/easy-dataset-main/app/projects/[projectId]/image-datasets/components/ImageDatasetCard.js b/easy-dataset-main/app/projects/[projectId]/image-datasets/components/ImageDatasetCard.js new file mode 100644 index 0000000..8922624 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/image-datasets/components/ImageDatasetCard.js @@ -0,0 +1,206 @@ +'use client'; + +import { Card, CardMedia, Box, Chip, Typography, Tooltip, IconButton } from '@mui/material'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import DeleteIcon from '@mui/icons-material/Delete'; +import AssessmentIcon from '@mui/icons-material/Assessment'; +import { useTranslation } from 'react-i18next'; +import { imageDatasetStyles } from '../styles/imageDatasetStyles'; + +export default function ImageDatasetCard({ + dataset, + onClick, + onView = () => {}, + onDelete = () => {}, + onEvaluate = () => {} +}) { + const { t } = useTranslation(); + + const getAnswerText = () => { + if (!dataset.answer) return t('imageDatasets.noAnswer', '暂无答案'); + if (dataset.answerType === 'label') { + try { + const labels = JSON.parse(dataset.answer); + return `${t('imageDatasets.labels', '标签')}: ${labels.join(', ')}`; + } catch { + return dataset.answer; + } + } + return dataset.answer; + }; + + const getAnswerTypeLabel = type => { + switch (type) { + case 'label': + return t('imageDatasets.typeLabel', '标签'); + case 'custom_format': + return t('imageDatasets.typeCustom', '自定义'); + default: + return t('imageDatasets.typeText', '文本'); + } + }; + + const getAnswerTypeColor = type => { + switch (type) { + case 'label': + return 'secondary'; + case 'custom_format': + return 'info'; + default: + return 'primary'; + } + }; + + const getScoreLabel = () => { + if (!dataset.score || dataset.score === 0) { + return t('imageDatasets.unscored', '未评分'); + } + return dataset.score; + }; + + return ( + + {/* 图片区域 */} + + + + {/* 悬停遮罩 */} + + + {/* 问题内容 - 底部,毛玻璃背景 */} + + + {dataset.question} + + + + + {/* 内容区域 - 标签和操作按钮 */} + + + + {/* 左侧:所有标签 */} + + + + ⭐} + label={getScoreLabel()} + size="small" + color={dataset.score && dataset.score > 0 ? 'warning' : 'default'} + sx={{ fontSize: '0.7rem', height: 20 }} + /> + + + {/* 右侧:操作按钮 - 不同颜色 */} + + + { + e.stopPropagation(); + onView(dataset.id); + }} + sx={{ + p: 0.5, + borderRadius: 1, + color: '#1976d2', + '&:hover': { + backgroundColor: 'rgba(25, 118, 210, 0.1)' + } + }} + > + + + + + + { + e.stopPropagation(); + onEvaluate(dataset.id); + }} + sx={{ + p: 0.5, + borderRadius: 1, + color: '#f57c00', + '&:hover': { + backgroundColor: 'rgba(245, 124, 0, 0.1)' + } + }} + > + + + + + + { + e.stopPropagation(); + onDelete(dataset.id); + }} + sx={{ + p: 0.5, + borderRadius: 1, + color: '#d32f2f', + '&:hover': { + backgroundColor: 'rgba(211, 47, 47, 0.1)' + } + }} + > + + + + + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/image-datasets/components/ImageDatasetFilterDialog.js b/easy-dataset-main/app/projects/[projectId]/image-datasets/components/ImageDatasetFilterDialog.js new file mode 100644 index 0000000..7581eff --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/image-datasets/components/ImageDatasetFilterDialog.js @@ -0,0 +1,87 @@ +'use client'; + +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Box, + Typography, + Select, + MenuItem, + Slider, + TextField, + Button +} from '@mui/material'; +import { useTranslation } from 'react-i18next'; + +export default function ImageDatasetFilterDialog({ + open, + onClose, + statusFilter, + scoreFilter, + onStatusChange, + onScoreChange, + onResetFilters, + onApplyFilters +}) { + const { t } = useTranslation(); + + return ( + + {t('datasets.filtersTitle', '筛选条件')} + + {/* 确认状态筛选 */} + + + {t('imageDatasets.status', { defaultValue: '确认状态' })} + + + + + {/* 评分范围筛选 */} + + + {t('imageDatasets.scoreRange', { defaultValue: '评分范围' })} + + + {scoreFilter[0]} - {scoreFilter[1]} 分 + + onScoreChange(newValue)} + valueLabelDisplay="auto" + min={0} + max={5} + step={1} + marks + sx={{ mt: 1 }} + /> + + + + {/* 对话框操作按钮 */} + + + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/image-datasets/components/ImageDatasetFilters.js b/easy-dataset-main/app/projects/[projectId]/image-datasets/components/ImageDatasetFilters.js new file mode 100644 index 0000000..b4dc57a --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/image-datasets/components/ImageDatasetFilters.js @@ -0,0 +1,48 @@ +'use client'; + +import { Box, Paper, IconButton, InputBase, Button, Badge } from '@mui/material'; +import SearchIcon from '@mui/icons-material/Search'; +import FilterListIcon from '@mui/icons-material/FilterList'; +import { useTranslation } from 'react-i18next'; + +export default function ImageDatasetFilters({ + searchQuery, + onSearchChange, + onMoreFiltersClick, + activeFilterCount = 0 +}) { + const { t } = useTranslation(); + + return ( + + {/* 搜索框 - 完全参考数据集管理的设计 */} + + + + + onSearchChange(e.target.value)} + /> + + + {/* 更多筛选按钮 - 带 Badge 显示活跃筛选条件数 */} + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/image-datasets/components/ImageDatasetHeader.js b/easy-dataset-main/app/projects/[projectId]/image-datasets/components/ImageDatasetHeader.js new file mode 100644 index 0000000..b226237 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/image-datasets/components/ImageDatasetHeader.js @@ -0,0 +1,82 @@ +'use client'; + +import { Box, Button, Divider, Typography, IconButton, CircularProgress, Paper } from '@mui/material'; +import NavigateBeforeIcon from '@mui/icons-material/NavigateBefore'; +import NavigateNextIcon from '@mui/icons-material/NavigateNext'; +import DeleteIcon from '@mui/icons-material/Delete'; +import UndoIcon from '@mui/icons-material/Undo'; +import { useTranslation } from 'react-i18next'; +import { useRouter } from 'next/navigation'; + +/** + * 图片数据集详情页面的头部导航组件 + */ +export default function ImageDatasetHeader({ + projectId, + datasetsAllCount, + datasetsConfirmCount, + confirming, + unconfirming, + currentDataset, + onNavigate, + onConfirm, + onUnconfirm, + onDelete +}) { + const router = useRouter(); + const { t } = useTranslation(); + + return ( + + + {/* 左侧:返回按钮和统计信息 */} + + + + + 共 {datasetsAllCount} 个数据集,已确认 {datasetsConfirmCount} 个 ( + {datasetsAllCount > 0 ? ((datasetsConfirmCount / datasetsAllCount) * 100).toFixed(2) : 0}%) + + + + {/* 右侧:翻页、确认/取消确认、删除按钮 */} + + onNavigate('prev')}> + + + onNavigate('next')}> + + + + + {/* 确认/取消确认按钮 */} + {currentDataset?.confirmed ? ( + + ) : ( + + )} + + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/image-datasets/components/MetadataEditor.js b/easy-dataset-main/app/projects/[projectId]/image-datasets/components/MetadataEditor.js new file mode 100644 index 0000000..8d113bc --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/image-datasets/components/MetadataEditor.js @@ -0,0 +1,159 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { Box, Typography, Divider, Paper } from '@mui/material'; +import { toast } from 'sonner'; +import StarRating from '@/components/datasets/StarRating'; +import TagSelector from '@/components/datasets/TagSelector'; +import NoteInput from '@/components/datasets/NoteInput'; +import { useTranslation } from 'react-i18next'; + +export default function MetadataEditor({ dataset, projectId, onUpdate }) { + const { t } = useTranslation(); + const [availableTags, setAvailableTags] = useState([]); + const [loading, setLoading] = useState(false); + + // 解析数据集中的标签 + const parseDatasetTags = tagsString => { + try { + return JSON.parse(tagsString || '[]'); + } catch (e) { + return []; + } + }; + + // 本地状态管理,从 props 初始化 + const [localScore, setLocalScore] = useState(dataset?.score || 0); + const [localTags, setLocalTags] = useState(() => { + const tags = parseDatasetTags(dataset?.tags); + // 确保 localTags 始终是数组 + return Array.isArray(tags) ? tags : []; + }); + const [localNote, setLocalNote] = useState(dataset?.note || ''); + + // 获取项目中已使用的标签 + useEffect(() => { + const fetchAvailableTags = async () => { + try { + const response = await fetch(`/api/projects/${projectId}/image-datasets/tags`); + if (response.ok) { + const data = await response.json(); + setAvailableTags(data.tags || []); + } + } catch (error) { + console.error('获取可用标签失败:', error); + } + }; + + if (projectId) { + fetchAvailableTags(); + } + }, [projectId]); + + // 同步props中的dataset到本地状态 + useEffect(() => { + if (dataset) { + setLocalScore(dataset.score || 0); + const tags = parseDatasetTags(dataset.tags); + setLocalTags(Array.isArray(tags) ? tags : []); + setLocalNote(dataset.note || ''); + } + }, [dataset]); + + // 更新数据集元数据 + const updateMetadata = async updates => { + if (loading) return; + + // 立即更新本地状态,提升响应速度 + if (updates.score !== undefined) { + setLocalScore(updates.score); + } + // 注意:tags 已经在 handleTagsChange 中更新过了,这里不需要再更新 + if (updates.note !== undefined) { + setLocalNote(updates.note); + } + + setLoading(true); + try { + // 调用父组件的更新方法 + if (onUpdate) { + await onUpdate(updates); + } + toast.success(t('imageDatasets.updateSuccess', '更新成功')); + } catch (error) { + console.error('更新数据集元数据失败:', error); + toast.error(t('imageDatasets.updateFailed', '更新失败')); + + // 出错时恢复本地状态 + if (updates.score !== undefined) { + setLocalScore(dataset?.score || 0); + } + if (updates.tags !== undefined) { + // 恢复为原始的标签数组 + const tags = parseDatasetTags(dataset?.tags); + setLocalTags(Array.isArray(tags) ? tags : []); + } + if (updates.note !== undefined) { + setLocalNote(dataset?.note || ''); + } + } finally { + setLoading(false); + } + }; + + // 处理评分变更 + const handleScoreChange = newScore => { + updateMetadata({ score: newScore }); + }; + + // 处理标签变更 + const handleTagsChange = newTags => { + // 立即更新本地状态(保持为数组) + setLocalTags(newTags); + // 发送给父组件时转换为 JSON 字符串 + updateMetadata({ tags: JSON.stringify(newTags) }); + }; + + // 处理备注变更 + const handleNoteChange = newNote => { + updateMetadata({ note: newNote }); + }; + + return ( + + {/* 评分区域 */} + + + {t('datasets.rating', '评分')} + + + + + + + {/* 标签区域 */} + + + {t('datasets.customTags', '自定义标签')} + + + + + + + {/* 备注区域 */} + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/image-datasets/components/MetadataInfo.js b/easy-dataset-main/app/projects/[projectId]/image-datasets/components/MetadataInfo.js new file mode 100644 index 0000000..22ac4e0 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/image-datasets/components/MetadataInfo.js @@ -0,0 +1,154 @@ +'use client'; + +import { Box, Typography, Chip, alpha, Divider } from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import { useTheme } from '@mui/material/styles'; + +/** + * 元数据信息展示组件 - Chip 形式(参考 DatasetMetadata) + */ +export default function MetadataInfo({ dataset }) { + const { t } = useTranslation(); + const theme = useTheme(); + + // 解析标签 + const parsedTags = (() => { + try { + if (typeof dataset.tags === 'string' && dataset.tags) { + return JSON.parse(dataset.tags); + } + return Array.isArray(dataset.tags) ? dataset.tags : []; + } catch { + return []; + } + })(); + + // 格式化文件大小 + const formatFileSize = bytes => { + if (!bytes) return '0 B'; + const k = 1024; + const sizes = ['B', 'KB', 'MB', 'GB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i]; + }; + + return ( + + {/* 数据集信息 */} + + {t('common.detailInfo', '详细信息')} + + + {/* 使用模型 */} + {dataset.model && ( + + )} + + {/* 标签数量 */} + {parsedTags.length > 0 && ( + + )} + + {/* 创建时间 */} + + + {/* 文本块信息 */} + {dataset.questionTemplate?.description && ( + + )} + + {/* 确认状态 */} + {dataset.confirmed && ( + + )} + + + {/* 图片信息 */} + {dataset.image && ( + <> + + + {t('images.imageInfo', '图片信息')} + + + {/* 图片尺寸 */} + {dataset.image.width && dataset.image.height && ( + + )} + + {/* 文件大小 */} + {dataset.image.size && ( + + )} + + {/* 图片创建时间 */} + {dataset.image.createAt && ( + + )} + + {/* 图片名称 */} + {dataset.image.imageName && ( + + )} + + + )} + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/image-datasets/hooks/useImageDatasetDetail.js b/easy-dataset-main/app/projects/[projectId]/image-datasets/hooks/useImageDatasetDetail.js new file mode 100644 index 0000000..4a382df --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/image-datasets/hooks/useImageDatasetDetail.js @@ -0,0 +1,77 @@ +import { useState, useEffect, useCallback } from 'react'; +import axios from 'axios'; +import { toast } from 'sonner'; +import { useTranslation } from 'react-i18next'; + +export function useImageDatasetDetail(projectId, datasetId) { + const { t } = useTranslation(); + const [dataset, setDataset] = useState(null); + const [loading, setLoading] = useState(false); + const [saving, setSaving] = useState(false); + + // 获取详情 + const fetchDetail = useCallback(async () => { + try { + setLoading(true); + const response = await axios.get(`/api/projects/${projectId}/image-datasets/${datasetId}`); + setDataset(response.data); + } catch (error) { + console.error('Failed to fetch dataset detail:', error); + toast.error(t('imageDatasets.fetchDetailFailed', { defaultValue: '获取详情失败' })); + } finally { + setLoading(false); + } + }, [projectId, datasetId, t]); + + // 更新数据集 + const updateDataset = useCallback( + async updates => { + try { + setSaving(true); + const response = await axios.put(`/api/projects/${projectId}/image-datasets/${datasetId}`, updates); + setDataset(response.data); + toast.success(t('imageDatasets.updateSuccess', { defaultValue: '更新成功' })); + return response.data; + } catch (error) { + console.error('Failed to update dataset:', error); + toast.error(t('imageDatasets.updateFailed', { defaultValue: '更新失败' })); + throw error; + } finally { + setSaving(false); + } + }, + [projectId, datasetId, t] + ); + + // AI 重新识别 + const regenerateAnswer = useCallback(async () => { + try { + setSaving(true); + const response = await axios.post(`/api/projects/${projectId}/image-datasets/${datasetId}/regenerate`); + setDataset(response.data); + toast.success(t('imageDatasets.regenerateSuccess', { defaultValue: 'AI 识别成功' })); + return response.data; + } catch (error) { + console.error('Failed to regenerate answer:', error); + toast.error(t('imageDatasets.regenerateFailed', { defaultValue: 'AI 识别失败' })); + throw error; + } finally { + setSaving(false); + } + }, [projectId, datasetId, t]); + + useEffect(() => { + if (projectId && datasetId) { + fetchDetail(); + } + }, [projectId, datasetId, fetchDetail]); + + return { + dataset, + loading, + saving, + updateDataset, + regenerateAnswer, + fetchDetail + }; +} diff --git a/easy-dataset-main/app/projects/[projectId]/image-datasets/hooks/useImageDatasetDetails.js b/easy-dataset-main/app/projects/[projectId]/image-datasets/hooks/useImageDatasetDetails.js new file mode 100644 index 0000000..259875a --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/image-datasets/hooks/useImageDatasetDetails.js @@ -0,0 +1,172 @@ +'use client'; + +import { useState, useEffect, useCallback } from 'react'; +import { useRouter } from 'next/navigation'; +import { useTranslation } from 'react-i18next'; +import { toast } from 'sonner'; +import axios from 'axios'; + +export default function useImageDatasetDetails(projectId, datasetId) { + const router = useRouter(); + const { t } = useTranslation(); + + const [currentDataset, setCurrentDataset] = useState(null); + const [loading, setLoading] = useState(true); + const [confirming, setConfirming] = useState(false); + const [unconfirming, setUnconfirming] = useState(false); + const [saving, setSaving] = useState(false); + const [datasetsAllCount, setDatasetsAllCount] = useState(0); + const [datasetsConfirmCount, setDatasetsConfirmCount] = useState(0); + + // 获取数据集列表信息 + const fetchDatasetsList = useCallback(async () => { + try { + // 获取所有数据集以正确统计已确认数量 + const response = await axios.get(`/api/projects/${projectId}/image-datasets?page=1&pageSize=10000`); + const data = response.data; + setDatasetsAllCount(data.total || 0); + setDatasetsConfirmCount(data.data?.filter(d => d.confirmed).length || 0); + } catch (error) { + console.error('Failed to fetch datasets list:', error); + } + }, [projectId]); + + // 获取当前数据集详情 + const fetchDatasetDetail = useCallback(async () => { + try { + setLoading(true); + const response = await axios.get(`/api/projects/${projectId}/image-datasets/${datasetId}`); + setCurrentDataset(response.data); + } catch (error) { + console.error('Failed to fetch dataset detail:', error); + toast.error(t('imageDatasets.fetchDetailFailed', '获取详情失败')); + } finally { + setLoading(false); + } + }, [projectId, datasetId, t]); + + useEffect(() => { + if (projectId && datasetId) { + fetchDatasetDetail(); + fetchDatasetsList(); + } + }, [projectId, datasetId, fetchDatasetDetail, fetchDatasetsList]); + + // 更新数据集 + const updateDataset = useCallback( + async updates => { + try { + setSaving(true); + await axios.put(`/api/projects/${projectId}/image-datasets/${datasetId}`, updates); + toast.success(t('imageDatasets.updateSuccess', '更新成功')); + // 刷新数据 + await fetchDatasetDetail(); + await fetchDatasetsList(); + } catch (error) { + console.error('Failed to update dataset:', error); + toast.error(t('imageDatasets.updateFailed', '更新失败')); + } finally { + setSaving(false); + } + }, + [projectId, datasetId, t, fetchDatasetDetail, fetchDatasetsList] + ); + + // 翻页导航 + const handleNavigate = useCallback( + async (direction, skipCurrentId = null) => { + try { + // 获取所有数据集(不分页),使用一个足够大的 pageSize + const response = await axios.get(`/api/projects/${projectId}/image-datasets?page=1&pageSize=10000`); + const datasets = response.data.data || []; + + if (datasets.length === 0) { + router.push(`/projects/${projectId}/image-datasets`); + return; + } + + // 确定当前索引 + let currentIndex = -1; + const searchId = skipCurrentId || datasetId; + const currentDatasetId = String(searchId); + + // 查找当前数据集的索引 + currentIndex = datasets.findIndex(d => String(d.id) === currentDatasetId); + + // 如果找不到(删除场景或其他原因),从第一个开始 + if (currentIndex === -1) { + currentIndex = 0; + } + + // 计算下一个索引 + let nextIndex; + if (direction === 'prev') { + nextIndex = currentIndex > 0 ? currentIndex - 1 : datasets.length - 1; + } else { + nextIndex = currentIndex < datasets.length - 1 ? currentIndex + 1 : 0; + } + + const nextDataset = datasets[nextIndex]; + if (nextDataset) { + router.push(`/projects/${projectId}/image-datasets/${nextDataset.id}`); + } + } catch (error) { + console.error('Failed to navigate:', error); + toast.error(t('common.navigationFailed', '导航失败')); + } + }, + [projectId, datasetId, router, t] + ); + + // 确认保留 + const handleConfirm = useCallback(async () => { + setConfirming(true); + try { + await updateDataset({ confirmed: true }); + // 确认后导航到下一条 + await handleNavigate('next'); + } finally { + setConfirming(false); + } + }, [updateDataset, handleNavigate]); + + // 取消确认 + const handleUnconfirm = useCallback(async () => { + setUnconfirming(true); + try { + await updateDataset({ confirmed: false }); + } finally { + setUnconfirming(false); + } + }, [updateDataset]); + + // 删除数据集 + const handleDelete = useCallback(async () => { + if (confirm(t('imageDatasets.deleteConfirm', '确定要删除这个数据集吗?'))) { + try { + await axios.delete(`/api/projects/${projectId}/image-datasets/${datasetId}`); + toast.success(t('imageDatasets.deleteSuccess', '删除成功')); + // 导航到下一条,传递 datasetId 以便 handleNavigate 知道是删除场景 + await handleNavigate('next', datasetId); + } catch (error) { + console.error('Failed to delete dataset:', error); + toast.error(t('imageDatasets.deleteFailed', '删除失败')); + } + } + }, [projectId, datasetId, handleNavigate, t]); + + return { + currentDataset, + loading, + saving, + confirming, + unconfirming, + datasetsAllCount, + datasetsConfirmCount, + updateDataset, + handleNavigate, + handleConfirm, + handleUnconfirm, + handleDelete + }; +} diff --git a/easy-dataset-main/app/projects/[projectId]/image-datasets/hooks/useImageDatasetExport.js b/easy-dataset-main/app/projects/[projectId]/image-datasets/hooks/useImageDatasetExport.js new file mode 100644 index 0000000..76577d2 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/image-datasets/hooks/useImageDatasetExport.js @@ -0,0 +1,195 @@ +'use client'; + +import { useTranslation } from 'react-i18next'; +import { toast } from 'sonner'; +import axios from 'axios'; + +const useImageDatasetExport = projectId => { + const { t } = useTranslation(); + + /** + * 解析标签格式的答案 + * 如果答案是 JSON 数组格式,解析并用逗号连接 + */ + const parseAnswerLabels = item => { + const { answer, answerType } = item; + if (answerType !== 'label' || !answer) { + return answer; + } + + try { + // 尝试解析 JSON + const parsed = JSON.parse(answer); + if (Array.isArray(parsed)) { + // 如果是数组,用逗号连接 + return parsed.join(', '); + } + return answer; + } catch (e) { + // 不是 JSON 格式,直接返回原答案 + return answer; + } + }; + + /** + * 导出图片数据集 + */ + const exportImageDatasets = async exportOptions => { + try { + // 1. 获取数据集数据 + const apiUrl = `/api/projects/${projectId}/image-datasets/export`; + const response = await axios.post(apiUrl, { + confirmedOnly: exportOptions.confirmedOnly + }); + + let datasets = response.data; + + if (!datasets || datasets.length === 0) { + toast.warning(t('imageDatasets.noDataToExport', '没有可导出的数据')); + return false; + } + + // 2. 处理答案中的标签格式 + datasets = datasets.map(item => ({ + ...item, + answer: parseAnswerLabels(item) + })); + + // 3. 根据格式类型转换数据 + let formattedData; + + if (exportOptions.formatType === 'raw') { + // 原始格式:直接导出数据集 + formattedData = datasets.map(item => { + const result = { ...item }; + + // 如果需要包含图片路径 + if (exportOptions.includeImagePath && item.imageName) { + result.image_path = `/images/${item.imageName}`; + } + + if (item.answerType === 'custom_format') { + try { + result.answerObj = JSON.parse(item.answer); + } catch {} + } + + return result; + }); + } else if (exportOptions.formatType === 'alpaca') { + formattedData = datasets.map(({ question, answer, imageName }) => { + const item = { + instruction: question, + input: '', + output: answer + }; + + // 如果需要包含图片路径 + if (exportOptions.includeImagePath && imageName) { + item.images = [`/images/${imageName}`]; + } + + return item; + }); + } else if (exportOptions.formatType === 'sharegpt') { + formattedData = datasets.map(({ question, answer, imageName }) => { + const messages = []; + + // 添加系统提示词(如果有) + if (exportOptions.systemPrompt) { + messages.push({ + role: 'system', + content: exportOptions.systemPrompt + }); + } + + // 添加用户问题 + const userContent = []; + + // 如果需要包含图片路径 + if (exportOptions.includeImagePath && imageName) { + userContent.push({ + type: 'image_url', + image_url: { + url: `/images/${imageName}` + } + }); + } + + userContent.push({ + type: 'text', + text: question + }); + + messages.push({ + role: 'user', + content: userContent + }); + + // 添加助手回答 + messages.push({ + role: 'assistant', + content: answer + }); + + return { messages }; + }); + } + + // 4. 生成 JSON 文件 + const jsonContent = JSON.stringify(formattedData, null, 2); + const blob = new Blob([jsonContent], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + + const formatSuffix = exportOptions.formatType; + const dateStr = new Date().toISOString().slice(0, 10); + a.download = `image-datasets-${projectId}-${formatSuffix}-${dateStr}.json`; + + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + + toast.success(t('imageDatasets.exportSuccess', '数据集导出成功')); + + // 5. 如果需要导出图片,调用压缩包接口 + if (exportOptions.exportImages) { + try { + const params = new URLSearchParams({ + confirmedOnly: exportOptions.confirmedOnly.toString() + }); + + const zipUrl = `/api/projects/${projectId}/image-datasets/export-zip?${params.toString()}`; + + // 创建一个隐藏的 a 标签来触发下载 + const a = document.createElement('a'); + a.href = zipUrl; + a.style.display = 'none'; + a.target = '_blank'; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + + toast.success(t('imageDatasets.exportImagesSuccess', '图片压缩包导出成功')); + } catch (error) { + console.error('Failed to export images:', error); + toast.error(t('imageDatasets.exportImagesFailed', '图片导出失败')); + } + } + + return true; + } catch (error) { + console.error('Export failed:', error); + toast.error(error.message || t('imageDatasets.exportFailed', '导出失败')); + return false; + } + }; + + return { + exportImageDatasets + }; +}; + +export default useImageDatasetExport; diff --git a/easy-dataset-main/app/projects/[projectId]/image-datasets/hooks/useImageDatasetFilters.js b/easy-dataset-main/app/projects/[projectId]/image-datasets/hooks/useImageDatasetFilters.js new file mode 100644 index 0000000..6a5329a --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/image-datasets/hooks/useImageDatasetFilters.js @@ -0,0 +1,71 @@ +import { useState, useEffect, useCallback } from 'react'; + +const STORAGE_KEY = 'imageDatasetFilters'; + +export function useImageDatasetFilters(projectId) { + const [searchQuery, setSearchQuery] = useState(''); + const [statusFilter, setStatusFilter] = useState('all'); + const [scoreFilter, setScoreFilter] = useState([0, 5]); + const [isInitialized, setIsInitialized] = useState(false); + + // 从 localStorage 恢复筛选条件 + useEffect(() => { + try { + const stored = localStorage.getItem(`${STORAGE_KEY}_${projectId}`); + if (stored) { + const filters = JSON.parse(stored); + setSearchQuery(filters.searchQuery || ''); + setStatusFilter(filters.statusFilter || 'all'); + setScoreFilter(filters.scoreFilter || [0, 5]); + } + } catch (error) { + console.error('Failed to restore filters:', error); + } + setIsInitialized(true); + }, [projectId]); + + // 保存筛选条件到 localStorage + useEffect(() => { + if (isInitialized) { + try { + localStorage.setItem( + `${STORAGE_KEY}_${projectId}`, + JSON.stringify({ + searchQuery, + statusFilter, + scoreFilter + }) + ); + } catch (error) { + console.error('Failed to save filters:', error); + } + } + }, [projectId, searchQuery, statusFilter, scoreFilter, isInitialized]); + + // 计算活跃筛选条件数 + const getActiveFilterCount = useCallback(() => { + let count = 0; + if (statusFilter !== 'all') count++; + if (scoreFilter[0] > 0 || scoreFilter[1] < 5) count++; + return count; + }, [statusFilter, scoreFilter]); + + // 重置筛选条件 + const resetFilters = useCallback(() => { + setSearchQuery(''); + setStatusFilter('all'); + setScoreFilter([0, 5]); + }, []); + + return { + searchQuery, + setSearchQuery, + statusFilter, + setStatusFilter, + scoreFilter, + setScoreFilter, + isInitialized, + getActiveFilterCount, + resetFilters + }; +} diff --git a/easy-dataset-main/app/projects/[projectId]/image-datasets/hooks/useImageDatasets.js b/easy-dataset-main/app/projects/[projectId]/image-datasets/hooks/useImageDatasets.js new file mode 100644 index 0000000..fe52a73 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/image-datasets/hooks/useImageDatasets.js @@ -0,0 +1,90 @@ +import { useState, useEffect, useCallback, useMemo } from 'react'; +import axios from 'axios'; +import { toast } from 'sonner'; +import { useTranslation } from 'react-i18next'; + +export function useImageDatasets(projectId, filters = {}) { + const { t } = useTranslation(); + const [datasets, setDatasets] = useState({ data: [], total: 0 }); + const [loading, setLoading] = useState(false); + const [page, setPage] = useState(1); + const pageSize = 20; + + // 使用 useMemo 稳定 filters 对象引用 + const stableFilters = useMemo( + () => ({ + search: filters.search || '', + confirmed: filters.confirmed, + minScore: filters.minScore, + maxScore: filters.maxScore + }), + [filters.search, filters.confirmed, filters.minScore, filters.maxScore] + ); + + // 获取数据集列表 + const fetchDatasets = useCallback(async () => { + try { + setLoading(true); + let url = `/api/projects/${projectId}/image-datasets?page=${page}&pageSize=${pageSize}`; + + // 搜索条件 + if (stableFilters.search) { + url += `&search=${encodeURIComponent(stableFilters.search)}`; + } + + // 确认状态筛选 + if (stableFilters.confirmed !== undefined) { + url += `&confirmed=${stableFilters.confirmed}`; + } + + // 评分筛选 + if (stableFilters.minScore !== undefined || stableFilters.maxScore !== undefined) { + if (stableFilters.minScore !== undefined) { + url += `&minScore=${stableFilters.minScore}`; + } + if (stableFilters.maxScore !== undefined) { + url += `&maxScore=${stableFilters.maxScore}`; + } + } + + const response = await axios.get(url); + setDatasets(response.data); + } catch (error) { + console.error('Failed to fetch datasets:', error); + toast.error(t('imageDatasets.fetchFailed', { defaultValue: '获取数据集失败' })); + } finally { + setLoading(false); + } + }, [projectId, page, pageSize, stableFilters, t]); + + // 删除数据集 + const deleteDataset = useCallback( + async datasetId => { + try { + await axios.delete(`/api/projects/${projectId}/image-datasets/${datasetId}`); + toast.success(t('imageDatasets.deleteSuccess', { defaultValue: '删除成功' })); + fetchDatasets(); + } catch (error) { + console.error('Failed to delete dataset:', error); + toast.error(t('imageDatasets.deleteFailed', { defaultValue: '删除失败' })); + } + }, + [projectId, fetchDatasets, t] + ); + + useEffect(() => { + if (projectId) { + fetchDatasets(); + } + }, [projectId, page, stableFilters, fetchDatasets]); + + return { + datasets, + loading, + page, + setPage, + pageSize, + fetchDatasets, + deleteDataset + }; +} diff --git a/easy-dataset-main/app/projects/[projectId]/image-datasets/page.js b/easy-dataset-main/app/projects/[projectId]/image-datasets/page.js new file mode 100644 index 0000000..07d2226 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/image-datasets/page.js @@ -0,0 +1,193 @@ +'use client'; + +import { useState } from 'react'; +import { Container, Box, Typography, Grid, Pagination, CircularProgress, Card, Button } from '@mui/material'; +import { useParams, useRouter } from 'next/navigation'; +import { useTranslation } from 'react-i18next'; +import axios from 'axios'; +import { toast } from 'sonner'; +import { imageDatasetStyles } from './styles/imageDatasetStyles'; +import { useImageDatasets } from './hooks/useImageDatasets'; +import { useImageDatasetFilters } from './hooks/useImageDatasetFilters'; +import ImageDatasetFilters from './components/ImageDatasetFilters'; +import ImageDatasetFilterDialog from './components/ImageDatasetFilterDialog'; +import ImageDatasetCard from './components/ImageDatasetCard'; +import EmptyState from './components/EmptyState'; +import ExportImageDatasetDialog from './components/ExportImageDatasetDialog'; +import useImageDatasetExport from './hooks/useImageDatasetExport'; +import FileDownloadIcon from '@mui/icons-material/FileDownload'; +import { alpha } from '@mui/material/styles'; + +export default function ImageDatasetsPage() { + const { projectId } = useParams(); + const router = useRouter(); + const { t } = useTranslation(); + const [filterDialogOpen, setFilterDialogOpen] = useState(false); + const [exportDialogOpen, setExportDialogOpen] = useState(false); + + // 使用筛选 Hook + const { + searchQuery, + setSearchQuery, + statusFilter, + setStatusFilter, + scoreFilter, + setScoreFilter, + getActiveFilterCount, + resetFilters + } = useImageDatasetFilters(projectId); + + // 使用数据 Hook + const { datasets, loading, page, setPage, pageSize, fetchDatasets } = useImageDatasets(projectId, { + search: searchQuery, + confirmed: statusFilter === 'all' ? undefined : statusFilter === 'confirmed', + minScore: scoreFilter[0], + maxScore: scoreFilter[1] + }); + + // 使用导出 Hook + const { exportImageDatasets } = useImageDatasetExport(projectId); + + const handlePageChange = (event, value) => { + setPage(value); + window.scrollTo({ top: 0, behavior: 'smooth' }); + }; + + const handleCardClick = datasetId => { + router.push(`/projects/${projectId}/image-datasets/${datasetId}`); + }; + + const handleViewDetails = datasetId => { + router.push(`/projects/${projectId}/image-datasets/${datasetId}`); + }; + + const handleDeleteDataset = async datasetId => { + if (confirm(t('imageDatasets.deleteConfirm', '确定要删除这个数据集吗?'))) { + try { + await axios.delete(`/api/projects/${projectId}/image-datasets/${datasetId}`); + toast.success(t('imageDatasets.deleteSuccess', '删除成功')); + // 重新查询数据 + fetchDatasets(); + } catch (error) { + console.error('Failed to delete dataset:', error); + toast.error(t('imageDatasets.deleteFailed', '删除失败')); + } + } + }; + + const handleEvaluateDataset = datasetId => { + toast.info(t('common.comingSoon', '功能开发中...')); + }; + + const handleResetFilters = () => { + resetFilters(); + setFilterDialogOpen(false); + }; + + const handleApplyFilters = () => { + setFilterDialogOpen(false); + setPage(1); + }; + + const handleExport = async exportOptions => { + setExportDialogOpen(false); + await exportImageDatasets(exportOptions); + }; + + const totalPages = Math.ceil(datasets.total / pageSize); + + return ( + + {/* 筛选区域 - 参考数据集管理的设计 */} + alpha(theme.palette.primary.main, 0.06) + }} + > + + { + setSearchQuery(value); + setPage(1); + }} + onMoreFiltersClick={() => setFilterDialogOpen(true)} + activeFilterCount={getActiveFilterCount()} + /> + + + + + {/* 数据集列表 */} + {loading ? ( + + + + ) : datasets.data.length === 0 ? ( + + ) : ( + <> + + {datasets.data.map(dataset => ( + + + + ))} + + + {/* 分页 */} + {totalPages > 1 && ( + + + + )} + + )} + + {/* 筛选对话框 */} + setFilterDialogOpen(false)} + statusFilter={statusFilter} + scoreFilter={scoreFilter} + onStatusChange={setStatusFilter} + onScoreChange={setScoreFilter} + onResetFilters={handleResetFilters} + onApplyFilters={handleApplyFilters} + /> + + {/* 导出对话框 */} + setExportDialogOpen(false)} + onExport={handleExport} + /> + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/image-datasets/styles/imageDatasetStyles.js b/easy-dataset-main/app/projects/[projectId]/image-datasets/styles/imageDatasetStyles.js new file mode 100644 index 0000000..fe0368c --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/image-datasets/styles/imageDatasetStyles.js @@ -0,0 +1,266 @@ +/** + * 图片数据集模块样式配置 + * 参考图片管理模块的精美设计 + */ + +export const imageDatasetStyles = { + // 页面容器 + pageContainer: { + py: 4 + }, + + // 页面头部 + header: { + mb: 4, + display: 'flex', + justifyContent: 'space-between', + alignItems: 'flex-start', + flexWrap: 'wrap', + gap: 3 + }, + + headerTitle: { + display: 'flex', + flexDirection: 'column', + gap: 0.5 + }, + + title: { + fontWeight: 700 + }, + + subtitle: { + color: 'text.secondary', + fontSize: '0.875rem' + }, + + headerActions: { + display: 'flex', + gap: 2, + flexWrap: 'wrap' + }, + + // 筛选区域 + filterCard: { + mb: 3, + borderRadius: 2, + boxShadow: 1, + border: '1px solid', + borderColor: 'divider', + overflow: 'visible' + }, + + filterContent: { + display: 'flex', + gap: 2, + alignItems: 'center', + flexWrap: 'wrap' + }, + + // 数据集卡片 - 参考图片管理的设计 + datasetCard: { + height: '100%', + display: 'flex', + flexDirection: 'column', + borderRadius: 3, + overflow: 'hidden', + transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)', + border: '1px solid', + borderColor: 'divider', + bgcolor: 'background.paper', + cursor: 'pointer', + '&:hover': { + transform: 'translateY(-8px)', + boxShadow: theme => `0 12px 24px ${theme.palette.mode === 'dark' ? 'rgba(0,0,0,0.4)' : 'rgba(0,0,0,0.15)'}`, + borderColor: 'primary.main', + '& .image-overlay': { + opacity: 1 + }, + '& .image-media': { + transform: 'scale(1.05)' + } + } + }, + + // 图片包装器 + imageWrapper: { + position: 'relative', + overflow: 'hidden', + bgcolor: 'grey.100' + }, + + // 图片媒体 + imageMedia: { + className: 'image-media', + height: 220, + objectFit: 'cover', + transition: 'transform 0.3s ease' + }, + + // 悬停遮罩 + imageOverlay: { + className: 'image-overlay', + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + background: + 'linear-gradient(to bottom, rgba(0,0,0,0.6) 0%, transparent 40%, transparent 60%, rgba(0,0,0,0.6) 100%)', + opacity: 0, + transition: 'opacity 0.3s ease', + pointerEvents: 'none' + }, + + // 状态标签容器 - 右上角 + statusChipsContainer: { + position: 'absolute', + top: 12, + right: 12, + display: 'flex', + gap: 0.5, + flexDirection: 'column', + alignItems: 'flex-end', + zIndex: 2 + }, + + // 状态标签 + statusChip: { + backdropFilter: 'blur(10px)', + fontWeight: 600, + fontSize: '0.75rem', + height: 24, + boxShadow: 2 + }, + + // 图片名称容器 - 底部 + imageNameContainer: { + position: 'absolute', + bottom: 12, + left: 12, + right: 12, + display: 'flex', + justifyContent: 'center', + zIndex: 2 + }, + + // 图片名称标签 + imageNameChip: { + backdropFilter: 'blur(10px)', + bgcolor: 'rgba(255, 255, 255, 0.95)', + fontWeight: 600, + maxWidth: '90%', + boxShadow: 2, + '& .MuiChip-label': { + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap' + } + }, + + // 卡片内容 + cardContent: { + flexGrow: 1, + p: 2.5 + }, + + // 问题文本 + questionText: { + fontWeight: 600, + fontSize: '0.95rem', + lineHeight: 1.5, + mb: 1.5, + display: '-webkit-box', + WebkitLineClamp: 2, + WebkitBoxOrient: 'vertical', + overflow: 'hidden', + textOverflow: 'ellipsis' + }, + + // 答案预览 + answerPreview: { + color: 'text.secondary', + fontSize: '0.875rem', + lineHeight: 1.6, + display: '-webkit-box', + WebkitLineClamp: 2, + WebkitBoxOrient: 'vertical', + overflow: 'hidden', + textOverflow: 'ellipsis', + mb: 2 + }, + + // 元数据信息 + metaInfo: { + display: 'flex', + gap: 1.5, + flexWrap: 'wrap', + mt: 2, + pt: 2, + borderTop: '1px solid', + borderColor: 'divider' + }, + + metaItem: { + display: 'flex', + alignItems: 'center', + gap: 0.5, + fontSize: '0.75rem', + color: 'text.secondary' + }, + + // 分页样式 + pagination: { + display: 'flex', + justifyContent: 'center', + mt: 4 + }, + + // 操作按钮容器 + actionButtonsContainer: { + display: 'flex', + justifyContent: 'flex-end', + gap: 0.5, + mt: 'auto' + }, + + // 操作按钮样式 + actionButton: { + p: 0.5, + borderRadius: 1, + color: 'text.secondary', + '&:hover': { + backgroundColor: 'action.hover', + color: 'primary.main' + } + }, + + // 空状态 + emptyState: { + textAlign: 'center', + py: 12, + px: 3 + }, + + emptyIcon: { + width: 120, + height: 120, + borderRadius: '50%', + bgcolor: 'primary.lighter', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + mx: 'auto', + mb: 3 + }, + + emptyTitle: { + fontWeight: 600, + mb: 1 + }, + + emptyDescription: { + color: 'text.secondary', + mb: 4 + } +}; diff --git a/easy-dataset-main/app/projects/[projectId]/images/components/DatasetDialog.js b/easy-dataset-main/app/projects/[projectId]/images/components/DatasetDialog.js new file mode 100644 index 0000000..73c38bb --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/images/components/DatasetDialog.js @@ -0,0 +1,135 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + TextField, + CircularProgress, + Alert, + Box, + Typography +} from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import { useAtomValue } from 'jotai'; +import { selectedModelInfoAtom } from '@/lib/store'; +import { toast } from 'sonner'; +import axios from 'axios'; + +export default function DatasetDialog({ open, projectId, image, onClose, onSuccess }) { + const { t, i18n } = useTranslation(); + const selectedModel = useAtomValue(selectedModelInfoAtom); + const [question, setQuestion] = useState(''); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(''); + + useEffect(() => { + if (open) { + setQuestion(''); + setError(''); + } + }, [open]); + + const handleGenerate = async () => { + if (!selectedModel) { + setError(t('images.selectModelFirst')); + return; + } + + if (selectedModel.type !== 'vision') { + setError(t('images.visionModelRequired')); + return; + } + + if (!question.trim()) { + setError(t('images.questionRequired')); + return; + } + + try { + setLoading(true); + setError(''); + + await axios.post(`/api/projects/${projectId}/images/datasets`, { + imageName: image.imageName, + question: { question: question.trim() }, + model: selectedModel, + language: i18n.language + }); + + toast.success(t('images.datasetGenerated')); + onSuccess?.(); + onClose(); + } catch (err) { + console.error('Failed to generate dataset:', err); + setError(err.response?.data?.error || t('images.generateFailed')); + } finally { + setLoading(false); + } + }; + + const handleClose = () => { + if (!loading) { + onClose(); + } + }; + + return ( + + {t('images.generateDataset')} + + {error && ( + + {error} + + )} + + {image && ( + + + {t('images.imageName')} + + + {image.imageName} + + + )} + + setQuestion(e.target.value)} + placeholder={t('images.questionPlaceholder')} + disabled={loading} + /> + + {selectedModel && ( + + + {t('images.currentModel')}: {selectedModel.modelName} + + + )} + + + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/images/components/ImageFilters.js b/easy-dataset-main/app/projects/[projectId]/images/components/ImageFilters.js new file mode 100644 index 0000000..8c16da8 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/images/components/ImageFilters.js @@ -0,0 +1,111 @@ +'use client'; + +import { + Box, + TextField, + Select, + MenuItem, + FormControl, + InputLabel, + InputAdornment, + Card, + CardContent, + ToggleButtonGroup, + ToggleButton +} from '@mui/material'; +import SearchIcon from '@mui/icons-material/Search'; +import GridViewIcon from '@mui/icons-material/GridView'; +import ViewListIcon from '@mui/icons-material/ViewList'; +import { useTranslation } from 'react-i18next'; +import { useDebounce } from '@/hooks/useDebounce'; +import { useEffect, useState } from 'react'; +import { imageStyles } from '../styles/imageStyles'; + +export default function ImageFilters({ + imageName, + onImageNameChange, + hasQuestions, + onHasQuestionsChange, + hasDatasets, + onHasDatasetsChange, + viewMode = 'grid', + onViewModeChange +}) { + const { t } = useTranslation(); + const [localImageName, setLocalImageName] = useState(imageName); + const debouncedImageName = useDebounce(localImageName, 500); + + useEffect(() => { + onImageNameChange(debouncedImageName); + }, [debouncedImageName]); + + return ( + + + + {/* 搜索框 */} + setLocalImageName(e.target.value)} + size="small" + sx={imageStyles.searchField} + InputProps={{ + startAdornment: ( + + + + ) + }} + /> + + {/* 问题状态筛选 */} + + {t('images.hasQuestions', { defaultValue: '问题状态' })} + + + + {/* 数据集状态筛选 */} + + {t('images.hasDatasets', { defaultValue: '数据集状态' })} + + + + {/* 视图切换 */} + {onViewModeChange && ( + newMode && onViewModeChange(newMode)} + size="small" + sx={imageStyles.viewToggle} + > + + + + + + + + )} + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/images/components/ImageGrid.js b/easy-dataset-main/app/projects/[projectId]/images/components/ImageGrid.js new file mode 100644 index 0000000..c7eda98 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/images/components/ImageGrid.js @@ -0,0 +1,193 @@ +'use client'; + +import { useState } from 'react'; +import { + Grid, + Card, + CardMedia, + CardContent, + CardActions, + Typography, + Chip, + Box, + Pagination, + Tooltip, + Dialog, + DialogContent, + IconButton, + Button +} from '@mui/material'; +import QuestionMarkIcon from '@mui/icons-material/QuestionMark'; +import DatasetIcon from '@mui/icons-material/Dataset'; +import DeleteIcon from '@mui/icons-material/Delete'; +import EditNoteIcon from '@mui/icons-material/EditNote'; +import PhotoLibraryIcon from '@mui/icons-material/PhotoLibrary'; +import { useTranslation } from 'react-i18next'; +import { imageStyles } from '../styles/imageStyles'; + +export default function ImageGrid({ + images, + total, + page, + pageSize, + onPageChange, + onGenerateQuestions, + onGenerateDataset, + onDelete, + onAnnotate +}) { + const { t } = useTranslation(); + const [previewImage, setPreviewImage] = useState(null); + + if (!images || images.length === 0) { + return ( + + + + + + {t('images.noImages', { defaultValue: '还没有图片' })} + + + {t('images.noImagesDescription', { defaultValue: '开始导入图片,创建您的第一个图片数据集' })} + + + ); + } + + return ( + <> + + {images.map(image => ( + + + {/* 图片区域 */} + + setPreviewImage(image)} + /> + + {/* 悬停遮罩 */} + + + {/* 状态标签 - 悬浮在图片右上角 */} + + 0 ? 'primary' : 'default'} + sx={imageStyles.statusChip} + /> + 0 ? 'success' : 'default'} + sx={imageStyles.statusChip} + /> + + + {/* 文件名标签 - 悬浮在图片底部 */} + + + + + + + + {/* 操作按钮区域 */} + + + + onGenerateQuestions(image)} sx={imageStyles.actionIconButton}> + + + + + onGenerateDataset(image)} sx={imageStyles.actionIconButton}> + + + + + onDelete(image.id)} + sx={imageStyles.actionIconButton} + > + + + + + + + ))} + + + {total > pageSize && ( + + onPageChange(newPage)} + color="primary" + size="large" + showFirstButton + showLastButton + /> + + )} + + {/* 图片预览对话框 */} + setPreviewImage(null)} + maxWidth="lg" + fullWidth + PaperProps={{ + sx: { + bgcolor: 'transparent', + boxShadow: 'none', + overflow: 'hidden' + } + }} + > + + {previewImage && ( + + {previewImage.imageName} + + {previewImage.imageName} + + + )} + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/images/components/ImageList.js b/easy-dataset-main/app/projects/[projectId]/images/components/ImageList.js new file mode 100644 index 0000000..668fc58 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/images/components/ImageList.js @@ -0,0 +1,315 @@ +'use client'; + +import { useState } from 'react'; +import { + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Paper, + Chip, + Box, + Pagination, + Tooltip, + IconButton, + Avatar, + Dialog, + DialogContent, + Typography, + Button, + Checkbox +} from '@mui/material'; +import QuestionMarkIcon from '@mui/icons-material/QuestionMark'; +import DatasetIcon from '@mui/icons-material/Dataset'; +import DeleteIcon from '@mui/icons-material/Delete'; +import EditNoteIcon from '@mui/icons-material/EditNote'; +import PhotoLibraryIcon from '@mui/icons-material/PhotoLibrary'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import { useTranslation } from 'react-i18next'; +import { imageStyles } from '../styles/imageStyles'; + +export default function ImageList({ + images, + total, + page, + pageSize, + onPageChange, + onGenerateQuestions, + onGenerateDataset, + onDelete, + onAnnotate, + selectedIds = [], + onSelectionChange +}) { + const { t } = useTranslation(); + const [previewImage, setPreviewImage] = useState(null); + + // 处理全选/取消全选 + const handleSelectAll = event => { + if (event.target.checked) { + const allIds = images.map(img => img.id); + onSelectionChange?.(allIds); + } else { + onSelectionChange?.([]); + } + }; + + // 处理单个选择 + const handleSelectOne = (imageId, checked) => { + if (checked) { + onSelectionChange?.([...selectedIds, imageId]); + } else { + onSelectionChange?.(selectedIds.filter(id => id !== imageId)); + } + }; + + // 判断是否全选 + const isAllSelected = images.length > 0 && selectedIds.length === images.length; + const isSomeSelected = selectedIds.length > 0 && selectedIds.length < images.length; + + // 格式化日期 + const formatDate = dateString => { + if (!dateString) return '-'; + const date = new Date(dateString); + return date.toLocaleDateString('zh-CN', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit' + }); + }; + + // 格式化文件大小 + const formatSize = bytes => { + if (!bytes) return '-'; + if (bytes < 1024) return `${bytes} B`; + if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} KB`; + return `${(bytes / (1024 * 1024)).toFixed(2)} MB`; + }; + + if (!images || images.length === 0) { + return ( + + + + + + {t('images.noImages', { defaultValue: '还没有图片' })} + + + {t('images.noImagesDescription', { defaultValue: '开始导入图片,创建您的第一个图片数据集' })} + + + ); + } + + return ( + <> + + + + + + + + {t('images.preview', { defaultValue: '预览' })} + {t('images.fileName', { defaultValue: '文件名' })} + {t('images.size', { defaultValue: '大小' })} + {t('images.dimensions', { defaultValue: '尺寸' })} + {t('images.questionCount', { defaultValue: '问题数' })} + {t('images.datasetCount', { defaultValue: '数据集数' })} + {t('images.uploadTime', { defaultValue: '上传时间' })} + + {t('common.actions', { defaultValue: '操作' })} + + + + + {images.map(image => ( + + {/* 复选框 */} + + handleSelectOne(image.id, e.target.checked)} + /> + + + {/* 预览缩略图 */} + + setPreviewImage(image)} + /> + + + {/* 文件名 */} + + + + {image.imageName} + + + + + {/* 文件大小 */} + + + {formatSize(image.size)} + + + + {/* 尺寸 */} + + {image.width && image.height ? ( + + {image.width} × {image.height} + + ) : ( + + - + + )} + + + {/* 问题数 */} + + 0 ? 'primary' : 'default'} + variant="outlined" + /> + + + {/* 数据集数 */} + + 0 ? 'success' : 'default'} + variant="outlined" + /> + + + {/* 上传时间 */} + + + {formatDate(image.createAt)} + + + + {/* 操作按钮 */} + + + + setPreviewImage(image)}> + + + + + onAnnotate(image)}> + + + + + onGenerateQuestions(image)}> + + + + + onGenerateDataset(image)}> + + + + + onDelete(image.id)}> + + + + + + + ))} + +
+
+ + {/* 分页 */} + {total > pageSize && ( + + onPageChange(newPage)} + color="primary" + size="large" + showFirstButton + showLastButton + /> + + )} + + {/* 图片预览对话框 */} + setPreviewImage(null)} + maxWidth="lg" + fullWidth + PaperProps={{ + sx: { + bgcolor: 'transparent', + boxShadow: 'none', + overflow: 'hidden' + } + }} + > + + {previewImage && ( + + {previewImage.imageName} + + {previewImage.imageName} + + + )} + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/images/components/ImportDialog.js b/easy-dataset-main/app/projects/[projectId]/images/components/ImportDialog.js new file mode 100644 index 0000000..90b5eea --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/images/components/ImportDialog.js @@ -0,0 +1,416 @@ +'use client'; + +import { useState } from 'react'; +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + Box, + Typography, + List, + ListItem, + ListItemText, + IconButton, + CircularProgress, + Alert, + TextField, + Tabs, + Tab, + Paper, + Chip, + Card +} from '@mui/material'; +import FolderOpenIcon from '@mui/icons-material/FolderOpen'; +import PictureAsPdfIcon from '@mui/icons-material/PictureAsPdf'; +import UploadFileIcon from '@mui/icons-material/UploadFile'; +import FolderZipIcon from '@mui/icons-material/FolderZip'; +import { useTranslation } from 'react-i18next'; +import { toast } from 'sonner'; +import axios from 'axios'; + +export default function ImportDialog({ open, projectId, onClose, onSuccess }) { + const { t } = useTranslation(); + const [mode, setMode] = useState(0); // 0: 目录导入, 1: PDF 导入, 2: 压缩包导入 + const [directories, setDirectories] = useState([]); + const [loading, setLoading] = useState(false); + const [inputPath, setInputPath] = useState(''); + const [selectedPdf, setSelectedPdf] = useState(null); + const [selectedZip, setSelectedZip] = useState(null); + + const handleAddDirectory = () => { + if (inputPath.trim() && !directories.includes(inputPath.trim())) { + setDirectories([...directories, inputPath.trim()]); + setInputPath(''); + } + }; + + const handleRemoveDirectory = index => { + setDirectories(directories.filter((_, i) => i !== index)); + }; + + const handleImport = async () => { + if (directories.length === 0) { + toast.error(t('images.selectAtLeastOne')); + return; + } + + try { + setLoading(true); + const response = await axios.post(`/api/projects/${projectId}/images`, { + directories + }); + + toast.success(t('images.importSuccess', { count: response.data.count })); + setDirectories([]); + onSuccess?.(); + } catch (error) { + console.error('Failed to import images:', error); + toast.error(error.response?.data?.error || t('images.importFailed')); + } finally { + setLoading(false); + } + }; + + const handlePdfSelect = event => { + const file = event.target.files?.[0]; + if (file && file.type === 'application/pdf') { + setSelectedPdf(file); + } else { + toast.error(t('images.invalidPdfFile', { defaultValue: '请选择有效的 PDF 文件' })); + } + }; + + const handlePdfImport = async () => { + if (!selectedPdf) { + toast.error(t('images.selectPdfFile', { defaultValue: '请选择 PDF 文件' })); + return; + } + + try { + setLoading(true); + const formData = new FormData(); + formData.append('file', selectedPdf); + + // 调用 PDF 转换 API + const response = await axios.post(`/api/projects/${projectId}/images/pdf-convert`, formData, { + headers: { + 'Content-Type': 'multipart/form-data' + } + }); + + toast.success( + t('images.pdfImportSuccess', { + defaultValue: `成功从 PDF "${response.data.pdfName}" 导入 ${response.data.count} 张图片`, + count: response.data.count, + name: response.data.pdfName + }) + ); + setSelectedPdf(null); + onSuccess?.(); + } catch (error) { + console.error('Failed to import PDF:', error); + toast.error(error.response?.data?.error || t('images.pdfImportFailed', { defaultValue: 'PDF 导入失败' })); + } finally { + setLoading(false); + } + }; + + const handleZipSelect = event => { + const file = event.target.files?.[0]; + if (file && file.name.toLowerCase().endsWith('.zip')) { + setSelectedZip(file); + } else { + toast.error(t('images.invalidZipFile', { defaultValue: '请选择有效的 ZIP 文件' })); + } + }; + + const handleZipImport = async () => { + if (!selectedZip) { + toast.error(t('images.selectZipFile', { defaultValue: '请选择 ZIP 文件' })); + return; + } + + try { + setLoading(true); + const formData = new FormData(); + formData.append('file', selectedZip); + + // 调用压缩包导入 API + const response = await axios.post(`/api/projects/${projectId}/images/zip-import`, formData, { + headers: { + 'Content-Type': 'multipart/form-data' + } + }); + + toast.success( + t('images.zipImportSuccess', { + defaultValue: `成功从压缩包 "${response.data.zipName}" 导入 ${response.data.count} 张图片`, + count: response.data.count, + name: response.data.zipName + }) + ); + setSelectedZip(null); + onSuccess?.(); + } catch (error) { + console.error('Failed to import ZIP:', error); + toast.error(error.response?.data?.error || t('images.zipImportFailed', { defaultValue: '压缩包导入失败' })); + } finally { + setLoading(false); + } + }; + + const handleClose = () => { + if (!loading) { + setDirectories([]); + setSelectedPdf(null); + setSelectedZip(null); + setMode(0); + onClose(); + } + }; + + return ( + + {t('images.importImages')} + + setMode(newValue)} + sx={{ mb: 3, borderBottom: 1, borderColor: 'divider' }} + > + } + iconPosition="start" + /> + } + iconPosition="start" + /> + } + iconPosition="start" + /> + + + {mode === 0 ? ( + <> + + {t('images.importTip')} + + + + setInputPath(e.target.value)} + onKeyPress={e => { + if (e.key === 'Enter') { + handleAddDirectory(); + } + }} + disabled={loading} + sx={{ + '& .MuiOutlinedInput-root': { + borderRadius: 2 + } + }} + /> + + + + {directories.length > 0 && ( + + + + + {t('images.selectedDirectories')} ({directories.length}) + + + + {directories.map((dir, index) => ( + handleRemoveDirectory(index)} + disabled={loading} + icon={} + sx={{ + borderRadius: 1.5, + fontWeight: 500, + maxWidth: '100%', + '& .MuiChip-label': { + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap' + } + }} + /> + ))} + + + )} + + ) : mode === 1 ? ( + <> + + {t('images.pdfImportTip', { defaultValue: '选择 PDF 文件,系统会自动将其转换为图片并导入' })} + + + document.getElementById('pdf-file-input').click()} + > + + + + {selectedPdf ? selectedPdf.name : t('images.clickToSelectPdf', { defaultValue: '点击选择 PDF 文件' })} + + + {t('images.supportedFormat', { defaultValue: '支持格式:PDF' })} + + {selectedPdf && ( + + {t('images.fileSize', { defaultValue: '文件大小' })}: {(selectedPdf.size / 1024 / 1024).toFixed(2)} MB + + )} + + + ) : ( + <> + + {t('images.zipImportTip', { defaultValue: '选择 ZIP 压缩包文件,系统会自动解压并导入其中的图片' })} + + + document.getElementById('zip-file-input').click()} + > + + + + {selectedZip ? selectedZip.name : t('images.clickToSelectZip', { defaultValue: '点击选择 ZIP 文件' })} + + + {t('images.supportedZipFormat', { defaultValue: '支持格式:ZIP' })} + + {selectedZip && ( + + {t('images.fileSize', { defaultValue: '文件大小' })}: {(selectedZip.size / 1024 / 1024).toFixed(2)} MB + + )} + + + )} + + + + {mode === 0 ? ( + + ) : mode === 1 ? ( + + ) : ( + + )} + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/images/components/QuestionDialog.js b/easy-dataset-main/app/projects/[projectId]/images/components/QuestionDialog.js new file mode 100644 index 0000000..9ff3229 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/images/components/QuestionDialog.js @@ -0,0 +1,135 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + TextField, + CircularProgress, + Alert, + Box, + Typography +} from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import { useAtomValue } from 'jotai'; +import { selectedModelInfoAtom } from '@/lib/store'; +import { toast } from 'sonner'; +import axios from 'axios'; + +export default function QuestionDialog({ open, projectId, image, onClose, onSuccess }) { + const { t, i18n } = useTranslation(); + const selectedModel = useAtomValue(selectedModelInfoAtom); + const [count, setCount] = useState(3); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(''); + + useEffect(() => { + if (open) { + setCount(3); + setError(''); + } + }, [open]); + + const handleGenerate = async () => { + if (!selectedModel) { + setError(t('images.selectModelFirst')); + return; + } + + if (selectedModel.type !== 'vision') { + setError(t('images.visionModelRequired')); + return; + } + + if (count < 1 || count > 10) { + setError(t('images.countRange')); + return; + } + + try { + setLoading(true); + setError(''); + + const response = await axios.post(`/api/projects/${projectId}/images/questions`, { + imageName: image.imageName, + count, + model: selectedModel, + language: i18n.language + }); + + toast.success(t('images.questionsGenerated', { count: response.data.questions.length })); + onSuccess?.(); + onClose(); + } catch (err) { + console.error('Failed to generate questions:', err); + setError(err.response?.data?.error || t('images.generateFailed')); + } finally { + setLoading(false); + } + }; + + const handleClose = () => { + if (!loading) { + onClose(); + } + }; + + return ( + + {t('images.generateQuestions')} + + {error && ( + + {error} + + )} + + {image && ( + + + {t('images.imageName')} + + + {image.imageName} + + + )} + + setCount(parseInt(e.target.value) || 1)} + inputProps={{ min: 1, max: 10 }} + helperText={t('images.questionCountHelp')} + disabled={loading} + /> + + {selectedModel && ( + + + {t('images.currentModel')}: {selectedModel.modelName} + + + )} + + + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/images/components/annotation/AIGenerateButton.js b/easy-dataset-main/app/projects/[projectId]/images/components/annotation/AIGenerateButton.js new file mode 100644 index 0000000..b33a321 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/images/components/annotation/AIGenerateButton.js @@ -0,0 +1,97 @@ +'use client'; + +import { Button, CircularProgress } from '@mui/material'; +import AutoAwesomeIcon from '@mui/icons-material/AutoAwesome'; +import { useTranslation } from 'react-i18next'; +import { useState } from 'react'; +import axios from 'axios'; +import { toast } from 'sonner'; +import { useAtomValue } from 'jotai/index'; +import { selectedModelInfoAtom } from '@/lib/store'; + +/** + * AI 生成答案按钮组件 + * @param {string} projectId - 项目ID + * @param {string} imageName - 图片名称 + * @param {string} question - 问题内容 + * @param {function} onSuccess - 生成成功的回调,接收生成的答案 + * @param {boolean} previewOnly - 是否只预览(不保存数据集),默认 true + * @param {object} sx - 自定义样式 + */ +export default function AIGenerateButton({ + projectId, + imageName, + question, + onSuccess, + previewOnly = true, + sx = {}, + answerType +}) { + const { t, i18n } = useTranslation(); + const [loading, setLoading] = useState(false); + const model = useAtomValue(selectedModelInfoAtom); + + const handleGenerate = async () => { + if (!projectId || !imageName || !question) { + toast.error(t('images.missingParameters', { defaultValue: '缺少必要参数' })); + return; + } + + if (model.type !== 'vision') { + toast.error(t('images.visionModelRequired', { defaultValue: '请选择支持视觉的模型' })); + return; + } + + setLoading(true); + try { + const response = await axios.post(`/api/projects/${projectId}/images/datasets`, { + imageName, + question, + model, + language: i18n.language, + previewOnly + }); + + if (response.data.success && response.data.answer) { + let data = response.data.answer; + if (answerType === 'label') { + try { + data = JSON.parse(response.data.answer); + } catch {} + } + onSuccess(data); + toast.success(t('images.aiGenerateSuccess', { defaultValue: 'AI 生成成功' })); + } + } catch (error) { + console.error('AI 生成失败:', error); + const errorMsg = error.response?.data?.error || t('images.aiGenerateFailed', { defaultValue: 'AI 生成失败' }); + toast.error(errorMsg); + } finally { + setLoading(false); + } + }; + + return ( + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/images/components/annotation/AnnotationDialog.js b/easy-dataset-main/app/projects/[projectId]/images/components/annotation/AnnotationDialog.js new file mode 100644 index 0000000..6c6a6c7 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/images/components/annotation/AnnotationDialog.js @@ -0,0 +1,268 @@ +'use client'; + +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + Box, + Typography, + Chip, + CircularProgress +} from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import Image from 'next/image'; +import QuestionSelector from './QuestionSelector'; +import AnswerInput from './AnswerInput'; + +export default function AnnotationDialog({ + open, + onClose, + image, + templates, + selectedTemplate, + onTemplateChange, + answer, + onAnswerChange, + onSave, + onSaveAndContinue, + saving, + loading, + onOpenCreateQuestion, + onOpenCreateTemplate +}) { + const { t } = useTranslation(); + + if (!image) return null; + + return ( + + + + + {t('images.annotateImage', { defaultValue: '标注图片' })} + + + {image && ( + + )} + + + + + + {/* 图片预览区域 */} + + {/* 图片预览 */} + + {image && ( + <> + + {image.base64 ? ( + {image.imageName} + ) : ( + + + {t('images.imageLoadError', { defaultValue: '图片加载失败' })} + + + )} + + + {/* 图片信息卡片 */} + + + {image.imageName} + + + {image.width && image.height && ( + + )} + {image.size && ( + + )} + {image.format && } + + + {t('images.annotatedCount', { defaultValue: '已标注' })}: {image.datasetCount || 0}{' '} + {t('images.questions', { defaultValue: '个问题' })} + + + + )} + + + {/* 标注区域 */} + + {/* 问题选择器 */} + + {loading ? ( + + + + ) : ( + + )} + + + {/* 答案输入区域 */} + {selectedTemplate && ( + + + + )} + + + + + + {/* 左侧:创建按钮 */} + + + + + + {/* 右侧:操作按钮 */} + + + + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/images/components/annotation/AnswerInput.js b/easy-dataset-main/app/projects/[projectId]/images/components/annotation/AnswerInput.js new file mode 100644 index 0000000..d96f1cf --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/images/components/annotation/AnswerInput.js @@ -0,0 +1,437 @@ +'use client'; + +import { Box, Typography, TextField, Chip, Button, Paper } from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import { useState } from 'react'; +import AddIcon from '@mui/icons-material/Add'; +import AIGenerateButton from './AIGenerateButton'; + +export default function AnswerInput({ + answerType, + answer, + onAnswerChange, + labels, + customFormat, + projectId, + imageName, + question +}) { + const { t, i18n } = useTranslation(); + const [newLabel, setNewLabel] = useState(''); + const [jsonError, setJsonError] = useState(''); + + // 文字类型输入 + if (answerType === 'text') { + return ( + + + + {t('images.answer', { defaultValue: '文本答案' })} * + + + + onAnswerChange(e.target.value)} + placeholder={t('images.answerPlaceholder', { defaultValue: '请输入答案...' })} + sx={{ + '& .MuiOutlinedInput-root': { + borderRadius: 3, + backgroundColor: 'background.paper', + '& fieldset': { + borderWidth: 2, + borderColor: 'divider' + }, + '&:hover fieldset': { + borderColor: 'primary.main' + }, + '&.Mui-focused fieldset': { + borderColor: 'primary.main', + borderWidth: 2 + } + }, + '& textarea': { + fontSize: '14px', + lineHeight: 1.6 + } + }} + /> + + ); + } + + // 标签类型输入 - 提前解析 labels,避免条件中的 hooks 问题 + if (answerType === 'label') { + const selectedLabels = Array.isArray(answer) ? answer : []; + + // 解析 labels(可能是 JSON 字符串或数组) + let labelOptions = []; + if (typeof labels === 'string' && labels) { + try { + labelOptions = JSON.parse(labels); + } catch (e) { + labelOptions = []; + } + } else if (Array.isArray(labels)) { + labelOptions = labels; + } + + if (!labelOptions.includes('其他') && !labelOptions.includes('other')) { + labelOptions.push(i18n.language === 'en' ? 'other' : '其他'); + } + + const handleToggleLabel = label => { + if (selectedLabels.includes(label)) { + onAnswerChange(selectedLabels.filter(l => l !== label)); + } else { + let newLabels = [...selectedLabels, label]; + onAnswerChange(newLabels); + } + }; + + const handleAddNewLabel = () => { + if (newLabel.trim() && !labelOptions.includes(newLabel.trim())) { + handleToggleLabel(newLabel.trim()); + setNewLabel(''); + } + }; + + return ( + + + + {t('images.selectLabels', { defaultValue: '标签选择' })} * + + + + + {/* 可选标签 */} + + + {t('images.availableLabels', { defaultValue: '可选标签' })} + + + {labelOptions && labelOptions.length > 0 ? ( + labelOptions.map(label => ( + handleToggleLabel(label)} + color={selectedLabels.includes(label) ? 'primary' : 'default'} + variant={selectedLabels.includes(label) ? 'filled' : 'outlined'} + sx={{ + borderRadius: 2, + fontWeight: 500, + fontSize: '0.875rem', + height: 36, + cursor: 'pointer', + transition: 'all 0.2s ease', + '&:hover': { + transform: 'translateY(-1px)', + boxShadow: 2 + } + }} + /> + )) + ) : ( + + {t('images.noLabelsAvailable', { defaultValue: '暂无可选标签' })} + + )} + + + + {/* 添加新标签 */} + {/* + setNewLabel(e.target.value)} + placeholder={t('images.addNewLabel', { defaultValue: '添加新标签...' })} + onKeyPress={e => { + if (e.key === 'Enter') { + handleAddNewLabel(); + } + }} + sx={{ + flex: 1, + '& .MuiOutlinedInput-root': { + borderRadius: 2, + backgroundColor: 'background.paper', + '& fieldset': { + borderWidth: 2 + }, + '&:hover fieldset': { + borderColor: 'primary.main' + } + } + }} + /> + + */} + + {/* 已选择标签 */} + {/* {selectedLabels.length > 0 && ( + + + {t('images.selectedLabels', { defaultValue: '已选择' })} ({selectedLabels.length}) + + + + {selectedLabels.map(label => ( + handleToggleLabel(label)} + color="primary" + sx={{ + borderRadius: 2, + fontWeight: 500, + fontSize: '0.875rem', + height: 36, + '& .MuiChip-deleteIcon': { + fontSize: '18px', + '&:hover': { + color: 'error.main' + } + } + }} + /> + ))} + + + + )} */} + + ); + } + + // 自定义格式输入 + if (answerType === 'custom_format') { + const handleJsonChange = value => { + onAnswerChange(value); + // 验证 JSON 格式 + if (value.trim()) { + try { + JSON.parse(value); + setJsonError(''); + } catch (e) { + setJsonError(t('images.invalidJsonFormat', { defaultValue: 'JSON 格式不正确' })); + } + } else { + setJsonError(''); + } + }; + + const handleUseTemplate = () => { + if (customFormat) { + try { + let templateJson; + if (typeof customFormat === 'string') { + templateJson = JSON.parse(customFormat); + } else { + templateJson = customFormat; + } + const formatted = JSON.stringify(templateJson, null, 2); + onAnswerChange(formatted); + setJsonError(''); + } catch (e) { + onAnswerChange('{}'); + } + } + }; + + if (answer && typeof answer === 'object') { + answer = JSON.stringify(answer, null, 2); + } + + return ( + + + + {t('images.customFormatAnswer', { defaultValue: '自定义格式答案' })} * + + + + {customFormat && ( + + )} + {/* */} + + + + {/* 显示格式要求 */} + {customFormat && ( + + + {t('images.formatRequirement', { defaultValue: '格式要求' })} + + +
+                {typeof customFormat === 'string' ? customFormat : JSON.stringify(customFormat, null, 2)}
+              
+
+
+ )} + + {/* JSON 输入框 */} + handleJsonChange(e.target.value)} + placeholder={t('images.customFormatPlaceholder', { defaultValue: '请输入符合格式的 JSON...' })} + error={!!jsonError} + sx={{ + '& .MuiOutlinedInput-root': { + borderRadius: 3, + backgroundColor: 'background.paper', + '& fieldset': { + borderWidth: 2 + }, + '&:hover fieldset': { + borderColor: 'primary.main' + }, + '&.Mui-focused fieldset': { + borderColor: 'primary.main', + borderWidth: 2 + }, + '&.Mui-error fieldset': { + borderColor: 'error.main', + borderWidth: 2 + } + }, + '& textarea': { + fontFamily: 'Monaco, Consolas, "Courier New", monospace', + fontSize: '13px', + lineHeight: 1.5 + }, + '& .MuiFormHelperText-root': { + fontSize: '0.875rem', + fontWeight: 500 + } + }} + /> +
+ ); + } + + return null; +} diff --git a/easy-dataset-main/app/projects/[projectId]/images/components/annotation/QuestionSelector.js b/easy-dataset-main/app/projects/[projectId]/images/components/annotation/QuestionSelector.js new file mode 100644 index 0000000..7246d94 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/images/components/annotation/QuestionSelector.js @@ -0,0 +1,200 @@ +'use client'; + +import { Autocomplete, TextField, Box, Typography, Chip, Button, Dialog } from '@mui/material'; +import AddIcon from '@mui/icons-material/Add'; +import { useTranslation } from 'react-i18next'; +import { useState } from 'react'; + +export default function QuestionSelector({ + templates, + selectedTemplate, + onTemplateChange, + answeredQuestions = [], + unansweredQuestions = [], + onOpenCreateQuestion, + onOpenCreateTemplate +}) { + const { t } = useTranslation(); + const [showNoQuestionsMessage, setShowNoQuestionsMessage] = useState(false); + + // 构建未完成标注的问题选项(用于下拉框) + const dropdownOptions = unansweredQuestions.map(q => ({ + ...q, + isUnanswered: true + })); + + const getAnswerTypeLabel = answerType => { + switch (answerType) { + case 'text': + return t('images.answerTypeText', { defaultValue: '文字' }); + case 'label': + return t('images.answerTypeLabel', { defaultValue: '标签' }); + case 'custom_format': + return t('images.answerTypeCustomFormat', { defaultValue: '自定义格式' }); + default: + return answerType; + } + }; + + // 判断是否有待标注问题 + const hasUnansweredQuestions = unansweredQuestions.length > 0; + const hasAnsweredQuestions = answeredQuestions.length > 0; + const hasAnyQuestions = hasUnansweredQuestions || hasAnsweredQuestions; + + return ( + + {/* 已标注问题区域 - 优化显示为一行,添加最大高度 */} + {answeredQuestions.length > 0 && ( + + + {t('images.answeredQuestions', { defaultValue: '已标注问题' })} ({answeredQuestions.length}) + + + {answeredQuestions.map(question => ( + + ))} + + + )} + + {/* 问题选择下拉框 */} + + + {t('images.selectNewQuestion', { defaultValue: '选择新问题' })} + + + {!hasUnansweredQuestions ? ( + // 没有待标注问题的提示 + + {hasAnsweredQuestions ? ( + + {t('images.allQuestionsAnnotated', { defaultValue: '当前图片所有问题已标注完成' })} + + ) : ( + + {t('images.noQuestionsAssociated', { defaultValue: '当前图片未关联任何问题' })} + + )} + + ) : ( + // 有待标注问题时显示下拉框 + { + if (newValue) { + onTemplateChange(newValue); + } + }} + getOptionLabel={option => option.question || ''} + renderOption={(props, option) => ( + + + + {option.question} + + + + + + + + )} + renderInput={params => ( + + )} + isOptionEqualToValue={(option, value) => option.id === value.id} + /> + )} + + {selectedTemplate && selectedTemplate.description && ( + + {selectedTemplate.description} + + )} + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/images/hooks/useAnnotation.js b/easy-dataset-main/app/projects/[projectId]/images/hooks/useAnnotation.js new file mode 100644 index 0000000..3637ef6 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/images/hooks/useAnnotation.js @@ -0,0 +1,287 @@ +import { useState } from 'react'; +import axios from 'axios'; +import { toast } from 'sonner'; +import { useTranslation } from 'react-i18next'; + +// 深度遍历 JSON,将所有值设为空字符串 +function clearJsonValues(obj) { + if (Array.isArray(obj)) { + return obj.map(item => clearJsonValues(item)); + } else if (obj !== null && typeof obj === 'object') { + const cleared = {}; + for (const key in obj) { + cleared[key] = clearJsonValues(obj[key]); + } + return cleared; + } else { + return ''; // 所有基础类型值都变为空字符串 + } +} + +export function useAnnotation(projectId, onSuccess, onFindNextImage) { + const { t } = useTranslation(); + const [open, setOpen] = useState(false); + const [saving, setSaving] = useState(false); + const [loading, setLoading] = useState(false); + const [currentImage, setCurrentImage] = useState(null); + const [selectedTemplate, setSelectedTemplate] = useState(null); + const [answer, setAnswer] = useState(''); + + // 打开标注对话框 + const openAnnotation = async (image, template = null) => { + setLoading(true); + try { + // 获取图片详情,包括已标注和未标注的问题 + const response = await axios.get(`/api/projects/${projectId}/images/${image.id}`); + if (response.data.success) { + const imageDetail = response.data.data; + setCurrentImage(imageDetail); + + // 如果没有指定模板,尝试选择第一个未标注的问题 + if (!template) { + if (imageDetail.unansweredQuestions?.length > 0) { + template = imageDetail.unansweredQuestions[0]; + } + } + + setSelectedTemplate(template); + + // 根据问题类型初始化答案 + let initialAnswer = ''; + if (template?.answerType === 'label') { + initialAnswer = []; + } else if (template?.answerType === 'custom_format' && template?.customFormat) { + // 为自定义格式提供默认值(所有字段值清空) + try { + let templateJson; + if (typeof template.customFormat === 'string') { + // 如果customFormat是字符串,尝试解析为JSON + templateJson = JSON.parse(template.customFormat); + } else { + // 如果customFormat已经是对象,直接使用 + templateJson = template.customFormat; + } + // 深度遍历,将所有字段值清空 + const clearedJson = clearJsonValues(templateJson); + initialAnswer = JSON.stringify(clearedJson, null, 2); + } catch (error) { + // 如枟解析失败,提供一个空的JSON对象 + initialAnswer = '{}'; + } + } + + setAnswer(initialAnswer); + setOpen(true); + } else { + toast.error(t('images.loadImageDetailFailed', { defaultValue: '加载图片详情失败' })); + } + } catch (error) { + console.error('获取图片详情失败:', error); + toast.error(t('images.loadImageDetailFailed', { defaultValue: '加载图片详情失败' })); + } finally { + setLoading(false); + } + }; + + // 关闭对话框 + const closeAnnotation = () => { + setOpen(false); + setCurrentImage(null); + setSelectedTemplate(null); + setAnswer(''); + }; + + // 刷新当前图片的问题列表(创建问题后调用) + const refreshCurrentImage = async () => { + if (!currentImage) return; + + try { + const response = await axios.get(`/api/projects/${projectId}/images/${currentImage.id}`); + if (response.data.success) { + const imageDetail = response.data.data; + // 更新当前图片数据 + setCurrentImage(imageDetail); + return imageDetail; + } + } catch (error) { + console.error('刷新图片详情失败:', error); + } + }; + + // 查找下一个未标注的问题 + const findNextUnansweredQuestion = async () => { + // 重新获取图片详情,获取最新的问题列表 + try { + const response = await axios.get(`/api/projects/${projectId}/images/${currentImage.id}`); + if (response.data.success) { + const imageDetail = response.data.data; + + // 更新当前图片数据 + setCurrentImage(imageDetail); + + // 返回第一个未标注的问题 + if (imageDetail.unansweredQuestions?.length > 0) { + return imageDetail.unansweredQuestions[0]; + } + + return null; + } + } catch (error) { + console.error('获取下一个问题失败:', error); + return null; + } + }; + + // 保存标注 + const saveAnnotation = async (continueNext = false) => { + if (!currentImage) { + toast.error(t('images.noImageSelected', { defaultValue: '未选择图片' })); + return; + } + + if (!selectedTemplate) { + toast.error(t('images.noTemplateSelected', { defaultValue: '请选择问题' })); + return; + } + + // 验证答案 + if (!answer || (Array.isArray(answer) && answer.length === 0)) { + toast.error(t('images.answerRequired', { defaultValue: '请输入答案' })); + return; + } + + // 如果是自定义格式,验证 JSON 格式 + if (selectedTemplate.answerType === 'custom_format') { + try { + JSON.parse(answer); + } catch (e) { + toast.error(t('images.invalidJsonFormat', { defaultValue: 'JSON 格式不正确' })); + return; + } + } + + console.log(999, answer); + setSaving(true); + try { + const response = await axios.post(`/api/projects/${projectId}/images/annotations`, { + imageId: currentImage.id, + imageName: currentImage.imageName, + questionId: selectedTemplate.id, + question: selectedTemplate.question, + templateId: selectedTemplate.id, + answerType: selectedTemplate.answerType, + answer + }); + + if (response.data.success) { + toast.success(t('images.annotationSuccess', { defaultValue: '标注保存成功' })); + + // 触发刷新回调 + if (onSuccess) { + onSuccess(); + } + + if (continueNext) { + // 查找下一个未标注的问题 + const nextQuestion = await findNextUnansweredQuestion(); + + if (nextQuestion) { + // 切换到下一个问题 + setSelectedTemplate(nextQuestion); + + // 根据问题类型初始化答案 + let initialAnswer = ''; + if (nextQuestion.answerType === 'label') { + initialAnswer = []; + } else if (nextQuestion.answerType === 'custom_format' && nextQuestion.customFormat) { + try { + let templateJson; + if (typeof nextQuestion.customFormat === 'string') { + templateJson = JSON.parse(nextQuestion.customFormat); + } else { + templateJson = nextQuestion.customFormat; + } + const clearedJson = clearJsonValues(templateJson); + initialAnswer = JSON.stringify(clearedJson, null, 2); + } catch (error) { + initialAnswer = '{}'; + } + } + setAnswer(initialAnswer); + } else { + // 没有更多未标注的问题了,尝试查找下一个有未标注问题的图片 + if (onFindNextImage) { + const nextImage = await onFindNextImage(); + if (nextImage) { + // 打开下一个图片的标注 + await openAnnotation(nextImage); + } else { + // 没有更多图片了 + toast.info(t('images.allImagesAnnotated', { defaultValue: '所有图片的问题都已标注完成' })); + closeAnnotation(); + } + } else { + toast.info(t('images.allQuestionsAnnotated', { defaultValue: '当前图片所有问题已标注完成' })); + closeAnnotation(); + } + } + } else { + closeAnnotation(); + } + } + } catch (error) { + console.error('保存标注失败:', error); + const errorMsg = error.response?.data?.error || t('images.annotationFailed', { defaultValue: '保存标注失败' }); + toast.error(errorMsg); + } finally { + setSaving(false); + } + }; + + // 处理模板变更 + const handleTemplateChange = template => { + setSelectedTemplate(template); + + // 根据新模板类型初始化答案 + let initialAnswer = ''; + if (template?.answerType === 'label') { + initialAnswer = []; + } else if (template?.answerType === 'custom_format' && template?.customFormat) { + // 为自定义格式提供默认值(所有字段值清空) + try { + let templateJson; + if (typeof template.customFormat === 'string') { + // 如果customFormat是字符串,尝试解析为JSON + templateJson = JSON.parse(template.customFormat); + } else { + // 如果customFormat已经是对象,直接使用 + templateJson = template.customFormat; + } + // 深度遍历,将所有字段值清空 + const clearedJson = clearJsonValues(templateJson); + initialAnswer = JSON.stringify(clearedJson, null, 2); + } catch (error) { + // 如枟解析失败,提供一个空的JSON对象 + initialAnswer = '{}'; + } + } + + setAnswer(initialAnswer); + }; + + return { + open, + saving, + loading, + currentImage, + selectedTemplate, + answer, + setSelectedTemplate, + setAnswer, + handleTemplateChange, + openAnnotation, + closeAnnotation, + saveAnnotation, + refreshCurrentImage + }; +} diff --git a/easy-dataset-main/app/projects/[projectId]/images/page.js b/easy-dataset-main/app/projects/[projectId]/images/page.js new file mode 100644 index 0000000..0d663b5 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/images/page.js @@ -0,0 +1,486 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { useParams, useRouter } from 'next/navigation'; +import { useTranslation } from 'react-i18next'; +import { + Container, + Box, + Typography, + Button, + CircularProgress, + Dialog, + DialogTitle, + DialogContent, + DialogActions, + TextField +} from '@mui/material'; +import AddPhotoAlternateIcon from '@mui/icons-material/AddPhotoAlternate'; +import AutoAwesomeIcon from '@mui/icons-material/AutoAwesome'; +import DeleteIcon from '@mui/icons-material/Delete'; +import { imageStyles } from './styles/imageStyles'; +import { toast } from 'sonner'; +import axios from 'axios'; +import { useAtomValue } from 'jotai'; +import { selectedModelInfoAtom } from '@/lib/store'; + +import ImageFilters from './components/ImageFilters'; +import ImageGrid from './components/ImageGrid'; +import ImageList from './components/ImageList'; +import ImportDialog from './components/ImportDialog'; +import QuestionDialog from './components/QuestionDialog'; +import DatasetDialog from './components/DatasetDialog'; +import AnnotationDialog from './components/annotation/AnnotationDialog'; +import { useQuestionTemplates } from '../questions/hooks/useQuestionTemplates'; +import { useAnnotation } from './hooks/useAnnotation'; +import { useQuestionEdit } from '../questions/hooks/useQuestionEdit'; +import QuestionEditDialog from '../questions/components/QuestionEditDialog'; +import TemplateFormDialog from '../questions/components/template/TemplateFormDialog'; + +export default function ImagesPage() { + const { projectId } = useParams(); + const router = useRouter(); + const { t, i18n } = useTranslation(); + const selectedModel = useAtomValue(selectedModelInfoAtom); + + const [loading, setLoading] = useState(false); + const [images, setImages] = useState([]); + const [total, setTotal] = useState(0); + const [page, setPage] = useState(1); + const [pageSize] = useState(8); + + // 筛选条件 + const [imageName, setImageName] = useState(''); + const [hasQuestions, setHasQuestions] = useState('all'); + const [hasDatasets, setHasDatasets] = useState('all'); + + // 视图模式 + const [viewMode, setViewMode] = useState('grid'); + + // 选中状态(仅列表视图使用) + const [selectedIds, setSelectedIds] = useState([]); + + // 对话框状态 + const [importDialogOpen, setImportDialogOpen] = useState(false); + const [questionDialogOpen, setQuestionDialogOpen] = useState(false); + const [datasetDialogOpen, setDatasetDialogOpen] = useState(false); + const [selectedImage, setSelectedImage] = useState(null); + const [autoGenerateDialogOpen, setAutoGenerateDialogOpen] = useState(false); + const [questionCount, setQuestionCount] = useState(3); + + // 问题模板和标注功能 (只获取图像类型的模板) + const { templates, createTemplate } = useQuestionTemplates(projectId, 'image'); + + // 问题编辑 Hook + const { editDialogOpen, editMode, editingQuestion, handleOpenCreateDialog, handleCloseDialog, handleSubmitQuestion } = + useQuestionEdit(projectId, async () => { + fetchImages(); + if (annotationOpen && currentImage) { + await refreshCurrentImage(); + } + toast.success(t('questions.operationSuccess')); + }); + + // 模板管理状态 + const [templateDialogOpen, setTemplateDialogOpen] = useState(false); + + // 获取图片列表 + const fetchImages = async () => { + try { + setLoading(true); + const params = new URLSearchParams({ + page: page.toString(), + pageSize: pageSize.toString() + }); + + if (imageName) params.append('imageName', imageName); + if (hasQuestions !== 'all') params.append('hasQuestions', hasQuestions); + if (hasDatasets !== 'all') params.append('hasDatasets', hasDatasets); + + const response = await axios.get(`/api/projects/${projectId}/images?${params.toString()}`); + setImages(response.data.data); + setTotal(response.data.total); + } catch (error) { + console.error('Failed to fetch images:', error); + toast.error(t('common.fetchError')); + } finally { + setLoading(false); + } + }; + + // 查找下一个有未标注问题的图片 + const handleFindNextImage = async () => { + try { + const response = await axios.get(`/api/projects/${projectId}/images/next-unanswered`); + return response.data.data || null; + } catch (error) { + console.error('查找下一个图片失败:', error); + return null; + } + }; + + const { + open: annotationOpen, + saving: annotationSaving, + loading: annotationLoading, + currentImage, + selectedTemplate, + answer, + setAnswer, + handleTemplateChange, + openAnnotation, + closeAnnotation, + saveAnnotation, + refreshCurrentImage + } = useAnnotation(projectId, fetchImages, handleFindNextImage); + + useEffect(() => { + fetchImages(); + }, [projectId, page, imageName, hasQuestions, hasDatasets]); + + useEffect(() => { + setSelectedIds([]); + }, [viewMode]); + + // 处理导入成功 + const handleImportSuccess = () => { + setImportDialogOpen(false); + setPage(1); + fetchImages(); + }; + + // 处理生成问题 + const handleGenerateQuestions = image => { + setSelectedImage(image); + setQuestionDialogOpen(true); + }; + + // 处理生成数据集 + const handleGenerateDataset = image => { + setSelectedImage(image); + setDatasetDialogOpen(true); + }; + + // 删除图片 + const handleDeleteImage = async imageId => { + if (!confirm(t('images.deleteConfirm', { defaultValue: '确定要删除这张图片吗?' }))) { + return; + } + + try { + await axios.delete(`/api/projects/${projectId}/images?imageId=${imageId}`); + toast.success(t('images.deleteSuccess', { defaultValue: '删除成功' })); + fetchImages(); + } catch (error) { + console.error('Failed to delete image:', error); + toast.error(t('images.deleteFailed', { defaultValue: '删除失败' })); + } + }; + + // 批量删除图片 + const handleBatchDelete = async () => { + if (selectedIds.length === 0) { + toast.error(t('images.selectImagesToDelete', { defaultValue: '请选择要删除的图片' })); + return; + } + + if ( + !confirm( + t('images.batchDeleteConfirm', { + defaultValue: `确定要删除选中的 ${selectedIds.length} 张图片吗?`, + count: selectedIds.length + }) + ) + ) { + return; + } + + try { + setLoading(true); + let successCount = 0; + let failCount = 0; + + // 逐个调用删除接口 + for (const imageId of selectedIds) { + try { + await axios.delete(`/api/projects/${projectId}/images?imageId=${imageId}`); + successCount++; + } catch (error) { + console.error(`Failed to delete image ${imageId}:`, error); + failCount++; + } + } + + // 显示结果 + if (failCount === 0) { + toast.success( + t('images.batchDeleteSuccess', { + defaultValue: `成功删除 ${successCount} 张图片`, + count: successCount + }) + ); + } else { + toast.warning( + t('images.batchDeletePartialSuccess', { + defaultValue: `成功删除 ${successCount} 张,失败 ${failCount} 张`, + success: successCount, + fail: failCount + }) + ); + } + + // 清空选中状态并刷新列表 + setSelectedIds([]); + fetchImages(); + } catch (error) { + console.error('Batch delete failed:', error); + toast.error(t('images.batchDeleteFailed', { defaultValue: '批量删除失败' })); + } finally { + setLoading(false); + } + }; + + // 处理自动提取问题 + const handleAutoGenerateQuestions = () => { + if (!selectedModel) { + toast.error(t('images.selectModelFirst')); + return; + } + + if (selectedModel.type !== 'vision') { + toast.error(t('images.visionModelRequired')); + return; + } + + setAutoGenerateDialogOpen(true); + }; + + // 确认创建自动提取任务 + const handleConfirmAutoGenerate = async () => { + // 验证问题数量 + if (questionCount < 1 || questionCount > 10) { + toast.error(t('images.countRange')); + return; + } + + try { + setAutoGenerateDialogOpen(false); + + const response = await axios.post(`/api/projects/${projectId}/tasks`, { + taskType: 'image-question-generation', + modelInfo: selectedModel, + language: i18n.language, + note: { questionCount } + }); + + if (response.data.code === 0) { + toast.success(t('images.taskCreated')); + // 跳转到任务管理页面 + router.push(`/projects/${projectId}/tasks`); + } else { + toast.error(response.data.error || t('images.taskCreateFailed')); + } + } catch (error) { + console.error('Failed to create auto-generate task:', error); + toast.error(t('images.taskCreateFailed')); + } + }; + + // 模板管理函数 + const handleOpenCreateTemplateDialog = () => { + setTemplateDialogOpen(true); + }; + + const handleCloseTemplateDialog = () => { + setTemplateDialogOpen(false); + }; + + const handleSubmitTemplate = async data => { + try { + await createTemplate(data); + handleCloseTemplateDialog(); + fetchImages(); + if (annotationOpen && currentImage) { + await refreshCurrentImage(); + } + toast.success(t('questions.operationSuccess')); + } catch (error) { + console.error('Failed to save template:', error); + } + }; + + return ( + + {/* 页面头部 */} + + + + {t('images.title', { defaultValue: '图片管理' })} + + + + {viewMode === 'list' && selectedIds.length > 0 && ( + + )} + + + + + + {/* 筛选区域 */} + + + {/* 图片列表 */} + {loading ? ( + + + + ) : viewMode === 'grid' ? ( + + ) : ( + + )} + + setImportDialogOpen(false)} + onSuccess={handleImportSuccess} + /> + + setQuestionDialogOpen(false)} + onSuccess={fetchImages} + /> + + setDatasetDialogOpen(false)} + onSuccess={fetchImages} + /> + + setAutoGenerateDialogOpen(false)} maxWidth="sm" fullWidth> + {t('images.autoGenerateQuestions')} + + {t('images.autoGenerateConfirm')} + + setQuestionCount(parseInt(e.target.value) || 1)} + inputProps={{ min: 1, max: 10 }} + helperText={t('images.questionCountHelp')} + sx={{ mb: 2 }} + /> + + + {t('images.currentModel')}: {selectedModel?.modelName || t('common.none')} + + + + + + + + + saveAnnotation(false)} + onSaveAndContinue={() => saveAnnotation(true)} + saving={annotationSaving} + loading={annotationLoading} + onOpenCreateQuestion={handleOpenCreateDialog} + onOpenCreateTemplate={handleOpenCreateTemplateDialog} + /> + + {/* 问题编辑对话框 */} + + + {/* 问题模板对话框 */} + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/images/styles/imageStyles.js b/easy-dataset-main/app/projects/[projectId]/images/styles/imageStyles.js new file mode 100644 index 0000000..f46c80d --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/images/styles/imageStyles.js @@ -0,0 +1,286 @@ +/** + * 图片管理页面样式配置 + */ + +export const imageStyles = { + // 页面容器 + pageContainer: { + py: 4 + }, + + // 页面头部 + header: { + mb: 4, + display: 'flex', + justifyContent: 'space-between', + alignItems: 'flex-start', + flexWrap: 'wrap', + gap: 3 + }, + + headerTitle: { + display: 'flex', + flexDirection: 'column', + gap: 0.5 + }, + + title: { + fontWeight: 700 + }, + + subtitle: { + color: 'text.secondary', + fontSize: '0.875rem' + }, + + headerActions: { + display: 'flex', + gap: 2, + flexWrap: 'wrap' + }, + + actionButton: { + borderRadius: 2, + textTransform: 'none', + px: 3, + fontWeight: 600, + boxShadow: 2, + transition: 'all 0.3s ease', + '&:hover': { + transform: 'translateY(-2px)', + boxShadow: 4 + } + }, + + // 筛选区域 + filterCard: { + mb: 3, + borderRadius: 2, + boxShadow: 1, + border: '1px solid', + borderColor: 'divider', + overflow: 'visible' + }, + + filterContent: { + display: 'flex', + gap: 2, + alignItems: 'center', + flexWrap: 'wrap' + }, + + searchField: { + minWidth: { xs: '100%', sm: 300 }, + flex: { xs: '1 1 100%', sm: '1 1 auto' }, + '& .MuiOutlinedInput-root': { + borderRadius: 2 + } + }, + + filterSelect: { + minWidth: { xs: '48%', sm: 150 }, + '& .MuiOutlinedInput-root': { + borderRadius: 2 + } + }, + + viewToggle: { + ml: 'auto', + borderRadius: 2, + '& .MuiToggleButton-root': { + border: '1px solid', + borderColor: 'divider', + '&.Mui-selected': { + bgcolor: 'primary.main', + color: 'white', + '&:hover': { + bgcolor: 'primary.dark' + } + } + } + }, + + // 图片网格 + gridContainer: { + spacing: 3 + }, + + // 图片卡片 + imageCard: { + height: '100%', + display: 'flex', + flexDirection: 'column', + borderRadius: 3, + overflow: 'hidden', + transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)', + border: '1px solid', + borderColor: 'divider', + bgcolor: 'background.paper', + '&:hover': { + transform: 'translateY(-8px)', + boxShadow: theme => `0 12px 24px ${theme.palette.mode === 'dark' ? 'rgba(0,0,0,0.4)' : 'rgba(0,0,0,0.15)'}`, + borderColor: 'primary.main', + '& .image-overlay': { + opacity: 1 + } + } + }, + + imageWrapper: { + position: 'relative', + overflow: 'hidden', + bgcolor: 'grey.100' + }, + + imageMedia: { + height: 220, + objectFit: 'cover', + transition: 'transform 0.3s ease', + cursor: 'pointer', + '&:hover': { + transform: 'scale(1.05)' + } + }, + + imageOverlay: { + className: 'image-overlay', + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + background: + 'linear-gradient(to bottom, rgba(0,0,0,0.6) 0%, transparent 40%, transparent 60%, rgba(0,0,0,0.6) 100%)', + opacity: 0, + transition: 'opacity 0.3s ease', + pointerEvents: 'none' + }, + + statusChipsContainer: { + position: 'absolute', + top: 12, + right: 12, + display: 'flex', + gap: 0.5, + flexDirection: 'column', + alignItems: 'flex-end', + zIndex: 2 + }, + + statusChip: { + backdropFilter: 'blur(10px)', + fontWeight: 600, + fontSize: '0.75rem', + height: 24, + boxShadow: 2 + }, + + imageNameContainer: { + position: 'absolute', + bottom: 12, + left: 12, + right: 12, + display: 'flex', + justifyContent: 'center', + zIndex: 2 + }, + + imageNameChip: { + backdropFilter: 'blur(10px)', + bgcolor: 'rgba(255, 255, 255, 0.95)', + fontWeight: 600, + maxWidth: '90%', + boxShadow: 2, + '& .MuiChip-label': { + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap' + } + }, + + cardContent: { + flexGrow: 1, + p: 2, + pb: 1.5 + }, + + imageName: { + fontWeight: 600, + fontSize: '0.9rem', + lineHeight: 1.4 + }, + + cardActions: { + p: 2, + pt: 0, + gap: 1, + mt: 2, + display: 'flex', + justifyContent: 'space-between' + }, + + actionIconButton: { + transition: 'all 0.2s ease', + '&:hover': { + transform: 'scale(1.1)' + } + }, + + primaryActionButton: { + borderRadius: 2, + textTransform: 'none', + fontWeight: 600, + flex: 1 + }, + + // 分页 + pagination: { + display: 'flex', + justifyContent: 'center', + mt: 4 + }, + + // 空状态 + emptyState: { + textAlign: 'center', + py: 12, + px: 3 + }, + + emptyIcon: { + width: 120, + height: 120, + borderRadius: '50%', + bgcolor: 'primary.lighter', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + mx: 'auto', + mb: 3 + }, + + emptyTitle: { + fontWeight: 600, + mb: 1 + }, + + emptyDescription: { + color: 'text.secondary', + mb: 4 + }, + + emptyButton: { + borderRadius: 2, + px: 4, + textTransform: 'none', + fontWeight: 600 + }, + + // 加载状态 + loadingContainer: { + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + py: 8 + } +}; diff --git a/easy-dataset-main/app/projects/[projectId]/layout.js b/easy-dataset-main/app/projects/[projectId]/layout.js new file mode 100644 index 0000000..b648530 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/layout.js @@ -0,0 +1,125 @@ +'use client'; + +import Navbar from '@/components/Navbar/index'; +import { useState, useEffect } from 'react'; +import { Box, CircularProgress, Typography, Button } from '@mui/material'; +import { useRouter } from 'next/navigation'; +import { useTranslation } from 'react-i18next'; +import { useSetAtom } from 'jotai'; +import { modelConfigListAtom, selectedModelInfoAtom } from '@/lib/store'; + +export default function ProjectLayout({ children, params }) { + const router = useRouter(); + const { projectId } = params; + const [projects, setProjects] = useState([]); + const [currentProject, setCurrentProject] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [t] = useTranslation(); + const setModelConfigList = useSetAtom(modelConfigListAtom); + const setSelectedModelInfo = useSetAtom(selectedModelInfoAtom); + + const fetchData = async () => { + try { + setLoading(true); + + const [projectsResponse, projectResponse, modelConfigResponse] = await Promise.all([ + fetch('/api/projects'), + fetch(`/api/projects/${projectId}`), + fetch(`/api/projects/${projectId}/model-config`) + ]); + + if (!projectsResponse.ok) { + throw new Error(t('projects.fetchFailed')); + } + const projectsData = await projectsResponse.json(); + setProjects(projectsData); + + if (!projectResponse.ok) { + if (projectResponse.status === 404) { + router.push('/'); + return; + } + throw new Error('Failed to load project details'); + } + const projectData = await projectResponse.json(); + setCurrentProject(projectData); + + if (modelConfigResponse.ok) { + const modelConfigData = await modelConfigResponse.json(); + const modelList = Array.isArray(modelConfigData?.data) ? modelConfigData.data : []; + setModelConfigList(modelList); + if (modelConfigData?.defaultModelConfigId) { + const defaultModel = modelList.find(item => item.id === modelConfigData.defaultModelConfigId); + setSelectedModelInfo(defaultModel || null); + } else { + setSelectedModelInfo(null); + } + } else { + setModelConfigList([]); + setSelectedModelInfo(null); + } + } catch (error) { + console.error('Failed to load project data:', error); + setError(error.message); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + if (!projectId || projectId === 'undefined') { + router.push('/'); + return; + } + + fetchData(); + }, [projectId, router]); + + if (loading) { + return ( + + + Loading project data... + + ); + } + + if (error) { + return ( + + + {t('projects.fetchFailed')}: {error} + + + + ); + } + + return ( + <> + + + {children} + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/multi-turn/[conversationId]/page.js b/easy-dataset-main/app/projects/[projectId]/multi-turn/[conversationId]/page.js new file mode 100644 index 0000000..6e4cdcb --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/multi-turn/[conversationId]/page.js @@ -0,0 +1,139 @@ +'use client'; + +import { + Container, + Box, + Typography, + Alert, + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + Paper +} from '@mui/material'; +import ConversationHeader from '@/components/conversations/ConversationHeader'; +import ConversationMetadata from '@/components/conversations/ConversationMetadata'; +import ConversationContent from '@/components/conversations/ConversationContent'; +import ConversationRatingSection from '@/components/conversations/ConversationRatingSection'; +import useConversationDetails from './useConversationDetails'; +import { useTranslation } from 'react-i18next'; + +/** + * 多轮对话详情页面 + */ +export default function ConversationDetailPage({ params }) { + const { projectId, conversationId } = params; + const { t } = useTranslation(); + + // 使用自定义Hook管理状态和逻辑 + const { + conversation, + messages, + loading, + editMode, + saving, + editData, + setEditData, + deleteDialogOpen, + setDeleteDialogOpen, + handleEdit, + handleSave, + handleCancel, + handleDelete, + handleNavigate, + updateMessageContent + } = useConversationDetails(projectId, conversationId); + + // 加载状态 + if (loading) { + return ( + + + {t('datasets.loadingDataset')} + + + ); + } + + // 无数据状态 + if (!conversation) { + return ( + + {t('datasets.conversationNotFound')} + + ); + } + + return ( + + {/* 顶部导航栏 */} + setDeleteDialogOpen(true)} + onNavigate={handleNavigate} + /> + + {/* 主要布局:左右分栏 */} + + {/* 左侧主要内容区域 */} + + + {/* 对话内容 */} + + + + + {/* 右侧固定侧边栏 */} + + {/* 元数据展示 */} + + + {/* 评分、标签、备注区域 */} + { + // 更新成功后刷新数据,保持页面状态同步 + // 这里可以调用 useConversationDetails 的刷新逻辑 + }} + /> + + + + {/* 删除确认对话框 */} + setDeleteDialogOpen(false)}> + {t('datasets.confirmDelete')} + + {t('datasets.confirmDeleteConversation')} + + + + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/multi-turn/[conversationId]/useConversationDetails.js b/easy-dataset-main/app/projects/[projectId]/multi-turn/[conversationId]/useConversationDetails.js new file mode 100644 index 0000000..9e63397 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/multi-turn/[conversationId]/useConversationDetails.js @@ -0,0 +1,211 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { useRouter } from 'next/navigation'; +import { toast } from 'sonner'; +import { useTranslation } from 'react-i18next'; + +/** + * 多轮对话详情页面的状态管理Hook + */ +export default function useConversationDetails(projectId, conversationId) { + const { t } = useTranslation(); + const router = useRouter(); + + // 基础状态 + const [conversation, setConversation] = useState(null); + const [messages, setMessages] = useState([]); + const [loading, setLoading] = useState(true); + + // 编辑状态 + const [editMode, setEditMode] = useState(false); + const [saving, setSaving] = useState(false); + const [editData, setEditData] = useState({ + score: 0, + tags: '', + note: '', + confirmed: false, + messages: [] + }); + + // 对话框状态 + const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); + + // 获取对话详情 + const fetchConversation = async () => { + try { + setLoading(true); + const response = await fetch(`/api/projects/${projectId}/dataset-conversations/${conversationId}`); + + if (!response.ok) { + if (response.status === 404) { + toast.error(t('datasets.conversationNotFound')); + router.push(`/projects/${projectId}/multi-turn`); + return; + } + throw new Error(t('datasets.fetchDataFailed')); + } + + const data = await response.json(); + setConversation(data); + + // 解析对话消息 + let parsedMessages = []; + try { + parsedMessages = JSON.parse(data.rawMessages || '[]'); + setMessages(parsedMessages); + } catch (error) { + console.error('解析对话消息失败:', error); + setMessages([]); + } + + // 设置编辑数据 + setEditData({ + score: data.score || 0, + tags: data.tags || '', + note: data.note || '', + confirmed: data.confirmed || false, + messages: parsedMessages + }); + } catch (error) { + console.error('获取对话详情失败:', error); + toast.error(error.message || t('datasets.fetchDataFailed')); + } finally { + setLoading(false); + } + }; + + // 保存编辑 + const handleSave = async () => { + try { + setSaving(true); + const response = await fetch(`/api/projects/${projectId}/dataset-conversations/${conversationId}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + score: editData.score, + tags: editData.tags, + note: editData.note, + confirmed: editData.confirmed, + messages: editData.messages + }) + }); + + if (!response.ok) { + throw new Error(t('datasets.saveFailed')); + } + + // 更新本地状态 + setConversation({ ...conversation, ...editData }); + setMessages(editData.messages); + setEditMode(false); + toast.success(t('datasets.saveSuccess')); + } catch (error) { + console.error('保存失败:', error); + toast.error(error.message || t('datasets.saveFailed')); + } finally { + setSaving(false); + } + }; + + // 开始编辑 + const handleEdit = () => { + setEditMode(true); + }; + + // 取消编辑 + const handleCancel = () => { + // 恢复到原始数据 + setEditData({ + score: conversation.score || 0, + tags: conversation.tags || '', + note: conversation.note || '', + confirmed: conversation.confirmed || false, + messages: messages + }); + setEditMode(false); + }; + + // 删除对话 + const handleDelete = async () => { + try { + const response = await fetch(`/api/projects/${projectId}/dataset-conversations/${conversationId}`, { + method: 'DELETE' + }); + + if (!response.ok) { + throw new Error(t('datasets.deleteFailed')); + } + + toast.success(t('datasets.deleteSuccess')); + router.push(`/projects/${projectId}/multi-turn`); + } catch (error) { + console.error('删除失败:', error); + toast.error(error.message || t('datasets.deleteFailed')); + } + }; + + // 更新消息内容 + const updateMessageContent = (index, newContent) => { + const updatedMessages = [...editData.messages]; + updatedMessages[index] = { ...updatedMessages[index], content: newContent }; + setEditData({ ...editData, messages: updatedMessages }); + }; + + // 翻页导航 + const handleNavigate = async direction => { + try { + const response = await fetch( + `/api/projects/${projectId}/dataset-conversations/${conversationId}?operateType=${direction}` + ); + + if (!response.ok) { + throw new Error('获取导航数据失败'); + } + + const data = await response.json(); + + if (data) { + router.push(`/projects/${projectId}/multi-turn/${data.id}`); + } else { + toast.warning(`已经是${direction === 'next' ? '最后' : '第'}一条对话了`); + } + } catch (error) { + console.error('导航失败:', error); + toast.error(error.message || '导航失败'); + } + }; + + // 初始化 + useEffect(() => { + fetchConversation(); + }, [projectId, conversationId]); + + return { + // 数据状态 + conversation, + messages, + loading, + + // 编辑状态 + editMode, + saving, + editData, + setEditData, + + // 对话框状态 + deleteDialogOpen, + setDeleteDialogOpen, + + // 操作方法 + handleEdit, + handleSave, + handleCancel, + handleDelete, + handleNavigate, + updateMessageContent, + fetchConversation + }; +} diff --git a/easy-dataset-main/app/projects/[projectId]/multi-turn/components/ConversationTable.js b/easy-dataset-main/app/projects/[projectId]/multi-turn/components/ConversationTable.js new file mode 100644 index 0000000..f4b462b --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/multi-turn/components/ConversationTable.js @@ -0,0 +1,313 @@ +'use client'; + +import { + Paper, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + TablePagination, + IconButton, + Tooltip, + Typography, + Chip, + CircularProgress, + Checkbox +} from '@mui/material'; +import { Delete as DeleteIcon, Visibility as ViewIcon } from '@mui/icons-material'; +import { useTranslation } from 'react-i18next'; +import { useState } from 'react'; +import RatingChip from './RatingChip'; + +const QUESTION_TOOLTIP_THRESHOLD = 80; +const SCENARIO_TOOLTIP_THRESHOLD = 120; + +const ConversationTable = ({ + conversations, + loading, + total, + page, + rowsPerPage, + onPageChange, + onRowsPerPageChange, + onView, + onDelete, + selectedIds = [], + onSelectionChange, + isAllSelected = false, + onSelectAll +}) => { + const { t } = useTranslation(); + const [expandedRows, setExpandedRows] = useState({}); + const columnWidths = { + checkbox: 52, + question: 280, + scenario: 340, + rounds: 90, + model: 120, + rating: 100, + createdAt: 110, + actions: 92 + }; + + const shouldShowTooltip = (value, threshold) => (value || '').length > threshold; + + const handleSelectOne = conversationId => { + if (selectedIds.includes(conversationId)) { + onSelectionChange(selectedIds.filter(id => id !== conversationId)); + } else { + onSelectionChange([...selectedIds, conversationId]); + } + }; + + const handleSelectAll = () => { + if (isAllSelected) { + onSelectionChange([]); + onSelectAll(false); + } else { + const currentPageIds = conversations.map(conv => conv.id); + onSelectionChange(currentPageIds); + onSelectAll(true); + } + }; + + const isIndeterminate = selectedIds.length > 0 && !isAllSelected; + const toggleRowExpanded = conversationId => { + setExpandedRows(prev => ({ ...prev, [conversationId]: !prev[conversationId] })); + }; + + return ( + + + + + + + + + {t('datasets.firstQuestion')} + + + {t('datasets.conversationScenario')} + + {t('datasets.conversationRounds')} + {t('datasets.modelUsed')} + {t('datasets.rating')} + {t('datasets.createTime')} + + {t('common.actions')} + + + + + {loading ? ( + + + + + + ) : conversations.length === 0 ? ( + + + + {t('datasets.noConversations')} + + + + ) : ( + conversations.map(conversation => { + const questionText = conversation.question || ''; + const scenarioText = conversation.scenario || ''; + const isExpanded = Boolean(expandedRows[conversation.id]); + const canToggleExpand = + questionText.length > QUESTION_TOOLTIP_THRESHOLD || scenarioText.length > SCENARIO_TOOLTIP_THRESHOLD; + + const questionContent = ( + + {questionText} + + ); + + const scenarioContent = ( + + + {scenarioText || t('datasets.notSet')} + + + ); + + return ( + + + handleSelectOne(conversation.id)} + /> + + + {shouldShowTooltip(questionText, QUESTION_TOOLTIP_THRESHOLD) ? ( + + {questionContent} + + ) : ( + questionContent + )} + {conversation.confirmed && ( + + )} + {canToggleExpand && ( + toggleRowExpanded(conversation.id)} + > + {isExpanded ? t('common.collapse') : t('common.expand')} + + )} + + + {shouldShowTooltip(scenarioText, SCENARIO_TOOLTIP_THRESHOLD) ? ( + + {scenarioContent} + + ) : ( + scenarioContent + )} + + + + {conversation.turnCount}/{conversation.maxTurns} + + + + + + + + + + {new Date(conversation.createAt).toLocaleDateString()} + + + + onView(conversation.id)}> + + + + + onDelete(conversation.id)}> + + + + + + ); + }) + )} + +
+ + onPageChange(newPage)} + rowsPerPage={rowsPerPage} + rowsPerPageOptions={[20, 50, 100]} + onRowsPerPageChange={event => { + onRowsPerPageChange(parseInt(event.target.value, 10)); + }} + labelRowsPerPage={t('datasets.rowsPerPage')} + /> +
+ ); +}; + +export default ConversationTable; diff --git a/easy-dataset-main/app/projects/[projectId]/multi-turn/components/FilterDialog.js b/easy-dataset-main/app/projects/[projectId]/multi-turn/components/FilterDialog.js new file mode 100644 index 0000000..2c96bf9 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/multi-turn/components/FilterDialog.js @@ -0,0 +1,104 @@ +'use client'; + +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + TextField, + Box, + FormControl, + InputLabel, + Select, + MenuItem +} from '@mui/material'; +import { useTranslation } from 'react-i18next'; + +/** + * 筛选对话框组件 + * @param {boolean} open - 对话框开启状态 + * @param {function} onClose - 关闭回调 + * @param {object} filters - 筛选条件 + * @param {function} onFiltersChange - 筛选条件变化回调 + * @param {function} onReset - 重置回调 + * @param {function} onApply - 应用回调 + */ +const FilterDialog = ({ open, onClose, filters, onFiltersChange, onReset, onApply }) => { + const { t } = useTranslation(); + + const handleFilterChange = (field, value) => { + onFiltersChange({ ...filters, [field]: value }); + }; + + return ( + + {t('datasets.filtersTitle')} + + + handleFilterChange('roleA', e.target.value)} + fullWidth + /> + handleFilterChange('roleB', e.target.value)} + fullWidth + /> + handleFilterChange('scenario', e.target.value)} + fullWidth + /> + + handleFilterChange('scoreMin', e.target.value)} + fullWidth + /> + handleFilterChange('scoreMax', e.target.value)} + fullWidth + /> + + + {t('datasets.filterConfirmationStatus')} + + + + + + + + + + + ); +}; + +export default FilterDialog; diff --git a/easy-dataset-main/app/projects/[projectId]/multi-turn/components/RatingChip.js b/easy-dataset-main/app/projects/[projectId]/multi-turn/components/RatingChip.js new file mode 100644 index 0000000..d9c2be6 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/multi-turn/components/RatingChip.js @@ -0,0 +1,33 @@ +'use client'; + +import { Chip } from '@mui/material'; +import { Star as StarIcon } from '@mui/icons-material'; +import { useTranslation } from 'react-i18next'; +import { getRatingConfigI18n, formatScore } from '@/components/datasets/utils/ratingUtils'; + +/** + * 评分展示组件 + * @param {number} score - 评分值 + */ +const RatingChip = ({ score }) => { + const { t } = useTranslation(); + const config = getRatingConfigI18n(score, t); + + return ( + } + label={`${formatScore(score)} ${config.label}`} + size="small" + sx={{ + backgroundColor: config.backgroundColor, + color: config.color, + fontWeight: 'medium', + '& .MuiChip-icon': { + color: config.color + } + }} + /> + ); +}; + +export default RatingChip; diff --git a/easy-dataset-main/app/projects/[projectId]/multi-turn/components/SearchBar.js b/easy-dataset-main/app/projects/[projectId]/multi-turn/components/SearchBar.js new file mode 100644 index 0000000..67bedde --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/multi-turn/components/SearchBar.js @@ -0,0 +1,92 @@ +'use client'; + +import { Box, Paper, Button, IconButton, InputBase, CircularProgress } from '@mui/material'; +import { + Search as SearchIcon, + FilterList as FilterIcon, + Download as DownloadIcon, + Delete as DeleteIcon +} from '@mui/icons-material'; +import { useTranslation } from 'react-i18next'; + +/** + * 搜索栏组件 + * @param {string} searchKeyword - 搜索关键词 + * @param {function} onSearchChange - 搜索关键词变化回调 + * @param {function} onSearch - 搜索回调 + * @param {function} onFilterClick - 筛选按钮点击回调 + * @param {function} onExportClick - 导出按钮点击回调 + * @param {boolean} exportLoading - 导出加载状态 + * @param {number} selectedCount - 选中的项目数量 + * @param {function} onBatchDelete - 批量删除回调 + * @param {boolean} batchDeleteLoading - 批量删除加载状态 + */ +const SearchBar = ({ + searchKeyword, + onSearchChange, + onSearch, + onFilterClick, + onExportClick, + exportLoading = false, + selectedCount = 0, + onBatchDelete, + batchDeleteLoading = false +}) => { + const { t } = useTranslation(); + + return ( + + + + + + + onSearchChange(e.target.value)} + onKeyPress={e => e.key === 'Enter' && onSearch()} + /> + + + + + {selectedCount > 0 && ( + + )} + + + + ); +}; + +export default SearchBar; diff --git a/easy-dataset-main/app/projects/[projectId]/multi-turn/hooks/useMultiTurnData.js b/easy-dataset-main/app/projects/[projectId]/multi-turn/hooks/useMultiTurnData.js new file mode 100644 index 0000000..9e11506 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/multi-turn/hooks/useMultiTurnData.js @@ -0,0 +1,346 @@ +'use client'; + +import { useState, useEffect, useRef } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useRouter } from 'next/navigation'; +import { toast } from 'sonner'; + +/** + * Multi-turn dataset data hook + * @param {string} projectId + */ +export const useMultiTurnData = projectId => { + const { t } = useTranslation(); + const router = useRouter(); + + const [conversations, setConversations] = useState([]); + const [loading, setLoading] = useState(true); + const [page, setPage] = useState(0); + const [rowsPerPage, setRowsPerPage] = useState(20); + const [total, setTotal] = useState(0); + const [searchKeyword, setSearchKeyword] = useState(''); + const [filterDialogOpen, setFilterDialogOpen] = useState(false); + const [exportLoading, setExportLoading] = useState(false); + + const [selectedIds, setSelectedIds] = useState([]); + const [isAllSelected, setIsAllSelected] = useState(false); + const [batchDeleteLoading, setBatchDeleteLoading] = useState(false); + + const [filters, setFilters] = useState({ + roleA: '', + roleB: '', + scenario: '', + scoreMin: '', + scoreMax: '', + confirmed: '' + }); + + const abortRef = useRef(null); + + const buildQuery = ({ pageIndex, keyword, filterValues }) => { + const params = new URLSearchParams({ + page: String(pageIndex + 1), + pageSize: String(rowsPerPage) + }); + + if (keyword) params.append('keyword', keyword); + if (filterValues.roleA) params.append('roleA', filterValues.roleA); + if (filterValues.roleB) params.append('roleB', filterValues.roleB); + if (filterValues.scenario) params.append('scenario', filterValues.scenario); + if (filterValues.scoreMin) params.append('scoreMin', filterValues.scoreMin); + if (filterValues.scoreMax) params.append('scoreMax', filterValues.scoreMax); + if (filterValues.confirmed) params.append('confirmed', filterValues.confirmed); + + return params; + }; + + const fetchConversations = async (newPage = page, options = {}) => { + const keyword = options.keyword ?? searchKeyword; + const filterValues = options.filterValues ?? filters; + const showLoading = options.showLoading ?? true; + + try { + if (abortRef.current) { + abortRef.current.abort(); + } + const controller = new AbortController(); + abortRef.current = controller; + + if (showLoading) { + setLoading(true); + } + + const params = buildQuery({ pageIndex: newPage, keyword, filterValues }); + const response = await fetch(`/api/projects/${projectId}/dataset-conversations?${params.toString()}`, { + signal: controller.signal + }); + + if (!response.ok) { + throw new Error(t('datasets.fetchDataFailed')); + } + + const data = await response.json(); + setConversations(data.data || []); + setTotal(data.total || 0); + } catch (error) { + if (error?.name === 'AbortError') return; + console.error('Failed to fetch multi-turn dataset list:', error); + toast.error(error.message || t('datasets.fetchDataFailed')); + } finally { + if (showLoading) { + setLoading(false); + } + if (abortRef.current === controller) { + abortRef.current = null; + } + } + }; + + const handleExport = async () => { + try { + setExportLoading(true); + const response = await fetch(`/api/projects/${projectId}/dataset-conversations/export`); + + if (!response.ok) { + throw new Error(t('datasets.exportFailed')); + } + + const data = await response.json(); + const dataStr = JSON.stringify(data, null, 2); + const dataBlob = new Blob([dataStr], { type: 'application/json' }); + const url = URL.createObjectURL(dataBlob); + const link = document.createElement('a'); + link.href = url; + link.download = `multi-turn-conversations-${projectId}-${new Date().toISOString().slice(0, 10)}.json`; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); + + toast.success(t('datasets.exportSuccess')); + } catch (error) { + console.error('Export failed:', error); + toast.error(error.message || t('datasets.exportFailed')); + } finally { + setExportLoading(false); + } + }; + + const fetchAllConversationIds = async () => { + try { + const params = new URLSearchParams({ getAllIds: 'true' }); + if (searchKeyword) params.append('keyword', searchKeyword); + if (filters.roleA) params.append('roleA', filters.roleA); + if (filters.roleB) params.append('roleB', filters.roleB); + if (filters.scenario) params.append('scenario', filters.scenario); + if (filters.scoreMin) params.append('scoreMin', filters.scoreMin); + if (filters.scoreMax) params.append('scoreMax', filters.scoreMax); + if (filters.confirmed) params.append('confirmed', filters.confirmed); + + const response = await fetch(`/api/projects/${projectId}/dataset-conversations?${params.toString()}`); + if (!response.ok) { + throw new Error(t('datasets.fetchDataFailed')); + } + + const data = await response.json(); + return data.allConversationIds || []; + } catch (error) { + console.error('Failed to fetch all conversation IDs:', error); + toast.error(error.message || t('datasets.fetchDataFailed')); + return []; + } + }; + + const handleDelete = async conversationId => { + if (!confirm(t('datasets.confirmDeleteConversation'))) { + return; + } + + try { + const response = await fetch(`/api/projects/${projectId}/dataset-conversations/${conversationId}`, { + method: 'DELETE' + }); + + if (!response.ok) { + throw new Error(t('datasets.deleteFailed')); + } + + toast.success(t('datasets.deleteSuccess')); + fetchConversations(); + } catch (error) { + console.error('Delete failed:', error); + toast.error(error.message || t('datasets.deleteFailed')); + } + }; + + const deleteConversationsConcurrently = async (conversationIds, concurrency = 10) => { + const results = []; + const errors = []; + + for (let i = 0; i < conversationIds.length; i += concurrency) { + const batch = conversationIds.slice(i, i + concurrency); + const promises = batch.map(async id => { + try { + const response = await fetch(`/api/projects/${projectId}/dataset-conversations/${id}`, { + method: 'DELETE' + }); + if (!response.ok) { + throw new Error(`Delete conversation ${id} failed`); + } + return { id, success: true }; + } catch (error) { + errors.push({ id, error: error.message }); + return { id, success: false, error: error.message }; + } + }); + + const batchResults = await Promise.all(promises); + results.push(...batchResults); + } + + return { results, errors }; + }; + + const handleBatchDelete = async () => { + let idsToDelete = selectedIds; + + if (isAllSelected) { + idsToDelete = await fetchAllConversationIds(); + if (idsToDelete.length === 0) { + toast.error(t('datasets.noDataToDelete')); + return; + } + } + + if (idsToDelete.length === 0) { + toast.error(t('datasets.pleaseSelectData')); + return; + } + + if (!confirm(t('common.confirmDelete', { count: idsToDelete.length }))) { + return; + } + + try { + setBatchDeleteLoading(true); + const { results, errors } = await deleteConversationsConcurrently(idsToDelete); + + const successCount = results.filter(r => r.success).length; + const failCount = errors.length; + + if (failCount === 0) { + toast.success(t('common.deleteSuccess', { count: successCount })); + } else { + toast.warning(t('datasets.batchDeletePartialSuccess', { success: successCount, fail: failCount })); + } + + setSelectedIds([]); + setIsAllSelected(false); + fetchConversations(); + } catch (error) { + console.error('Batch delete failed:', error); + toast.error(error.message || t('datasets.batchDeleteFailed')); + } finally { + setBatchDeleteLoading(false); + } + }; + + const handleSelectionChange = newSelectedIds => { + setSelectedIds(newSelectedIds); + if (newSelectedIds.length === 0) { + setIsAllSelected(false); + } + }; + + const handleSelectAll = selectAll => { + setIsAllSelected(selectAll); + if (!selectAll) { + setSelectedIds([]); + } + }; + + const handleView = conversationId => { + router.push(`/projects/${projectId}/multi-turn/${conversationId}`); + }; + + const applyFilters = () => { + setPage(0); + setFilterDialogOpen(false); + fetchConversations(0, { keyword: searchKeyword, filterValues: filters }); + }; + + const resetFilters = () => { + const clearedFilters = { + roleA: '', + roleB: '', + scenario: '', + scoreMin: '', + scoreMax: '', + confirmed: '' + }; + setFilters(clearedFilters); + setSearchKeyword(''); + setPage(0); + fetchConversations(0, { keyword: '', filterValues: clearedFilters }); + }; + + const handleSearch = () => { + setPage(0); + fetchConversations(0, { keyword: searchKeyword, filterValues: filters }); + }; + + const handlePageChange = newPage => { + setPage(newPage); + }; + + const handleRowsPerPageChange = newRowsPerPage => { + setRowsPerPage(newRowsPerPage); + setPage(0); + }; + + useEffect(() => { + fetchConversations(page, { showLoading: true }); + }, [projectId, page, rowsPerPage]); + + useEffect(() => { + return () => { + if (abortRef.current) { + abortRef.current.abort(); + } + }; + }, []); + + return { + conversations, + loading, + page, + rowsPerPage, + total, + searchKeyword, + filterDialogOpen, + exportLoading, + filters, + + selectedIds, + isAllSelected, + batchDeleteLoading, + + setSearchKeyword, + setFilterDialogOpen, + setFilters, + + fetchConversations, + handleExport, + handleDelete, + handleView, + applyFilters, + resetFilters, + handleSearch, + handlePageChange, + handleRowsPerPageChange, + + handleBatchDelete, + handleSelectionChange, + handleSelectAll + }; +}; diff --git a/easy-dataset-main/app/projects/[projectId]/multi-turn/page.js b/easy-dataset-main/app/projects/[projectId]/multi-turn/page.js new file mode 100644 index 0000000..cc8c8c4 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/multi-turn/page.js @@ -0,0 +1,106 @@ +'use client'; + +import { Container, Typography, Box, Card, useTheme, alpha } from '@mui/material'; +import { Chat as ChatIcon } from '@mui/icons-material'; +import { useTranslation } from 'react-i18next'; + +// 导入拆分后的组件 +import SearchBar from './components/SearchBar'; +import ConversationTable from './components/ConversationTable'; +import FilterDialog from './components/FilterDialog'; +import { useMultiTurnData } from './hooks/useMultiTurnData'; + +export default function MultiTurnDatasetPage({ params }) { + const { t } = useTranslation(); + const theme = useTheme(); + const { projectId } = params; + + // 使用自定义Hook管理状态和逻辑 + const { + conversations, + loading, + page, + rowsPerPage, + total, + searchKeyword, + filterDialogOpen, + exportLoading, + filters, + selectedIds, + isAllSelected, + batchDeleteLoading, + setSearchKeyword, + setFilterDialogOpen, + setFilters, + fetchConversations, + handleExport, + handleDelete, + handleView, + applyFilters, + resetFilters, + handleSearch, + handlePageChange, + handleRowsPerPageChange, + handleBatchDelete, + handleSelectionChange, + handleSelectAll + } = useMultiTurnData(projectId); + + return ( + + + {/* + + + {t('datasets.multiTurnConversations')} + + */} + + setFilterDialogOpen(true)} + onExportClick={handleExport} + exportLoading={exportLoading} + selectedCount={isAllSelected ? total : selectedIds.length} + onBatchDelete={handleBatchDelete} + batchDeleteLoading={batchDeleteLoading} + /> + + + + + setFilterDialogOpen(false)} + filters={filters} + onFiltersChange={setFilters} + onReset={resetFilters} + onApply={applyFilters} + /> + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/page.js b/easy-dataset-main/app/projects/[projectId]/page.js new file mode 100644 index 0000000..4f529cb --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/page.js @@ -0,0 +1,39 @@ +'use client'; + +import { useEffect } from 'react'; +import { useRouter } from 'next/navigation'; +import axios from 'axios'; +import { toast } from 'sonner'; +import { useSetAtom } from 'jotai/index'; +import { modelConfigListAtom, selectedModelInfoAtom } from '@/lib/store'; + +export default function ProjectPage({ params }) { + const router = useRouter(); + const setConfigList = useSetAtom(modelConfigListAtom); + const setSelectedModelInfo = useSetAtom(selectedModelInfoAtom); + const { projectId } = params; + + // 默认重定向到文本分割页面 + useEffect(() => { + getModelConfigList(projectId); + router.push(`/projects/${projectId}/text-split`); + }, [projectId, router]); + + const getModelConfigList = projectId => { + axios + .get(`/api/projects/${projectId}/model-config`) + .then(response => { + setConfigList(response.data.data); + if (response.data.defaultModelConfigId) { + setSelectedModelInfo(response.data.data.find(item => item.id === response.data.defaultModelConfigId)); + } else { + setSelectedModelInfo(null); + } + }) + .catch(error => { + toast.error('get model list error'); + }); + }; + + return null; +} diff --git a/easy-dataset-main/app/projects/[projectId]/playground/page.js b/easy-dataset-main/app/projects/[projectId]/playground/page.js new file mode 100644 index 0000000..faf5378 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/playground/page.js @@ -0,0 +1,92 @@ +'use client'; + +import React from 'react'; +import { Box, Typography, Paper, Alert } from '@mui/material'; +import { useParams } from 'next/navigation'; +import { useTheme } from '@mui/material/styles'; +import ChatArea from '@/components/playground/ChatArea'; +import MessageInput from '@/components/playground/MessageInput'; +import PlaygroundHeader from '@/components/playground/PlaygroundHeader'; +import useModelPlayground from '@/hooks/useModelPlayground'; +import { playgroundStyles } from '@/styles/playground'; +import { useTranslation } from 'react-i18next'; +import { useAtomValue } from 'jotai/index'; +import { modelConfigListAtom } from '@/lib/store'; + +export default function ModelPlayground({ searchParams }) { + const theme = useTheme(); + const params = useParams(); + const { projectId } = params; + const modelId = searchParams?.modelId || null; + const styles = playgroundStyles(theme); + const { t } = useTranslation(); + + const { + selectedModels, + loading, + userInput, + conversations, + error, + outputMode, + uploadedImage, + handleModelSelection, + handleInputChange, + handleImageUpload, + handleRemoveImage, + handleSendMessage, + handleClearConversations, + handleOutputModeChange + } = useModelPlayground(projectId, modelId); + + const availableModels = useAtomValue(modelConfigListAtom); + + // 获取模型名称 + const getModelName = modelId => { + const model = availableModels.find(m => m.id === modelId); + return model ? `${model.providerName}: ${model.modelName}` : modelId; + }; + return ( + + + {t('playground.title')} + + + {error && ( + + {error} + + )} + + + + + + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/questions/components/ConfirmDialog.js b/easy-dataset-main/app/projects/[projectId]/questions/components/ConfirmDialog.js new file mode 100644 index 0000000..e7f5753 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/questions/components/ConfirmDialog.js @@ -0,0 +1,58 @@ +'use client'; + +import { Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Button } from '@mui/material'; +import { useTranslation } from 'react-i18next'; + +/** + * 确认对话框组件 + * @param {Object} props + * @param {boolean} props.open - 对话框是否打开 + * @param {Function} props.onClose - 关闭对话框的回调函数 + * @param {Function} props.onConfirm - 确认操作的回调函数 + * @param {string} props.title - 对话框标题 + * @param {string} props.content - 对话框内容 + * @param {string} props.confirmText - 确认按钮文本,默认为 "确认删除" + * @param {string} props.cancelText - 取消按钮文本,默认为 "取消" + * @param {string} props.confirmColor - 确认按钮颜色,默认为 "error" + */ +export default function ConfirmDialog({ + open, + onClose, + onConfirm, + title, + content, + confirmText, + cancelText, + confirmColor = 'error' +}) { + const { t } = useTranslation(); + + const handleConfirm = () => { + onClose(); + if (onConfirm) { + onConfirm(); + } + }; + + return ( + + {title} + + {content} + + + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/questions/components/ExportQuestionsDialog.js b/easy-dataset-main/app/projects/[projectId]/questions/components/ExportQuestionsDialog.js new file mode 100644 index 0000000..78f7dfa --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/questions/components/ExportQuestionsDialog.js @@ -0,0 +1,82 @@ +'use client'; + +import { useState } from 'react'; +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + FormControl, + FormLabel, + RadioGroup, + FormControlLabel, + Radio, + Box, + Divider +} from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import DownloadIcon from '@mui/icons-material/Download'; + +export default function ExportQuestionsDialog({ open, onClose, onExport, selectedCount, totalCount }) { + const { t } = useTranslation(); + const [format, setFormat] = useState('json'); + const [exportScope, setExportScope] = useState('all'); + + const handleExport = () => { + const exportOptions = { + format, + selectedIds: exportScope === 'selected' ? [] : undefined + }; + + onExport(exportOptions); + onClose(); + }; + + return ( + + {t('questions.exportQuestions')} + + + {/* 导出范围 */} + + {t('questions.exportScope')} + setExportScope(e.target.value)}> + } + label={t('questions.exportAll', { count: totalCount })} + /> + {selectedCount > 0 && ( + } + label={t('questions.exportSelected', { count: selectedCount })} + /> + )} + + + + + + {/* 导出格式 */} + + {t('questions.exportFormat')} + setFormat(e.target.value)}> + } label="JSON" /> + } label="JSONL" /> + } label={t('questions.txtFormat')} /> + } label="CSV" /> + + + + + + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/questions/components/QuestionEditDialog.js b/easy-dataset-main/app/projects/[projectId]/questions/components/QuestionEditDialog.js new file mode 100644 index 0000000..b4ed4b5 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/questions/components/QuestionEditDialog.js @@ -0,0 +1,220 @@ +'use client'; + +import { useState, useEffect, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + TextField, + Button, + Box, + Autocomplete, + TextField as MuiTextField, + FormControl, + InputLabel, + Select, + MenuItem +} from '@mui/material'; +import axios from 'axios'; + +export default function QuestionEditDialog({ + open, + onClose, + onSubmit, + initialData, + projectId, + tags, + mode = 'create' // 'create' or 'edit' +}) { + const [chunks, setChunks] = useState([]); + const [images, setImages] = useState([]); + const { t } = useTranslation(); + + // 获取文本块的标题 + const getChunkTitle = chunkId => { + const chunk = chunks.find(c => c.id === chunkId); + return chunk?.name || chunkId; // 直接使用文件名 + }; + + const [formData, setFormData] = useState({ + id: '', + question: '', + sourceType: 'text', // 新增:数据源类型 + chunkId: '', + imageId: '', // 新增:图片ID + label: '' // 默认不选中任何标签 + }); + + const getChunks = async projectId => { + // 获取文本块列表 + const response = await axios.get(`/api/projects/${projectId}/split`); + if (response.status !== 200) { + throw new Error(t('common.fetchError')); + } + setChunks(response.data.chunks || []); + }; + + const getImages = async projectId => { + // 获取图片列表(只获取ID和名称) + try { + const response = await axios.get(`/api/projects/${projectId}/images?page=1&pageSize=10000&simple=true`); + if (response.status === 200) { + setImages(response.data.data || []); + } + } catch (error) { + console.error('Failed to fetch images:', error); + } + }; + + useEffect(() => { + getChunks(projectId); + getImages(projectId); + if (initialData) { + // 根据 imageId 判断数据源类型 + console.log('initialData:', initialData); + const sourceType = initialData.imageId ? 'image' : 'text'; + setFormData({ + id: initialData.id, + question: initialData.question || '', + sourceType: sourceType, + chunkId: initialData.chunkId || '', + imageId: initialData.imageId || '', + label: initialData.label || 'other' // 改用 label 而不是 label + }); + } else { + setFormData({ + id: '', + question: '', + sourceType: 'text', + chunkId: '', + imageId: '', + label: '' + }); + } + }, [initialData]); + + const handleSubmit = () => { + onSubmit(formData); + onClose(); + }; + + const flattenTags = (tags = [], prefix = '') => { + let flatTags = []; + const traverse = node => { + flatTags.push({ + id: node.label, // 使用标签名作为 id + label: node.label, // 直接使用原始标签名 + originalLabel: node.label + }); + if (node.child && node.child.length > 0) { + node.child.forEach(child => traverse(child)); + } + }; + tags.forEach(tag => traverse(tag)); + flatTags.push({ + id: 'other', + label: t('datasets.uncategorized'), + originalLabel: 'other' + }); + return flatTags; + }; + + const flattenedTags = useMemo(() => flattenTags(tags), [tags, t]); + + return ( + + {mode === 'create' ? t('questions.createQuestion') : t('questions.editQuestion')} + + + {/* 数据源类型选择 */} + + {t('questions.sourceType', { defaultValue: '数据源类型' })} + + + + {/* 问题内容 */} + setFormData({ ...formData, question: e.target.value })} + /> + + {/* 文本块选择(仅当数据源为文本时显示) */} + {formData.sourceType === 'text' && ( + getChunkTitle(chunk.id)} + value={chunks.find(chunk => chunk.id === formData.chunkId) || null} + onChange={(e, newValue) => setFormData({ ...formData, chunkId: newValue ? newValue.id : '' })} + renderInput={params => ( + + )} + /> + )} + + {/* 图片选择(仅当数据源为图片时显示) */} + {formData.sourceType === 'image' && ( + image.imageName || ''} + value={images.find(image => image.id === formData.imageId) || null} + onChange={(e, newValue) => setFormData({ ...formData, imageId: newValue ? newValue.id : '' })} + renderInput={params => ( + + )} + /> + )} + + {/* 标签选择 */} + {formData.sourceType === 'text' && ( + tag.label} + value={flattenedTags.find(tag => tag.id === formData.label) || null} + onChange={(e, newValue) => setFormData({ ...formData, label: newValue ? newValue.id : '' })} + renderInput={params => ( + + )} + /> + )} + + + + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/questions/components/QuestionsFilter.js b/easy-dataset-main/app/projects/[projectId]/questions/components/QuestionsFilter.js new file mode 100644 index 0000000..c417903 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/questions/components/QuestionsFilter.js @@ -0,0 +1,192 @@ +'use client'; + +import { Box, Stack, Checkbox, Typography, TextField, InputAdornment, Select, MenuItem, useTheme } from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import SearchIcon from '@mui/icons-material/Search'; + +export default function QuestionsFilter({ + // 选择相关 + selectedQuestionsCount, + totalQuestions, + isAllSelected, + isIndeterminate, + onSelectAll, + + // 搜索相关 + searchTerm, + onSearchChange, + searchMatchMode, + onSearchMatchModeChange, + + // 过滤相关 + answerFilter, + onFilterChange, + + // 文本块名称筛选 + chunkNameFilter, + onChunkNameFilterChange, + + // 数据源类型筛选 + sourceTypeFilter, + onSourceTypeFilterChange, + + activeTab +}) { + const { t } = useTranslation(); + const theme = useTheme(); + + if (activeTab === 1) { + return <>; + } + return ( + + + {/* 选择区域 */} + + + + {selectedQuestionsCount > 0 + ? t('questions.selectedCount', { count: selectedQuestionsCount }) + : t('questions.selectAll')} + ( + {t('questions.totalCount', { + count: totalQuestions + })} + ) + + + + {/* 搜索和过滤区域 */} + + {/* 组合搜索框:下拉选择(匹配/不匹配)+ 输入框 */} + + + + + + ) + }} + /> + + + + + ) + }} + /> + + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/questions/components/QuestionsPageHeader.js b/easy-dataset-main/app/projects/[projectId]/questions/components/QuestionsPageHeader.js new file mode 100644 index 0000000..ac46d54 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/questions/components/QuestionsPageHeader.js @@ -0,0 +1,192 @@ +'use client'; + +import { useState } from 'react'; +import { Box, Typography, Button, Tooltip, Menu, MenuItem, ListItemIcon, ListItemText } from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import AutoFixHighIcon from '@mui/icons-material/AutoFixHigh'; +import DeleteIcon from '@mui/icons-material/Delete'; +import AddIcon from '@mui/icons-material/Add'; +import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; +import DatasetIcon from '@mui/icons-material/Dataset'; +import ChatIcon from '@mui/icons-material/Chat'; +import ImageIcon from '@mui/icons-material/Image'; +import LibraryAddIcon from '@mui/icons-material/LibraryAdd'; +import DownloadIcon from '@mui/icons-material/Download'; + +export default function QuestionsPageHeader({ + questionsTotal, + selectedQuestionsCount, + onBatchDeleteQuestions, + onOpenCreateDialog, + onOpenCreateTemplateDialog, + onBatchGenerateAnswers, + onAutoGenerateDatasets, + onAutoGenerateMultiTurnDatasets, + onAutoGenerateImageDatasets, + onExportQuestions, + activeTab +}) { + const { t } = useTranslation(); + const [anchorEl, setAnchorEl] = useState(null); + const [createAnchorEl, setCreateAnchorEl] = useState(null); + const open = Boolean(anchorEl); + const createMenuOpen = Boolean(createAnchorEl); + + const handleMenuClick = event => { + setAnchorEl(event.currentTarget); + }; + + const handleMenuClose = () => { + setAnchorEl(null); + }; + + const handleCreateMenuClick = event => { + setCreateAnchorEl(event.currentTarget); + }; + + const handleCreateMenuClose = () => { + setCreateAnchorEl(null); + }; + + const handleCreateQuestion = () => { + handleCreateMenuClose(); + onOpenCreateDialog(); + }; + + const handleCreateTemplate = () => { + handleCreateMenuClose(); + onOpenCreateTemplateDialog(); + }; + + const handleSingleTurnGenerate = () => { + handleMenuClose(); + onAutoGenerateDatasets(); + }; + + const handleMultiTurnGenerate = () => { + handleMenuClose(); + onAutoGenerateMultiTurnDatasets(); + }; + + const handleImageDatasetGenerate = () => { + handleMenuClose(); + onAutoGenerateImageDatasets(); + }; + + return ( + + + {t('questions.title')} ({questionsTotal}) + + + + + + + + + + + + + + + + + + + + + + + + {/* */} + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/questions/components/TemplateListView.js b/easy-dataset-main/app/projects/[projectId]/questions/components/TemplateListView.js new file mode 100644 index 0000000..3232982 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/questions/components/TemplateListView.js @@ -0,0 +1,121 @@ +'use client'; + +import { + Box, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + IconButton, + Chip, + Typography, + Alert +} from '@mui/material'; +import EditIcon from '@mui/icons-material/Edit'; +import DeleteIcon from '@mui/icons-material/Delete'; +import { useTranslation } from 'react-i18next'; + +export default function TemplateListView({ templates, onEditTemplate, onDeleteTemplate, loading }) { + const { t } = useTranslation(); + + const getAnswerTypeLabel = type => { + const labels = { + text: t('questions.template.answerType.text'), + label: t('questions.template.answerType.tags'), + custom_format: t('questions.template.answerType.customFormat') + }; + return labels[type] || type; + }; + + const getSourceTypeLabel = type => { + const labels = { + image: t('questions.template.sourceType.image'), + text: t('questions.template.sourceType.text') + }; + return labels[type] || type; + }; + + if (loading) { + return ( + + {t('common.loading')} + + ); + } + + if (!templates || templates.length === 0) { + return ( + + {t('questions.template.noTemplates')} + + ); + } + + return ( + + + + + {t('questions.template.question')} + {t('questions.template.sourceType.label')} + {t('questions.template.answerType.label')} + {t('questions.template.description')} + {t('questions.template.used')} + {t('common.actions')} + + + + {templates.map(template => ( + + + + {template.question} + + + + + + + + + + + {template.description || '-'} + + + + {template.usageCount > 0 ? ( + + ) : ( + + 0 + + )} + + + onEditTemplate(template)} sx={{ mr: 1 }}> + + + onDeleteTemplate(template.id)} + disabled={template.usageCount > 0} + color="error" + > + + + + + ))} + +
+
+ ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/questions/components/template/TemplateFormDialog.js b/easy-dataset-main/app/projects/[projectId]/questions/components/template/TemplateFormDialog.js new file mode 100644 index 0000000..38aca6c --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/questions/components/template/TemplateFormDialog.js @@ -0,0 +1,335 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + TextField, + FormControl, + InputLabel, + Select, + MenuItem, + Box, + Chip, + Typography, + Alert, + FormControlLabel, + Checkbox +} from '@mui/material'; +import { useTranslation } from 'react-i18next'; + +export default function TemplateFormDialog({ open, onClose, onSubmit, template }) { + const { t } = useTranslation(); + const [formData, setFormData] = useState({ + question: '', + sourceType: 'text', + answerType: 'text', + description: '', + labels: [], + customFormat: '', + autoGenerate: true + }); + const [labelInput, setLabelInput] = useState(''); + const [errors, setErrors] = useState({}); + const [showConfirmDialog, setShowConfirmDialog] = useState(false); + + useEffect(() => { + if (template) { + setFormData({ + question: template.question || '', + sourceType: template.sourceType || 'text', + answerType: template.answerType || 'text', + description: template.description || '', + labels: template.labels || [], + customFormat: template.customFormat ? JSON.stringify(template.customFormat, null, 2) : '', + autoGenerate: true // 编辑模式下默认不自动生成 + }); + } else { + setFormData({ + question: '', + sourceType: 'text', + answerType: 'text', + description: '', + labels: [], + customFormat: '', + autoGenerate: true + }); + } + setErrors({}); + setShowConfirmDialog(false); + }, [template, open]); + + const handleChange = (field, value) => { + setFormData(prev => ({ ...prev, [field]: value })); + // 清除该字段的错误 + if (errors[field]) { + setErrors(prev => ({ ...prev, [field]: null })); + } + }; + + const handleAddLabel = () => { + const trimmed = labelInput.trim(); + if (trimmed && !formData.labels.includes(trimmed)) { + setFormData(prev => ({ + ...prev, + labels: [...prev.labels, trimmed] + })); + setLabelInput(''); + } + }; + + const handleDeleteLabel = labelToDelete => { + setFormData(prev => ({ + ...prev, + labels: prev.labels.filter(label => label !== labelToDelete) + })); + }; + + const validate = () => { + const newErrors = {}; + + if (!formData.question.trim()) { + newErrors.question = t('questions.template.errors.questionRequired'); + } + + if (formData.answerType === 'label' && formData.labels.length === 0) { + newErrors.labels = t('questions.template.errors.labelsRequired'); + } + + if (formData.answerType === 'custom_format') { + if (!formData.customFormat.trim()) { + newErrors.customFormat = t('questions.template.errors.customFormatRequired'); + } else { + try { + JSON.parse(formData.customFormat); + } catch (e) { + newErrors.customFormat = t('questions.template.errors.invalidJson'); + } + } + } + + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }; + + const handleSubmit = () => { + if (!validate()) { + return; + } + + // 如果选择了自动生成,显示确认对话框 + if (formData.autoGenerate) { + setShowConfirmDialog(true); + return; + } + + // 直接提交 + submitTemplate(); + }; + + const submitTemplate = () => { + const submitData = { + question: formData.question.trim(), + sourceType: formData.sourceType, + answerType: formData.answerType, + description: formData.description.trim(), + autoGenerate: formData.autoGenerate, + templateId: template?.id // 编辑模式时传递模板ID,用于查找未创建问题的数据源 + }; + + if (formData.answerType === 'label') { + submitData.labels = formData.labels; + } + + if (formData.answerType === 'custom_format') { + try { + submitData.customFormat = JSON.parse(formData.customFormat); + } catch (e) { + // 已在验证中处理 + return; + } + } + + onSubmit(submitData); + setShowConfirmDialog(false); + }; + + const handleConfirmGenerate = () => { + submitTemplate(); + }; + + const handleCancelGenerate = () => { + setShowConfirmDialog(false); + }; + + return ( + + {template ? t('questions.template.edit') : t('questions.template.create')} + + + {/* 数据源类型 */} + + {t('questions.template.sourceTypeInfo')} + + + + {/* 问题内容 */} + handleChange('question', e.target.value)} + error={!!errors.question} + helperText={errors.question} + required + /> + + {/* 答案类型 */} + + {t('questions.template.answerType.label')} + + + + {/* 描述 */} + handleChange('description', e.target.value)} + helperText={t('questions.template.descriptionHelp')} + multiline + rows={2} + /> + + {/* 标签输入 (仅当答案类型为 label 时显示) */} + {formData.answerType === 'label' && ( + + + setLabelInput(e.target.value)} + onKeyPress={e => { + if (e.key === 'Enter') { + e.preventDefault(); + handleAddLabel(); + } + }} + error={!!errors.labels} + helperText={errors.labels} + /> + + + + {formData.labels.map(label => ( + handleDeleteLabel(label)} + color="primary" + variant="outlined" + /> + ))} + + + )} + + {/* 自定义格式输入 (仅当答案类型为 custom_format 时显示) */} + {formData.answerType === 'custom_format' && ( + + handleChange('customFormat', e.target.value)} + multiline + rows={6} + error={!!errors.customFormat} + helperText={errors.customFormat || t('questions.template.customFormatHelp')} + placeholder='{"field1": "description", "field2": "description"}' + /> + + {t('questions.template.customFormatInfo')} + + + )} + + {/* 自动生成问题选项 */} + + handleChange('autoGenerate', e.target.checked)} + color="primary" + /> + } + label={t('questions.template.autoGenerate')} + /> + + {formData.sourceType === 'text' + ? t('questions.template.autoGenerateHelpText') + : t('questions.template.autoGenerateHelpImage')} + + + + + + + + + + {/* 自动生成确认对话框 */} + + {t('questions.template.confirmAutoGenerate')} + + + {template + ? formData.sourceType === 'text' + ? t('questions.template.confirmAutoGenerateEditTextMessage', { + defaultValue: '您选择了自动生成问题。系统将为所有还未创建此模板问题的文本块创建问题。' + }) + : t('questions.template.confirmAutoGenerateEditImageMessage', { + defaultValue: '您选择了自动生成问题。系统将为所有还未创建此模板问题的图片创建问题。' + }) + : formData.sourceType === 'text' + ? t('questions.template.confirmAutoGenerateTextMessage') + : t('questions.template.confirmAutoGenerateImageMessage')} + + + {t('questions.template.autoGenerateWarning')} + + + + + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/questions/components/template/TemplateManagementDialog.js b/easy-dataset-main/app/projects/[projectId]/questions/components/template/TemplateManagementDialog.js new file mode 100644 index 0000000..6bf3c72 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/questions/components/template/TemplateManagementDialog.js @@ -0,0 +1,165 @@ +'use client'; + +import { useState } from 'react'; +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + Box, + List, + ListItem, + ListItemText, + ListItemSecondaryAction, + IconButton, + Chip, + Typography, + Alert, + Tabs, + Tab +} from '@mui/material'; +import EditIcon from '@mui/icons-material/Edit'; +import DeleteIcon from '@mui/icons-material/Delete'; +import AddIcon from '@mui/icons-material/Add'; +import { useTranslation } from 'react-i18next'; +import TemplateFormDialog from './TemplateFormDialog'; + +export default function TemplateManagementDialog({ + open, + onClose, + templates, + onCreateTemplate, + onUpdateTemplate, + onDeleteTemplate, + loading +}) { + const { t } = useTranslation(); + const [formOpen, setFormOpen] = useState(false); + const [editingTemplate, setEditingTemplate] = useState(null); + const [currentTab, setCurrentTab] = useState(0); // 0: image, 1: text + + const handleCreate = () => { + setEditingTemplate(null); + setFormOpen(true); + }; + + const handleEdit = template => { + setEditingTemplate(template); + setFormOpen(true); + }; + + const handleDelete = async templateId => { + const confirmed = window.confirm(t('questions.template.deleteConfirm')); + if (confirmed) { + await onDeleteTemplate(templateId); + } + }; + + const handleFormSubmit = async data => { + // 根据当前tab添加sourceType + const sourceType = currentTab === 0 ? 'image' : 'text'; + const templateData = { ...data, sourceType }; + + if (editingTemplate) { + await onUpdateTemplate(editingTemplate.id, templateData); + } else { + await onCreateTemplate(templateData); + } + setFormOpen(false); + }; + + const getAnswerTypeLabel = type => { + const labels = { + text: t('questions.template.answerType.text'), + label: t('questions.template.answerType.tags'), + custom_format: t('questions.template.answerType.customFormat') + }; + return labels[type] || type; + }; + + // 按数据源类型分组模板 + const imageTemplates = templates.filter(t => t.sourceType === 'image'); + const textTemplates = templates.filter(t => t.sourceType === 'text'); + + const currentTemplates = currentTab === 0 ? imageTemplates : textTemplates; + + const renderTemplateList = templateList => { + if (templateList.length === 0) { + return {t('questions.template.noTemplates')}; + } + + return ( + + {templateList.map(template => ( + + + {template.question} + + {template.usageCount > 0 && ( + + )} + + } + secondary={template.description} + /> + + handleEdit(template)} sx={{ mr: 1 }}> + + + handleDelete(template.id)} disabled={template.usageCount > 0}> + + + + + ))} + + ); + }; + + return ( + <> + + + + {t('questions.template.management')} + + + + + + setCurrentTab(newValue)}> + + + + + {renderTemplateList(currentTemplates)} + + + + + + + setFormOpen(false)} + onSubmit={handleFormSubmit} + template={editingTemplate} + sourceType={currentTab === 0 ? 'image' : 'text'} + /> + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/questions/hooks/useQuestionDelete.js b/easy-dataset-main/app/projects/[projectId]/questions/hooks/useQuestionDelete.js new file mode 100644 index 0000000..78de9af --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/questions/hooks/useQuestionDelete.js @@ -0,0 +1,125 @@ +'use client'; + +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import axios from 'axios'; +import { toast } from 'sonner'; + +export function useQuestionDelete(projectId, onDeleteSuccess) { + const { t } = useTranslation(); + + // 确认对话框状态 + const [confirmDialog, setConfirmDialog] = useState({ + open: false, + title: '', + content: '', + confirmAction: null + }); + + // 执行单个问题删除 + const executeDeleteQuestion = async (questionId, selectedQuestions, setSelectedQuestions) => { + toast.promise(axios.delete(`/api/projects/${projectId}/questions/${questionId}`), { + loading: '数据删除中', + success: data => { + // 更新选中状态 + setSelectedQuestions(prev => (prev.includes(questionId) ? prev.filter(id => id !== questionId) : prev)); + // 调用成功回调 + if (onDeleteSuccess) { + onDeleteSuccess(); + } + return t('common.deleteSuccess'); + }, + error: error => { + return error.response?.data?.message || '删除失败'; + } + }); + }; + + // 确认删除单个问题 + const confirmDeleteQuestion = (questionId, selectedQuestions, setSelectedQuestions) => { + setConfirmDialog({ + open: true, + title: t('common.confirmDelete'), + content: t('common.confirmDeleteQuestion'), + confirmAction: () => executeDeleteQuestion(questionId, selectedQuestions, setSelectedQuestions) + }); + }; + + // 处理删除单个问题的入口函数 + const handleDeleteQuestion = (questionId, selectedQuestions, setSelectedQuestions) => { + confirmDeleteQuestion(questionId, selectedQuestions, setSelectedQuestions); + }; + + // 执行批量删除问题 + const executeBatchDeleteQuestions = async (selectedQuestions, setSelectedQuestions) => { + toast.promise( + axios.delete(`/api/projects/${projectId}/questions/batch-delete`, { + data: { questionIds: selectedQuestions } + }), + { + loading: `正在删除 ${selectedQuestions.length} 个问题...`, + success: data => { + // 调用成功回调 + if (onDeleteSuccess) { + onDeleteSuccess(); + } + // 清空选中状态 + setSelectedQuestions([]); + return `成功删除 ${selectedQuestions.length} 个问题`; + }, + error: error => { + return error.response?.data?.message || '批量删除问题失败'; + } + } + ); + }; + + // 确认批量删除问题 + const confirmBatchDeleteQuestions = (selectedQuestions, setSelectedQuestions) => { + if (selectedQuestions.length === 0) { + toast.warning('请先选择问题'); + return; + } + + setConfirmDialog({ + open: true, + title: '确认批量删除问题', + content: `您确定要删除选中的 ${selectedQuestions.length} 个问题吗?此操作不可恢复。`, + confirmAction: () => executeBatchDeleteQuestions(selectedQuestions, setSelectedQuestions) + }); + }; + + // 处理批量删除问题的入口函数 + const handleBatchDeleteQuestions = (selectedQuestions, setSelectedQuestions) => { + confirmBatchDeleteQuestions(selectedQuestions, setSelectedQuestions); + }; + + // 关闭确认对话框 + const closeConfirmDialog = () => { + setConfirmDialog({ + open: false, + title: '', + content: '', + confirmAction: null + }); + }; + + // 确认对话框的确认操作 + const handleConfirmAction = () => { + closeConfirmDialog(); + if (confirmDialog.confirmAction) { + confirmDialog.confirmAction(); + } + }; + + return { + // 状态 + confirmDialog, + + // 方法 + handleDeleteQuestion, + handleBatchDeleteQuestions, + closeConfirmDialog, + handleConfirmAction + }; +} diff --git a/easy-dataset-main/app/projects/[projectId]/questions/hooks/useQuestionEdit.js b/easy-dataset-main/app/projects/[projectId]/questions/hooks/useQuestionEdit.js new file mode 100644 index 0000000..8759e9a --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/questions/hooks/useQuestionEdit.js @@ -0,0 +1,84 @@ +'use client'; + +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import request from '@/lib/util/request'; + +export function useQuestionEdit(projectId, onSuccess) { + const { t } = useTranslation(); + const [editDialogOpen, setEditDialogOpen] = useState(false); + const [editMode, setEditMode] = useState('create'); + const [editingQuestion, setEditingQuestion] = useState(null); + + const handleOpenCreateDialog = () => { + setEditMode('create'); + setEditingQuestion(null); + setEditDialogOpen(true); + }; + + const handleOpenEditDialog = question => { + setEditMode('edit'); + setEditingQuestion(question); + setEditDialogOpen(true); + }; + + const handleCloseDialog = () => { + setEditDialogOpen(false); + setEditingQuestion(null); + }; + + const handleSubmitQuestion = async formData => { + try { + const response = await request(`/api/projects/${projectId}/questions`, { + method: editMode === 'create' ? 'POST' : 'PUT', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify( + editMode === 'create' + ? { + question: formData.question, + chunkId: formData.chunkId, + label: formData.label, + imageId: formData.imageId, + imageName: formData.imageName + } + : { + id: formData.id, + question: formData.question, + chunkId: formData.chunkId, + label: formData.label, + imageId: formData.imageId, + imageName: formData.imageName + } + ) + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.error || t('questions.operationFailed')); + } + + // 获取更新后的问题数据 + const updatedQuestion = await response.json(); + + // 直接更新问题列表中的数据,而不是重新获取整个列表 + if (onSuccess) { + onSuccess(updatedQuestion); + } + handleCloseDialog(); + } catch (error) { + console.error('操作失败:', error); + } + }; + + return { + editDialogOpen, + editMode, + editingQuestion, + handleOpenCreateDialog, + handleOpenEditDialog, + handleCloseDialog, + handleSubmitQuestion + }; +} diff --git a/easy-dataset-main/app/projects/[projectId]/questions/hooks/useQuestionExport.js b/easy-dataset-main/app/projects/[projectId]/questions/hooks/useQuestionExport.js new file mode 100644 index 0000000..f5b9a5b --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/questions/hooks/useQuestionExport.js @@ -0,0 +1,114 @@ +'use client'; + +import { useTranslation } from 'react-i18next'; +import { toast } from 'sonner'; +import axios from 'axios'; + +const useQuestionExport = projectId => { + const { t } = useTranslation(); + + // 导出问题集 + const exportQuestions = async exportOptions => { + try { + const apiUrl = `/api/projects/${projectId}/questions/export`; + const requestBody = { + format: exportOptions.format || 'json' + }; + + // 如果有选中的问题 ID,传递 ID 列表 + if (exportOptions.selectedIds && exportOptions.selectedIds.length > 0) { + requestBody.selectedIds = exportOptions.selectedIds; + } + + // 如果有筛选条件,传递筛选参数 + if (exportOptions.filters) { + requestBody.filters = exportOptions.filters; + } + + const response = await axios.post(apiUrl, requestBody); + const questions = response.data; + + // 处理和下载数据 + await processAndDownloadData(questions, exportOptions); + + toast.success(t('questions.exportSuccess')); + return true; + } catch (error) { + console.error('Export failed:', error); + toast.error(error.message || t('questions.exportFailed')); + return false; + } + }; + + // 处理和下载数据的通用函数 + const processAndDownloadData = async (data, exportOptions) => { + const format = exportOptions.format || 'json'; + let content; + let filename; + let mimeType; + + const timestamp = new Date().toISOString().split('T')[0]; + + switch (format) { + case 'json': + content = JSON.stringify(data, null, 2); + filename = `questions-${projectId}-${timestamp}.json`; + mimeType = 'application/json'; + break; + + case 'jsonl': + content = data.map(item => JSON.stringify(item)).join('\n'); + filename = `questions-${projectId}-${timestamp}.jsonl`; + mimeType = 'application/jsonl'; + break; + + case 'txt': + content = data.map(item => item.question).join('\n\n'); + filename = `questions-${projectId}-${timestamp}.txt`; + mimeType = 'text/plain'; + break; + + case 'csv': + // CSV 格式 + const headers = Object.keys(data[0] || {}); + const csvRows = [headers.join(',')]; + data.forEach(item => { + const values = headers.map(header => { + const value = item[header] || ''; + // 处理包含逗号或引号的值 + if (typeof value === 'string' && (value.includes(',') || value.includes('"') || value.includes('\n'))) { + return `"${value.replace(/"/g, '""')}"`; + } + return value; + }); + csvRows.push(values.join(',')); + }); + content = csvRows.join('\n'); + filename = `questions-${projectId}-${timestamp}.csv`; + mimeType = 'text/csv'; + break; + + default: + content = JSON.stringify(data, null, 2); + filename = `questions-${projectId}-${timestamp}.json`; + mimeType = 'application/json'; + } + + // 创建下载链接 + const blob = new Blob([content], { type: mimeType }); + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = filename; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); + }; + + return { + exportQuestions + }; +}; + +export default useQuestionExport; diff --git a/easy-dataset-main/app/projects/[projectId]/questions/hooks/useQuestionGeneration.js b/easy-dataset-main/app/projects/[projectId]/questions/hooks/useQuestionGeneration.js new file mode 100644 index 0000000..952bac3 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/questions/hooks/useQuestionGeneration.js @@ -0,0 +1,291 @@ +'use client'; + +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { toast } from 'sonner'; +import axios from 'axios'; +import i18n from '@/lib/i18n'; +import request from '@/lib/util/request'; +import { processInParallel } from '@/lib/util/processInParallel'; + +export function useQuestionGeneration(projectId, model, taskSettings, getQuestionList) { + const { t } = useTranslation(); + + // 处理状态 + const [processing, setProcessing] = useState(false); + + // 进度状态 + const [progress, setProgress] = useState({ + total: 0, // 总共选择的问题数量 + completed: 0, // 已处理完成的数量 + percentage: 0, // 进度百分比 + datasetCount: 0 // 已生成的数据集数量 + }); + + // 批量生成答案 + const handleBatchGenerateAnswers = async selectedQuestions => { + if (selectedQuestions.length === 0) { + toast.warning(t('questions.noQuestionsSelected')); + return; + } + + if (!model) { + toast.warning(t('models.configNotFound')); + return; + } + + try { + setProgress({ + total: selectedQuestions.length, + completed: 0, + percentage: 0, + datasetCount: 0 + }); + + // 然后设置处理状态为真,确保进度条显示 + setProcessing(true); + + toast.info(t('questions.batchGenerateStart', { count: selectedQuestions.length })); + + // 单个问题处理函数 + const processQuestion = async questionId => { + try { + console.log('开始生成数据集:', { questionId }); + const language = i18n.language === 'zh-CN' ? '中文' : 'en'; + // 调用API生成数据集 + const response = await request(`/api/projects/${projectId}/datasets`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + questionId, + model, + language + }) + }); + + if (!response.ok) { + const errorData = await response.json(); + console.error(t('datasets.generateError'), errorData.error || t('datasets.generateFailed')); + + // 更新进度状态(即使失败也计入已处理) + setProgress(prev => { + const completed = prev.completed + 1; + const percentage = Math.round((completed / prev.total) * 100); + + return { + ...prev, + completed, + percentage + }; + }); + + return { success: false, questionId, error: errorData.error || t('datasets.generateFailed') }; + } + + const data = await response.json(); + + // 更新进度状态 + setProgress(prev => { + const completed = prev.completed + 1; + const percentage = Math.round((completed / prev.total) * 100); + const datasetCount = prev.datasetCount + 1; + + return { + ...prev, + completed, + percentage, + datasetCount + }; + }); + + console.log(`数据集生成成功: ${questionId}`); + return { success: true, questionId, data: data.dataset }; + } catch (error) { + console.error('生成数据集失败:', error); + + // 更新进度状态(即使失败也计入已处理) + setProgress(prev => { + const completed = prev.completed + 1; + const percentage = Math.round((completed / prev.total) * 100); + + return { + ...prev, + completed, + percentage + }; + }); + + return { success: false, questionId, error: error.message }; + } + }; + + // 并行处理所有问题,最多同时处理2个 + const results = await processInParallel(selectedQuestions, processQuestion, taskSettings.concurrencyLimit); + + // 刷新数据 + getQuestionList(); + + // 处理完成后设置结果消息 + const successCount = results.filter(r => r.success).length; + const failCount = results.filter(r => !r.success).length; + + if (failCount > 0) { + toast.warning( + t('datasets.partialSuccess', { + successCount, + total: selectedQuestions.length, + failCount + }) + ); + } else { + toast.success(t('common.success', { successCount })); + } + } catch (error) { + console.error('生成数据集出错:', error); + toast.error(error.message || '生成数据集失败'); + } finally { + // 延迟关闭处理状态,确保用户可以看到完成的进度 + setTimeout(() => { + setProcessing(false); + // 再次延迟重置进度状态 + setTimeout(() => { + setProgress({ + total: 0, + completed: 0, + percentage: 0, + datasetCount: 0 + }); + }, 500); + }, 2000); // 延迟关闭处理状态,让用户看到完成的进度 + } + }; + + // 自动生成数据集 + const handleAutoGenerateDatasets = async () => { + try { + if (!model) { + toast.error(t('questions.selectModelFirst', { defaultValue: '请先选择模型' })); + return; + } + + // 调用创建任务接口 + const response = await axios.post(`/api/projects/${projectId}/tasks`, { + taskType: 'answer-generation', + modelInfo: model, + language: i18n.language + }); + + if (response.data?.code === 0) { + toast.success(t('tasks.createSuccess', { defaultValue: '后台任务已创建,系统将自动处理未生成答案的问题' })); + } else { + toast.error(t('tasks.createFailed', { defaultValue: '创建后台任务失败' })); + } + } catch (error) { + console.error('创建任务失败:', error); + toast.error(t('tasks.createFailed', { defaultValue: '创建任务失败' }) + ': ' + error.message); + } + }; + + // 自动生成图像问答数据集 + const handleAutoGenerateImageDatasets = async () => { + try { + if (!model) { + toast.error(t('questions.selectModelFirst', { defaultValue: '请先选择模型' })); + return; + } + + if (model.type !== 'vision') { + toast.error(t('images.visionModelRequired', { defaultValue: '请选择支持视觉的模型' })); + return; + } + + // 调用创建任务接口 + const response = await axios.post(`/api/projects/${projectId}/tasks`, { + taskType: 'image-dataset-generation', + modelInfo: model, + language: i18n.language + }); + + if (response.data?.code === 0) { + toast.success(t('tasks.createSuccess', { defaultValue: '后台任务已创建,系统将自动处理未生成答案的图片问题' })); + } else { + toast.error(t('tasks.createFailed', { defaultValue: '创建后台任务失败' })); + } + } catch (error) { + console.error('创建图片数据集任务失败:', error); + toast.error(t('tasks.createFailed', { defaultValue: '创建任务失败' }) + ': ' + error.message); + } + }; + + // 自动生成多轮对话数据集 + const handleAutoGenerateMultiTurnDatasets = async () => { + try { + if (!model) { + toast.error(t('questions.selectModelFirst', { defaultValue: '请先选择模型' })); + return; + } + + // 首先检查项目是否配置了多轮对话设置 + const configResponse = await axios.get(`/api/projects/${projectId}/tasks`); + if (configResponse.status !== 200) { + throw new Error('获取项目配置失败'); + } + + const config = configResponse.data; + const multiTurnConfig = { + systemPrompt: config.multiTurnSystemPrompt, + scenario: config.multiTurnScenario, + rounds: config.multiTurnRounds, + roleA: config.multiTurnRoleA, + roleB: config.multiTurnRoleB + }; + + // 检查是否已配置必要的多轮对话设置 + if ( + !multiTurnConfig.scenario || + !multiTurnConfig.roleA || + !multiTurnConfig.roleB || + !multiTurnConfig.rounds || + multiTurnConfig.rounds < 1 + ) { + toast.error(t('questions.multiTurnNotConfigured', '请先在项目设置中配置多轮对话相关参数')); + return; + } + + // 调用创建任务接口 + const response = await axios.post(`/api/projects/${projectId}/tasks`, { + taskType: 'multi-turn-generation', + modelInfo: model, + language: i18n.language, + config: JSON.stringify(multiTurnConfig) + }); + + if (response.data?.code === 0) { + toast.success( + t('tasks.multiTurnCreateSuccess', { + defaultValue: '多轮对话生成任务已创建,系统将自动处理未生成多轮对话的问题' + }) + ); + } else { + toast.error(t('tasks.createFailed', { defaultValue: '创建后台任务失败' })); + } + } catch (error) { + console.error('创建多轮对话任务失败:', error); + toast.error(t('tasks.createFailed', { defaultValue: '创建任务失败' }) + ': ' + error.message); + } + }; + + return { + // 状态 + processing, + progress, + + // 方法 + handleBatchGenerateAnswers, + handleAutoGenerateDatasets, + handleAutoGenerateMultiTurnDatasets, + handleAutoGenerateImageDatasets + }; +} diff --git a/easy-dataset-main/app/projects/[projectId]/questions/hooks/useQuestionTemplates.js b/easy-dataset-main/app/projects/[projectId]/questions/hooks/useQuestionTemplates.js new file mode 100644 index 0000000..414d99e --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/questions/hooks/useQuestionTemplates.js @@ -0,0 +1,122 @@ +import { useState, useEffect } from 'react'; +import axios from 'axios'; +import { toast } from 'sonner'; +import { useTranslation } from 'react-i18next'; + +/** + * 问题模板管理 Hook + * @param {string} projectId - 项目ID + * @param {string} sourceType - 数据源类型: 'image' | 'text' | null (null表示获取所有) + */ +export function useQuestionTemplates(projectId, sourceType = null) { + const { t } = useTranslation(); + const [templates, setTemplates] = useState([]); + const [loading, setLoading] = useState(false); + + // 获取模板列表 + const fetchTemplates = async () => { + try { + setLoading(true); + const params = sourceType ? `?sourceType=${sourceType}` : ''; + const response = await axios.get(`/api/projects/${projectId}/questions/templates${params}`); + if (response.data.success) { + setTemplates(response.data.templates); + } + } catch (error) { + console.error('Failed to fetch templates:', error); + toast.error(t('questions.fetchTemplatesFailed')); + } finally { + setLoading(false); + } + }; + + // 创建模板 + const createTemplate = async data => { + try { + const response = await axios.post(`/api/projects/${projectId}/questions/templates`, data); + if (response.data.success) { + const { template, generation } = response.data; + + // 显示模板创建成功消息 + toast.success(t('questions.createTemplateSuccess')); + + // 如果有自动生成结果,显示相应消息 + if (generation) { + if (generation.success) { + if (generation.successCount > 0) { + toast.success( + t('questions.template.autoGenerateSuccess', { + count: generation.successCount + }) + ); + } + if (generation.failCount > 0) { + toast.warning( + t('questions.template.autoGeneratePartialFail', { + success: generation.successCount, + fail: generation.failCount + }) + ); + } + } else { + toast.error(generation.message || t('questions.template.autoGenerateFailed')); + } + } + + fetchTemplates(); + return template; + } + } catch (error) { + console.error('Failed to create template:', error); + toast.error(t('questions.createTemplateFailed')); + throw error; + } + }; + + // 更新模板 + const updateTemplate = async (templateId, data) => { + try { + const response = await axios.put(`/api/projects/${projectId}/questions/templates/${templateId}`, data); + if (response.data.success) { + toast.success(t('questions.updateTemplateSuccess')); + fetchTemplates(); + return response.data.template; + } + } catch (error) { + console.error('Failed to update template:', error); + toast.error(t('questions.updateTemplateFailed')); + throw error; + } + }; + + // 删除模板 + const deleteTemplate = async templateId => { + try { + const response = await axios.delete(`/api/projects/${projectId}/questions/templates/${templateId}`); + if (response.data.success) { + toast.success(t('questions.deleteTemplateSuccess')); + fetchTemplates(); + } + } catch (error) { + console.error('Failed to delete template:', error); + toast.error(t('questions.deleteTemplateFailed')); + throw error; + } + }; + + // 初始加载 + useEffect(() => { + if (projectId) { + fetchTemplates(); + } + }, [projectId, sourceType]); + + return { + templates, + loading, + createTemplate, + updateTemplate, + deleteTemplate, + refetch: fetchTemplates + }; +} diff --git a/easy-dataset-main/app/projects/[projectId]/questions/hooks/useQuestionsFilter.js b/easy-dataset-main/app/projects/[projectId]/questions/hooks/useQuestionsFilter.js new file mode 100644 index 0000000..5f874cb --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/questions/hooks/useQuestionsFilter.js @@ -0,0 +1,117 @@ +'use client'; + +import { useState } from 'react'; +import { useDebounce } from '@/hooks/useDebounce'; +import axios from 'axios'; + +export function useQuestionsFilter(projectId) { + // 过滤和搜索状态 + const [answerFilter, setAnswerFilter] = useState('all'); // 'all', 'answered', 'unanswered' + const [searchTerm, setSearchTerm] = useState(''); + const [searchMatchMode, setSearchMatchMode] = useState('match'); // 'match', 'notMatch' + const [chunkNameFilter, setChunkNameFilter] = useState(''); + const [sourceTypeFilter, setSourceTypeFilter] = useState('all'); // 'all', 'text', 'image' + const debouncedSearchTerm = useDebounce(searchTerm); + const debouncedChunkNameFilter = useDebounce(chunkNameFilter); + + // 选择状态 + const [selectedQuestions, setSelectedQuestions] = useState([]); + + // 处理问题选择 + const handleSelectQuestion = (questionKey, newSelected) => { + if (newSelected) { + // 处理批量选择的情况 + setSelectedQuestions(newSelected); + } else { + // 处理单个问题选择的情况 + setSelectedQuestions(prev => { + if (prev.includes(questionKey)) { + return prev.filter(id => id !== questionKey); + } else { + return [...prev, questionKey]; + } + }); + } + }; + + // 全选/取消全选 + const handleSelectAll = async () => { + if (selectedQuestions.length > 0) { + setSelectedQuestions([]); + } else { + const response = await axios.get( + `/api/projects/${projectId}/questions?status=${answerFilter}&input=${searchTerm}&searchMatchMode=${searchMatchMode}&chunkName=${encodeURIComponent(chunkNameFilter)}&sourceType=${sourceTypeFilter}&selectedAll=1` + ); + setSelectedQuestions(response.data.map(dataset => dataset.id)); + } + }; + + // 处理搜索输入变化 + const handleSearchChange = event => { + setSearchTerm(event.target.value); + }; + + // 处理过滤器变化 + const handleFilterChange = event => { + setAnswerFilter(event.target.value); + }; + + // 处理文本块名称筛选变化 + const handleChunkNameFilterChange = event => { + setChunkNameFilter(event.target.value); + }; + + // 处理数据源类型筛选变化 + const handleSourceTypeFilterChange = event => { + setSourceTypeFilter(event.target.value); + }; + + // 处理搜索匹配模式变化 + const handleSearchMatchModeChange = event => { + setSearchMatchMode(event.target.value); + }; + + // 清空选择 + const clearSelection = () => { + setSelectedQuestions([]); + }; + + // 重置所有过滤条件 + const resetFilters = () => { + setSearchTerm(''); + setSearchMatchMode('match'); + setAnswerFilter('all'); + setChunkNameFilter(''); + setSourceTypeFilter('all'); + setSelectedQuestions([]); + }; + + return { + // 状态 + answerFilter, + searchTerm, + debouncedSearchTerm, + searchMatchMode, + chunkNameFilter, + debouncedChunkNameFilter, + sourceTypeFilter, + selectedQuestions, + + // 方法 + setAnswerFilter, + setSearchTerm, + setSearchMatchMode, + setChunkNameFilter, + setSourceTypeFilter, + setSelectedQuestions, + handleSelectQuestion, + handleSelectAll, + handleSearchChange, + handleFilterChange, + handleChunkNameFilterChange, + handleSourceTypeFilterChange, + handleSearchMatchModeChange, + clearSelection, + resetFilters + }; +} diff --git a/easy-dataset-main/app/projects/[projectId]/questions/page.js b/easy-dataset-main/app/projects/[projectId]/questions/page.js new file mode 100644 index 0000000..199afbf --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/questions/page.js @@ -0,0 +1,416 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Container, Typography, Box, Paper, Tabs, Tab, CircularProgress, Divider, LinearProgress } from '@mui/material'; + +import QuestionListView from '@/components/questions/QuestionListView'; +import QuestionTreeView from '@/components/questions/QuestionTreeView'; +import TabPanel from '@/components/text-split/components/TabPanel'; +import useTaskSettings from '@/hooks/useTaskSettings'; +import QuestionEditDialog from './components/QuestionEditDialog'; +import QuestionsPageHeader from './components/QuestionsPageHeader'; +import ConfirmDialog from './components/ConfirmDialog'; +import TemplateListView from './components/TemplateListView'; +import TemplateFormDialog from './components/template/TemplateFormDialog'; +import ExportQuestionsDialog from './components/ExportQuestionsDialog'; +import { useQuestionTemplates } from './hooks/useQuestionTemplates'; +import { useQuestionEdit } from './hooks/useQuestionEdit'; +import { useQuestionDelete } from './hooks/useQuestionDelete'; +import { useQuestionsFilter } from './hooks/useQuestionsFilter'; +import QuestionsFilter from './components/QuestionsFilter'; +import { useQuestionGeneration } from './hooks/useQuestionGeneration'; +import useQuestionExport from './hooks/useQuestionExport'; +import axios from 'axios'; +import { toast } from 'sonner'; +import { useAtomValue } from 'jotai/index'; +import { selectedModelInfoAtom } from '@/lib/store'; + +export default function QuestionsPage({ params }) { + const { t } = useTranslation(); + const { projectId } = params; + const [loading, setLoading] = useState(true); + const [questions, setQuestions] = useState({}); + const [currentPage, setCurrentPage] = useState(1); + const [pageSize, setPageSize] = useState(10); + const [tags, setTags] = useState([]); + const model = useAtomValue(selectedModelInfoAtom); + const [activeTab, setActiveTab] = useState(0); + + // 模板管理 + const { + templates, + loading: templatesLoading, + createTemplate, + updateTemplate, + deleteTemplate + } = useQuestionTemplates(projectId, null); // null 表示获取所有类型的模板 + + const [templateDialogOpen, setTemplateDialogOpen] = useState(false); + const [editingTemplate, setEditingTemplate] = useState(null); + const [exportDialogOpen, setExportDialogOpen] = useState(false); + + // 使用新的过滤和搜索 Hook + const { + answerFilter, + searchTerm, + debouncedSearchTerm, + searchMatchMode, + chunkNameFilter, + debouncedChunkNameFilter, + sourceTypeFilter, + selectedQuestions, + setSelectedQuestions, + handleSelectQuestion, + handleSelectAll, + handleSearchChange, + handleFilterChange, + handleChunkNameFilterChange, + handleSourceTypeFilterChange, + handleSearchMatchModeChange + } = useQuestionsFilter(projectId); + + const getQuestionList = async () => { + try { + // 获取问题列表 + const questionsResponse = await axios.get( + `/api/projects/${projectId}/questions?page=${currentPage}&size=10&status=${answerFilter}&input=${searchTerm}&searchMatchMode=${searchMatchMode}&chunkName=${encodeURIComponent(debouncedChunkNameFilter)}&sourceType=${sourceTypeFilter}` + ); + if (questionsResponse.status !== 200) { + throw new Error(t('common.fetchError')); + } + setQuestions(questionsResponse.data || {}); + + // 获取标签树 + const tagsResponse = await axios.get(`/api/projects/${projectId}/tags`); + if (tagsResponse.status !== 200) { + throw new Error(t('common.fetchError')); + } + setTags(tagsResponse.data.tags || []); + + setLoading(false); + } catch (error) { + console.error(t('common.fetchError'), error); + toast.error(error.message); + } + }; + + // 当筛选条件改变时,重置页码到第1页 + useEffect(() => { + setCurrentPage(1); + }, [answerFilter, debouncedSearchTerm, debouncedChunkNameFilter, sourceTypeFilter, searchMatchMode]); + + useEffect(() => { + getQuestionList(); + }, [currentPage, answerFilter, debouncedSearchTerm, debouncedChunkNameFilter, sourceTypeFilter, searchMatchMode]); + + const { taskSettings } = useTaskSettings(projectId); + + // 使用新的问题生成 Hook + const { + processing, + progress, + handleBatchGenerateAnswers, + handleAutoGenerateDatasets, + handleAutoGenerateMultiTurnDatasets, + handleAutoGenerateImageDatasets + } = useQuestionGeneration(projectId, model, taskSettings, getQuestionList); + + const { + editDialogOpen, + editMode, + editingQuestion, + handleOpenCreateDialog, + handleOpenEditDialog, + handleCloseDialog, + handleSubmitQuestion + } = useQuestionEdit(projectId, updatedQuestion => { + getQuestionList(); + toast.success(t('questions.operationSuccess')); + }); + + const { confirmDialog, handleDeleteQuestion, handleBatchDeleteQuestions, closeConfirmDialog, handleConfirmAction } = + useQuestionDelete(projectId, () => { + getQuestionList(); + }); + + const { exportQuestions } = useQuestionExport(projectId); + + // 获取所有数据 + useEffect(() => { + getQuestionList(); + }, [projectId]); + + // 处理标签页切换 + const handleTabChange = (event, newValue) => { + setActiveTab(newValue); + }; + + // 模板管理函数 + const handleOpenCreateTemplateDialog = () => { + setEditingTemplate(null); + setTemplateDialogOpen(true); + }; + + const handleEditTemplate = template => { + setEditingTemplate(template); + setTemplateDialogOpen(true); + }; + + const handleCloseTemplateDialog = () => { + setTemplateDialogOpen(false); + setEditingTemplate(null); + }; + + const handleSubmitTemplate = async data => { + try { + if (editingTemplate) { + await updateTemplate(editingTemplate.id, data); + } else { + await createTemplate(data); + } + getQuestionList(); + handleCloseTemplateDialog(); + } catch (error) { + console.error('Failed to save template:', error); + } + }; + + const handleDeleteTemplate = async templateId => { + const confirmed = window.confirm(t('questions.template.deleteConfirm')); + if (confirmed) { + try { + await deleteTemplate(templateId); + } catch (error) { + console.error('Failed to delete template:', error); + } + } + }; + + const handleOpenExportDialog = () => { + setExportDialogOpen(true); + }; + + const handleCloseExportDialog = () => { + setExportDialogOpen(false); + }; + + const handleExportQuestions = async exportOptions => { + const options = { + ...exportOptions, + selectedIds: selectedQuestions, + filters: { + searchTerm: debouncedSearchTerm, + chunkName: debouncedChunkNameFilter, + sourceType: sourceTypeFilter + } + }; + await exportQuestions(options); + }; + + if (loading) { + return ( + + + + + + ); + } + + return ( + + {/* 处理中的进度显示 - 全局蒙版样式 */} + {processing && ( + + + + {t('datasets.generatingDataset')} + + + + + + {progress.percentage}% + + + + + + + + + {t('questions.generatingProgress', { + completed: progress.completed, + total: progress.total + })} + + + {t('questions.generatedCount', { count: progress.datasetCount })} + + + + + + + + {t('questions.pleaseWait')} + + + + )} + + handleBatchDeleteQuestions(selectedQuestions, setSelectedQuestions)} + onOpenCreateDialog={handleOpenCreateDialog} + onOpenCreateTemplateDialog={handleOpenCreateTemplateDialog} + onBatchGenerateAnswers={() => handleBatchGenerateAnswers(selectedQuestions)} + onAutoGenerateDatasets={handleAutoGenerateDatasets} + onAutoGenerateMultiTurnDatasets={handleAutoGenerateMultiTurnDatasets} + onAutoGenerateImageDatasets={handleAutoGenerateImageDatasets} + onExportQuestions={handleOpenExportDialog} + /> + + + + + + + + + 0 && selectedQuestions.length === questions?.total} + isIndeterminate={selectedQuestions.length > 0 && selectedQuestions.length < questions?.total} + onSelectAll={handleSelectAll} + searchTerm={searchTerm} + onSearchChange={handleSearchChange} + searchMatchMode={searchMatchMode} + onSearchMatchModeChange={handleSearchMatchModeChange} + answerFilter={answerFilter} + onFilterChange={handleFilterChange} + chunkNameFilter={chunkNameFilter} + onChunkNameFilterChange={handleChunkNameFilterChange} + sourceTypeFilter={sourceTypeFilter} + onSourceTypeFilterChange={handleSourceTypeFilterChange} + activeTab={activeTab} + /> + + + + + setCurrentPage(newPage)} + selectedQuestions={selectedQuestions} + onSelectQuestion={handleSelectQuestion} + onDeleteQuestion={questionId => handleDeleteQuestion(questionId, selectedQuestions, setSelectedQuestions)} + onEditQuestion={handleOpenEditDialog} + refreshQuestions={getQuestionList} + projectId={projectId} + /> + + + + + + + + handleDeleteQuestion(questionId, selectedQuestions, setSelectedQuestions)} + onEditQuestion={handleOpenEditDialog} + projectId={projectId} + searchTerm={searchTerm} + /> + + + + {/* 确认对话框 */} + + + + + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/settings/components/CategoryTabs.js b/easy-dataset-main/app/projects/[projectId]/settings/components/CategoryTabs.js new file mode 100644 index 0000000..99aa6fe --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/settings/components/CategoryTabs.js @@ -0,0 +1,25 @@ +import React from 'react'; +import { Tabs, Tab } from '@mui/material'; + +/** + * 顶部分类选择标签页组件 + */ +const CategoryTabs = ({ categoryEntries, selectedCategory, currentLanguage, onCategoryChange }) => { + return ( + { + onCategoryChange(newValue); + }} + variant="scrollable" + scrollButtons="auto" + sx={{ borderBottom: 1, borderColor: 'divider', mb: 3 }} + > + {categoryEntries.map(([categoryKey, categoryConfig]) => ( + + ))} + + ); +}; + +export default CategoryTabs; diff --git a/easy-dataset-main/app/projects/[projectId]/settings/components/PromptDetail.js b/easy-dataset-main/app/projects/[projectId]/settings/components/PromptDetail.js new file mode 100644 index 0000000..9bac229 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/settings/components/PromptDetail.js @@ -0,0 +1,92 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { Card, CardContent, Box, Typography, Chip, Button, Paper } from '@mui/material'; +import { Edit as EditIcon, Restore as RestoreIcon } from '@mui/icons-material'; +import ReactMarkdown from 'react-markdown'; + +import 'github-markdown-css/github-markdown-light.css'; + +/** + * 右侧提示词详情展示组件 + */ +const PromptDetail = ({ + currentPromptConfig, + selectedPrompt, + promptContent, + isCustomized, + onEditClick, + onDeleteClick +}) => { + const { t } = useTranslation(); + + if (!currentPromptConfig) { + return ( + {t('settings.prompts.selectPromptFirst')} + ); + } + + const handleEditClick = () => { + onEditClick(); + }; + + const handleDeleteClick = () => { + onDeleteClick(); + }; + + return ( + + + {/* 标题、描述与操作区域 */} + + + + {currentPromptConfig.name} + {isCustomized(selectedPrompt) && ( + + )} + + + + + + {isCustomized(selectedPrompt) && ( + + )} + + + + + {currentPromptConfig.description} + + + + {/* Markdown 渲染提示词内容 */} + +
+ {promptContent} +
+
+
+
+ ); +}; + +export default PromptDetail; diff --git a/easy-dataset-main/app/projects/[projectId]/settings/components/PromptEditDialog.js b/easy-dataset-main/app/projects/[projectId]/settings/components/PromptEditDialog.js new file mode 100644 index 0000000..bfaa95a --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/settings/components/PromptEditDialog.js @@ -0,0 +1,71 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + TextField, + Box, + Typography, + Chip +} from '@mui/material'; +import SaveIcon from '@mui/icons-material/Save'; +import RestoreIcon from '@mui/icons-material/Restore'; + +/** + * 提示词编辑对话框组件 + */ +const PromptEditDialog = ({ + open, + title, + promptType, + promptKey, + content, + loading, + onClose, + onSave, + onRestore, + onContentChange +}) => { + const { t } = useTranslation(); + return ( + + {title} + + + + {t('settings.prompts.promptType')}: {promptType} + + + {t('settings.prompts.keyName')}: {promptKey} + + + onContentChange(e.target.value)} + placeholder={t('settings.prompts.contentPlaceholder')} + variant="outlined" + /> + + + + + + + + + + + ); +}; + +export default PromptEditDialog; diff --git a/easy-dataset-main/app/projects/[projectId]/settings/components/PromptList.js b/easy-dataset-main/app/projects/[projectId]/settings/components/PromptList.js new file mode 100644 index 0000000..84de7e7 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/settings/components/PromptList.js @@ -0,0 +1,81 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { Box, Tabs, Tab, Typography, Chip } from '@mui/material'; +import { shouldShowPrompt } from './promptUtils'; + +/** + * 左侧提示词列表组件 + */ +const PromptList = ({ + currentCategory, + currentCategoryConfig, + selectedPrompt, + currentLanguage, + isCustomized, + onPromptSelect +}) => { + const { t } = useTranslation(); + + if (!currentCategoryConfig?.prompts) { + return ( + + {t('settings.prompts.noPromptsAvailable')} + + ); + } + + return ( + onPromptSelect(newValue)} + variant="scrollable" + scrollButtons="auto" + sx={{ + borderRight: 1, + borderColor: 'divider', + '& .MuiTabs-indicator': { + left: 0, + right: 'auto' + }, + '& .MuiTab-root': { + alignItems: 'flex-start', + textAlign: 'left' + } + }} + > + {currentCategoryConfig && + Object.entries(currentCategoryConfig.prompts).map(([promptKey, promptConfig]) => { + if (!shouldShowPrompt(promptKey, currentLanguage)) return null; + + const customized = isCustomized(promptKey); + + return ( + + + {promptConfig.name} + + {customized && ( + + )} + + } + sx={{ + alignItems: 'flex-start', + minHeight: 60, + px: 2, + justifyContent: 'flex-start', + width: '100%' + }} + /> + ); + })} + + ); +}; + +export default PromptList; diff --git a/easy-dataset-main/app/projects/[projectId]/settings/components/PromptSettings.js b/easy-dataset-main/app/projects/[projectId]/settings/components/PromptSettings.js new file mode 100644 index 0000000..41a4715 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/settings/components/PromptSettings.js @@ -0,0 +1,400 @@ +import React, { useState, useEffect } from 'react'; +import { useParams } from 'next/navigation'; +import { useTranslation } from 'react-i18next'; +import { Box, Grid, Card, CardContent } from '@mui/material'; +import { fetchWithRetry } from '@/lib/util/request'; +import { useSnackbar } from '@/hooks/useSnackbar'; + +// 导入拆分后的组件 +import CategoryTabs from './CategoryTabs'; +import PromptList from './PromptList'; +import PromptDetail from './PromptDetail'; +import PromptEditDialog from './PromptEditDialog'; +import { getLanguageFromPromptKey, shouldShowPrompt } from './promptUtils'; + +/** + * 提示词设置主组件 + */ +export default function PromptSettings() { + const { projectId } = useParams(); + const { i18n, t } = useTranslation(); + const { showSuccess, showErrorMessage, SnackbarComponent } = useSnackbar(); + + // 基础状态 + const [currentLanguage, setCurrentLanguage] = useState(i18n.language === 'en' ? 'en' : 'zh-CN'); + const [loading, setLoading] = useState(false); + const [templates, setTemplates] = useState({}); + const [customPrompts, setCustomPrompts] = useState([]); + + // 当前选中状态 + const [selectedCategory, setSelectedCategory] = useState(null); + const [selectedPrompt, setSelectedPrompt] = useState(null); + const [promptContent, setPromptContent] = useState(''); + + // 编辑对话框状态 + const [editDialog, setEditDialog] = useState({ + open: false, + promptType: '', + promptKey: '', + language: '', + content: '', + defaultContent: '', + isNew: false + }); + + // ======= 数据加载与初始化 ======= + + // 加载提示词数据 + useEffect(() => { + loadPromptData(); + }, [projectId, currentLanguage]); + + // 监听语言变化 + useEffect(() => { + const newLang = i18n.language === 'en' ? 'en' : 'zh-CN'; + if (newLang !== currentLanguage) { + setCurrentLanguage(newLang); + } + }, [i18n.language, currentLanguage]); + + // 监听选中提示词变化 + useEffect(() => { + if (selectedPrompt) { + loadPromptContent(); + } + }, [selectedPrompt]); + + // 初始化选择第一个分类和提示词 + useEffect(() => { + if (Object.keys(templates).length > 0 && currentLanguage && !selectedCategory) { + const firstCategory = Object.keys(templates)[0]; + setSelectedCategory(firstCategory); + + // 根据当前语言环境选择第一个匹配的提示词 + const promptEntries = Object.keys(templates[firstCategory]?.prompts || {}); + const firstPrompt = promptEntries.find(promptKey => shouldShowPrompt(promptKey, currentLanguage)); + + if (firstPrompt) { + setSelectedPrompt(firstPrompt); + } + } + }, [templates, selectedCategory, currentLanguage]); + + // ======= API 操作函数 ======= + + // 加载提示词数据 + const loadPromptData = async () => { + try { + setLoading(true); + const response = await fetchWithRetry(`/api/projects/${projectId}/custom-prompts?language=${currentLanguage}`); + const data = await response.json(); + + if (data.success) { + setTemplates(data.templates); + setCustomPrompts(data.customPrompts); + } else { + showErrorMessage(data.message || '加载提示词数据失败'); + } + } catch (error) { + console.error('加载提示词数据出错:', error); + showErrorMessage('加载提示词数据失败'); + } finally { + setLoading(false); + } + }; + + // 加载提示词内容 + const loadPromptContent = async (forceRefresh = false) => { + if (!selectedPrompt) return; + try { + setLoading(true); + const content = await getCurrentPromptContent(selectedPrompt, forceRefresh); + setPromptContent(content); + } catch (error) { + console.error('加载提示词内容出错:', error); + showErrorMessage('加载提示词内容失败'); + } finally { + setLoading(false); + } + }; + + // 加载默认提示词内容 + const loadDefaultContent = async (promptType, promptKey) => { + if (i18n.language === 'en' && !promptKey.endsWith('_EN')) { + promptKey += '_EN'; + } + try { + const response = await fetchWithRetry( + `/api/projects/${projectId}/default-prompts?promptType=${promptType}&promptKey=${promptKey}` + ); + const data = await response.json(); + + if (data.success) { + return data.content; + } + return ''; + } catch (error) { + console.error('加载默认提示词内容出错:', error); + return ''; + } + }; + + // ======= 交互处理函数 ======= + + // 处理编辑提示词 + const handleEditPrompt = async (promptType, promptKey, language) => { + const existingPrompt = customPrompts.find( + p => p.promptType === promptType && p.promptKey === promptKey && p.language === language + ); + + const defaultContent = await loadDefaultContent(promptType, promptKey); + + setEditDialog({ + open: true, + promptType, + promptKey, + language, + content: existingPrompt?.content || defaultContent, + defaultContent, + isNew: !existingPrompt + }); + }; + + // 处理删除提示词 + const handleDeletePrompt = async (promptType, promptKey, language) => { + try { + setLoading(true); + const query = new URLSearchParams({ + promptType, + promptKey, + language + }).toString(); + + const response = await fetchWithRetry(`/api/projects/${projectId}/custom-prompts?${query}`, { + method: 'DELETE' + }); + const data = await response.json(); + + if (data.success) { + showSuccess(t('settings.prompts.restoreSuccess')); + // 先重新加载数据,然后强制刷新内容 + await loadPromptData(); + await loadPromptContent(true); // 强制刷新 + } else { + showErrorMessage(data.message || t('settings.prompts.restoreFailed')); + } + } catch (error) { + console.error(t('settings.prompts.deleteError'), error); + showErrorMessage(t('settings.prompts.restoreFailed')); + } finally { + setLoading(false); + } + }; + + // 处理保存提示词 + const handleSavePrompt = async () => { + try { + setLoading(true); + const { promptType, promptKey, language, content } = editDialog; + + const response = await fetchWithRetry(`/api/projects/${projectId}/custom-prompts`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ promptType, promptKey, language, content }) + }); + const data = await response.json(); + + if (data.success) { + showSuccess(t('settings.prompts.saveSuccess')); + setEditDialog({ ...editDialog, open: false }); + // 先重新加载数据,然后强制刷新内容 + await loadPromptData(); + await loadPromptContent(true); // 强制刷新 + } else { + showErrorMessage(data.message || t('settings.prompts.saveFailed')); + } + } catch (error) { + console.error(t('settings.prompts.saveError'), error); + showErrorMessage(t('settings.prompts.saveFailed')); + } finally { + setLoading(false); + } + }; + + // 恢复默认内容 + const handleRestoreDefault = () => { + setEditDialog(prev => ({ + ...prev, + content: prev.defaultContent + })); + }; + + // ======= 工具函数 ======= + + // 检查提示词是否已自定义 + const isCustomized = promptKey => { + if (!selectedCategory || !promptKey || !templates[selectedCategory]) return false; + + const language = getLanguageFromPromptKey(promptKey); + const promptType = templates[selectedCategory]?.prompts?.[promptKey]?.type; + + if (!promptType) return false; + + return customPrompts.some(p => p.promptType === promptType && p.promptKey === promptKey && p.language === language); + }; + + // 获取当前提示词内容(直接从服务器获取最新数据) + const getCurrentPromptContent = async (promptKey, forceRefresh = false) => { + if (!selectedCategory || !promptKey || !templates[selectedCategory]) return ''; + + const language = getLanguageFromPromptKey(promptKey); + const promptType = templates[selectedCategory]?.prompts?.[promptKey]?.type; + + if (!promptType) { + return ''; + } + + // 如果需要强制刷新,直接从服务器获取 + if (forceRefresh) { + try { + const response = await fetchWithRetry( + `/api/projects/${projectId}/custom-prompts?promptType=${promptType}&language=${language}` + ); + const data = await response.json(); + + if (data.success) { + const existingPrompt = data.customPrompts.find( + p => p.promptType === promptType && p.promptKey === promptKey && p.language === language + ); + + if (existingPrompt) { + return existingPrompt.content; + } + } + } catch (error) { + console.error(t('settings.prompts.fetchContentError'), error); + } + } else { + // 使用缓存的状态 + const existingPrompt = customPrompts.find( + p => p.promptType === promptType && p.promptKey === promptKey && p.language === language + ); + + if (existingPrompt) { + return existingPrompt.content; + } + } + + // 回退到默认内容 + return await loadDefaultContent(promptType, promptKey); + }; + + // ======= 数据准备 ======= + + // 当前分类的配置 + const currentCategoryConfig = templates[selectedCategory]; + + // 当前提示词的配置 + const currentPromptConfig = currentCategoryConfig?.prompts?.[selectedPrompt]; + + // 分类配置项 + const categoryEntries = Object.entries(templates); + + // 处理分类变更 + const handleCategoryChange = newCategory => { + setSelectedCategory(newCategory); + // 根据当前语言环境选择第一个匹配的提示词 + const promptEntries = Object.keys(templates[newCategory]?.prompts || {}); + console.log('所有提示词:', promptEntries); + + const firstPrompt = promptEntries.find(promptKey => shouldShowPrompt(promptKey, currentLanguage)); + + setSelectedPrompt(firstPrompt); + }; + + // 处理编辑按钮点击 + const handleEditButtonClick = () => { + const promptType = templates[selectedCategory]?.prompts?.[selectedPrompt]?.type; + // 使用当前界面语言而不是从 promptKey 推断的语言 + const language = currentLanguage; + + if (promptType) { + handleEditPrompt(promptType, selectedPrompt, language); + } + }; + + // 处理删除按钮点击 + const handleDeleteButtonClick = () => { + const promptType = templates[selectedCategory]?.prompts?.[selectedPrompt]?.type; + // 使用当前界面语言而不是从 promptKey 推断的语言 + const language = currentLanguage; + + if (promptType) { + handleDeletePrompt(promptType, selectedPrompt, language); + } + }; + + // 处理对话框内容变更 + const handleDialogContentChange = newContent => { + setEditDialog({ ...editDialog, content: newContent }); + }; + + return ( + + + + {/* 主要分类选择 */} + + + {/* 左右布局:左侧垂直提示词选择,右侧内容展示 */} + + {/* 左侧:垂直 TAB 选择具体提示词 */} + + + + + + + + + {/* 右侧:提示词内容展示和操作 */} + + + + + + {/* 编辑提示词对话框 */} + setEditDialog({ ...editDialog, open: false })} + onSave={handleSavePrompt} + onRestore={handleRestoreDefault} + onContentChange={handleDialogContentChange} + /> + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/settings/components/promptUtils.js b/easy-dataset-main/app/projects/[projectId]/settings/components/promptUtils.js new file mode 100644 index 0000000..b188de4 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/settings/components/promptUtils.js @@ -0,0 +1,34 @@ +/** + * 提示词设置相关工具函数 + */ + +/** + * 从提示词键名解析语言 + * @param {string} promptKey 提示词键名 + * @returns {string} 语言代码 ('zh-CN' 或 'en') + */ +export const getLanguageFromPromptKey = promptKey => { + return promptKey?.endsWith('_EN') ? 'en' : 'zh-CN'; +}; + +/** + * 判断是否应该显示当前提示词(基于语言) + * @param {string} promptKey 提示词键名 + * @param {string} currentLanguage 当前界面语言 + * @returns {boolean} 是否应该显示 + */ +export const shouldShowPrompt = (promptKey, currentLanguage) => { + const promptLang = getLanguageFromPromptKey(promptKey); + return promptLang === currentLanguage; +}; + +/** + * 构建提示词标题显示组件 + * @param {Object} options 配置项 + * @param {string} options.name 提示词名称 + * @param {boolean} options.customized 是否已自定义 + * @returns {Object} 包含名称和自定义标记的显示配置 + */ +export const buildPromptTitle = ({ name, customized }) => { + return { name, customized }; +}; diff --git a/easy-dataset-main/app/projects/[projectId]/settings/page.js b/easy-dataset-main/app/projects/[projectId]/settings/page.js new file mode 100644 index 0000000..ebd193a --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/settings/page.js @@ -0,0 +1,125 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { Container, Typography, Box, Tabs, Tab, Paper, Alert, CircularProgress } from '@mui/material'; +import { useSearchParams, useRouter } from 'next/navigation'; +import { useTranslation } from 'react-i18next'; + +// 导入设置组件 +import BasicSettings from '@/components/settings/BasicSettings'; +import ModelSettings from '@/components/settings/ModelSettings'; +import TaskSettings from '@/components/settings/TaskSettings'; +import PromptSettings from './components/PromptSettings'; + +// 定义 TAB 枚举 +const TABS = { + BASIC: 'basic', + MODEL: 'model', + TASK: 'task', + PROMPTS: 'prompts' +}; + +export default function SettingsPage({ params }) { + const { t } = useTranslation(); + const { projectId } = params; + const searchParams = useSearchParams(); + const router = useRouter(); + const [activeTab, setActiveTab] = useState(TABS.BASIC); + const [projectExists, setProjectExists] = useState(true); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + // 从 URL hash 中获取当前 tab + useEffect(() => { + const tab = searchParams.get('tab'); + if (tab && Object.values(TABS).includes(tab)) { + setActiveTab(tab); + } + }, [searchParams]); + + // 检查项目是否存在 + useEffect(() => { + async function checkProject() { + try { + setLoading(true); + const response = await fetch(`/api/projects/${projectId}`); + + if (!response.ok) { + if (response.status === 404) { + setProjectExists(false); + } else { + throw new Error(t('projects.fetchFailed')); + } + } else { + setProjectExists(true); + } + } catch (error) { + console.error('获取项目详情出错:', error); + setError(error.message); + } finally { + setLoading(false); + } + } + + checkProject(); + }, [projectId, t]); + + // 处理 tab 切换 + const handleTabChange = (event, newValue) => { + setActiveTab(newValue); + // 更新 URL hash + router.push(`/projects/${projectId}/settings?tab=${newValue}`); + }; + + if (loading) { + return ( + + + + ); + } + + if (!projectExists) { + return ( + + {t('projects.notExist')} + + ); + } + + if (error) { + return ( + + {error} + + ); + } + + return ( + + + + + + + + + + + {activeTab === TABS.BASIC && } + + {activeTab === TABS.MODEL && } + + {activeTab === TABS.TASK && } + + {activeTab === TABS.PROMPTS && } + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/tasks/page.js b/easy-dataset-main/app/projects/[projectId]/tasks/page.js new file mode 100644 index 0000000..5933377 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/tasks/page.js @@ -0,0 +1,175 @@ +'use client'; + +import React, { useState, useEffect } from 'react'; +import { Box, Typography, Container, LinearProgress, Paper } from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import axios from 'axios'; +import TaskIcon from '@mui/icons-material/Task'; +import { toast } from 'sonner'; + +import TaskFilters from '@/components/tasks/TaskFilters'; +import TasksTable from '@/components/tasks/TasksTable'; + +export default function TasksPage({ params }) { + const { projectId } = params; + const { t } = useTranslation(); + + const [loading, setLoading] = useState(false); + const [tasks, setTasks] = useState([]); + const [statusFilter, setStatusFilter] = useState('all'); + const [typeFilter, setTypeFilter] = useState('all'); + const [page, setPage] = useState(0); + const [rowsPerPage, setRowsPerPage] = useState(10); + const [totalCount, setTotalCount] = useState(0); + + const processingTasks = tasks.filter(task => task.status === 0 && task.totalCount > 0); + const totalProgressCount = processingTasks.reduce((sum, task) => sum + task.totalCount, 0); + const completedProgressCount = processingTasks.reduce((sum, task) => sum + task.completedCount, 0); + const overallProgress = totalProgressCount > 0 ? Math.round((completedProgressCount / totalProgressCount) * 100) : 0; + + const fetchTasks = async () => { + if (!projectId) return; + + try { + setLoading(true); + let url = `/api/projects/${projectId}/tasks/list`; + const queryParams = []; + + if (statusFilter !== 'all') { + queryParams.push(`status=${statusFilter}`); + } + + if (typeFilter !== 'all') { + queryParams.push(`taskType=${typeFilter}`); + } + + queryParams.push(`page=${page}`); + queryParams.push(`limit=${rowsPerPage}`); + + if (queryParams.length > 0) { + url += `?${queryParams.join('&')}`; + } + + const response = await axios.get(url); + if (response.data?.code === 0) { + setTasks(response.data.data || []); + setTotalCount(response.data.total || response.data.data?.length || 0); + } + } catch (error) { + console.error('Failed to fetch tasks:', error); + toast.error(t('tasks.fetchFailed')); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + fetchTasks(); + + const intervalId = setInterval(() => { + if (statusFilter === 'all' || statusFilter === '0') { + fetchTasks(); + } + }, 5000); + + return () => clearInterval(intervalId); + }, [projectId, statusFilter, typeFilter, page, rowsPerPage]); + + const handleDeleteTask = async taskId => { + if (!confirm(t('tasks.confirmDelete'))) return; + + try { + const response = await axios.delete(`/api/projects/${projectId}/tasks/${taskId}`); + if (response.data?.code === 0) { + toast.success(t('tasks.deleteSuccess')); + fetchTasks(); + } else { + toast.error(t('tasks.deleteFailed')); + } + } catch (error) { + console.error('Failed to delete task:', error); + toast.error(t('tasks.deleteFailed')); + } + }; + + const handleAbortTask = async taskId => { + if (!confirm(t('tasks.confirmAbort'))) return; + + try { + const response = await axios.patch(`/api/projects/${projectId}/tasks/${taskId}`, { + status: 3, + note: t('tasks.status.aborted') + }); + + if (response.data?.code === 0) { + toast.success(t('tasks.abortSuccess')); + fetchTasks(); + } else { + toast.error(t('tasks.abortFailed')); + } + } catch (error) { + console.error('Failed to abort task:', error); + toast.error(t('tasks.abortFailed')); + } + }; + + const handleChangePage = (event, newPage) => { + setPage(newPage); + }; + + const handleChangeRowsPerPage = event => { + setRowsPerPage(parseInt(event.target.value, 10)); + setPage(0); + }; + + return ( + + + + + {t('tasks.title')} + + + + + + {processingTasks.length > 0 && ( + + + {t('tasks.pending', { count: processingTasks.length })} - {completedProgressCount}/{totalProgressCount} ( + {overallProgress}%) + + + + )} + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/text-split/page.js b/easy-dataset-main/app/projects/[projectId]/text-split/page.js new file mode 100644 index 0000000..a30c640 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/text-split/page.js @@ -0,0 +1,440 @@ +'use client'; + +import axios from 'axios'; +import { useState, useEffect, useRef } from 'react'; +import { useTranslation } from 'react-i18next'; +import { + Container, + Box, + Tabs, + Tab, + IconButton, + Collapse, + Dialog, + DialogContent, + DialogTitle, + Typography, + LinearProgress, + CircularProgress +} from '@mui/material'; +import { useTheme } from '@mui/material/styles'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import ExpandLessIcon from '@mui/icons-material/ExpandLess'; +import FullscreenIcon from '@mui/icons-material/Fullscreen'; +import CloseIcon from '@mui/icons-material/Close'; +import FileUploader from '@/components/text-split/FileUploader'; +import FileList from '@/components/text-split/components/FileList'; +import DeleteConfirmDialog from '@/components/text-split/components/DeleteConfirmDialog'; +import PdfSettings from '@/components/text-split/PdfSettings'; +import ChunkList from '@/components/text-split/ChunkList'; +import DomainAnalysis from '@/components/text-split/DomainAnalysis'; +import useTaskSettings from '@/hooks/useTaskSettings'; +import { useAtomValue } from 'jotai/index'; +import { selectedModelInfoAtom } from '@/lib/store'; +import useChunks from './useChunks'; +import useQuestionGeneration from './useQuestionGeneration'; +import useDataCleaning from './useDataCleaning'; +import useEvalGeneration from './useEvalGeneration'; +import useFileProcessing from './useFileProcessing'; +import useFileProcessingStatus from '@/hooks/useFileProcessingStatus'; +import { toast } from 'sonner'; + +export default function TextSplitPage({ params }) { + const { t } = useTranslation(); + const theme = useTheme(); + const { projectId } = params; + const [activeTab, setActiveTab] = useState(0); + const [renderedTab, setRenderedTab] = useState(0); + const [tabSwitching, setTabSwitching] = useState(false); + const tabSwitchTimerRef = useRef(null); + const { taskSettings } = useTaskSettings(projectId); + const [pdfStrategy, setPdfStrategy] = useState('default'); + const [questionFilter, setQuestionFilter] = useState('all'); // 'all', 'generated', 'ungenerated' + const [selectedViosnModel, setSelectedViosnModel] = useState(''); + const selectedModelInfo = useAtomValue(selectedModelInfoAtom); + const { taskFileProcessing, task } = useFileProcessingStatus(); + const [currentPage, setCurrentPage] = useState(1); + const [uploadedFiles, setUploadedFiles] = useState({ data: [], total: 0 }); + const [searchFileName, setSearchFileName] = useState(''); + const [showLoadingBar, setShowLoadingBar] = useState(false); + + // 娑撳﹣绱堕崠鍝勭厵閻ㄥ嫬鐫嶅鈧?閹舵ê褰旈悩鑸碘偓? + const [uploaderExpanded, setUploaderExpanded] = useState(true); + + // 閺傚洨灏為崚妤勩€?FileList)鐏炴洜銇氱€电鐦藉鍡欏Ц閹? + const [fileListDialogOpen, setFileListDialogOpen] = useState(false); + + // 娴h法鏁ら懛顏勭暰娑斿“ooks + const { chunks, tocData, loading, fetchChunks, handleDeleteChunk, handleEditChunk, updateChunks, setLoading } = + useChunks(projectId, questionFilter); + + // 閼惧嘲褰囬弬鍥︽閸掓銆? + const fetchUploadedFiles = async (page = currentPage, fileName = searchFileName) => { + try { + setLoading(true); + const params = new URLSearchParams({ + page: page.toString(), + size: '10' + }); + + if (fileName && fileName.trim()) { + params.append('fileName', fileName.trim()); + } + + const response = await axios.get(`/api/projects/${projectId}/files?${params}`); + setUploadedFiles(response.data); + } catch (error) { + console.error('Error fetching files:', error); + toast.error(error.message || '閼惧嘲褰囬弬鍥︽閸掓銆冩径杈Е'); + } finally { + setLoading(false); + } + }; + + // 閸掔娀娅庨弬鍥︽绾喛顓荤€电鐦藉鍡欏Ц閹? + const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false); + const [fileToDelete, setFileToDelete] = useState(null); + + // 閹垫挸绱戦崚鐘绘珟绾喛顓荤€电鐦藉? + const openDeleteConfirm = (fileId, fileName) => { + setFileToDelete({ fileId, fileName }); + setDeleteConfirmOpen(true); + }; + + // 閸忔娊妫撮崚鐘绘珟绾喛顓荤€电鐦藉? + const closeDeleteConfirm = () => { + setDeleteConfirmOpen(false); + setFileToDelete(null); + }; + + // 绾喛顓婚崚鐘绘珟閺傚洣娆? + const confirmDeleteFile = async () => { + if (!fileToDelete) return; + + try { + setLoading(true); + closeDeleteConfirm(); + + await axios.delete(`/api/projects/${projectId}/files/${fileToDelete.fileId}`); + await fetchUploadedFiles(); + fetchChunks(); + + toast.success( + t('textSplit.deleteSuccess', { fileName: fileToDelete.fileName }) || `删除 ${fileToDelete.fileName} 成功` + ); + } catch (error) { + console.error('删除文件出错:', error); + toast.error(error.message || '删除文件失败'); + } finally { + setLoading(false); + setFileToDelete(null); + } + }; + + const { handleGenerateQuestions } = useQuestionGeneration(projectId, taskSettings); + const { handleDataCleaning } = useDataCleaning(projectId, taskSettings); + const { handleGenerateEvalQuestions } = useEvalGeneration(projectId); + const { handleFileProcessing } = useFileProcessing(projectId); + + // 文本块数据刷新:初始化 + 文件处理任务状态变化 + useEffect(() => { + fetchChunks('all'); + }, [fetchChunks, taskFileProcessing]); + + // 文件列表刷新:文件分页、搜索关键词变化时触发 + useEffect(() => { + fetchUploadedFiles(currentPage, searchFileName); + }, [projectId, currentPage, searchFileName]); + + useEffect(() => { + let timerId; + if (loading) { + timerId = setTimeout(() => setShowLoadingBar(true), 180); + } else { + setShowLoadingBar(false); + } + return () => { + if (timerId) clearTimeout(timerId); + }; + }, [loading]); + + useEffect(() => { + return () => { + if (tabSwitchTimerRef.current) { + clearTimeout(tabSwitchTimerRef.current); + } + }; + }, []); + + const handleTabChange = (event, newValue) => { + if (newValue === activeTab) return; + + setActiveTab(newValue); + setTabSwitching(true); + + if (tabSwitchTimerRef.current) { + clearTimeout(tabSwitchTimerRef.current); + } + + const switchContent = () => { + setRenderedTab(newValue); + tabSwitchTimerRef.current = null; + if (typeof window !== 'undefined') { + window.requestAnimationFrame(() => setTabSwitching(false)); + } else { + setTabSwitching(false); + } + }; + + if (typeof window !== 'undefined') { + window.requestAnimationFrame(() => { + tabSwitchTimerRef.current = setTimeout(switchContent, 80); + }); + } else { + switchContent(); + } + }; + + /** + * 鐎甸€涚瑐娴肩姴鎮楅惃鍕瀮娴犳儼绻樼悰灞筋槱閻? + */ + const handleUploadSuccess = async (fileNames, pdfFiles, domainTreeAction) => { + try { + await handleFileProcessing(fileNames, pdfStrategy, selectedViosnModel, domainTreeAction); + location.reload(); + } catch (error) { + toast.error('File upload failed' + error.message || ''); + } + }; + + // 閸栧懓顥婇悽鐔稿灇闂傤噣顣介惃鍕槱閻炲棗鍤遍弫? + const onGenerateQuestions = async chunkIds => { + await handleGenerateQuestions(chunkIds, selectedModelInfo, fetchChunks); + }; + + // 閸栧懓顥婇弫鐗堝祦濞撳懏绀傞惃鍕槱閻炲棗鍤遍弫? + const onDataCleaning = async chunkIds => { + await handleDataCleaning(chunkIds, selectedModelInfo, fetchChunks); + }; + + // 閸栧懓顥婇悽鐔稿灇濞村鐦庢0妯兼窗閻ㄥ嫬顦╅悶鍡楀毐閺? + const onGenerateEvalQuestions = async chunkId => { + await handleGenerateEvalQuestions(chunkId, selectedModelInfo, () => { + // 閹存劕濮涢崥搴″煕閺傛澘鍨悰? + fetchChunks(); + }); + }; + + useEffect(() => { + const url = new URL(window.location.href); + if (questionFilter !== 'all') { + url.searchParams.set('filter', questionFilter); + } else { + url.searchParams.delete('filter'); + } + window.history.replaceState({}, '', url); + fetchChunks(questionFilter); + }, [questionFilter]); + + const handleSelected = array => { + if (array.length > 0) { + axios.post(`/api/projects/${projectId}/chunks`, { array }).then(response => { + updateChunks(response.data); + }); + } else { + fetchChunks(); + } + }; + + return ( + + {/* 閺傚洣娆㈡稉濠佺炊缂佸嫪娆?*/} + + + setUploaderExpanded(!uploaderExpanded)} + sx={{ + bgcolor: 'background.paper', + boxShadow: 1, + mr: uploaderExpanded ? 1 : 0 // 鐏炴洖绱戦弮鑸靛瘻闁筋喕绠i梻瀵告殌閻愬綊妫跨捄? + }} + size="small" + > + {uploaderExpanded ? : } + + + {/* 閺傚洨灏為崚妤勩€冮幍鈺佺潔閹稿鎸抽敍灞肩矌閸︺劋绗傞柈銊ュ隘閸╃喎鐫嶅鈧弮鑸垫▔缁€?*/} + {uploaderExpanded && ( + setFileListDialogOpen(true)} + sx={{ bgcolor: 'background.paper', boxShadow: 1 }} + size="small" + title={t('textSplit.expandFileList') || '扩展文件列表'} + > + + + )} + + + + + + + + + {/* 閺嶅洨顒锋い?*/} + + + + + + + + + {/* 閺呴缚鍏橀崚鍡楀閺嶅洨顒烽崘鍛啇 */} + {tabSwitching ? ( + + + + {t('common.loading')} + + + ) : ( + <> + {renderedTab === 0 && ( + + )} + + {renderedTab === 1 && } + + )} + + + {/* 閸旂姾娴囨稉顓℃寢閻?*/} + {showLoadingBar && ( + + + + {t('textSplit.loading')} + + + + + )} + + {/* 婢跺嫮鎮婃稉顓℃寢閻?*/} + + {/* 閺佺増宓佸〒鍛鏉╂稑瀹抽拏娆戝 */} + + {/* 閺傚洣娆㈡径鍕倞鏉╂稑瀹抽拏娆戝 */} + + {/* 閺傚洣娆㈤崚鐘绘珟绾喛顓荤€电鐦藉?*/} + + + {/* 閺傚洨灏為崚妤勩€冪€电鐦藉?*/} + setFileListDialogOpen(false)} + maxWidth="lg" + fullWidth + sx={{ '& .MuiDialog-paper': { bgcolor: 'background.default' } }} + > + + {t('textSplit.fileList')} + setFileListDialogOpen(false)} aria-label="close"> + + + + + {/* 濮濄倕顦╂径宥囨暏 FileUploader 缂佸嫪娆㈡稉顓犳畱 FileList 闁劌鍨?*/} + + {/* 閺傚洣娆㈤崚妤勩€冮崘鍛啇 */} + handleSelected(array)} + onDeleteFile={(fileId, fileName) => openDeleteConfirm(fileId, fileName)} + projectId={projectId} + currentPage={currentPage} + onPageChange={(page, fileName) => { + if (fileName !== undefined) { + // 閹兼粎鍌ㄩ弮鑸垫纯閺傜増鎮崇槐銏犲彠闁款喛鐦濋崪宀勩€夐惍? + setSearchFileName(fileName); + setCurrentPage(page); + } else { + // 缂堝銆夐弮璺哄涧閺囧瓨鏌婃い鐢电垳 + setCurrentPage(page); + } + }} + onRefresh={fetchUploadedFiles} // 娴肩娀鈧帒鍩涢弬鏉垮毐閺? + isFullscreen={true} // 閸︺劌顕拠婵囶攱娑擃厾些闂勩倝鐝惔锕傛閸? + /> + + + + + ); +} diff --git a/easy-dataset-main/app/projects/[projectId]/text-split/useChunks.js b/easy-dataset-main/app/projects/[projectId]/text-split/useChunks.js new file mode 100644 index 0000000..81a5b1f --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/text-split/useChunks.js @@ -0,0 +1,162 @@ +'use client'; + +import { useState, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { toast } from 'sonner'; + +/** + * 文本块管理的自定义Hook + * @param {string} projectId - 项目ID + * @param {string} [currentFilter='all'] - 当前筛选条件 + * @returns {Object} - 文本块状态和操作方法 + */ +export default function useChunks(projectId, currentFilter = 'all') { + const { t } = useTranslation(); + const [chunks, setChunks] = useState([]); + const [tocData, setTocData] = useState(''); + const [loading, setLoading] = useState(false); + + /** + * 获取文本块列表 + * @param {string} filter - 筛选条件 + */ + const fetchChunks = useCallback( + async (filter = 'all') => { + try { + setLoading(true); + const response = await fetch(`/api/projects/${projectId}/split?filter=${filter}`); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.error || t('textSplit.fetchChunksFailed')); + } + + const data = await response.json(); + setChunks(data.chunks || []); + + // 如果有文件结果,处理详细信息 + if (data.toc) { + console.log(t('textSplit.fileResultReceived'), data.fileResult); + // 如果有目录结构,设置目录数据 + setTocData(data.toc); + } + } catch (error) { + toast.error(error.message); + } finally { + setLoading(false); + } + }, + [projectId, t, setLoading, setChunks, setTocData] + ); + + /** + * 处理删除文本块 + * @param {string} chunkId - 文本块ID + */ + const handleDeleteChunk = useCallback( + async chunkId => { + try { + const response = await fetch(`/api/projects/${projectId}/chunks/${encodeURIComponent(chunkId)}`, { + method: 'DELETE' + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.error || t('textSplit.deleteChunkFailed')); + } + + // 更新文本块列表 + setChunks(prev => prev.filter(chunk => chunk.id !== chunkId)); + } catch (error) { + toast.error(error.message); + } + }, + [projectId, t] + ); + + /** + * 处理文本块编辑 + * @param {string} chunkId - 文本块ID + * @param {string} newContent - 新内容 + */ + const handleEditChunk = useCallback( + async (chunkId, newContent) => { + try { + setLoading(true); + + const response = await fetch(`/api/projects/${projectId}/chunks/${encodeURIComponent(chunkId)}`, { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ content: newContent }) + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.error || t('textSplit.editChunkFailed')); + } + + // 更新成功后使用当前筛选条件刷新文本块列表 + // 直接从 URL 获取当前筛选参数,确保获取到的是最新的值 + const url = new URL(window.location.href); + const filterParam = url.searchParams.get('filter') || 'all'; + await fetchChunks(filterParam); + + toast.success(t('textSplit.editChunkSuccess')); + } catch (error) { + toast.error(error.message); + } finally { + setLoading(false); + } + }, + [projectId, t, fetchChunks] + ); + + /** + * 设置文本块列表 + * @param {Array} data - 新的文本块列表 + */ + const updateChunks = useCallback(data => { + setChunks(data); + }, []); + + /** + * 添加新的文本块 + * @param {Array} newChunks - 新的文本块列表 + */ + const addChunks = useCallback(newChunks => { + setChunks(prev => { + const updatedChunks = [...prev]; + newChunks.forEach(chunk => { + if (!updatedChunks.find(c => c.id === chunk.id)) { + updatedChunks.push(chunk); + } + }); + return updatedChunks; + }); + }, []); + + /** + * 设置TOC数据 + * @param {string} toc - TOC数据 + */ + const updateTocData = useCallback(toc => { + if (toc) { + setTocData(toc); + } + }, []); + + return { + chunks, + tocData, + loading, + fetchChunks, + handleDeleteChunk, + handleEditChunk, + updateChunks, + addChunks, + updateTocData, + setLoading + }; +} diff --git a/easy-dataset-main/app/projects/[projectId]/text-split/useDataCleaning.js b/easy-dataset-main/app/projects/[projectId]/text-split/useDataCleaning.js new file mode 100644 index 0000000..2d4daa5 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/text-split/useDataCleaning.js @@ -0,0 +1,116 @@ +'use client'; + +import { useState, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import i18n from '@/lib/i18n'; +import request from '@/lib/util/request'; +import { toast } from 'sonner'; + +export default function useDataCleaning(projectId) { + const { t } = useTranslation(); + const [processing, setProcessing] = useState(false); + const [progress, setProgress] = useState({ + total: 0, + completed: 0, + percentage: 0, + cleanedCount: 0 + }); + + const resetProgress = useCallback(() => { + setTimeout(() => { + setProgress({ + total: 0, + completed: 0, + percentage: 0, + cleanedCount: 0 + }); + }, 500); + }, []); + + const handleDataCleaning = useCallback( + async (chunkIds, selectedModelInfo, fetchChunks) => { + try { + if (!chunkIds || chunkIds.length === 0) return; + + if (!selectedModelInfo) { + throw new Error(t('textSplit.selectModelFirst')); + } + + setProcessing(true); + + if (chunkIds.length === 1) { + const chunkId = chunkIds[0]; + const currentLanguage = i18n.language === 'zh-CN' ? '中文' : 'en'; + + const response = await request(`/api/projects/${projectId}/chunks/${chunkId}/clean`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + model: selectedModelInfo, + language: currentLanguage + }) + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.error || t('textSplit.dataCleaningFailed', { chunkId })); + } + + const data = await response.json(); + toast.success( + t('textSplit.dataCleaningSuccess', { + originalLength: data.originalLength, + cleanedLength: data.cleanedLength + }) + ); + + if (fetchChunks) fetchChunks(); + return; + } + + const response = await request(`/api/projects/${projectId}/tasks`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + taskType: 'data-cleaning', + modelInfo: selectedModelInfo, + language: i18n.language, + detail: '批量数据清洗任务', + note: { chunkIds } + }) + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.error || t('tasks.createFailed')); + } + + const data = await response.json(); + if (data?.code !== 0) { + throw new Error(data?.message || t('tasks.createFailed')); + } + + toast.success(`${t('tasks.createSuccess')},${t('tasks.title')}查看进度`); + } catch (error) { + toast.error(error.message); + } finally { + setProcessing(false); + resetProgress(); + } + }, + [projectId, t, resetProgress] + ); + + return { + processing, + progress, + setProgress, + setProcessing, + handleDataCleaning, + resetProgress + }; +} diff --git a/easy-dataset-main/app/projects/[projectId]/text-split/useEvalGeneration.js b/easy-dataset-main/app/projects/[projectId]/text-split/useEvalGeneration.js new file mode 100644 index 0000000..4ae9928 --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/text-split/useEvalGeneration.js @@ -0,0 +1,91 @@ +'use client'; + +import { useState, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import i18n from '@/lib/i18n'; +import request from '@/lib/util/request'; +import { toast } from 'sonner'; + +/** + * 测评题目生成的自定义Hook + * @param {string} projectId - 项目ID + * @returns {Object} - 测评题目生成状态和操作方法 + */ +export default function useEvalGeneration(projectId) { + const { t } = useTranslation(); + const [generating, setGenerating] = useState({}); + + /** + * 为单个文本块生成测评题目 + * @param {string} chunkId - 文本块ID + * @param {Object} selectedModelInfo - 选定的模型信息 + * @param {Function} onSuccess - 成功回调 + */ + const handleGenerateEvalQuestions = useCallback( + async (chunkId, selectedModelInfo, onSuccess) => { + try { + // 检查模型信息 + if (!selectedModelInfo) { + throw new Error(t('textSplit.selectModelFirst')); + } + + // 设置生成状态 + setGenerating(prev => ({ ...prev, [chunkId]: true })); + + // 获取当前语言环境 + const currentLanguage = i18n.language === 'zh-CN' ? 'zh-CN' : 'en'; + + // 调用API生成测评题目 + const response = await request( + `/api/projects/${projectId}/chunks/${encodeURIComponent(chunkId)}/eval-questions`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + model: selectedModelInfo, + language: currentLanguage + }) + } + ); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.error || t('textSplit.generateEvalQuestionsFailed')); + } + + const data = await response.json(); + + // 显示成功消息 + toast.success( + t('textSplit.evalQuestionsGeneratedSuccess', { + total: data.total, + defaultValue: `成功生成 ${data.total} 道测评题目` + }) + ); + + // 调用成功回调 + if (onSuccess) { + onSuccess(data); + } + } catch (error) { + console.error('Error generating eval questions:', error); + toast.error(error.message || t('textSplit.generateEvalQuestionsFailed')); + } finally { + // 清除生成状态 + setGenerating(prev => { + const newState = { ...prev }; + delete newState[chunkId]; + return newState; + }); + } + }, + [projectId, t] + ); + + return { + generating, + handleGenerateEvalQuestions + }; +} diff --git a/easy-dataset-main/app/projects/[projectId]/text-split/useFileProcessing.js b/easy-dataset-main/app/projects/[projectId]/text-split/useFileProcessing.js new file mode 100644 index 0000000..20bd5df --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/text-split/useFileProcessing.js @@ -0,0 +1,91 @@ +'use client'; + +import { useState, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { selectedModelInfoAtom } from '@/lib/store'; +import { useAtomValue } from 'jotai/index'; +import { toast } from 'sonner'; +import i18n from '@/lib/i18n'; +import axios from 'axios'; + +/** + * 文件处理的自定义Hook + * @param {string} projectId - 项目ID + * @returns {Object} - 文件处理状态和操作方法 + */ +export default function useFileProcessing(projectId) { + const { t } = useTranslation(); + const [fileProcessing, setFileProcessing] = useState(false); + const [progress, setProgress] = useState({ + total: 0, + completed: 0, + percentage: 0, + questionCount: 0 + }); + const model = useAtomValue(selectedModelInfoAtom); + + /** + * 重置进度状态 + */ + const resetProgress = useCallback(() => { + setTimeout(() => { + setProgress({ + total: 0, + completed: 0, + percentage: 0, + questionCount: 0 + }); + }, 1000); // 延迟重置,让用户看到完成的进度 + }, []); + + /** + * 处理文件 + * @param {Array} files - 文件列表 + * @param {string} pdfStrategy - PDF处理策略 + * @param {string} selectedViosnModel - 选定的视觉模型 + */ + const handleFileProcessing = useCallback( + async (files, pdfStrategy, selectedViosnModel, domainTreeAction) => { + try { + const currentLanguage = i18n.language === 'zh-CN' ? '中文' : 'en'; + + //获取到视觉策略要使用的模型 + const availableModels = JSON.parse(localStorage.getItem('modelConfigList')); + const vsionModel = availableModels.find(m => m.id === selectedViosnModel); + + const response = await axios.post(`/api/projects/${projectId}/tasks`, { + taskType: 'file-processing', + modelInfo: model, + language: currentLanguage, + detail: '文件处理任务', + note: { + vsionModel, + projectId, + fileList: files, + strategy: pdfStrategy, + domainTreeAction + } + }); + + if (response.data?.code !== 0) { + throw new Error(t('textSplit.pdfProcessingFailed') + (response.data?.error || '')); + } + + //提示后台任务进行中 + toast.success(t('textSplit.pdfProcessingToast')); + } catch (error) { + toast.error(t('textSplit.pdfProcessingFailed') + error.message || ''); + } + }, + [projectId, t, resetProgress, model] + ); + + return { + fileProcessing, + progress, + setFileProcessing, + setProgress, + handleFileProcessing, + resetProgress + }; +} diff --git a/easy-dataset-main/app/projects/[projectId]/text-split/useQuestionGeneration.js b/easy-dataset-main/app/projects/[projectId]/text-split/useQuestionGeneration.js new file mode 100644 index 0000000..8daee2e --- /dev/null +++ b/easy-dataset-main/app/projects/[projectId]/text-split/useQuestionGeneration.js @@ -0,0 +1,116 @@ +'use client'; + +import { useState, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import i18n from '@/lib/i18n'; +import request from '@/lib/util/request'; +import { toast } from 'sonner'; + +export default function useQuestionGeneration(projectId) { + const { t } = useTranslation(); + const [processing, setProcessing] = useState(false); + const [progress, setProgress] = useState({ + total: 0, + completed: 0, + percentage: 0, + questionCount: 0 + }); + + const resetProgress = useCallback(() => { + setTimeout(() => { + setProgress({ + total: 0, + completed: 0, + percentage: 0, + questionCount: 0 + }); + }, 500); + }, []); + + const handleGenerateQuestions = useCallback( + async (chunkIds, selectedModelInfo, fetchChunks) => { + try { + if (!chunkIds || chunkIds.length === 0) return; + + if (!selectedModelInfo) { + throw new Error(t('textSplit.selectModelFirst')); + } + + setProcessing(true); + + if (chunkIds.length === 1) { + const chunkId = chunkIds[0]; + const currentLanguage = i18n.language === 'zh-CN' ? '中文' : 'en'; + + const response = await request(`/api/projects/${projectId}/chunks/${chunkId}/questions`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + model: selectedModelInfo, + language: currentLanguage, + enableGaExpansion: true + }) + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.error || t('textSplit.generateQuestionsFailed', { chunkId })); + } + + const data = await response.json(); + toast.success( + t('textSplit.questionsGeneratedSuccess', { + total: data.total + }) + ); + + if (fetchChunks) fetchChunks(); + return; + } + + const response = await request(`/api/projects/${projectId}/tasks`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + taskType: 'question-generation', + modelInfo: selectedModelInfo, + language: i18n.language, + detail: '批量生成问题任务', + note: { chunkIds } + }) + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.error || t('tasks.createFailed')); + } + + const data = await response.json(); + if (data?.code !== 0) { + throw new Error(data?.message || t('tasks.createFailed')); + } + + toast.success(`${t('tasks.createSuccess')},${t('tasks.title')}查看进度`); + } catch (error) { + toast.error(error.message); + } finally { + setProcessing(false); + resetProgress(); + } + }, + [projectId, t, resetProgress] + ); + + return { + processing, + progress, + setProgress, + setProcessing, + handleGenerateQuestions, + resetProgress + }; +} diff --git a/easy-dataset-main/commitlint.config.mjs b/easy-dataset-main/commitlint.config.mjs new file mode 100644 index 0000000..3f5e287 --- /dev/null +++ b/easy-dataset-main/commitlint.config.mjs @@ -0,0 +1 @@ +export default { extends: ['@commitlint/config-conventional'] }; diff --git a/easy-dataset-main/components/ExportDatasetDialog.js b/easy-dataset-main/components/ExportDatasetDialog.js new file mode 100644 index 0000000..88639a5 --- /dev/null +++ b/easy-dataset-main/components/ExportDatasetDialog.js @@ -0,0 +1,226 @@ +// ExportDatasetDialog.js 组件 +import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Dialog, DialogTitle, DialogContent, DialogActions, Button, Box, Tabs, Tab } from '@mui/material'; + +// 导入拆分后的组件 +import LocalExportTab from './export/LocalExportTab'; +import LlamaFactoryTab from './export/LlamaFactoryTab'; +import HuggingFaceTab from './export/HuggingFaceTab'; + +const ExportDatasetDialog = ({ open, onClose, onExport, projectId }) => { + const { t } = useTranslation(); + const [formatType, setFormatType] = useState('alpaca'); + const [systemPrompt, setSystemPrompt] = useState(''); + const [reasoningLanguage, setReasoningLanguage] = useState(''); + const [confirmedOnly, setConfirmedOnly] = useState(false); + const [fileFormat, setFileFormat] = useState('json'); + const [includeCOT, setIncludeCOT] = useState(true); + const [currentTab, setCurrentTab] = useState(0); + // alpaca 格式特有的设置 + const [alpacaFieldType, setAlpacaFieldType] = useState('instruction'); // 'instruction' 或 'input' + const [customInstruction, setCustomInstruction] = useState(''); // 当选择 input 时使用的自定义 instruction + const [customFields, setCustomFields] = useState({ + questionField: 'instruction', + answerField: 'output', + cotField: 'complexCOT', // 添加思维链字段名 + includeLabels: false, + includeChunk: false, // 添加是否包含chunk字段 + questionOnly: false // 添加仅导出问题选项 + }); + + const handleFileFormatChange = event => { + setFileFormat(event.target.value); + }; + + const handleFormatChange = event => { + setFormatType(event.target.value); + // 根据格式类型设置默认字段名 + if (event.target.value === 'alpaca') { + setCustomFields({ + ...customFields, + questionField: 'instruction', + answerField: 'output' + }); + } else if (event.target.value === 'sharegpt') { + setCustomFields({ + ...customFields, + questionField: 'content', + answerField: 'content' + }); + } else if (event.target.value === 'multilingual-thinking') { + setCustomFields({ + ...customFields, + questionField: 'content', + answerField: 'content' + }); + } else if (event.target.value === 'custom') { + // 自定义格式保持当前值 + } + }; + + const handleSystemPromptChange = event => { + setSystemPrompt(event.target.value); + }; + + const handleReasoningLanguageChange = event => { + setReasoningLanguage(event.target.value); + }; + const handleConfirmedOnlyChange = event => { + setConfirmedOnly(event.target.checked); + }; + + // 新增处理函数 + const handleIncludeCOTChange = event => { + setIncludeCOT(event.target.checked); + }; + + const handleCustomFieldChange = field => event => { + setCustomFields({ + ...customFields, + [field]: event.target.value + }); + }; + + const handleIncludeLabelsChange = event => { + setCustomFields({ + ...customFields, + includeLabels: event.target.checked + }); + }; + + const handleIncludeChunkChange = event => { + setCustomFields({ + ...customFields, + includeChunk: event.target.checked + }); + }; + + const handleQuestionOnlyChange = event => { + setCustomFields({ + ...customFields, + questionOnly: event.target.checked + }); + }; + + // 处理 Alpaca 字段类型变更 + const handleAlpacaFieldTypeChange = event => { + setAlpacaFieldType(event.target.value); + }; + + // 处理自定义 instruction 变更 + const handleCustomInstructionChange = event => { + setCustomInstruction(event.target.value); + }; + + const handleExport = options => { + // 如果 LocalExportTab 传入了完整的导出配置(例如平衡导出),直接使用该配置 + if (options && typeof options === 'object' && options.balanceMode) { + onExport(options); + return; + } + + // 否则使用当前对话框内的状态组装导出配置 + onExport({ + formatType, + systemPrompt, + reasoningLanguage, + confirmedOnly, + fileFormat, + includeCOT, + alpacaFieldType, // 添加 alpaca 字段类型 + customInstruction, // 添加自定义 instruction + customFields: formatType === 'custom' ? customFields : undefined + }); + }; + + return ( + + {t('export.title')} + + + setCurrentTab(newValue)} aria-label="export tabs"> + + + + + + + {/* 第一个标签页:本地导出 */} + {currentTab === 0 && ( + + )} + + {/* 第二个标签页:Llama Factory */} + {currentTab === 1 && ( + + )} + + {/* 第三个标签页:HuggingFace */} + {currentTab === 2 && ( + + )} + + + ); +}; + +export default ExportDatasetDialog; diff --git a/easy-dataset-main/components/ExportProgressDialog.js b/easy-dataset-main/components/ExportProgressDialog.js new file mode 100644 index 0000000..db80bfc --- /dev/null +++ b/easy-dataset-main/components/ExportProgressDialog.js @@ -0,0 +1,104 @@ +'use client'; + +import React from 'react'; +import { Dialog, DialogTitle, DialogContent, Box, LinearProgress, Typography, CircularProgress } from '@mui/material'; +import { useTranslation } from 'react-i18next'; + +const ExportProgressDialog = ({ open, progress }) => { + const { t } = useTranslation(); + + const { processed, total, hasMore } = progress; + + // 计算进度百分比 + const percentage = total > 0 ? Math.round((processed / total) * 100) : 0; + + return ( + + {t('datasets.exportProgress')} + + + + {/* 圆形进度指示器 */} + + + + + {`${percentage}%`} + + + + + {/* 进度详情 */} + + + {t('datasets.exportingData')} + + + + {t('datasets.processedCount', { processed, total })} + + + {/* 线性进度条 */} + + + + {/* 状态提示 */} + + {hasMore ? t('datasets.exportInProgress') : t('datasets.exportFinalizing')} + + + + + ); +}; + +export default ExportProgressDialog; diff --git a/easy-dataset-main/components/I18nProvider.js b/easy-dataset-main/components/I18nProvider.js new file mode 100644 index 0000000..d253537 --- /dev/null +++ b/easy-dataset-main/components/I18nProvider.js @@ -0,0 +1,16 @@ +'use client'; + +import { useEffect } from 'react'; +import i18n from '@/lib/i18n'; +import { I18nextProvider } from 'react-i18next'; + +export default function I18nProvider({ children }) { + useEffect(() => { + // 确保i18n只在客户端初始化 + if (typeof window !== 'undefined') { + // 这里可以添加任何客户端特定的i18n初始化逻辑 + } + }, []); + + return {children}; +} diff --git a/easy-dataset-main/components/LanguageSwitcher.js b/easy-dataset-main/components/LanguageSwitcher.js new file mode 100644 index 0000000..3336a5b --- /dev/null +++ b/easy-dataset-main/components/LanguageSwitcher.js @@ -0,0 +1,88 @@ +'use client'; + +import { useTranslation } from 'react-i18next'; +import { IconButton, Menu, MenuItem, Tooltip, useTheme, Typography } from '@mui/material'; +import { useState } from 'react'; +import TranslateIcon from '@mui/icons-material/Translate'; + +export default function LanguageSwitcher() { + const { i18n, t } = useTranslation(); + const theme = useTheme(); + const [anchorEl, setAnchorEl] = useState(null); + const open = Boolean(anchorEl); + + const languages = [ + { code: 'en', label: t('language.english', 'English'), short: 'EN' }, + { code: 'zh-CN', label: t('language.chineseSimplified', '简体中文'), short: '中文' }, + { code: 'tr', label: t('language.turkish', 'Türkçe'), short: 'TR' }, + { code: 'pt-BR', label: t('language.portugues', 'Portugues'), short: 'pt-BR' } + ]; + + const normalizedCurrentLanguage = + i18n.language && String(i18n.language).toLowerCase().startsWith('zh') ? 'zh-CN' : i18n.language; + const currentLanguage = languages.find(lang => lang.code === normalizedCurrentLanguage) || languages[0]; + + const handleClick = event => { + setAnchorEl(event.currentTarget); + }; + + const handleClose = () => { + setAnchorEl(null); + }; + + const handleLanguageChange = langCode => { + i18n.changeLanguage(langCode); + handleClose(); + }; + + return ( + <> + + + + {currentLanguage.short} + + + + + + {languages.map(lang => ( + handleLanguageChange(lang.code)} + selected={normalizedCurrentLanguage === lang.code} + > + + {lang.short} + + {lang.label} + + ))} + + + ); +} diff --git a/easy-dataset-main/components/ModelSelect.js b/easy-dataset-main/components/ModelSelect.js new file mode 100644 index 0000000..e554a85 --- /dev/null +++ b/easy-dataset-main/components/ModelSelect.js @@ -0,0 +1,346 @@ +'use client'; + +import React, { useEffect, useState, useMemo } from 'react'; +import { FormControl, Select, MenuItem, useTheme, ListSubheader, Box, IconButton, Tooltip } from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import { useAtom, useAtomValue } from 'jotai/index'; +import { modelConfigListAtom, selectedModelInfoAtom } from '@/lib/store'; +import axios from 'axios'; +import SmartToyIcon from '@mui/icons-material/SmartToy'; +import { getModelIcon } from '@/lib/util/modelIcon'; + +export default function ModelSelect({ + size = 'small', + minWidth = 50, + projectId, + minHeight = 36, + required = false, + onError +}) { + const theme = useTheme(); + const { t } = useTranslation(); + const models = useAtomValue(modelConfigListAtom); + const [selectedModelInfo, setSelectedModelInfo] = useAtom(selectedModelInfoAtom); + const [selectedModel, setSelectedModel] = useState(() => { + if (selectedModelInfo && selectedModelInfo.id) { + return selectedModelInfo.id; + } + return ''; + }); + const [error, setError] = useState(false); + const [isHovered, setIsHovered] = useState(false); + const [isOpen, setIsOpen] = useState(false); + + const handleModelChange = event => { + if (!event || !event.target) return; + const newModelId = event.target.value; + + if (error) { + setError(false); + if (onError) onError(false); + } + + if (!newModelId) { + setSelectedModel(''); + setSelectedModelInfo(null); + updateDefaultModel(null); + } else { + const selectedModelObj = models.find(model => model.id === newModelId); + if (selectedModelObj) { + setSelectedModel(newModelId); + setSelectedModelInfo(selectedModelObj); + updateDefaultModel(newModelId); + } else { + setSelectedModel(newModelId); + setSelectedModelInfo({ id: newModelId }); + } + } + + setTimeout(() => { + setIsHovered(false); + setIsOpen(false); + }, 200); + }; + + const updateDefaultModel = async id => { + const res = await axios.put(`/api/projects/${projectId}`, { projectId, defaultModelConfigId: id }); + if (res.status === 200) { + console.log('更新成功'); + } + }; + + const validateModel = () => { + if (required && (!selectedModel || selectedModel === '')) { + setError(true); + if (onError) onError(true); + return false; + } + return true; + }; + + useEffect(() => { + if (selectedModelInfo && selectedModelInfo.id) { + setSelectedModel(selectedModelInfo.id); + } else { + setSelectedModel(''); + } + }, [selectedModelInfo]); + + useEffect(() => { + if (required) { + validateModel(); + } + }, [required]); + + const renderSelectedValue = value => { + if (!value) { + return ( + + + {t('models.unselectedModel', t('playground.selectModelFirst'))} + + ); + } + + const selectedModelObj = models.find(model => model.id === value); + if (!selectedModelObj) return null; + + return ( + + { + e.target.src = '/imgs/models/default.svg'; + }} + /> + {selectedModelObj.modelName} + + ); + }; + + const currentModelIcon = useMemo(() => { + const selectedModelObj = models.find(model => model.id === selectedModel); + return selectedModelObj ? getModelIcon(selectedModelObj.modelName, selectedModelObj.modelId) : null; + }, [selectedModel, models]); + + const shouldShowFullSelect = isHovered || isOpen; + + return ( + setIsHovered(true)} + onMouseLeave={() => { + setIsHovered(false); + if (!isOpen) { + setIsOpen(false); + } + }} + sx={{ + position: 'relative', + display: 'flex', + alignItems: 'center' + }} + > + {!shouldShowFullSelect && ( + m.id === selectedModel)?.modelName + : t('playground.selectModelFirst', '请先选择模型') + } + placement="bottom" + > + + {currentModelIcon ? ( + { + e.target.src = '/imgs/models/default.svg'; + }} + /> + ) : ( + + )} + + + )} + + + + + + ); +} diff --git a/easy-dataset-main/components/Navbar/ActionButtons.js b/easy-dataset-main/components/Navbar/ActionButtons.js new file mode 100644 index 0000000..5184a9d --- /dev/null +++ b/easy-dataset-main/components/Navbar/ActionButtons.js @@ -0,0 +1,112 @@ +'use client'; + +import React from 'react'; +import { Box, IconButton, Tooltip } from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import LightModeOutlinedIcon from '@mui/icons-material/LightModeOutlined'; +import DarkModeOutlinedIcon from '@mui/icons-material/DarkModeOutlined'; +import HelpOutlineIcon from '@mui/icons-material/HelpOutline'; +import GitHubIcon from '@mui/icons-material/GitHub'; +import BarChartIcon from '@mui/icons-material/BarChart'; +import LanguageSwitcher from '../LanguageSwitcher'; +import UpdateChecker from '../UpdateChecker'; +import TaskIcon from '../TaskIcon'; +import ModelSelect from '../ModelSelect'; +import * as styles from './styles'; + +/** + * ActionButtons 组件 + * 右侧操作区按钮:语言切换、主题切换、文档、GitHub、更新检查 + */ +export default function ActionButtons({ + theme, + resolvedTheme, + toggleTheme, + isProjectDetail, + currentProject, + onActionAreaEnter +}) { + const { t, i18n } = useTranslation(); + const isZhLanguage = String(i18n.language || '') + .toLowerCase() + .startsWith('zh'); + + return ( + + {isProjectDetail && } + {isProjectDetail && } + + {/* Monitoring Dashboard - Only visible on Home page */} + {!isProjectDetail && ( + + + + + + )} + + {/* Language Switcher - Always visible */} + + + {/* Theme Toggle - Always visible */} + + + {resolvedTheme === 'dark' ? ( + + ) : ( + + )} + + + + {/* Documentation - Hide below xl */} + + + + + + + {/* GitHub - Hide at larger tablet screens and below */} + + + + + + + {/* Update Checker - Hide below xl */} + + + + + ); +} diff --git a/easy-dataset-main/components/Navbar/ContextBar.js b/easy-dataset-main/components/Navbar/ContextBar.js new file mode 100644 index 0000000..196f1d3 --- /dev/null +++ b/easy-dataset-main/components/Navbar/ContextBar.js @@ -0,0 +1,175 @@ +'use client'; + +import React, { useState } from 'react'; +import { + Box, + Chip, + Typography, + useTheme, + Menu, + MenuItem, + ListItemIcon, + ListItemText, + Paper, + Tooltip +} from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import { useRouter } from 'next/navigation'; +import { useSetAtom } from 'jotai'; +import { modelConfigListAtom, selectedModelInfoAtom } from '@/lib/store'; +import { toast } from 'sonner'; +import axios from 'axios'; + +// Icons +import FolderIcon from '@mui/icons-material/Folder'; +import CheckIcon from '@mui/icons-material/Check'; +import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; + +// 样式 +import * as styles from './contextBarStyles'; + +export default function ContextBar({ projects = [], currentProjectId, onMouseLeave }) { + const { t } = useTranslation(); + const theme = useTheme(); + const router = useRouter(); + + // State + const [projectMenuAnchor, setProjectMenuAnchor] = useState(null); + + // Jotai atoms + const setConfigList = useSetAtom(modelConfigListAtom); + const setSelectedModelInfo = useSetAtom(selectedModelInfoAtom); + + // Get current project + const currentProject = projects.find(p => p.id === currentProjectId); + + // Handlers + const handleProjectMenuOpen = event => { + event.preventDefault(); + setProjectMenuAnchor(event.currentTarget); + }; + + const handleProjectMenuClose = () => { + setProjectMenuAnchor(null); + // 菜单关闭时,如果提供了 onMouseLeave 回调,则调用它 + if (onMouseLeave) { + onMouseLeave(); + } + }; + + const handleProjectChange = async newProjectId => { + handleProjectMenuClose(); + + try { + // Fetch model config for new project + const response = await axios.get(`/api/projects/${newProjectId}/model-config`); + setConfigList(response.data.data); + + if (response.data.defaultModelConfigId) { + const defaultModel = response.data.data.find(item => item.id === response.data.defaultModelConfigId); + setSelectedModelInfo(defaultModel || null); + } else { + setSelectedModelInfo(null); + } + + // Navigate to the new project's text-split page + router.push(`/projects/${newProjectId}/text-split`); + } catch (error) { + console.error('Error switching project:', error); + toast.error(t('common.error', 'Error switching project')); + } + }; + + if (!currentProjectId || !currentProject) { + return null; + } + + return ( + + + {/* Project Selector */} + + + {t('common.project', 'Project')}: + + + } + label={ + + + {currentProject?.name || t('projects.selectProject', 'Select Project')} + + + + } + onClick={handleProjectMenuOpen} + clickable + variant="outlined" + size="medium" + sx={styles.getProjectChipStyles(theme)} + aria-label={t('projects.selectProject', 'Select project')} + aria-controls={projectMenuAnchor ? 'project-menu' : undefined} + aria-haspopup="true" + aria-expanded={Boolean(projectMenuAnchor)} + /> + + + + + {/* Project Menu */} + + + {t('projects.allProjects', 'All Projects')} + + {projects.map((project, index) => ( + handleProjectChange(project.id)} + selected={project.id === currentProjectId} + role="menuitem" + sx={styles.getMenuItemStyles(theme)} + > + + {project.id === currentProjectId ? ( + + ) : ( + + )} + + + + ))} + + + ); +} diff --git a/easy-dataset-main/components/Navbar/DesktopMenus.js b/easy-dataset-main/components/Navbar/DesktopMenus.js new file mode 100644 index 0000000..2f0f967 --- /dev/null +++ b/easy-dataset-main/components/Navbar/DesktopMenus.js @@ -0,0 +1,315 @@ +'use client'; + +import React from 'react'; +import { Menu, MenuItem, ListItemIcon, ListItemText, Divider } from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import Link from 'next/link'; +import DescriptionOutlinedIcon from '@mui/icons-material/DescriptionOutlined'; +import ImageIcon from '@mui/icons-material/Image'; +import DatasetOutlinedIcon from '@mui/icons-material/DatasetOutlined'; +import ChatIcon from '@mui/icons-material/Chat'; +import SettingsOutlinedIcon from '@mui/icons-material/SettingsOutlined'; +import ScienceOutlinedIcon from '@mui/icons-material/ScienceOutlined'; +import StorageIcon from '@mui/icons-material/Storage'; +import AssessmentOutlinedIcon from '@mui/icons-material/AssessmentOutlined'; +import PlaylistPlayIcon from '@mui/icons-material/PlaylistPlay'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import * as styles from './styles'; + +/** + * DesktopMenus 缂備礁瀚▎? + * 婵℃鐭傚鎵博椤栨稑浜鹃柛瀣矎瑜板秹宕¢弴顏嗙闁告牕鎳庨幆鍫ュ极閻楀牆绁︽繝褎鍔戦埀顑跨劍閺嗙喖骞戦鈧▔锔剧不閿涘嫭鍊為柕鍡曠劍濞叉寧寰勫顐ょ憦濞戞搩浜hぐ宥夊础? + */ +export default function DesktopMenus({ + theme, + menuState, + isMenuOpen, + handleMenuClose, + currentProject, + onNavigateStart +}) { + const { t } = useTranslation(); + + return ( + <> + {/* 闁轰胶澧楀畵浣糕攦閹邦垰缍呴柛?*/} + + { + onNavigateStart?.(); + handleMenuClose(); + }} + role="menuitem" + sx={styles.getMenuItemStyles(theme)} + > + + + + + + + { + onNavigateStart?.(); + handleMenuClose(); + }} + role="menuitem" + sx={styles.getMenuItemStyles(theme)} + > + + + + + + + + {/* 闁轰胶澧楀畵渚€姊块崱娆樺悁闁荤偛妫滆ぐ宥夊础?*/} + + { + onNavigateStart?.(); + handleMenuClose(); + }} + sx={styles.getSimpleMenuItemStyles(theme)} + > + + + + + + + { + onNavigateStart?.(); + handleMenuClose(); + }} + sx={styles.getSimpleMenuItemStyles(theme)} + > + + + + + + + { + onNavigateStart?.(); + handleMenuClose(); + }} + sx={styles.getSimpleMenuItemStyles(theme)} + > + + + + + + + + {/* 閻犲洤瀚崣濠囨嚕濠婂啫绀?*/} + + { + onNavigateStart?.(); + handleMenuClose(); + }} + sx={styles.getSimpleMenuItemStyles(theme)} + > + + + + + + + { + onNavigateStart?.(); + handleMenuClose(); + }} + sx={styles.getSimpleMenuItemStyles(theme)} + > + + + + + + + { + onNavigateStart?.(); + handleMenuClose(); + }} + sx={styles.getSimpleMenuItemStyles(theme)} + > + + + + + + + + {/* 闁哄洦娼欓ˇ鍧楁嚕濠婂啫绀?*/} + + { + onNavigateStart?.(); + handleMenuClose(); + }} + sx={styles.getSimpleMenuItemStyles(theme)} + > + + + + + + + { + onNavigateStart?.(); + handleMenuClose(); + }} + sx={styles.getSimpleMenuItemStyles(theme)} + > + + + + + + + { + onNavigateStart?.(); + handleMenuClose(); + }} + sx={styles.getSimpleMenuItemStyles(theme)} + > + + + + + + + + ); +} diff --git a/easy-dataset-main/components/Navbar/Logo.js b/easy-dataset-main/components/Navbar/Logo.js new file mode 100644 index 0000000..7fc8e30 --- /dev/null +++ b/easy-dataset-main/components/Navbar/Logo.js @@ -0,0 +1,42 @@ +'use client'; + +import React from 'react'; +import { Box, Typography, Tooltip } from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import * as styles from './styles'; + +/** + * Logo 组件 + * 显示应用 Logo 和标题,支持点击跳转到首页 + */ +export default function Logo({ theme }) { + const { t } = useTranslation(); + + return ( + + { + e.preventDefault(); + window.location.href = '/'; + }} + onKeyDown={e => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + window.location.href = '/'; + } + }} + > + + + Easy DataSet + + + + ); +} diff --git a/easy-dataset-main/components/Navbar/MobileDrawer.js b/easy-dataset-main/components/Navbar/MobileDrawer.js new file mode 100644 index 0000000..b8cd3b0 --- /dev/null +++ b/easy-dataset-main/components/Navbar/MobileDrawer.js @@ -0,0 +1,405 @@ +'use client'; + +import React from 'react'; +import { + Drawer, + Box, + Typography, + IconButton, + Tooltip, + List, + ListItem, + ListItemButton, + ListItemIcon, + ListItemText, + Collapse +} from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import Link from 'next/link'; +import CloseIcon from '@mui/icons-material/Close'; +import DescriptionOutlinedIcon from '@mui/icons-material/DescriptionOutlined'; +import TokenOutlinedIcon from '@mui/icons-material/TokenOutlined'; +import QuestionAnswerOutlinedIcon from '@mui/icons-material/QuestionAnswerOutlined'; +import DatasetOutlinedIcon from '@mui/icons-material/DatasetOutlined'; +import SettingsOutlinedIcon from '@mui/icons-material/SettingsOutlined'; +import ScienceOutlinedIcon from '@mui/icons-material/ScienceOutlined'; +import MoreVertIcon from '@mui/icons-material/MoreVert'; +import ChatIcon from '@mui/icons-material/Chat'; +import ImageIcon from '@mui/icons-material/Image'; +import StorageIcon from '@mui/icons-material/Storage'; +import HelpOutlineIcon from '@mui/icons-material/HelpOutline'; +import GitHubIcon from '@mui/icons-material/GitHub'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import ExpandLessIcon from '@mui/icons-material/ExpandLess'; +import AssessmentOutlinedIcon from '@mui/icons-material/AssessmentOutlined'; +import PlaylistPlayIcon from '@mui/icons-material/PlaylistPlay'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import UpdateChecker from '../UpdateChecker'; +import * as styles from './styles'; + +/** + * MobileDrawer 组件 + * 移动端抽屉菜单,包含所有导航项 + */ +export default function MobileDrawer({ + theme, + drawerOpen, + toggleDrawer, + expandedMenu, + toggleMobileSubmenu, + currentProject, + onNavigateStart +}) { + const { t, i18n } = useTranslation(); + const handleNavigateStart = () => { + onNavigateStart?.(); + toggleDrawer(); + }; + + return ( + + {/* Drawer Header */} + + + + + {t('common.navigation', 'Navigation')} + + + + + + + + + + {/* Drawer Menu List */} + + {/* 数据源菜单 */} + + toggleMobileSubmenu('source')} + aria-expanded={expandedMenu === 'source'} + aria-controls="source-submenu" + role="menuitem" + sx={styles.getDrawerListItemButtonStyles(theme)} + > + + + + + {expandedMenu === 'source' ? : } + + + + + + + + + + + + + + + + + + + + {/* 数据蒸馏 */} + + + + + + + + + + {/* 问题管理 */} + + + + + + + + + + {/* 数据集管理 */} + + toggleMobileSubmenu('dataset')} + role="menuitem" + aria-expanded={expandedMenu === 'dataset'} + aria-controls="dataset-submenu" + sx={styles.getDrawerListItemButtonStyles(theme)} + > + + + + + {expandedMenu === 'dataset' ? : } + + + + + + + + + + + + + + + + + + + + + + + + + + {/* 评估菜单 */} + + toggleMobileSubmenu('eval')} + role="menuitem" + aria-expanded={expandedMenu === 'eval'} + aria-controls="eval-submenu" + sx={styles.getDrawerListItemButtonStyles(theme)} + > + + + + + {expandedMenu === 'eval' ? : } + + + + + + + + + + + + + + + + + + + + + + + + + + {/* 更多菜单 */} + + toggleMobileSubmenu('more')} + role="menuitem" + aria-expanded={expandedMenu === 'more'} + aria-controls="more-submenu" + sx={styles.getDrawerListItemButtonStyles(theme)} + > + + + + + {expandedMenu === 'more' ? : } + + + + + + + + + + + + + + + + + + + + + + + + + + {/* Utilities Section */} + + + + + + + + + + + + { + window.open('https://github.com/ConardLi/easy-dataset', '_blank'); + toggleDrawer(); + }} + sx={styles.getDrawerListItemButtonStyles(theme)} + > + + + + + + + + + + + + + + + + ); +} diff --git a/easy-dataset-main/components/Navbar/NavigationTabs.js b/easy-dataset-main/components/Navbar/NavigationTabs.js new file mode 100644 index 0000000..252b79d --- /dev/null +++ b/easy-dataset-main/components/Navbar/NavigationTabs.js @@ -0,0 +1,139 @@ +'use client'; + +import React from 'react'; +import { Box, Tabs, Tab } from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import Link from 'next/link'; +import DescriptionOutlinedIcon from '@mui/icons-material/DescriptionOutlined'; +import TokenOutlinedIcon from '@mui/icons-material/TokenOutlined'; +import QuestionAnswerOutlinedIcon from '@mui/icons-material/QuestionAnswerOutlined'; +import DatasetOutlinedIcon from '@mui/icons-material/DatasetOutlined'; +import AssessmentOutlinedIcon from '@mui/icons-material/AssessmentOutlined'; +import MoreVertIcon from '@mui/icons-material/MoreVert'; +import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; +import * as styles from './styles'; + +/** + * NavigationTabs 组件 + * 桌面端导航 Tabs,包含数据源、数据蒸馏、问题管理、数据集管理、更多等 Tab + */ +export default function NavigationTabs({ + theme, + pathname, + currentProject, + handleMenuOpen, + handleMenuClose, + onNavigateStart +}) { + const { t } = useTranslation(); + + // 计算当前 Tab 值 + const getCurrentTabValue = () => { + if (pathname.includes('/settings') || pathname.includes('/playground') || pathname.includes('/datasets-sq')) { + return 'more'; + } + if (pathname.includes('/eval-datasets') || pathname.includes('/eval-tasks')) { + return 'eval'; + } + if (pathname.includes('/datasets') || pathname.includes('/multi-turn') || pathname.includes('/image-datasets')) { + return 'datasets'; + } + if (pathname.includes('/text-split') || pathname.includes('/images')) { + return 'source'; + } + return pathname; + }; + + return ( + + + } + iconPosition="start" + label={ + + {t('common.dataSource')} + + + } + value="source" + onMouseEnter={e => handleMenuOpen(e, 'source')} + sx={styles.tabIconWrapperStyles} + /> + } + iconPosition="start" + label={t('distill.title')} + value={`/projects/${currentProject}/distill`} + component={Link} + href={`/projects/${currentProject}/distill`} + onClick={() => { + onNavigateStart?.(); + handleMenuClose(); + }} + sx={styles.tabIconWrapperStyles} + /> + } + iconPosition="start" + label={t('questions.title')} + value={`/projects/${currentProject}/questions`} + component={Link} + href={`/projects/${currentProject}/questions`} + onClick={() => { + onNavigateStart?.(); + handleMenuClose(); + }} + sx={styles.tabIconWrapperStyles} + /> + } + iconPosition="start" + label={ + + {t('datasets.management')} + + + } + value="datasets" + onMouseEnter={e => handleMenuOpen(e, 'dataset')} + sx={styles.tabIconWrapperStyles} + /> + } + iconPosition="start" + label={ + + {t('eval.title')} + + + } + value="eval" + onMouseEnter={e => handleMenuOpen(e, 'eval')} + sx={styles.tabIconWrapperStyles} + /> + } + iconPosition="start" + label={ + + {t('common.more')} + + + } + onMouseEnter={e => handleMenuOpen(e, 'more')} + value="more" + sx={styles.tabIconWrapperStyles} + /> + + + ); +} diff --git a/easy-dataset-main/components/Navbar/contextBarStyles.js b/easy-dataset-main/components/Navbar/contextBarStyles.js new file mode 100644 index 0000000..dda7467 --- /dev/null +++ b/easy-dataset-main/components/Navbar/contextBarStyles.js @@ -0,0 +1,247 @@ +/** + * ContextBar 组件样式 + * 包含项目选择器和模型选择器的所有样式 + */ + +import { alpha } from '@mui/material'; + +// ===== 主容器样式 ===== +export const getContextBarPaperStyles = theme => ({ + position: 'absolute', + top: 64, // Below navbar + left: 0, + zIndex: 1100, + borderBottom: 1, + borderColor: 'divider', + bgcolor: + theme.palette.mode === 'dark' + ? alpha(theme.palette.background.paper, 0.9) + : alpha(theme.palette.background.paper, 0.95), + backdropFilter: 'blur(16px)', + WebkitBackdropFilter: 'blur(16px)', + px: { xs: 2, sm: 3, md: 4 }, + py: { xs: 1.25, sm: 1.5 }, + transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)', + boxShadow: theme.palette.mode === 'dark' ? '0 1px 3px rgba(0, 0, 0, 0.2)' : '0 1px 3px rgba(0, 0, 0, 0.08)', + width: 'auto' +}); + +export const contextBarContainerStyles = { + display: 'flex', + alignItems: 'center', + gap: { xs: 1, sm: 1.5, md: 2 }, + flexWrap: 'nowrap', + width: 'auto' +}; + +// ===== 选择器容器样式 ===== +export const selectorContainerStyles = { + display: 'flex', + alignItems: 'center', + gap: 1 +}; + +// ===== 标签样式 ===== +export const labelTypographyStyles = { + color: 'text.secondary', + fontWeight: 600, + textTransform: 'uppercase', + letterSpacing: '0.5px', + fontSize: '0.7rem', + display: { xs: 'none', sm: 'block' } +}; + +// ===== Chip 内部文本样式 ===== +export const chipLabelBoxStyles = { + display: 'flex', + alignItems: 'center', + gap: 0.5 +}; + +export const chipTextStyles = { + fontWeight: 600, + fontSize: { xs: '0.8rem', sm: '0.875rem' }, + maxWidth: { xs: '80px', sm: '120px', md: '150px' }, + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis' +}; + +export const chipArrowStyles = { + ml: -0.25, + flexShrink: 0 +}; + +// ===== 项目选择器 Chip 样式 ===== +export const getProjectChipStyles = theme => ({ + minWidth: 'auto', + maxWidth: { xs: '120px', sm: '150px', md: '180px' }, + height: { xs: 32, sm: 36 }, + minWidth: { xs: 120, sm: 150, md: 180 }, + maxWidth: { xs: '120px', sm: '150px', md: '180px' }, + borderRadius: 1.5, + borderColor: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.23)' : 'rgba(0, 0, 0, 0.23)', + bgcolor: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.05)' : 'rgba(0, 0, 0, 0.02)', + transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)', + '&:hover': { + borderColor: 'primary.main', + bgcolor: theme.palette.mode === 'dark' ? 'rgba(144, 202, 249, 0.08)' : 'rgba(25, 118, 210, 0.04)', + transform: 'translateY(-1px)', + boxShadow: + theme.palette.mode === 'dark' ? '0 4px 12px rgba(144, 202, 249, 0.15)' : '0 4px 12px rgba(25, 118, 210, 0.15)' + }, + '&:active': { + transform: 'translateY(0)' + }, + '&:focus-visible': { + outline: `2px solid ${theme.palette.primary.main}`, + outlineOffset: 2 + }, + '& .MuiChip-icon': { + color: 'text.primary', + fontSize: '1.1rem', + ml: 0.5, + flexShrink: 0 + }, + '& .MuiChip-label': { + px: 1, + overflow: 'hidden' + } +}); + +// ===== 模型选择器 Chip 样式 ===== +export const getModelChipStyles = theme => ({ + minWidth: { xs: 140, sm: 160, md: 180 }, + maxWidth: { xs: 200, sm: 280, md: 360 }, + height: { xs: 36, sm: 40 }, + borderRadius: 2, + bgcolor: theme.palette.mode === 'dark' ? 'rgba(144, 202, 249, 0.08)' : 'rgba(25, 118, 210, 0.04)', + transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)', + '&:hover': { + bgcolor: theme.palette.mode === 'dark' ? 'rgba(144, 202, 249, 0.15)' : 'rgba(25, 118, 210, 0.08)', + transform: 'translateY(-1px)', + boxShadow: + theme.palette.mode === 'dark' ? '0 4px 12px rgba(144, 202, 249, 0.25)' : '0 4px 12px rgba(25, 118, 210, 0.25)' + }, + '&:active': { + transform: 'translateY(0)' + }, + '&:focus-visible': { + outline: `2px solid ${theme.palette.primary.main}`, + outlineOffset: 2 + }, + '& .MuiChip-icon': { + color: 'primary.main', + fontSize: '1.1rem', + ml: 0.5, + flexShrink: 0 + }, + '& .MuiChip-label': { + px: 1, + overflow: 'hidden' + } +}); + +// ===== 菜单样式 ===== +export const getMenuPaperStyles = theme => ({ + mt: 1, + minWidth: 240, + maxWidth: 400, + maxHeight: 400, + borderRadius: 2, + overflow: 'visible', + bgcolor: theme.palette.mode === 'dark' ? 'background.paper' : 'background.paper', + backdropFilter: 'blur(20px)', + WebkitBackdropFilter: 'blur(20px)', + boxShadow: + theme.palette.mode === 'dark' + ? '0 12px 40px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.1)' + : '0 12px 40px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(0, 0, 0, 0.05)', + '&::before': { + content: '""', + display: 'block', + position: 'absolute', + top: -6, + left: 24, + width: 12, + height: 12, + bgcolor: 'background.paper', + transform: 'translateY(-50%) rotate(45deg)', + zIndex: 0, + borderLeft: `1px solid ${theme.palette.divider}`, + borderTop: `1px solid ${theme.palette.divider}` + } +}); + +export const menuListPropsStyles = { + dense: false, + sx: { py: 1 } +}; + +// ===== 菜单标题样式 ===== +export const menuHeaderTypographyStyles = { + px: 2, + py: 1, + display: 'block', + color: 'text.secondary', + fontWeight: 600, + textTransform: 'uppercase', + letterSpacing: '0.5px', + fontSize: '0.7rem' +}; + +// ===== 菜单项样式 ===== +export const getMenuItemStyles = theme => ({ + mx: 1, + borderRadius: 1.5, + minHeight: 44, + py: 1.25, + px: 1.5, + transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)', + '&:hover': { + bgcolor: theme.palette.mode === 'dark' ? 'rgba(144, 202, 249, 0.08)' : 'rgba(25, 118, 210, 0.04)', + transform: 'translateX(4px)' + }, + '&.Mui-selected': { + bgcolor: theme.palette.mode === 'dark' ? 'rgba(144, 202, 249, 0.16)' : 'rgba(25, 118, 210, 0.08)', + '&:hover': { + bgcolor: theme.palette.mode === 'dark' ? 'rgba(144, 202, 249, 0.24)' : 'rgba(25, 118, 210, 0.12)' + } + } +}); + +export const menuItemIconStyles = { + minWidth: 36 +}; + +export const getMenuItemTextPrimaryProps = isSelected => ({ + variant: 'body2', + fontWeight: isSelected ? 600 : 400 +}); + +export const menuItemTextSecondaryProps = { + variant: 'caption', + sx: { fontSize: '0.7rem' } +}; + +// ===== 模型图标样式 ===== +export const modelIconStyles = { + width: 20, + height: 20, + objectFit: 'contain', + flexShrink: 0, + borderRadius: '50%', + mr: 1 +}; + +// ===== 分组标题样式 ===== +export const getProviderHeaderStyles = theme => ({ + pl: 2, + color: theme.palette.text.secondary, + fontWeight: 600, + fontSize: '0.75rem', + textTransform: 'uppercase', + letterSpacing: '0.5px', + mt: 1, + mb: 0.5 +}); diff --git a/easy-dataset-main/components/Navbar/index.js b/easy-dataset-main/components/Navbar/index.js new file mode 100644 index 0000000..b6c52c2 --- /dev/null +++ b/easy-dataset-main/components/Navbar/index.js @@ -0,0 +1,257 @@ +'use client'; + +import React, { useState, useRef, useEffect } from 'react'; +import { + AppBar, + Toolbar, + Box, + IconButton, + useTheme as useMuiTheme, + Tooltip, + useMediaQuery, + LinearProgress +} from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import { usePathname, useRouter } from 'next/navigation'; +import { useTheme } from 'next-themes'; +import MenuIcon from '@mui/icons-material/Menu'; + +// 样式 +import * as styles from './styles'; + +// 子组件 +import Logo from './Logo'; +import ActionButtons from './ActionButtons'; +import NavigationTabs from './NavigationTabs'; +import MobileDrawer from './MobileDrawer'; +import DesktopMenus from './DesktopMenus'; +import ContextBar from './ContextBar'; + +export default function Navbar({ projects = [], currentProject }) { + const { t } = useTranslation(); + const pathname = usePathname(); + const router = useRouter(); + const theme = useMuiTheme(); + const { resolvedTheme, setTheme } = useTheme(); + const isProjectDetail = pathname.includes('/projects/') && pathname.split('/').length > 3; + + // 检测移动设备 + const isMobile = useMediaQuery(theme.breakpoints.down('lg')); + + // 移动端抽屉状态 + const [drawerOpen, setDrawerOpen] = useState(false); + const [expandedMenu, setExpandedMenu] = useState(null); + + // 桌面端菜单状态 + const [menuState, setMenuState] = useState({ anchorEl: null, menuType: null }); + const [navLoading, setNavLoading] = useState(false); + const navLoadingTimeoutRef = useRef(null); + + // ContextBar 悬浮状态 + const [contextBarHovered, setContextBarHovered] = useState(false); + const contextTriggerRef = useRef(null); + const contextBarRef = useRef(null); + + useEffect(() => { + if (!contextBarHovered) return; + + const handleOutsideClick = event => { + if (contextBarRef.current?.contains(event.target)) return; + if (contextTriggerRef.current?.contains(event.target)) return; + const projectMenuEl = document.getElementById('project-menu'); + if (projectMenuEl?.contains(event.target)) return; + setContextBarHovered(false); + }; + + document.addEventListener('pointerdown', handleOutsideClick, true); + return () => { + document.removeEventListener('pointerdown', handleOutsideClick, true); + }; + }, [contextBarHovered]); + + useEffect(() => { + if (!menuState.menuType) return; + + const handleOutsideMenuClick = event => { + if (menuState.anchorEl?.contains(event.target)) return; + if (event.target?.closest?.('.MuiMenu-root')) return; + setMenuState({ anchorEl: null, menuType: null }); + }; + + document.addEventListener('pointerdown', handleOutsideMenuClick, true); + return () => { + document.removeEventListener('pointerdown', handleOutsideMenuClick, true); + }; + }, [menuState.anchorEl, menuState.menuType]); + + useEffect(() => { + setNavLoading(false); + if (navLoadingTimeoutRef.current) { + clearTimeout(navLoadingTimeoutRef.current); + navLoadingTimeoutRef.current = null; + } + }, [pathname]); + + useEffect(() => { + if (!isProjectDetail || !currentProject) return; + const prefetchRoutes = [ + `/projects/${currentProject}/multi-turn`, + `/projects/${currentProject}/eval-datasets`, + `/projects/${currentProject}/eval-tasks` + ]; + prefetchRoutes.forEach(route => router.prefetch(route)); + }, [router, currentProject, isProjectDetail]); + + useEffect(() => { + return () => { + if (navLoadingTimeoutRef.current) { + clearTimeout(navLoadingTimeoutRef.current); + } + }; + }, []); + + const handleNavigateStart = () => { + setNavLoading(true); + if (navLoadingTimeoutRef.current) { + clearTimeout(navLoadingTimeoutRef.current); + } + navLoadingTimeoutRef.current = setTimeout(() => { + setNavLoading(false); + navLoadingTimeoutRef.current = null; + }, 12000); + }; + + const handleMenuOpen = (event, menuType) => { + setMenuState({ anchorEl: event.currentTarget, menuType }); + }; + + const handleMenuClose = () => { + setMenuState({ anchorEl: null, menuType: null }); + }; + + const isMenuOpen = menuType => menuState.menuType === menuType; + + const toggleDrawer = () => { + setDrawerOpen(!drawerOpen); + setExpandedMenu(null); + }; + + const toggleMobileSubmenu = menuType => { + setExpandedMenu(expandedMenu === menuType ? null : menuType); + }; + + const toggleTheme = () => { + setTheme(resolvedTheme === 'dark' ? 'light' : 'dark'); + }; + + return ( + <> + + + {/* 左侧: 汉堡菜单(移动端) + Logo */} + isProjectDetail && setContextBarHovered(true)} + > + {/* 汉堡菜单按钮 */} + {isProjectDetail && isMobile && ( + + + + + + )} + + {/* Logo 组件 */} + + + + {/* 中间导航 - 仅桌面端 */} + {isProjectDetail && !isMobile && ( + + )} + + {/* 右侧操作区 */} + + + {isProjectDetail && ( + + )} + + + {/* ContextBar - 在 Logo 或 ContextBar 悬浮时展示 */} + {isProjectDetail && contextBarHovered && ( + setContextBarHovered(false)}> + setContextBarHovered(false)} + /> + + )} + + {/* 移动端抽屉组件 */} + + + {/* 桌面端菜单组件 */} + + + ); +} diff --git a/easy-dataset-main/components/Navbar/styles.js b/easy-dataset-main/components/Navbar/styles.js new file mode 100644 index 0000000..af95d31 --- /dev/null +++ b/easy-dataset-main/components/Navbar/styles.js @@ -0,0 +1,374 @@ +/** + * Navbar 组件样式配置 + */ + +// AppBar 样式 +export const getAppBarStyles = theme => ({ + borderBottom: `1px solid ${theme.palette.divider}`, + bgcolor: theme.palette.mode === 'dark' ? 'background.paper' : 'primary.main', + backdropFilter: 'blur(20px)', + WebkitBackdropFilter: 'blur(20px)', + transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)', + boxShadow: theme.palette.mode === 'dark' ? '0 1px 3px rgba(0, 0, 0, 0.3)' : '0 1px 3px rgba(0, 0, 0, 0.1)' +}); + +// Toolbar 样式 +export const toolbarStyles = { + height: '64px', + minHeight: '64px !important', + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + px: { xs: 2, sm: 2, md: 3 }, + gap: 2 +}; + +// Logo 容器样式 +export const logoContainerStyles = { + display: 'flex', + alignItems: 'center', + gap: 1.5, + flexShrink: 0 +}; + +// 汉堡菜单按钮样式 +export const getHamburgerButtonStyles = theme => ({ + color: theme.palette.mode === 'dark' ? 'inherit' : 'white', + minWidth: 44, + minHeight: 44, + transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)', + '&:hover': { + transform: 'scale(1.1)', + bgcolor: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.08)' : 'rgba(255, 255, 255, 0.15)' + }, + '&:active': { + transform: 'scale(0.95)' + }, + '&:focus-visible': { + outline: `2px solid ${theme.palette.mode === 'dark' ? theme.palette.secondary.main : 'white'}`, + outlineOffset: 2 + } +}); + +// Logo 链接样式 +export const getLogoLinkStyles = theme => ({ + display: 'flex', + alignItems: 'center', + cursor: 'pointer', + textDecoration: 'none', + transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)', + borderRadius: 1.5, + px: 0.5, + '&:hover': { + opacity: 0.85, + transform: 'translateY(-1px)' + }, + '&:active': { + transform: 'translateY(0)' + }, + '&:focus-visible': { + outline: `2px solid ${theme.palette.mode === 'dark' ? theme.palette.secondary.main : 'white'}`, + outlineOffset: 2 + } +}); + +// Logo 图片样式 +export const logoImageStyles = { + width: 32, + height: 32, + mr: 1.5, + transition: 'transform 0.2s ease' +}; + +// Logo 文字样式 +export const getLogoTextStyles = theme => ({ + fontWeight: 700, + letterSpacing: '-0.5px', + fontSize: '1.125rem', + display: { xs: 'none', md: 'block' }, + color: 'white', + ...(theme.palette.mode === 'dark' && { + background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', + WebkitBackgroundClip: 'text', + WebkitTextFillColor: 'transparent', + backgroundClip: 'text' + }) +}); + +// 中间导航容器样式 +export const navContainerStyles = { + flexGrow: 1, + display: 'flex', + justifyContent: 'center', + mx: { lg: 1, xl: 3 }, + overflow: 'hidden' +}; + +// Tabs 样式 +export const getTabsStyles = theme => ({ + minHeight: '64px', + '& .MuiTab-root': { + minWidth: 100, + maxWidth: 180, + fontSize: '0.875rem', + fontWeight: 500, + transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)', + color: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.7)' : 'rgba(255, 255, 255, 1)', + px: 2, + minHeight: '64px', + textTransform: 'none', + letterSpacing: '0.3px', + '&:hover': { + color: 'white', + bgcolor: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.08)' : 'rgba(255, 255, 255, 0.15)' + } + }, + '& .Mui-selected': { + color: 'white !important', + fontWeight: 600, + bgcolor: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.12)' : 'rgba(255, 255, 255, 0.2)' + }, + '& .MuiTabs-indicator': { + height: 3, + borderRadius: '3px 3px 0 0', + backgroundColor: theme.palette.mode === 'dark' ? theme.palette.secondary.main : 'white', + boxShadow: theme.palette.mode === 'dark' ? '0 0 8px rgba(103, 126, 234, 0.5)' : '0 0 8px rgba(255, 255, 255, 0.5)' + } +}); + +// Tab 图标包装器样式 +export const tabIconWrapperStyles = { + '& .MuiTab-iconWrapper': { mr: 1 } +}; + +// 右侧操作区容器样式 +export const actionAreaStyles = { + display: 'flex', + alignItems: 'center', + gap: 1, + flexShrink: 0 +}; + +// 文档/GitHub 按钮样式 +export const getIconButtonStyles = theme => ({ + display: { xs: 'none', xl: 'flex' }, + bgcolor: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.08)' : 'rgba(255, 255, 255, 0.2)', + color: theme.palette.mode === 'dark' ? 'inherit' : 'white', + borderRadius: 1.5, + transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)', + '&:hover': { + bgcolor: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.15)' : 'rgba(255, 255, 255, 0.35)' + }, + '&:focus-visible': { + outline: `2px solid ${theme.palette.mode === 'dark' ? theme.palette.secondary.main : 'white'}`, + outlineOffset: 2 + } +}); + +// Drawer Paper 样式 +export const getDrawerPaperStyles = theme => ({ + width: { xs: '85vw', sm: 320 }, + maxWidth: 380, + bgcolor: theme.palette.mode === 'dark' ? 'background.paper' : 'background.default', + backgroundImage: + theme.palette.mode === 'dark' ? 'linear-gradient(rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.05))' : 'none', + boxShadow: theme.palette.mode === 'dark' ? '0 8px 32px rgba(0, 0, 0, 0.6)' : '0 8px 32px rgba(0, 0, 0, 0.15)' +}); + +// Drawer 头部样式 +export const getDrawerHeaderStyles = theme => ({ + p: 2.5, + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + borderBottom: `1px solid ${theme.palette.divider}`, + minHeight: 64 +}); + +// Drawer 关闭按钮样式 +export const getDrawerCloseButtonStyles = theme => ({ + minWidth: 44, + minHeight: 44, + transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)', + '&:hover': { + transform: 'rotate(90deg)', + bgcolor: 'action.hover' + }, + '&:focus-visible': { + outline: `2px solid ${theme.palette.primary.main}`, + outlineOffset: 2 + } +}); + +// Drawer 列表样式 +export const drawerListStyles = { + pt: 1, + px: 1 +}; + +// Drawer 列表项按钮样式 +export const getDrawerListItemButtonStyles = theme => ({ + borderRadius: '8px', + minHeight: 48, + transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)', + '&:hover': { + bgcolor: theme.palette.mode === 'dark' ? 'rgba(103, 126, 234, 0.12)' : 'rgba(103, 126, 234, 0.08)' + }, + '&:focus-visible': { + outline: `2px solid ${theme.palette.primary.main}`, + outlineOffset: -2 + } +}); + +// Drawer 子菜单容器样式 +export const getDrawerSubmenuContainerStyles = theme => ({ + bgcolor: theme.palette.mode === 'dark' ? 'rgba(0, 0, 0, 0.2)' : 'rgba(0, 0, 0, 0.02)', + borderRadius: '8px', + my: 0.5 +}); + +// Drawer 子菜单项样式 +export const getDrawerSubmenuItemStyles = theme => ({ + pl: 4, + mx: 1, + borderRadius: '8px', + minHeight: 44, + py: 1.5, + transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)', + '&:hover': { + bgcolor: theme.palette.mode === 'dark' ? 'rgba(103, 126, 234, 0.08)' : 'rgba(103, 126, 234, 0.05)' + }, + '&:focus-visible': { + outline: `2px solid ${theme.palette.primary.main}`, + outlineOffset: -2 + } +}); + +// Drawer 工具区域样式 +export const getDrawerUtilitiesStyles = theme => ({ + mt: 'auto', + pt: 2, + borderTop: `1px solid ${theme.palette.divider}` +}); + +// Menu Paper 样式 +export const getMenuPaperStyles = theme => ({ + mt: 1.5, + borderRadius: '12px', + minWidth: 220, + overflow: 'visible', + bgcolor: theme.palette.mode === 'dark' ? 'rgba(30, 30, 30, 0.98)' : 'rgba(255, 255, 255, 0.98)', + backdropFilter: 'blur(20px)', + WebkitBackdropFilter: 'blur(20px)', + boxShadow: + theme.palette.mode === 'dark' + ? '0 12px 40px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 255, 255, 0.1)' + : '0 12px 40px rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(0, 0, 0, 0.05)', + '&::before': { + content: '""', + display: 'block', + position: 'absolute', + top: 0, + right: '50%', + width: 12, + height: 12, + bgcolor: theme.palette.mode === 'dark' ? 'rgba(30, 30, 30, 0.98)' : 'rgba(255, 255, 255, 0.98)', + transform: 'translateY(-50%) translateX(50%) rotate(45deg)', + zIndex: 0, + boxShadow: theme.palette.mode === 'dark' ? '-2px -2px 4px rgba(0, 0, 0, 0.3)' : '-2px -2px 4px rgba(0, 0, 0, 0.1)' + } +}); + +// Menu 列表样式 +export const menuListStyles = { + py: 1.5 +}; + +// Menu 项样式 +export const getMenuItemStyles = theme => ({ + mx: 1, + borderRadius: '8px', + py: 1.25, + minHeight: 44, + transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)', + '&:hover': { + bgcolor: theme.palette.mode === 'dark' ? 'rgba(103, 126, 234, 0.15)' : 'rgba(103, 126, 234, 0.1)', + transform: 'translateX(4px)' + }, + '&:focus-visible': { + outline: `2px solid ${theme.palette.primary.main}`, + outlineOffset: -2 + } +}); + +// Dataset/More Menu Paper 样式(简化版) +export const getSimpleMenuPaperStyles = theme => ({ + mt: 1.5, + borderRadius: '12px', + minWidth: 220, + overflow: 'visible', + bgcolor: theme.palette.mode === 'dark' ? 'rgba(30, 30, 30, 0.98)' : 'rgba(255, 255, 255, 0.98)', + backdropFilter: 'blur(20px)', + boxShadow: + theme.palette.mode === 'dark' + ? '0 8px 32px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.1)' + : '0 8px 32px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(0, 0, 0, 0.05)', + '&::before': { + content: '""', + display: 'block', + position: 'absolute', + top: 0, + right: '50%', + width: 12, + height: 12, + bgcolor: theme.palette.mode === 'dark' ? 'rgba(30, 30, 30, 0.98)' : 'rgba(255, 255, 255, 0.98)', + transform: 'translateY(-50%) translateX(50%) rotate(45deg)', + zIndex: 0 + } +}); + +// 简化 Menu 列表样式 +export const simpleMenuListStyles = { + py: 1 +}; + +// 简化 Menu 项样式 +export const getSimpleMenuItemStyles = theme => ({ + mx: 0.75, + borderRadius: '8px', + py: 1, + transition: 'all 0.15s ease', + '&:hover': { + bgcolor: theme.palette.mode === 'dark' ? 'rgba(103, 126, 234, 0.15)' : 'rgba(103, 126, 234, 0.1)', + transform: 'translateX(4px)' + } +}); + +// ListItemIcon 样式 +export const listItemIconStyles = { + minWidth: 40 +}; + +export const smallListItemIconStyles = { + minWidth: 36 +}; + +// ListItemText 样式 +export const listItemTextStyles = { + fontWeight: 600, + fontSize: '0.95rem' +}; + +export const smallListItemTextStyles = { + fontSize: '0.9rem', + fontWeight: 500 +}; + +// 图标颜色样式 +export const getIconColorStyles = theme => ({ + color: theme.palette.mode === 'dark' ? 'primary.light' : 'primary.main' +}); + +export const getPrimaryIconColorStyles = theme => ({ + color: theme.palette.primary.main +}); diff --git a/easy-dataset-main/components/TaskIcon.js b/easy-dataset-main/components/TaskIcon.js new file mode 100644 index 0000000..1bd9fe6 --- /dev/null +++ b/easy-dataset-main/components/TaskIcon.js @@ -0,0 +1,223 @@ +'use client'; + +import React, { useState, useEffect } from 'react'; +import { Badge, IconButton, Tooltip, CircularProgress, Menu, MenuItem, Divider, ListItemIcon } from '@mui/material'; +import TaskAltIcon from '@mui/icons-material/TaskAlt'; +import ListAltIcon from '@mui/icons-material/ListAlt'; +import QuizIcon from '@mui/icons-material/Quiz'; +import AssessmentIcon from '@mui/icons-material/Assessment'; +import CleaningServicesIcon from '@mui/icons-material/CleaningServices'; +import { useTranslation } from 'react-i18next'; +import { useRouter, usePathname } from 'next/navigation'; +import useFileProcessingStatus from '@/hooks/useFileProcessingStatus'; +import { useAtomValue } from 'jotai/index'; +import { selectedModelInfoAtom } from '@/lib/store'; +import axios from 'axios'; +import { toast } from 'sonner'; + +export default function TaskIcon({ projectId, theme }) { + const { t, i18n } = useTranslation(); + const router = useRouter(); + const pathname = usePathname(); + const [tasks, setTasks] = useState([]); + const [menuAnchorEl, setMenuAnchorEl] = useState(null); + const isMenuOpen = Boolean(menuAnchorEl); + const selectedModel = useAtomValue(selectedModelInfoAtom); + const { setTaskFileProcessing, setTask } = useFileProcessingStatus(); + + const fetchPendingTasks = async () => { + if (!projectId) return; + + try { + const response = await axios.get(`/api/projects/${projectId}/tasks/list?status=0`); + if (response.data?.code === 0) { + const pendingTasks = response.data.data || []; + setTasks(pendingTasks); + + const hasActiveFileTask = pendingTasks.some( + task => task.projectId === projectId && task.taskType === 'file-processing' + ); + setTaskFileProcessing(hasActiveFileTask); + + if (hasActiveFileTask) { + const activeTask = pendingTasks.find( + task => task.projectId === projectId && task.taskType === 'file-processing' + ); + try { + const detailInfo = JSON.parse(activeTask?.detail || '{}'); + setTask(detailInfo); + } catch { + setTask(null); + } + } + } + } catch (error) { + console.error('Failed to fetch task list:', error); + } + }; + + useEffect(() => { + if (!projectId) return; + + fetchPendingTasks(); + + const intervalId = setInterval(() => { + fetchPendingTasks(); + }, 10000); + + return () => { + clearInterval(intervalId); + }; + }, [projectId]); + + useEffect(() => { + setMenuAnchorEl(null); + }, [pathname]); + + const handleOpenTaskList = () => { + setMenuAnchorEl(null); + router.push(`/projects/${projectId}/tasks`); + }; + + const handleMenuOpen = event => { + if (isMenuOpen) { + setMenuAnchorEl(null); + return; + } + setMenuAnchorEl(event.currentTarget); + }; + + const handleMenuClose = () => { + setMenuAnchorEl(null); + }; + + const createBatchTask = async (taskType, detail) => { + if (!projectId || !selectedModel?.id) { + toast.error(t('textSplit.selectModelFirst')); + return; + } + + try { + const response = await axios.post(`/api/projects/${projectId}/tasks`, { + taskType, + modelInfo: selectedModel, + language: i18n.language, + detail + }); + + if (response.data?.code === 0) { + toast.success(t('tasks.createSuccess')); + await fetchPendingTasks(); + } else { + toast.error(`${t('tasks.createFailed')}: ${response.data?.message || ''}`); + } + } catch (error) { + console.error('Create batch task failed:', error); + toast.error(`${t('tasks.createFailed')}: ${error.message}`); + } + }; + + const handleCreateAutoQuestionTask = async () => { + await createBatchTask('question-generation', '批量生成问题任务'); + handleMenuClose(); + }; + + const handleCreateAutoEvalTask = async () => { + await createBatchTask('eval-generation', '批量生成评估集任务'); + handleMenuClose(); + }; + + const handleCreateAutoCleaningTask = async () => { + await createBatchTask('data-cleaning', '批量数据清洗任务'); + handleMenuClose(); + }; + + const renderTaskIcon = () => { + const pendingTasks = tasks.filter(task => task.status === 0); + + if (pendingTasks.length > 0) { + return ( + + + + ); + } + + return ; + }; + + const getTooltipText = () => { + const pendingTasks = tasks.filter(task => task.status === 0); + + if (pendingTasks.length > 0) { + return t('tasks.pending', { count: pendingTasks.length }); + } + + return t('tasks.completed'); + }; + + if (!projectId) return null; + + return ( + <> + + + {renderTaskIcon()} + + + + + + + + + {t('tasks.title')} + + + + + + + + + {t('textSplit.autoGenerateQuestions', { defaultValue: '自动提取问题' })} + + + + + + + {t('textSplit.autoEvalGeneration', { defaultValue: '自动生成评估集' })} + + + + + + + {t('textSplit.autoDataCleaning', { defaultValue: '自动数据清洗' })} + + + + ); +} diff --git a/easy-dataset-main/components/ThemeRegistry.js b/easy-dataset-main/components/ThemeRegistry.js new file mode 100644 index 0000000..9911448 --- /dev/null +++ b/easy-dataset-main/components/ThemeRegistry.js @@ -0,0 +1,342 @@ +'use client'; + +import { createTheme, ThemeProvider } from '@mui/material/styles'; +import CssBaseline from '@mui/material/CssBaseline'; +import { ThemeProvider as NextThemeProvider, useTheme } from 'next-themes'; +import { useEffect, useState } from 'react'; + +// 导入字体 +import '@fontsource/inter/300.css'; +import '@fontsource/inter/400.css'; +import '@fontsource/inter/500.css'; +import '@fontsource/inter/600.css'; +import '@fontsource/inter/700.css'; +import '@fontsource/jetbrains-mono/400.css'; +import '@fontsource/jetbrains-mono/500.css'; + +// 创建主题配置 +const getTheme = mode => { + // 主色调 + const mainBlue = '#2A5CAA'; + const darkGray = '#2D2D2D'; + + // 辅助色 - 数据可视化色谱 + const dataVizColors = [ + '#6366F1', // 紫蓝色 + '#10B981', // 绿色 + '#F59E0B', // 琥珀色 + '#EC4899', // 粉色 + '#8B5CF6', // 紫色 + '#3B82F6' // 蓝色 + ]; + + // 状态色 + const successColor = '#10B981'; // 翡翠绿 + const warningColor = '#F59E0B'; // 琥珀色 + const errorColor = '#EF4444'; // 珊瑚红 + + // 渐变色 + const gradientPrimary = 'linear-gradient(90deg, #2A5CAA 0%, #8B5CF6 100%)'; + + // 根据模式调整颜色 + return createTheme({ + palette: { + mode, + primary: { + main: mainBlue, + dark: '#1E4785', + light: '#4878C6', + contrastText: '#FFFFFF' + }, + secondary: { + main: '#8B5CF6', + dark: '#7039F2', + light: '#A78BFA', + contrastText: '#FFFFFF' + }, + error: { + main: errorColor, + dark: '#DC2626', + light: '#F87171' + }, + warning: { + main: warningColor, + dark: '#D97706', + light: '#FBBF24' + }, + success: { + main: successColor, + dark: '#059669', + light: '#34D399' + }, + background: { + default: mode === 'dark' ? '#121212' : '#F8F9FA', + paper: mode === 'dark' ? '#1E1E1E' : '#FFFFFF', + subtle: mode === 'dark' ? '#2A2A2A' : '#F3F4F6' + }, + text: { + primary: mode === 'dark' ? '#F3F4F6' : darkGray, + secondary: mode === 'dark' ? '#9CA3AF' : '#6B7280', + disabled: mode === 'dark' ? '#4B5563' : '#9CA3AF' + }, + divider: mode === 'dark' ? 'rgba(255, 255, 255, 0.12)' : 'rgba(0, 0, 0, 0.12)', + dataViz: dataVizColors, + gradient: { + primary: gradientPrimary + } + }, + typography: { + fontFamily: + '"Inter", "HarmonyOS Sans", "PingFang SC", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif', + fontSize: 14, + fontWeightLight: 300, + fontWeightRegular: 400, + fontWeightMedium: 500, + fontWeightBold: 600, + h1: { + fontSize: '2rem', // 32px + fontWeight: 600, + lineHeight: 1.2, + letterSpacing: '-0.01em' + }, + h2: { + fontSize: '1.5rem', // 24px + fontWeight: 600, + lineHeight: 1.3, + letterSpacing: '-0.005em' + }, + h3: { + fontSize: '1.25rem', // 20px + fontWeight: 600, + lineHeight: 1.4 + }, + h4: { + fontSize: '1.125rem', // 18px + fontWeight: 600, + lineHeight: 1.4 + }, + h5: { + fontSize: '1rem', // 16px + fontWeight: 600, + lineHeight: 1.5 + }, + h6: { + fontSize: '0.875rem', // 14px + fontWeight: 600, + lineHeight: 1.5 + }, + body1: { + fontSize: '1rem', // 16px + lineHeight: 1.5 + }, + body2: { + fontSize: '0.875rem', // 14px + lineHeight: 1.5 + }, + caption: { + fontSize: '0.75rem', // 12px + lineHeight: 1.5 + }, + code: { + fontFamily: '"JetBrains Mono", monospace', + fontSize: '0.875rem' + } + }, + shape: { + borderRadius: 8 + }, + spacing: 8, // 基础间距单位为8px + components: { + MuiCssBaseline: { + styleOverrides: { + body: { + scrollbarWidth: 'thin', + scrollbarColor: mode === 'dark' ? '#4B5563 transparent' : '#9CA3AF transparent', + '&::-webkit-scrollbar': { + width: '8px', + height: '8px' + }, + '&::-webkit-scrollbar-track': { + background: 'transparent' + }, + '&::-webkit-scrollbar-thumb': { + background: mode === 'dark' ? '#4B5563' : '#9CA3AF', + borderRadius: '4px' + } + }, + // 确保代码块使用 JetBrains Mono 字体 + 'code, pre': { + fontFamily: '"JetBrains Mono", monospace' + }, + // 自定义渐变文本的通用样式 + '.gradient-text': { + background: gradientPrimary, + WebkitBackgroundClip: 'text', + WebkitTextFillColor: 'transparent', + backgroundClip: 'text', + textFillColor: 'transparent' + } + } + }, + MuiButton: { + styleOverrides: { + root: { + textTransform: 'none', + fontWeight: 500, + borderRadius: '8px', + padding: '6px 16px' + }, + contained: { + boxShadow: 'none', + '&:hover': { + boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.1)' + } + }, + containedPrimary: { + background: mainBlue, + '&:hover': { + backgroundColor: '#1E4785' + } + }, + containedSecondary: { + background: '#8B5CF6', + '&:hover': { + backgroundColor: '#7039F2' + } + }, + outlined: { + borderWidth: '1.5px', + '&:hover': { + borderWidth: '1.5px' + } + } + } + }, + MuiAppBar: { + styleOverrides: { + root: { + boxShadow: 'none', + background: mode === 'dark' ? '#1A1A1A' : mainBlue + } + } + }, + MuiCard: { + styleOverrides: { + root: { + borderRadius: '12px', + boxShadow: mode === 'dark' ? '0px 4px 8px rgba(0, 0, 0, 0.4)' : '0px 4px 8px rgba(0, 0, 0, 0.05)' + } + } + }, + MuiPaper: { + styleOverrides: { + root: { + borderRadius: '12px' + } + } + }, + MuiChip: { + styleOverrides: { + root: { + borderRadius: '6px', + fontWeight: 500 + } + } + }, + MuiTableHead: { + styleOverrides: { + root: { + '& .MuiTableCell-head': { + fontWeight: 600, + backgroundColor: mode === 'dark' ? '#2A2A2A' : '#F3F4F6' + } + } + } + }, + MuiTabs: { + styleOverrides: { + indicator: { + height: '3px', + borderRadius: '3px 3px 0 0' + } + } + }, + MuiTab: { + styleOverrides: { + root: { + textTransform: 'none', + fontWeight: 500, + '&.Mui-selected': { + fontWeight: 600 + } + } + } + }, + MuiListItemButton: { + styleOverrides: { + root: { + borderRadius: '8px' + } + } + }, + MuiModal: { + defaultProps: { + disableScrollLock: true + } + }, + MuiDialog: { + defaultProps: { + disableScrollLock: true + } + }, + MuiPopover: { + defaultProps: { + disableScrollLock: true + } + }, + MuiMenu: { + defaultProps: { + disableScrollLock: true + } + }, + MuiDialogTitle: { + styleOverrides: { + root: { + fontSize: '1.25rem', + fontWeight: 600 + } + } + } + } + }); +}; + +export default function ThemeRegistry({ children }) { + const [mounted, setMounted] = useState(false); + + useEffect(() => { + setMounted(true); + }, []); + + if (!mounted) { + return null; + } + + return ( + + {children} + + ); +} + +function InnerThemeRegistry({ children }) { + const { resolvedTheme } = useTheme(); + const theme = getTheme(resolvedTheme === 'dark' ? 'dark' : 'light'); + + return ( + + + {children} + + ); +} diff --git a/easy-dataset-main/components/UpdateChecker.js b/easy-dataset-main/components/UpdateChecker.js new file mode 100644 index 0000000..3b0d353 --- /dev/null +++ b/easy-dataset-main/components/UpdateChecker.js @@ -0,0 +1,235 @@ +import React, { useState, useEffect } from 'react'; +import { Box, Button, Snackbar, Alert, Typography, Link, CircularProgress, LinearProgress } from '@mui/material'; +import UpdateIcon from '@mui/icons-material/Update'; +import { useTranslation } from 'react-i18next'; + +const UpdateChecker = () => { + const { t } = useTranslation(); + const [updateAvailable, setUpdateAvailable] = useState(false); + const [updateInfo, setUpdateInfo] = useState(null); + const [open, setOpen] = useState(false); + const [checking, setChecking] = useState(false); + const [downloading, setDownloading] = useState(false); + const [downloadProgress, setDownloadProgress] = useState(0); + const [updateDownloaded, setUpdateDownloaded] = useState(false); + const [updateError, setUpdateError] = useState(null); + + // 检查更新 + const checkForUpdates = async () => { + if (!window.electron?.updater) { + console.warn('Update feature is not available, possibly running in browser environment'); + return; + } + + try { + setChecking(true); + setUpdateError(null); + + const result = await window.electron.updater.checkForUpdates(); + console.log('Update check result:', result); + + // 返回当前版本信息 + if (result) { + setUpdateInfo(prev => ({ + ...prev, + currentVersion: result.currentVersion + })); + } + } catch (error) { + console.error('Failed to check for updates:', error); + // setUpdateError(error.message || 'Failed to check for updates'); + } finally { + setChecking(false); + } + }; + + // 下载更新 + const downloadUpdate = async () => { + if (!window.electron?.updater) return; + + try { + setDownloading(true); + setUpdateError(null); + await window.electron.updater.downloadUpdate(); + } catch (error) { + console.error('下载更新失败:', error); + setUpdateError(error.message || '下载更新失败'); + setDownloading(false); + } + }; + + // 安装更新 + const installUpdate = async () => { + if (!window.electron?.updater) return; + + try { + await window.electron.updater.installUpdate(); + } catch (error) { + console.error('Failed to install update:', error); + // setUpdateError(error.message || 'Failed to install update'); + } + }; + + // 设置更新事件监听 + useEffect(() => { + if (!window.electron?.updater) return; + + // 有可用更新 + const removeUpdateAvailable = window.electron.updater.onUpdateAvailable(info => { + console.log('发现新版本:', info); + setUpdateAvailable(true); + setUpdateInfo(prev => ({ + ...prev, + ...info, + releaseUrl: `https://github.com/ConardLi/easy-dataset/releases` + })); + setOpen(true); + }); + + // 没有可用更新 + const removeUpdateNotAvailable = window.electron.updater.onUpdateNotAvailable(() => { + console.log('没有可用更新'); + setUpdateAvailable(false); + }); + + // 更新错误 + const removeUpdateError = window.electron.updater.onUpdateError(error => { + console.error('更新错误:', error); + // setUpdateError(error); + }); + + // 下载进度 + const removeDownloadProgress = window.electron.updater.onDownloadProgress(progress => { + console.log('下载进度:', progress); + setDownloadProgress(progress.percent || 0); + }); + + // 更新下载完成 + const removeUpdateDownloaded = window.electron.updater.onUpdateDownloaded(info => { + console.log('更新下载完成:', info); + setDownloading(false); + setUpdateDownloaded(true); + }); + + // 组件挂载时检查更新 + const timer = setTimeout(() => { + checkForUpdates(); + }, 5000); + + // 清理函数 + return () => { + clearTimeout(timer); + removeUpdateAvailable(); + removeUpdateNotAvailable(); + removeUpdateError(); + removeDownloadProgress(); + removeUpdateDownloaded(); + }; + }, []); + + // 定期检查更新(每小时一次) + useEffect(() => { + if (!window.electron?.updater) return; + + const interval = setInterval( + () => { + checkForUpdates(); + }, + 60 * 60 * 1000 + ); + + return () => clearInterval(interval); + }, []); + + const handleClose = () => { + setOpen(false); + }; + + // 如果没有更新或者不在 Electron 环境中,不显示任何内容 + if (!updateAvailable && !open) return null; + + return ( + <> + {updateAvailable && ( + + )} + + + + + {t('update.newVersionAvailable')} + + {updateInfo && ( + <> + + {t('update.currentVersion')}: {updateInfo.currentVersion} + + + {t('update.latestVersion')}: {updateInfo.version} + + + )} + + {checking && ( + + + {t('update.checking')} + + )} + + {updateError && ( + + {updateError} + + )} + + {downloading && ( + + + {t('update.downloading')}: {Math.round(downloadProgress)}% + + + + )} + + + {/* {!downloading && !updateDownloaded ? ( + + ) : updateDownloaded ? ( + + ) : null} */} + + {updateInfo?.releaseUrl && ( + + + + )} + + + + + + ); +}; + +export default UpdateChecker; diff --git a/easy-dataset-main/components/common/MessageAlert.js b/easy-dataset-main/components/common/MessageAlert.js new file mode 100644 index 0000000..c2c0acd --- /dev/null +++ b/easy-dataset-main/components/common/MessageAlert.js @@ -0,0 +1,23 @@ +'use client'; + +import { Snackbar, Alert } from '@mui/material'; + +export default function MessageAlert({ message, onClose }) { + if (!message) return null; + + const severity = message.severity || 'error'; + const text = typeof message === 'string' ? message : message.message; + + return ( + + + {text} + + + ); +} diff --git a/easy-dataset-main/components/conversations/ConversationContent.js b/easy-dataset-main/components/conversations/ConversationContent.js new file mode 100644 index 0000000..600f6ac --- /dev/null +++ b/easy-dataset-main/components/conversations/ConversationContent.js @@ -0,0 +1,88 @@ +'use client'; + +import { Box, Typography, Card, CardContent, Chip, TextField } from '@mui/material'; +import { useTranslation } from 'react-i18next'; + +/** + * 多轮对话内容展示和编辑组件 + */ +export default function ConversationContent({ messages, editMode, onMessageChange, conversation }) { + const { t } = useTranslation(); + + // 获取角色显示信息 + const getRoleDisplay = role => { + switch (role) { + case 'system': + return { name: t('datasets.system'), color: 'default' }; + case 'user': + return { name: conversation?.roleA || t('datasets.user'), color: 'primary' }; + case 'assistant': + return { name: conversation?.roleB || t('datasets.assistant'), color: 'secondary' }; + default: + return { name: role, color: 'default' }; + } + }; + + return ( + + + {t('datasets.conversationContent')} + + + + {messages.map((message, index) => { + const roleInfo = getRoleDisplay(message.role); + return ( + + + + {message.role !== 'system' && ( + + {t('datasets.round', { round: Math.floor((index + 1) / 2) + 1 })} + + )} + + + + {editMode ? ( + onMessageChange && onMessageChange(index, e.target.value)} + variant="outlined" + size="small" + sx={{ + '& .MuiInputBase-input': { + fontFamily: 'inherit', + fontSize: '0.875rem', + lineHeight: 1.5 + } + }} + /> + ) : ( + + {message.content} + + )} + + + + ); + })} + + + ); +} diff --git a/easy-dataset-main/components/conversations/ConversationHeader.js b/easy-dataset-main/components/conversations/ConversationHeader.js new file mode 100644 index 0000000..78f48aa --- /dev/null +++ b/easy-dataset-main/components/conversations/ConversationHeader.js @@ -0,0 +1,87 @@ +'use client'; + +import { Box, Button, Divider, Typography, IconButton, CircularProgress, Paper, Tooltip } from '@mui/material'; +import NavigateBeforeIcon from '@mui/icons-material/NavigateBefore'; +import NavigateNextIcon from '@mui/icons-material/NavigateNext'; +import DeleteIcon from '@mui/icons-material/Delete'; +import EditIcon from '@mui/icons-material/Edit'; +import SaveIcon from '@mui/icons-material/Save'; +import { useTranslation } from 'react-i18next'; +import { useRouter } from 'next/navigation'; + +/** + * 多轮对话详情页面的头部导航组件 + */ +export default function ConversationHeader({ + projectId, + conversationId, + conversation, + editMode, + saving, + onEdit, + onSave, + onCancel, + onDelete, + onNavigate +}) { + const router = useRouter(); + const { t } = useTranslation(); + + return ( + + + + + + {t('datasets.conversationDetail')} + {conversation && ( + + {conversation.scenario && ( + <> + {conversation.scenario} • {conversation.turnCount}/{conversation.maxTurns} 轮 + + )} + + )} + + + + {/* 翻页按钮 */} + onNavigate && onNavigate('prev')}> + + + onNavigate && onNavigate('next')}> + + + + + {/* 编辑/保存按钮 */} + {editMode ? ( + <> + + + + ) : ( + <> + + + + )} + + + + ); +} diff --git a/easy-dataset-main/components/conversations/ConversationMetadata.js b/easy-dataset-main/components/conversations/ConversationMetadata.js new file mode 100644 index 0000000..7b600ef --- /dev/null +++ b/easy-dataset-main/components/conversations/ConversationMetadata.js @@ -0,0 +1,77 @@ +'use client'; + +import { Box, Typography, Chip, Tooltip, alpha, Paper } from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import { useTheme } from '@mui/material/styles'; + +/** + * 多轮对话元数据展示组件 + */ +export default function ConversationMetadata({ conversation }) { + const { t } = useTranslation(); + const theme = useTheme(); + + if (!conversation) return null; + + return ( + + + {t('datasets.metadata')} + + + + + {conversation.scenario && ( + + )} + + + + {conversation.roleA && ( + + )} + + {conversation.roleB && ( + + )} + + + + {conversation.confirmed && ( + + )} + + + ); +} diff --git a/easy-dataset-main/components/conversations/ConversationRatingSection.js b/easy-dataset-main/components/conversations/ConversationRatingSection.js new file mode 100644 index 0000000..3a7522e --- /dev/null +++ b/easy-dataset-main/components/conversations/ConversationRatingSection.js @@ -0,0 +1,201 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { Box, Typography, Divider, Paper, TextField } from '@mui/material'; +import { toast } from 'sonner'; +import StarRating from '@/components/datasets/StarRating'; +import TagSelector from '@/components/datasets/TagSelector'; +import NoteInput from '@/components/datasets/NoteInput'; +import { useTranslation } from 'react-i18next'; + +/** + * 多轮对话评分、标签、备注综合组件 + */ +export default function ConversationRatingSection({ conversation, projectId, onUpdate }) { + const { t } = useTranslation(); + const [availableTags, setAvailableTags] = useState([]); + const [loading, setLoading] = useState(false); + + // 解析对话中的标签 + const parseConversationTags = tagsString => { + try { + if (typeof tagsString === 'string' && tagsString.trim()) { + return tagsString.split(/\s+/).filter(tag => tag.length > 0); + } + return []; + } catch (e) { + return []; + } + }; + + // 本地状态管理 + const [localScore, setLocalScore] = useState(conversation.score || 0); + const [localTags, setLocalTags] = useState(() => parseConversationTags(conversation.tags)); + const [localNote, setLocalNote] = useState(conversation.note || ''); + + // 获取项目中已使用的标签 + useEffect(() => { + const fetchAvailableTags = async () => { + try { + const response = await fetch(`/api/projects/${projectId}/dataset-conversations/tags`); + if (response.ok) { + const data = await response.json(); + setAvailableTags(data.tags || []); + } + } catch (error) { + console.error('获取可用标签失败:', error); + } + }; + + if (projectId) { + fetchAvailableTags(); + } + }, [projectId]); + + // 同步props中的conversation到本地状态 + useEffect(() => { + setLocalScore(conversation.score || 0); + setLocalTags(parseConversationTags(conversation.tags)); + setLocalNote(conversation.note || ''); + }, [conversation]); + + // 更新对话元数据 + const updateMetadata = async updates => { + if (loading) return; + + // 立即更新本地状态 + if (updates.score !== undefined) { + setLocalScore(updates.score); + } + if (updates.tagsArray !== undefined) { + setLocalTags(updates.tagsArray); + } + if (updates.note !== undefined) { + setLocalNote(updates.note); + } + + setLoading(true); + try { + const response = await fetch(`/api/projects/${projectId}/dataset-conversations/${conversation.id}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + score: updates.score, + tags: updates.tags, + note: updates.note + }) + }); + + if (!response.ok) { + throw new Error(t('datasets.saveFailed')); + } + + const result = await response.json(); + toast.success(t('datasets.saveSuccess')); + + // 如果有父组件的更新回调,调用它 + if (onUpdate) { + onUpdate(result.data); + } + } catch (error) { + console.error('更新对话元数据失败:', error); + toast.error(error.message || t('datasets.saveFailed')); + + // 出错时恢复本地状态 + if (updates.score !== undefined) { + setLocalScore(conversation.score || 0); + } + if (updates.tagsArray !== undefined) { + setLocalTags(parseConversationTags(conversation.tags)); + } + if (updates.note !== undefined) { + setLocalNote(conversation.note || ''); + } + } finally { + setLoading(false); + } + }; + + // 处理评分变更 + const handleScoreChange = newScore => { + updateMetadata({ score: newScore }); + }; + + // 处理标签变更 + const handleTagsChange = newTags => { + const tagsString = Array.isArray(newTags) ? newTags.join(' ') : ''; + updateMetadata({ tags: tagsString, tagsArray: newTags }); + }; + + // 处理备注变更 + const handleNoteChange = newNote => { + updateMetadata({ note: newNote }); + }; + + return ( + + {/* 评分区域 */} + + + {t('datasets.rating')} + + + + + + + {/* 标签区域 */} + + + {t('datasets.customTags')} + + + + + + + {/* 备注区域 */} + + + + + {/* 确认状态 */} + {/* + + {t('datasets.confirmationStatus')} + + + {conversation.confirmed ? t('datasets.confirmed') : t('datasets.unconfirmed')} + + */} + + {/* AI评估 */} + {conversation.aiEvaluation && ( + <> + + + + {t('datasets.aiEvaluation')} + + + {conversation.aiEvaluation} + + + + )} + + ); +} diff --git a/easy-dataset-main/components/dataset-square/DatasetSearchBar.js b/easy-dataset-main/components/dataset-square/DatasetSearchBar.js new file mode 100644 index 0000000..d84da83 --- /dev/null +++ b/easy-dataset-main/components/dataset-square/DatasetSearchBar.js @@ -0,0 +1,264 @@ +'use client'; + +import { useState, useEffect, useRef } from 'react'; +import { + Box, + TextField, + InputAdornment, + List, + ListItem, + ListItemButton, + ListItemText, + Paper, + Typography, + ClickAwayListener, + Fade, + Avatar, + useTheme, + alpha +} from '@mui/material'; +import SearchIcon from '@mui/icons-material/Search'; +import LaunchIcon from '@mui/icons-material/Launch'; +import TravelExploreIcon from '@mui/icons-material/TravelExplore'; +import sites from '@/constant/sites.json'; +import { useTranslation } from 'react-i18next'; + +export function DatasetSearchBar() { + const [searchQuery, setSearchQuery] = useState(''); + const [showSuggestions, setShowSuggestions] = useState(false); + const [recentSearches, setRecentSearches] = useState([]); + const searchRef = useRef(null); + const suggestionsRef = useRef(null); + const theme = useTheme(); + const { t } = useTranslation(); + + // 从 localStorage 加载最近搜索 + useEffect(() => { + const savedSearches = localStorage.getItem('recentDatasetSearches'); + if (savedSearches) { + try { + const searches = JSON.parse(savedSearches); + setRecentSearches(searches); + } catch (e) { + console.error('解析最近搜索失败', e); + } + } + }, []); + + // 处理搜索输入变化 + const handleSearchChange = event => { + setSearchQuery(event.target.value); + if (event.target.value) { + setShowSuggestions(true); + } else { + setShowSuggestions(false); + } + }; + + // 处理回车搜索 + const handleSearchSubmit = event => { + if (event.key === 'Enter' && searchQuery.trim()) { + // 默认使用第一个搜索引擎 + if (sites.length > 0) { + handleSuggestionClick(sites[0]); + } + } + }; + + // 保存最近搜索 + const saveRecentSearch = query => { + if (!query.trim()) return; + + // 添加到最近搜索并去重 + const updatedSearches = [query, ...recentSearches.filter(s => s !== query)].slice(0, 5); + setRecentSearches(updatedSearches); + + // 保存到 localStorage + try { + localStorage.setItem('recentDatasetSearches', JSON.stringify(updatedSearches)); + } catch (e) { + console.error('保存最近搜索失败', e); + } + }; + + // 处理点击搜索建议 + const handleSuggestionClick = site => { + if (searchQuery.trim()) { + // 根据不同网站处理搜索参数 + let searchUrl = site.link; + + // 如果链接中不包含问号,则添加搜索参数 + if (site.link.includes('huggingface.co')) { + searchUrl = `${site.link}?sort=trending&search=${encodeURIComponent(searchQuery)}`; + } else if (site.link.includes('kaggle.com')) { + searchUrl = `${site.link}?search=${encodeURIComponent(searchQuery)}`; + } else if (site.link.includes('datasetsearch.research.google.com')) { + searchUrl = `${site.link}/search?query=${encodeURIComponent(searchQuery)}&src=0`; + } else if (site.link.includes('paperswithcode.com')) { + searchUrl = `${site.link}?q=${encodeURIComponent(searchQuery)}`; + } else if (site.link.includes('modelscope.cn')) { + searchUrl = `${site.link}?query=${encodeURIComponent(searchQuery)}`; + } else if (site.link.includes('opendatalab.com')) { + searchUrl = `${site.link}?keywords=${encodeURIComponent(searchQuery)}`; + } else if (site.link.includes('tianchi.aliyun.com')) { + searchUrl = `${site.link}?q=${encodeURIComponent(searchQuery)}`; + } else { + // 默认处理方式,在URL后添加搜索参数 + searchUrl = `${site.link}${site.link.includes('?') ? '&' : '?'}search=${encodeURIComponent(searchQuery)}`; + } + + // 保存最近搜索 + saveRecentSearch(searchQuery); + + window.open(searchUrl, '_blank'); + } + setShowSuggestions(false); + }; + + // 处理点击外部关闭建议 + const handleClickAway = event => { + // 确保点击的不是建议框本身 + if (suggestionsRef.current && !suggestionsRef.current.contains(event.target)) { + setShowSuggestions(false); + } + }; + + return ( + + + searchQuery && setShowSuggestions(true)} + InputProps={{ + startAdornment: ( + + + + ), + sx: { + height: 56, + borderRadius: 3, + backgroundColor: + theme.palette.mode === 'dark' + ? alpha(theme.palette.background.default, 0.6) + : alpha(theme.palette.background.default, 0.8), + backdropFilter: 'blur(8px)', + px: 2, + transition: 'all 0.3s ease', + boxShadow: `0 0 0 1px ${alpha(theme.palette.primary.main, 0.15)}`, + '&.MuiOutlinedInput-root': { + '& fieldset': { + borderColor: 'transparent' + }, + '&:hover fieldset': { + borderColor: 'transparent' + }, + '&.Mui-focused': { + boxShadow: `0 0 0 2px ${alpha(theme.palette.primary.main, 0.3)}`, + backgroundColor: + theme.palette.mode === 'dark' + ? alpha(theme.palette.background.paper, 0.8) + : alpha(theme.palette.common.white, 0.95) + }, + '&.Mui-focused fieldset': { + borderColor: 'transparent' + } + } + } + }} + sx={{ + mb: 1, + '& .MuiInputBase-input': { + fontSize: '1rem', + fontWeight: 500, + color: theme.palette.text.primary + }, + '& .MuiInputBase-input::placeholder': { + color: alpha(theme.palette.text.primary, 0.6), + opacity: 0.7 + } + }} + /> + + {/* 搜索建议下拉框 - 使用绝对定位确保不被裁剪 */} + {showSuggestions && searchQuery && ( + + + + + {sites.slice(0, 5).map((site, index) => ( + + handleSuggestionClick(site)} + sx={{ + py: 1.5, + '&:hover': { + bgcolor: alpha(theme.palette.primary.main, 0.05) + } + }} + > + + + + + + + {t('datasetSquare.searchVia')} {site.name} Search + + + + + "{searchQuery}" + + + + + } + /> + + + ))} + + + + + )} + + + ); +} diff --git a/easy-dataset-main/components/dataset-square/DatasetSiteCard.js b/easy-dataset-main/components/dataset-square/DatasetSiteCard.js new file mode 100644 index 0000000..0ef45b4 --- /dev/null +++ b/easy-dataset-main/components/dataset-square/DatasetSiteCard.js @@ -0,0 +1,197 @@ +'use client'; + +import { Card, CardActionArea, CardContent, CardMedia, Typography, Box, Chip, useTheme, alpha } from '@mui/material'; +import LaunchIcon from '@mui/icons-material/Launch'; +import StorageIcon from '@mui/icons-material/Storage'; +import { useTranslation } from 'react-i18next'; + +export function DatasetSiteCard({ site }) { + const { name, link, description, image, labels } = site; + const theme = useTheme(); + + // 处理图片路径,如果没有图片则使用默认图片 + const imageUrl = image || `/imgs/default-dataset.png`; + const { t } = useTranslation(); + + // 处理卡片点击 + const handleCardClick = () => { + window.open(link, '_blank'); + }; + + return ( + + + {/* 网站截图 */} + + + + } + label={t('datasetSquare.dataset')} + size="small" + sx={{ + position: 'absolute', + top: 10, + right: 10, + zIndex: 2, + backgroundColor: alpha(theme.palette.background.paper, 0.8), + backdropFilter: 'blur(4px)', + border: `1px solid ${alpha(theme.palette.primary.main, 0.2)}`, + '& .MuiChip-icon': { + color: theme.palette.primary.main + } + }} + /> + + + {/* 网站信息 */} + + + + {name} + + + + + + {description} + + + + {/* 标签显示 */} + {labels && labels.length > 0 && ( + + {labels.map((label, index) => ( + + ))} + + )} + + + + + + + + + ); +} diff --git a/easy-dataset-main/components/dataset-square/DatasetSiteList.js b/easy-dataset-main/components/dataset-square/DatasetSiteList.js new file mode 100644 index 0000000..0f77cab --- /dev/null +++ b/easy-dataset-main/components/dataset-square/DatasetSiteList.js @@ -0,0 +1,211 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { Grid, Box, Typography, Skeleton, Divider, Tabs, Tab, Fade, Chip, useTheme, alpha, Paper } from '@mui/material'; +import StorageIcon from '@mui/icons-material/Storage'; +import CategoryIcon from '@mui/icons-material/Category'; +import StarIcon from '@mui/icons-material/Star'; +import { DatasetSiteCard } from './DatasetSiteCard'; +import sites from '@/constant/sites.json'; +import { useTranslation } from 'react-i18next'; + +export function DatasetSiteList() { + const [loading, setLoading] = useState(true); + const theme = useTheme(); + const { t } = useTranslation(); + + // 定义类别 + const CATEGORIES = { + ALL: t('datasetSquare.categories.all'), + POPULAR: t('datasetSquare.categories.popular'), + CHINESE: t('datasetSquare.categories.chinese'), + ENGLISH: t('datasetSquare.categories.english'), + RESEARCH: t('datasetSquare.categories.research'), + MULTIMODAL: t('datasetSquare.categories.multimodal') + }; + const [activeCategory, setActiveCategory] = useState(CATEGORIES.ALL); + + // 模拟加载效果 + useEffect(() => { + const timer = setTimeout(() => { + setLoading(false); + }, 800); + + return () => clearTimeout(timer); + }, []); + + // 处理类别切换 + const handleCategoryChange = (event, newValue) => { + setActiveCategory(newValue); + }; + + // 根据当前选中的类别过滤网站 + const getFilteredSites = () => { + if (activeCategory === CATEGORIES.ALL) { + return sites; + } else if (activeCategory === CATEGORIES.POPULAR) { + return sites.filter(site => site.labels && site.labels.includes(t('datasetSquare.categories.popular'))); + } else if (activeCategory === CATEGORIES.CHINESE) { + return sites.filter(site => site.labels && site.labels.includes(t('datasetSquare.categories.chinese'))); + } else if (activeCategory === CATEGORIES.ENGLISH) { + return sites.filter(site => site.labels && site.labels.includes(t('datasetSquare.categories.english'))); + } else if (activeCategory === CATEGORIES.RESEARCH) { + return sites.filter(site => site.labels && site.labels.includes(t('datasetSquare.categories.research'))); + } else if (activeCategory === CATEGORIES.MULTIMODAL) { + return sites.filter(site => site.labels && site.labels.includes(t('datasetSquare.categories.multimodal'))); + } + return sites; + }; + + const filteredSites = getFilteredSites(); + + return ( + + {/* 类别选择器 */} + + + + + {t('datasetSquare.categoryTitle')} + + + + + + } + iconPosition="start" + /> + } + iconPosition="start" + /> + + + + + + + + + {/* 数据集网站列表 */} + + {loading ? ( + // 加载骨架屏 + + {Array.from(new Array(8)).map((_, index) => ( + + + + + + + + + + + + ))} + + ) : ( + + + {/* 结果数量提示 */} + + + {t('datasetSquare.foundResources', { count: filteredSites.length })}{' '} + + + + {activeCategory !== CATEGORIES.ALL && ( + setActiveCategory(CATEGORIES.ALL)} + sx={{ borderRadius: 1.5 }} + /> + )} + + + {filteredSites.length > 0 ? ( + + {filteredSites.map((site, index) => ( + + + + ))} + + ) : ( + + + + {t('datasetSquare.noDatasets')} + + + {t('datasetSquare.tryOtherCategories')} + + + )} + + + )} + + + ); +} diff --git a/easy-dataset-main/components/datasets/DatasetHeader.js b/easy-dataset-main/components/datasets/DatasetHeader.js new file mode 100644 index 0000000..0d88718 --- /dev/null +++ b/easy-dataset-main/components/datasets/DatasetHeader.js @@ -0,0 +1,97 @@ +'use client'; + +import { Box, Button, Divider, Typography, IconButton, CircularProgress, Paper, Tooltip } from '@mui/material'; +import NavigateBeforeIcon from '@mui/icons-material/NavigateBefore'; +import NavigateNextIcon from '@mui/icons-material/NavigateNext'; +import DeleteIcon from '@mui/icons-material/Delete'; +import UndoIcon from '@mui/icons-material/Undo'; +import { useTranslation } from 'react-i18next'; +import { useRouter } from 'next/navigation'; + +/** + * 数据集详情页面的头部导航组件 + */ +export default function DatasetHeader({ + projectId, + datasetsAllCount, + datasetsConfirmCount, + confirming, + unconfirming, + currentDataset, + shortcutsEnabled, + setShortcutsEnabled, + onNavigate, + onConfirm, + onUnconfirm, + onDelete +}) { + const router = useRouter(); + const { t } = useTranslation(); + + return ( + + + + + + {t('datasets.datasetDetail')} + + {t('datasets.stats', { + total: datasetsAllCount, + confirmed: datasetsConfirmCount, + percentage: ((datasetsConfirmCount / datasetsAllCount) * 100).toFixed(2) + })} + + + {/* 快捷键启用选项 - 已注释掉,保持原代码结构 */} + {/* + {t('datasets.enableShortcuts')} + + + ? + + + + */} + + onNavigate('prev')}> + + + onNavigate('next')}> + + + + + {/* 确认/取消确认按钮 */} + {currentDataset.confirmed ? ( + + ) : ( + + )} + + + + + + ); +} diff --git a/easy-dataset-main/components/datasets/DatasetMetadata.js b/easy-dataset-main/components/datasets/DatasetMetadata.js new file mode 100644 index 0000000..8ecdcf8 --- /dev/null +++ b/easy-dataset-main/components/datasets/DatasetMetadata.js @@ -0,0 +1,77 @@ +'use client'; + +import { Box, Typography, Chip, Tooltip, alpha, CircularProgress } from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import { useTheme } from '@mui/material/styles'; +import { useState } from 'react'; + +/** + * 数据集元数据展示组件 + */ +export default function DatasetMetadata({ currentDataset, onViewChunk }) { + const { t } = useTranslation(); + const theme = useTheme(); + + return ( + + + {t('datasets.metadata')} + + + + {currentDataset.questionLabel && ( + + )} + + + { + try { + // 使用新API接口获取文本块内容 + const response = await fetch( + `/api/projects/${currentDataset.projectId}/chunks/name?chunkName=${encodeURIComponent(currentDataset.chunkName)}` + ); + + if (!response.ok) { + throw new Error(`获取文本块失败: ${response.statusText}`); + } + + const chunkData = await response.json(); + + // 调用父组件的方法显示文本块 + onViewChunk({ + name: currentDataset.chunkName, + content: chunkData.content + }); + } catch (error) { + console.error('获取文本块内容失败:', error); + // 即使API请求失败,也尝试调用查看方法 + onViewChunk({ + name: currentDataset.chunkName, + content: '内容加载失败,请重试' + }); + } + }} + sx={{ cursor: 'pointer' }} + /> + + {currentDataset.confirmed && ( + + )} + + + ); +} diff --git a/easy-dataset-main/components/datasets/DatasetRatingSection.js b/easy-dataset-main/components/datasets/DatasetRatingSection.js new file mode 100644 index 0000000..a43ece3 --- /dev/null +++ b/easy-dataset-main/components/datasets/DatasetRatingSection.js @@ -0,0 +1,330 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { Box, Typography, Divider, Paper, Button, Stack } from '@mui/material'; +import { toast } from 'sonner'; +import PlaylistAddIcon from '@mui/icons-material/PlaylistAdd'; +import AutoFixHighIcon from '@mui/icons-material/AutoFixHigh'; +import StarRating from './StarRating'; +import TagSelector from './TagSelector'; +import NoteInput from './NoteInput'; +import EvalVariantDialog from './EvalVariantDialog'; +import { useTranslation } from 'react-i18next'; +import { useAtomValue } from 'jotai'; +import { selectedModelInfoAtom } from '@/lib/store'; + +/** + * 数据集评分、标签、备注综合组件 + */ +export default function DatasetRatingSection({ dataset, projectId, onUpdate, currentDataset }) { + const { t, i18n } = useTranslation(); + const [availableTags, setAvailableTags] = useState([]); + const [loading, setLoading] = useState(false); + const [addingToEval, setAddingToEval] = useState(false); + const [generatingVariant, setGeneratingVariant] = useState(false); + const [variantDialog, setVariantDialog] = useState({ + open: false, + data: null + }); + + const selectedModel = useAtomValue(selectedModelInfoAtom); + + // 解析数据集中的标签 + const parseDatasetTags = tagsString => { + try { + return JSON.parse(tagsString || '[]'); + } catch (e) { + return []; + } + }; + + // 本地状态管理,从 props 初始化 + const [localScore, setLocalScore] = useState(dataset.score || 0); + const [localTags, setLocalTags] = useState(() => parseDatasetTags(dataset.tags)); + const [localNote, setLocalNote] = useState(dataset.note || ''); + + // 获取项目中已使用的标签 + useEffect(() => { + const fetchAvailableTags = async () => { + try { + const response = await fetch(`/api/projects/${projectId}/datasets/tags`); + if (response.ok) { + const data = await response.json(); + setAvailableTags(data.tags || []); + } + } catch (error) { + console.error('获取可用标签失败:', error); + } + }; + + if (projectId) { + fetchAvailableTags(); + } + }, [projectId]); + + // 同步props中的dataset到本地状态 + useEffect(() => { + setLocalScore(dataset.score || 0); + setLocalTags(parseDatasetTags(dataset.tags)); + setLocalNote(dataset.note || ''); + }, [dataset]); + + // 更新数据集元数据 + const updateMetadata = async updates => { + if (loading) return; + + // 立即更新本地状态,提升响应速度 + if (updates.score !== undefined) { + setLocalScore(updates.score); + } + if (updates.tags !== undefined) { + setLocalTags(updates.tags); + } + if (updates.note !== undefined) { + setLocalNote(updates.note); + } + + setLoading(true); + try { + const response = await fetch(`/api/projects/${projectId}/datasets/${dataset.id}`, { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(updates) + }); + + if (!response.ok) { + throw new Error('更新失败'); + } + + const result = await response.json(); + + // 显示成功提示 + toast.success(t('datasets.updateSuccess', '更新成功')); + + // 如果有父组件的更新回调,调用它 + if (onUpdate) { + onUpdate(result.dataset); + } + } catch (error) { + console.error('更新数据集元数据失败:', error); + // 显示错误提示 + toast.error(t('datasets.updateFailed', '更新失败')); + + // 出错时恢复本地状态 + if (updates.score !== undefined) { + setLocalScore(dataset.score || 0); + } + if (updates.tags !== undefined) { + setLocalTags(parseDatasetTags(dataset.tags)); + } + if (updates.note !== undefined) { + setLocalNote(dataset.note || ''); + } + } finally { + setLoading(false); + } + }; + + // 处理评分变更 + const handleScoreChange = newScore => { + updateMetadata({ score: newScore }); + }; + + // 处理标签变更 + const handleTagsChange = newTags => { + updateMetadata({ tags: newTags }); + }; + + // 处理备注变更 + const handleNoteChange = newNote => { + updateMetadata({ note: newNote }); + }; + + // 添加到评估数据集 + const handleAddToEval = async () => { + if (addingToEval) return; + + setAddingToEval(true); + try { + const response = await fetch(`/api/projects/${projectId}/datasets/${dataset.id}/copy-to-eval`, { + method: 'POST' + }); + + if (!response.ok) { + throw new Error('Failed to add to eval dataset'); + } + + toast.success(t('datasets.addToEvalSuccess', '成功添加到评估数据集')); + + // 更新本地标签显示 + const currentTags = localTags || []; + if (!currentTags.includes('Eval')) { + setLocalTags([...currentTags, 'Eval']); + } + } catch (error) { + console.error('添加评估数据集失败:', error); + toast.error(t('datasets.addToEvalFailed', '添加失败')); + } finally { + setAddingToEval(false); + } + }; + + // 生成评估集变体 + const handleGenerateEvalVariant = async config => { + if (!selectedModel) { + toast.error(t('datasets.selectModelFirst', '请先选择模型')); + throw new Error('No model selected'); + } + + try { + const language = i18n.language === 'zh-CN' ? 'zh-CN' : 'en'; + const response = await fetch(`/api/projects/${projectId}/datasets/generate-eval-variant`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + datasetId: dataset.id, + model: selectedModel, + language, + questionType: config.questionType, + count: config.count + }) + }); + + if (!response.ok) { + throw new Error('Failed to generate variant'); + } + + const { data } = await response.json(); + + // 为每个生成的项添加题型信息,以便保存时使用 + return Array.isArray(data) ? data.map(item => ({ ...item, questionType: config.questionType })) : []; + } catch (error) { + console.error('生成变体失败:', error); + toast.error(t('datasets.generateVariantFailed', '生成变体失败')); + throw error; + } + }; + + // 保存评估集变体 + const handleSaveEvalVariant = async variantItems => { + try { + // 过滤掉 'Eval' 标签,并确保转为逗号分隔的字符串 + const tagsToSync = (localTags || []).filter(tag => tag !== 'Eval').join(','); + + const itemsToSave = variantItems.map(item => ({ + question: item.question, + correctAnswer: item.correctAnswer, + questionType: item.questionType || 'open_ended', + options: item.options, + tags: tagsToSync, + note: dataset.note, + chunkId: null // 变体暂时不关联原始文本块 + })); + + const response = await fetch(`/api/projects/${projectId}/eval-datasets`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ items: itemsToSave }) + }); + + if (!response.ok) { + throw new Error('Failed to save eval dataset'); + } + + const result = await response.json(); + toast.success(t('datasets.saveVariantSuccess', '已保存到评估数据集')); + + // 关闭对话框 + setVariantDialog({ open: false, data: null }); + } catch (error) { + console.error('保存变体失败:', error); + toast.error(t('datasets.saveVariantFailed', '保存失败')); + } + }; + + return ( + + {/* 评分区域 */} + + + {t('datasets.rating', '评分')} + + + + + + + {/* 标签区域 */} + + + {t('datasets.customTags', '自定义标签')} + + + + + + + {/* 备注区域 */} + + + + + + + + + {currentDataset.aiEvaluation && ( + + + {t('datasets.aiEvaluation')} + + + {currentDataset.aiEvaluation} + + + )} + + setVariantDialog({ open: false, data: null })} + onGenerate={handleGenerateEvalVariant} + onSave={handleSaveEvalVariant} + /> + + ); +} diff --git a/easy-dataset-main/components/datasets/EditableField.js b/easy-dataset-main/components/datasets/EditableField.js new file mode 100644 index 0000000..e1e7c2a --- /dev/null +++ b/easy-dataset-main/components/datasets/EditableField.js @@ -0,0 +1,286 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { + Box, + Typography, + Button, + TextField, + IconButton, + Switch, + FormControlLabel, + CircularProgress, + Chip +} from '@mui/material'; +import EditIcon from '@mui/icons-material/Edit'; +import AutoFixHighIcon from '@mui/icons-material/AutoFixHigh'; +import ReactMarkdown from 'react-markdown'; +import { useTranslation } from 'react-i18next'; +import { useTheme } from '@mui/material/styles'; +import 'github-markdown-css/github-markdown-light.css'; + +function getValue(value, answerType, useMarkdown, t, onOptimize) { + if (value) { + if (answerType === 'custom_format' && onOptimize) { + try { + const data = JSON.parse(value); + value = JSON.stringify(data, null, 2); + return ( + + + {JSON.stringify(data, null, 2)} + + + ); + } catch {} + } + if (answerType === 'label' && onOptimize) { + try { + const labels = JSON.parse(value); + if (Array.isArray(labels)) { + return ( + + {labels.map((label, idx) => ( + + ))} + + ); + } + } catch { + return {value}; + } + } + return useMarkdown ? ( +
+ {value} +
+ ) : ( + {value} + ); + } else { + return ( + + {t('common.noData')} + + ); + } +} + +/** + * 可编辑字段组件,支持 Markdown 和原始文本两种展示方式 + */ +export default function EditableField({ + label, + value, + multiline = true, + editing, + onEdit, + onChange, + onSave, + onCancel, + onOptimize, + tokenCount, + optimizing = false, + dataset +}) { + const { t } = useTranslation(); + const theme = useTheme(); + const { answerType } = dataset; + const custom = answerType === 'custom_format' || answerType === 'label'; + + // 从 localStorage 读取 Markdown 展示设置,默认为 false + const [useMarkdown, setUseMarkdown] = useState(() => { + if (typeof window !== 'undefined') { + const saved = localStorage.getItem('dataset-use-markdown'); + return saved ? JSON.parse(saved) : false; + } + return false; + }); + + // 当 useMarkdown 状态改变时,保存到 localStorage + useEffect(() => { + if (typeof window !== 'undefined') { + localStorage.setItem('dataset-use-markdown', JSON.stringify(useMarkdown)); + } + }, [useMarkdown]); + + const toggleMarkdown = () => { + setUseMarkdown(!useMarkdown); + }; + + const getAnswerTypeLabel = type => { + switch (type) { + case 'label': + return t('imageDatasets.typeLabel', '标签'); + case 'custom_format': + return t('imageDatasets.typeCustom', '自定义'); + default: + return t('imageDatasets.typeText', '文本'); + } + }; + + return ( + + + + {label} + + {!editing && value && ( + <> + {onOptimize && ( + + {getAnswerTypeLabel(answerType)} + + )} + {/* 字符数标签 */} + + {value.length} Characters + + + {/* Token 标签 */} + {tokenCount > 0 && ( + + {tokenCount} Tokens + + )} + + )} + {!editing && ( + <> + + + + {onOptimize && !custom && ( + + {optimizing ? : } + + )} + {!custom && ( + + } + label={{useMarkdown ? 'Markdown' : 'Text'}} + sx={{ ml: 1 }} + /> + )} + + )} + + {editing ? ( + <> + + + + + + + ) : ( + + {getValue(value, answerType, useMarkdown, t, onOptimize)} + + )} + + ); +} diff --git a/easy-dataset-main/components/datasets/EvalVariantDialog.js b/easy-dataset-main/components/datasets/EvalVariantDialog.js new file mode 100644 index 0000000..2b6abdb --- /dev/null +++ b/easy-dataset-main/components/datasets/EvalVariantDialog.js @@ -0,0 +1,238 @@ +'use client'; + +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + TextField, + Box, + Typography, + Select, + MenuItem, + FormControl, + InputLabel, + Slider, + Card, + CardContent, + IconButton, + CircularProgress +} from '@mui/material'; +import DeleteIcon from '@mui/icons-material/Delete'; +import { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; + +/** + * 评估集变体编辑对话框 + */ +export default function EvalVariantDialog({ open, onClose, onGenerate, onSave }) { + const { t } = useTranslation(); + const [step, setStep] = useState('config'); // 'config' | 'preview' + const [loading, setLoading] = useState(false); + const [config, setConfig] = useState({ + questionType: 'open_ended', + count: 1 + }); + const [items, setItems] = useState([]); + + // Reset state when dialog opens + useEffect(() => { + if (open) { + setStep('config'); + setConfig({ questionType: 'open_ended', count: 1 }); + setItems([]); + setLoading(false); + } + }, [open]); + + const handleGenerate = async () => { + setLoading(true); + try { + const data = await onGenerate(config); + // Ensure data is an array + const newItems = Array.isArray(data) ? data : [data]; + setItems(newItems); + setStep('preview'); + } catch (error) { + console.error(error); + } finally { + setLoading(false); + } + }; + + const handleSave = () => { + onSave(items); + }; + + const handleItemChange = (index, field, value) => { + const newItems = [...items]; + newItems[index] = { ...newItems[index], [field]: value }; + setItems(newItems); + }; + + const handleDeleteItem = index => { + const newItems = items.filter((_, i) => i !== index); + setItems(newItems); + if (newItems.length === 0) { + setStep('config'); + } + }; + + const renderConfigStep = () => ( + + + {t('datasets.evalVariantConfigHint', '请选择生成的题目类型和数量,AI 将基于当前问答对进行改写。')} + + + + {t('datasets.questionType', '题目类型')} + + + + + + {t('datasets.generateCount', '生成数量')}: {config.count} + + setConfig({ ...config, count: value })} + step={1} + marks + min={1} + max={5} + valueLabelDisplay="auto" + /> + + + ); + + const renderPreviewStep = () => ( + + + {t('datasets.evalVariantPreviewHint', '您可以编辑生成的题目,确认无误后保存到评估集。')} + + + {items.map((item, index) => ( + + + handleDeleteItem(index)} + sx={{ position: 'absolute', right: 8, top: 8 }} + > + + + + + {t('datasets.questionIndex', '题目 {{index}}', { index: index + 1 })} + + + handleItemChange(index, 'question', e.target.value)} + size="small" + /> + + {/* Render Options for choice questions */} + {(item.options || config.questionType.includes('choice')) && ( + { + let val = e.target.value; + try { + // Try to parse if user inputs valid JSON, otherwise keep string + const parsed = JSON.parse(val); + if (Array.isArray(parsed)) val = parsed; + } catch (e) {} + handleItemChange(index, 'options', val); + }} + helperText={t('datasets.optionsHint', '例如: ["选项A", "选项B"]')} + size="small" + /> + )} + + { + let val = e.target.value; + // For multiple choice, answer might be array + if (config.questionType === 'multiple_choice') { + try { + const parsed = JSON.parse(val); + if (Array.isArray(parsed)) val = parsed; + } catch (e) {} + } + handleItemChange(index, 'correctAnswer', val); + }} + helperText={ + config.questionType === 'multiple_choice' + ? t('datasets.answerArrayHint', '多选题答案请输入数组,如 ["A", "C"]') + : config.questionType === 'true_false' + ? t('datasets.answerBoolHint', '判断题答案请输入 ✅ 或 ❌') + : '' + } + size="small" + /> + + + ))} + + ); + + return ( + + + {step === 'config' + ? t('datasets.evalVariantTitle', '生成评估集变体') + : t('datasets.evalVariantPreviewTitle', '确认生成的题目')} + + + {step === 'config' ? renderConfigStep() : renderPreviewStep()} + + + + + {step === 'config' ? ( + + ) : ( + + )} + + + ); +} diff --git a/easy-dataset-main/components/datasets/ImportDatasetDialog.js b/easy-dataset-main/components/datasets/ImportDatasetDialog.js new file mode 100644 index 0000000..c8a425f --- /dev/null +++ b/easy-dataset-main/components/datasets/ImportDatasetDialog.js @@ -0,0 +1,169 @@ +'use client'; + +import { useState } from 'react'; +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + Box, + Typography, + Stepper, + Step, + StepLabel, + Alert +} from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import FileUploadStep from './import/FileUploadStep'; +// import DatasetSourceStep from './import/DatasetSourceStep'; // 不再需要 +import FieldMappingStep from './import/FieldMappingStep'; +import ImportProgressStep from './import/ImportProgressStep'; + +/** + * 数据集导入对话框 + */ +export default function ImportDatasetDialog({ open, onClose, projectId, onImportSuccess }) { + const { t } = useTranslation(); + const [importType, setImportType] = useState('file'); // 只支持文件上传 + const [currentStep, setCurrentStep] = useState(0); + const [importData, setImportData] = useState({ + rawData: null, + previewData: null, + fieldMapping: {}, + sourceInfo: null + }); + const [error, setError] = useState(''); + + const steps = [ + t('import.fileUpload', '文件上传'), + t('import.mapFields', '字段映射'), + t('import.importing', '导入中') + ]; + + const handleNext = () => { + setCurrentStep(prev => prev + 1); + }; + + const handleBack = () => { + setCurrentStep(prev => prev - 1); + }; + + const handleClose = () => { + setCurrentStep(0); + setImportData({ + rawData: null, + previewData: null, + fieldMapping: {}, + sourceInfo: null + }); + setError(''); + onClose(); + }; + + const handleDataLoaded = (data, preview, source) => { + setImportData({ + ...importData, + rawData: data, + previewData: preview, + sourceInfo: source + }); + setError(''); + handleNext(); + }; + + const handleFieldMappingComplete = mapping => { + setImportData({ + ...importData, + fieldMapping: mapping + }); + handleNext(); + }; + + const handleImportComplete = () => { + handleClose(); + if (onImportSuccess) { + onImportSuccess(); + } + }; + + const renderStepContent = () => { + switch (currentStep) { + case 0: + return ; + case 1: + return ( + + ); + case 2: + return ( + + ); + default: + return null; + } + }; + + return ( + + {t('import.title', '导入数据集')} + + + {/* 导入类型选择 - 只保留文件上传 */} + + + {t('import.fileUpload', '文件上传')} + + + {t('import.fileUploadDescription', '上传本地文件导入数据集')} + + + + {/* 步骤指示器 */} + + + {steps.map(label => ( + + {label} + + ))} + + + + {/* 错误提示 */} + {error && ( + + {error} + + )} + + {/* 步骤内容 */} + {renderStepContent()} + + + + + {currentStep > 0 && currentStep < 2 && } + + + ); +} diff --git a/easy-dataset-main/components/datasets/NoteInput.js b/easy-dataset-main/components/datasets/NoteInput.js new file mode 100644 index 0000000..e8e9f62 --- /dev/null +++ b/easy-dataset-main/components/datasets/NoteInput.js @@ -0,0 +1,199 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { Box, TextField, Typography, IconButton, Tooltip, Collapse } from '@mui/material'; +import EditIcon from '@mui/icons-material/Edit'; +import SaveIcon from '@mui/icons-material/Save'; +import CancelIcon from '@mui/icons-material/Cancel'; +import NotesIcon from '@mui/icons-material/Notes'; +import { useTranslation } from 'react-i18next'; + +/** + * 备注输入组件 + */ +export default function NoteInput({ + value = '', + onChange, + placeholder, + readOnly = false, + maxLength = 500, + minRows = 3, + maxRows = 6 +}) { + const { t } = useTranslation(); + const [isEditing, setIsEditing] = useState(false); + const [noteValue, setNoteValue] = useState(value); + const [tempValue, setTempValue] = useState(value); + + // 同步外部value变化 + useEffect(() => { + setNoteValue(value); + setTempValue(value); + }, [value]); + + // 开始编辑 + const handleStartEdit = () => { + setIsEditing(true); + setTempValue(noteValue); + }; + + // 保存备注 + const handleSave = () => { + setNoteValue(tempValue); + setIsEditing(false); + if (onChange) { + onChange(tempValue); + } + }; + + // 取消编辑 + const handleCancel = () => { + setTempValue(noteValue); + setIsEditing(false); + }; + + // 处理键盘快捷键 + const handleKeyDown = event => { + if (event.ctrlKey || event.metaKey) { + if (event.key === 'Enter') { + event.preventDefault(); + handleSave(); + } else if (event.key === 'Escape') { + event.preventDefault(); + handleCancel(); + } + } + }; + + if (readOnly) { + return ( + + + + + {t('datasets.note', '备注')} + + + {noteValue ? ( + + {noteValue} + + ) : ( + + {t('datasets.noNote', '暂无备注')} + + )} + + ); + } + + return ( + + {/* 标题和操作按钮 */} + + + + + {t('datasets.note', '备注')} + + {noteValue && !isEditing && ( + + ({noteValue.length} / {maxLength}) + + )} + + + {!isEditing && ( + + + + + + )} + + + {/* 显示模式 */} + + + {noteValue ? ( + + {noteValue} + + ) : ( + + {placeholder || t('datasets.clickToAddNote', '点击添加备注...')} + + )} + + + + {/* 编辑模式 */} + + + setTempValue(e.target.value)} + onKeyDown={handleKeyDown} + placeholder={placeholder || t('datasets.enterNote', '请输入备注...')} + inputProps={{ maxLength }} + helperText={ + + + {t('datasets.noteShortcuts', 'Ctrl+Enter 保存,Esc 取消')} + + maxLength * 0.9 ? 'warning.main' : 'text.secondary'} + > + {tempValue.length} / {maxLength} + + + } + sx={{ mb: 1 }} + /> + + + + + + + + + maxLength}> + + + + + + + + ); +} diff --git a/easy-dataset-main/components/datasets/OptimizeDialog.js b/easy-dataset-main/components/datasets/OptimizeDialog.js new file mode 100644 index 0000000..b265a9c --- /dev/null +++ b/easy-dataset-main/components/datasets/OptimizeDialog.js @@ -0,0 +1,50 @@ +'use client'; + +import { Dialog, DialogTitle, DialogContent, DialogActions, Button, TextField } from '@mui/material'; +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; + +/** + * AI优化对话框组件 + */ +export default function OptimizeDialog({ open, onClose, onConfirm }) { + const [advice, setAdvice] = useState(''); + const { t } = useTranslation(); + + const handleConfirm = () => { + onConfirm(advice); + setAdvice(''); + onClose(); + }; + + const handleClose = () => { + onClose(); + setAdvice(''); + }; + + return ( + + {t('datasets.optimizeTitle')} + + setAdvice(e.target.value)} + placeholder={t('datasets.optimizePlaceholder')} + /> + + + + + + + ); +} diff --git a/easy-dataset-main/components/datasets/StarRating.js b/easy-dataset-main/components/datasets/StarRating.js new file mode 100644 index 0000000..6edfe93 --- /dev/null +++ b/easy-dataset-main/components/datasets/StarRating.js @@ -0,0 +1,69 @@ +'use client'; + +import { useState } from 'react'; +import { Box, Rating, Typography } from '@mui/material'; +import StarIcon from '@mui/icons-material/Star'; +import { useTranslation } from 'react-i18next'; + +/** + * 五星评分组件 + */ +export default function StarRating({ value = 0, onChange, readOnly = false, size = 'medium', showLabel = true }) { + const { t } = useTranslation(); + const [hover, setHover] = useState(-1); + + const labels = { + 0.5: t('rating.veryPoor', '很差'), + 1: t('rating.poor', '差'), + 1.5: t('rating.belowAverage', '偏差'), + 2: t('rating.fair', '一般'), + 2.5: t('rating.average', '中等'), + 3: t('rating.good', '良好'), + 3.5: t('rating.veryGood', '很好'), + 4: t('rating.excellent', '优秀'), + 4.5: t('rating.outstanding', '杰出'), + 5: t('rating.perfect', '完美') + }; + + const getLabelText = value => { + return `${value} Star${value !== 1 ? 's' : ''}, ${labels[value]}`; + }; + + return ( + + { + if (!readOnly && onChange) { + onChange(newValue || 0); + } + }} + onChangeActive={(event, newHover) => { + if (!readOnly) { + setHover(newHover); + } + }} + readOnly={readOnly} + size={size} + icon={} + emptyIcon={} + sx={{ + '& .MuiRating-iconFilled': { + color: '#ffc107' + }, + '& .MuiRating-iconHover': { + color: '#ffb300' + } + }} + /> + {showLabel && ( + + {labels[hover !== -1 ? hover : value] || (value === 0 ? t('rating.unrated', '未评分') : '')} + + )} + + ); +} diff --git a/easy-dataset-main/components/datasets/TagSelector.js b/easy-dataset-main/components/datasets/TagSelector.js new file mode 100644 index 0000000..054bb50 --- /dev/null +++ b/easy-dataset-main/components/datasets/TagSelector.js @@ -0,0 +1,185 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { Box, Chip, TextField, Autocomplete, Typography, IconButton, Tooltip } from '@mui/material'; +import AddIcon from '@mui/icons-material/Add'; +import CloseIcon from '@mui/icons-material/Close'; +import { useTranslation } from 'react-i18next'; + +/** + * 标签选择器组件 + * 支持从已有标签选择和自定义添加新标签 + */ +export default function TagSelector({ + value = [], + onChange, + availableTags = [], + placeholder, + readOnly = false, + maxTags = 10 +}) { + const { t } = useTranslation(); + const [inputValue, setInputValue] = useState(''); + + // 确保 value 始终是数组 + const normalizeValue = val => { + if (Array.isArray(val)) { + return val; + } + if (typeof val === 'string') { + try { + const parsed = JSON.parse(val); + return Array.isArray(parsed) ? parsed : []; + } catch { + return []; + } + } + return []; + }; + + const [selectedTags, setSelectedTags] = useState(() => normalizeValue(value)); + + // 同步外部value变化 + useEffect(() => { + setSelectedTags(normalizeValue(value)); + }, [value]); + + // 处理标签变更 + const handleTagsChange = newTags => { + setSelectedTags(newTags); + if (onChange) { + onChange(newTags); + } + }; + + // 添加新标签 + const handleAddTag = newTag => { + if (!newTag || newTag.trim() === '') return; + + const trimmedTag = newTag.trim(); + if (selectedTags.includes(trimmedTag)) return; + + if (selectedTags.length >= maxTags) { + return; + } + + const updatedTags = [...selectedTags, trimmedTag]; + handleTagsChange(updatedTags); + setInputValue(''); + }; + + // 删除标签 + const handleDeleteTag = tagToDelete => { + const updatedTags = selectedTags.filter(tag => tag !== tagToDelete); + handleTagsChange(updatedTags); + }; + + // 处理键盘事件 + const handleKeyPress = event => { + if (event.key === 'Enter' && inputValue.trim()) { + event.preventDefault(); + handleAddTag(inputValue); + } + }; + + // 获取可选的标签选项(排除已选择的) + const getAvailableOptions = () => { + return availableTags.filter(tag => !selectedTags.includes(tag)); + }; + + if (readOnly) { + return ( + + {selectedTags.length > 0 ? ( + selectedTags.map((tag, index) => ( + + )) + ) : ( + + {t('tags.noTags', '暂无标签')} + + )} + + ); + } + + return ( + + {/* 已选择的标签 */} + {selectedTags.length > 0 && ( + + {selectedTags.map((tag, index) => ( + handleDeleteTag(tag)} + deleteIcon={} + /> + ))} + + )} + + {/* 标签输入区域 */} + {selectedTags.length < maxTags && ( + + { + setInputValue(newInputValue); + }} + onChange={(event, newValue) => { + if (newValue) { + handleAddTag(newValue); + } + }} + renderInput={params => ( + + )} + renderOption={(props, option) => ( + + {option} + + )} + sx={{ flexGrow: 1 }} + /> + + + handleAddTag(inputValue)} + disabled={!inputValue.trim()} + color="primary" + > + + + + + )} + + {/* 标签数量提示 */} + {selectedTags.length >= maxTags && ( + + {t('tags.maxTagsReached', `最多可添加 ${maxTags} 个标签`)} + + )} + + {/* 可用标签提示 */} + {availableTags.length > 0 && selectedTags.length < maxTags && ( + + {t('tags.availableTagsHint', '可从已有标签中选择,或输入新标签')} + + )} + + ); +} diff --git a/easy-dataset-main/components/datasets/import/FieldMappingStep.js b/easy-dataset-main/components/datasets/import/FieldMappingStep.js new file mode 100644 index 0000000..ede45ed --- /dev/null +++ b/easy-dataset-main/components/datasets/import/FieldMappingStep.js @@ -0,0 +1,314 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { + Box, + Typography, + FormControl, + InputLabel, + Select, + MenuItem, + Paper, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Alert, + Button, + Chip +} from '@mui/material'; +import { useTranslation } from 'react-i18next'; + +/** + * 字段映射步骤组件 + */ +export default function FieldMappingStep({ previewData, onMappingComplete, onError }) { + const { t } = useTranslation(); + const [fieldMapping, setFieldMapping] = useState({ + question: '', + answer: '', + cot: '', + tags: '' + }); + const [availableFields, setAvailableFields] = useState([]); + const [mappingValid, setMappingValid] = useState(false); + + // 智能字段识别(支持 Alpaca: instruction + input -> question,output -> answer) + const smartFieldMapping = fields => { + const mapping = { + question: '', + answer: '', + cot: '', + tags: '' + }; + + const lower = fields.map(f => f.toLowerCase()); + const instructionIdx = lower.findIndex(f => f.includes('instruction')); + const inputIdx = lower.findIndex(f => f.includes('input')); + const outputIdx = lower.findIndex(f => f.includes('output')); + + // Alpaca 格式的优先识别 + if (instructionIdx !== -1 && inputIdx !== -1) { + // 如果同时有instruction和input字段,将它们组合为question + mapping.question = [fields[instructionIdx], fields[inputIdx]]; + } else if (instructionIdx !== -1) { + // 如果只有instruction字段(比如从ShareGPT转换而来),直接映射为question + mapping.question = fields[instructionIdx]; + } + + if (outputIdx !== -1) { + mapping.answer = fields[outputIdx]; + } + + const questionKeywords = ['question', 'input', 'query', 'prompt', 'instruction', '问题', '输入', '指令']; + const answerKeywords = ['answer', 'output', 'response', 'completion', 'target', '答案', '输出', '回答']; + const cotKeywords = ['cot', 'reasoning', 'explanation', 'thinking', 'rationale', '思维链', '推理', '解释']; + const tagKeywords = ['tag', 'tags', 'label', 'labels', 'category', 'categories', '标签', '类别']; + + fields.forEach(field => { + const fieldLower = field.toLowerCase(); + + if (!mapping.question || (typeof mapping.question === 'string' && !mapping.question)) { + if (questionKeywords.some(keyword => fieldLower.includes(keyword))) { + mapping.question = field; + } + } else if (!mapping.answer) { + if (answerKeywords.some(keyword => fieldLower.includes(keyword))) { + mapping.answer = field; + } + } else if (!mapping.cot) { + if (cotKeywords.some(keyword => fieldLower.includes(keyword))) { + mapping.cot = field; + } + } else if (!mapping.tags) { + if (tagKeywords.some(keyword => fieldLower.includes(keyword))) { + mapping.tags = field; + } + } + }); + + return mapping; + }; + + useEffect(() => { + if (previewData && previewData.length > 0) { + const fields = Object.keys(previewData[0]); + setAvailableFields(fields); + + // 智能识别字段映射 + const smartMapping = smartFieldMapping(fields); + setFieldMapping(smartMapping); + } + }, [previewData]); + + useEffect(() => { + // 验证映射是否有效(问题和答案字段必须选择) + const hasQuestion = Array.isArray(fieldMapping.question) + ? fieldMapping.question.length > 0 + : !!fieldMapping.question; + const hasAnswer = !!fieldMapping.answer; + const isValid = hasQuestion && hasAnswer; + setMappingValid(isValid); + }, [fieldMapping]); + + const handleFieldChange = (targetField, sourceField) => { + setFieldMapping(prev => ({ + ...prev, + [targetField]: + targetField === 'question' + ? Array.isArray(sourceField) + ? sourceField.filter(Boolean) + : sourceField + : sourceField + })); + }; + + const handleConfirmMapping = () => { + if (!mappingValid) { + onError(t('import.mappingRequired', '问题和答案字段为必选项')); + return; + } + + // 检查是否有重复映射(兼容数组) + const flatFields = Object.values(fieldMapping) + .filter(Boolean) + .flatMap(f => (Array.isArray(f) ? f.filter(Boolean) : [f])); + const uniqueFields = [...new Set(flatFields)]; + if (flatFields.length !== uniqueFields.length) { + onError(t('import.duplicateMapping', '不能将多个目标字段映射到同一个源字段')); + return; + } + + onMappingComplete(fieldMapping); + }; + + const getFieldDescription = field => { + switch (field) { + case 'question': + return t('import.questionDesc', '用户的问题或输入内容(必选,可多选)'); + case 'answer': + return t('import.answerDesc', 'AI的回答或输出内容(必选)'); + case 'cot': + return t('import.cotDesc', '思维链或推理过程(可选)'); + case 'tags': + return t('import.tagsDesc', '标签数组,多个标签用逗号分隔(可选)'); + default: + return ''; + } + }; + + const isFieldRequired = field => { + return field === 'question' || field === 'answer'; + }; + + if (!previewData || previewData.length === 0) { + return {t('import.noPreviewData', '没有可预览的数据')}; + } + + return ( + + + {t('import.fieldMapping', '字段映射')} + + + + {t( + 'import.mappingDescription', + '请将源数据的字段映射到目标字段。系统已自动识别可能的映射关系,您可以根据需要调整。' + )} + + + {/* 字段映射选择 */} + + + {t('import.selectMapping', '选择字段映射')} + + + + {Object.keys(fieldMapping).map(targetField => ( + + + {t(`import.${targetField}Field`, targetField)} + {isFieldRequired(targetField) && *} + + {targetField === 'question' ? ( + + ) : ( + + )} + + {getFieldDescription(targetField)} + + + ))} + + + + {/* 数据预览 */} + + + {t('import.dataPreview', '数据预览')} + + {t('import.previewNote', '显示前3条记录,每个字段值最多显示100个字符')} + + + + + + + + {availableFields.map(field => ( + + + {field} + {Object.entries(fieldMapping).map(([targetField, sourceField]) => { + const match = Array.isArray(sourceField) ? sourceField.includes(field) : sourceField === field; + if (match) { + return ( + + ); + } + return null; + })} + + + ))} + + + + {previewData.map((row, index) => ( + + {availableFields.map(field => ( + + + {row[field] || '-'} + + + ))} + + ))} + +
+
+
+ + {/* 确认按钮 */} + + + + + {!mappingValid && ( + + {t('import.requiredFields', '请至少选择问题和答案字段的映射')} + + )} +
+ ); +} diff --git a/easy-dataset-main/components/datasets/import/FileUploadStep.js b/easy-dataset-main/components/datasets/import/FileUploadStep.js new file mode 100644 index 0000000..6d85467 --- /dev/null +++ b/easy-dataset-main/components/datasets/import/FileUploadStep.js @@ -0,0 +1,344 @@ +'use client'; + +import { useState, useCallback } from 'react'; +import { + Box, + Typography, + Button, + Paper, + List, + ListItem, + ListItemIcon, + ListItemText, + LinearProgress, + Alert +} from '@mui/material'; +import { CloudUpload as UploadIcon, Description as FileIcon, CheckCircle as CheckIcon } from '@mui/icons-material'; +import { useTranslation } from 'react-i18next'; +// import { useDropzone } from 'react-dropzone'; + +/** + * 文件上传步骤组件 + */ +export default function FileUploadStep({ onDataLoaded, onError }) { + const { t } = useTranslation(); + const [uploading, setUploading] = useState(false); + const [uploadedFiles, setUploadedFiles] = useState([]); + + // 健壮的CSV解析函数,支持多行字段和引号转义 + const parseCSV = text => { + const result = []; + const lines = []; + let currentLine = ''; + let inQuotes = false; + + // 逐字符解析,正确处理引号内的换行符 + for (let i = 0; i < text.length; i++) { + const char = text[i]; + const nextChar = text[i + 1]; + + if (char === '"') { + if (inQuotes && nextChar === '"') { + // 转义的引号 + currentLine += '"'; + i++; // 跳过下一个引号 + } else { + // 切换引号状态 + inQuotes = !inQuotes; + } + } else if (char === '\n' && !inQuotes) { + // 行结束(不在引号内) + if (currentLine.trim()) { + lines.push(currentLine); + } + currentLine = ''; + } else { + currentLine += char; + } + } + + // 添加最后一行 + if (currentLine.trim()) { + lines.push(currentLine); + } + + if (lines.length < 2) { + throw new Error('CSV文件格式不正确,至少需要标题行和一行数据'); + } + + // 解析标题行 + const headers = parseCSVLine(lines[0]); + + // 解析数据行 + for (let i = 1; i < lines.length; i++) { + const values = parseCSVLine(lines[i]); + if (values.length > 0) { + const obj = {}; + headers.forEach((header, index) => { + obj[header] = values[index] || ''; + }); + result.push(obj); + } + } + + return result; + }; + + // 解析单行CSV,处理逗号分隔和引号转义 + const parseCSVLine = line => { + const result = []; + let current = ''; + let inQuotes = false; + + for (let i = 0; i < line.length; i++) { + const char = line[i]; + const nextChar = line[i + 1]; + + if (char === '"') { + if (inQuotes && nextChar === '"') { + // 转义的引号 + current += '"'; + i++; // 跳过下一个引号 + } else { + // 切换引号状态 + inQuotes = !inQuotes; + } + } else if (char === ',' && !inQuotes) { + // 字段分隔符(不在引号内) + result.push(current.trim()); + current = ''; + } else { + current += char; + } + } + + // 添加最后一个字段 + result.push(current.trim()); + + return result; + }; + + // 检测并转换ShareGPT格式为Alpaca格式 + const convertShareGPTToAlpaca = item => { + // 检查是否包含conversations字段且格式正确 + if (item.conversations && Array.isArray(item.conversations)) { + const conversations = item.conversations; + + // 查找system、human、gpt消息 + let systemMessage = ''; + let instruction = ''; + let output = ''; + + for (const conv of conversations) { + if (conv.from === 'system' && conv.value) { + systemMessage = conv.value; + } else if (conv.from === 'human' && conv.value) { + instruction = conv.value; + } else if (conv.from === 'gpt' && conv.value) { + output = conv.value; + break; // 只取第一轮对话 + } + } + + // 如果有system消息,将其作为instruction的前缀 + if (systemMessage && instruction) { + instruction = `${systemMessage}\n\n${instruction}`; + } else if (systemMessage && !instruction) { + instruction = systemMessage; + } + + // 转换为Alpaca格式 + return { + instruction: instruction || '', + input: '', // ShareGPT格式通常没有单独的input字段 + output: output || '', + // 保留其他字段 + ...Object.fromEntries(Object.entries(item).filter(([key]) => key !== 'conversations')) + }; + } + + return item; // 如果不是ShareGPT格式,返回原始数据 + }; + + const parseFileContent = async file => { + const text = await file.text(); + const extension = file.name.split('.').pop().toLowerCase(); + + try { + let data = []; + + if (extension === 'json') { + const parsed = JSON.parse(text); + data = Array.isArray(parsed) ? parsed : [parsed]; + } else if (extension === 'jsonl') { + data = text + .split('\n') + .filter(line => line.trim()) + .map(line => JSON.parse(line)); + } else if (extension === 'csv') { + // 更健壮的CSV解析,支持多行字段和引号转义 + data = parseCSV(text); + if (data.length === 0) { + throw new Error('CSV文件格式不正确或没有数据'); + } + } else { + throw new Error('不支持的文件格式'); + } + + if (data.length === 0) { + throw new Error('文件中没有找到有效数据'); + } + + // 检测并转换ShareGPT格式为Alpaca格式 + data = data.map(convertShareGPTToAlpaca); + + // 生成预览数据(取前3条记录,每个字段值截取前100字符) + const previewData = data.slice(0, 3).map(item => { + const preview = {}; + Object.keys(item).forEach(key => { + const value = String(item[key] || ''); + preview[key] = value.length > 100 ? value.substring(0, 100) + '...' : value; + }); + return preview; + }); + + return { + data, + preview: previewData, + source: { + type: 'file', + fileName: file.name, + fileSize: file.size, + totalRecords: data.length + } + }; + } catch (error) { + throw new Error(`解析文件失败: ${error.message}`); + } + }; + + const handleFileSelect = async event => { + const files = event.target.files; + if (!files || files.length === 0) return; + + const file = files[0]; + setUploading(true); + + try { + const result = await parseFileContent(file); + setUploadedFiles([ + { + name: file.name, + size: file.size, + status: 'success' + } + ]); + + onDataLoaded(result.data, result.preview, result.source); + } catch (error) { + setUploadedFiles([ + { + name: file.name, + size: file.size, + status: 'error', + error: error.message + } + ]); + onError(error.message); + } finally { + setUploading(false); + } + }; + + const formatFileSize = bytes => { + if (bytes === 0) return '0 Bytes'; + const k = 1024; + const sizes = ['Bytes', 'KB', 'MB', 'GB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; + }; + + return ( + + + {t('import.uploadFile', '上传文件')} + + + + {t('import.supportedFormats', '支持 JSON、JSONL、CSV 格式文件')} + + + {/* 文件上传区域 */} + document.getElementById('file-upload-input').click()} + > + + + + {t('import.dragDropFile', '拖拽文件到此处或点击选择文件')} + + + {t('import.maxFileSize', '最大文件大小: 50MB')} + + + + {/* 上传进度 */} + {uploading && ( + + + {t('import.processingFile', '正在处理文件...')} + + + + )} + + {/* 已上传文件列表 */} + {uploadedFiles.length > 0 && ( + + + {t('import.uploadedFiles', '已上传文件')} + + + {uploadedFiles.map((file, index) => ( + + + {file.status === 'success' ? : } + + + + ))} + + + {uploadedFiles.some(f => f.status === 'error') && ( + + {t('import.uploadError', '文件上传失败,请检查文件格式是否正确')} + + )} + + )} + + ); +} diff --git a/easy-dataset-main/components/datasets/import/ImportProgressStep.js b/easy-dataset-main/components/datasets/import/ImportProgressStep.js new file mode 100644 index 0000000..28bb6a9 --- /dev/null +++ b/easy-dataset-main/components/datasets/import/ImportProgressStep.js @@ -0,0 +1,303 @@ +'use client'; + +import { useState, useEffect, useRef } from 'react'; +import { + Box, + Typography, + LinearProgress, + Alert, + Paper, + List, + ListItem, + ListItemIcon, + ListItemText, + Chip +} from '@mui/material'; +import { CheckCircle as CheckIcon, Error as ErrorIcon, Info as InfoIcon } from '@mui/icons-material'; +import { useTranslation } from 'react-i18next'; + +/** + * 导入进度步骤组件 + */ +export default function ImportProgressStep({ projectId, rawData, fieldMapping, sourceInfo, onComplete, onError }) { + const { t } = useTranslation(); + const [progress, setProgress] = useState(0); + const [currentStep, setCurrentStep] = useState(''); + const [importStats, setImportStats] = useState({ + total: 0, + processed: 0, + success: 0, + failed: 0, + skipped: 0, + errors: [] + }); + const [completed, setCompleted] = useState(false); + const startedRef = useRef(false); // 防止在开发模式下因严格模式导致重复执行 + + useEffect(() => { + if (!startedRef.current && rawData && fieldMapping && projectId) { + startedRef.current = true; + startImport(); + } + }, [rawData, fieldMapping, projectId]); + + const startImport = async () => { + try { + setCurrentStep(t('import.preparingData', '准备数据...')); + setImportStats(prev => ({ ...prev, total: rawData.length })); + + // 转换数据格式 + const convertedData = rawData.map(item => { + // 支持 question 映射多个字段,拼接为一个字符串 + const qFields = fieldMapping.question; + const question = Array.isArray(qFields) + ? qFields + .map(f => item[f] || '') + .filter(v => v && String(v).trim()) + .join('\n') + : item[qFields] || ''; + + const converted = { + question, + answer: item[fieldMapping.answer] || '', + cot: fieldMapping.cot ? item[fieldMapping.cot] || '' : '', + questionLabel: '', // 默认标签,后续可以通过AI生成 + chunkName: sourceInfo?.datasetName || sourceInfo?.fileName || 'Imported Data', + chunkContent: `Imported from ${sourceInfo?.type || 'file'}`, + model: 'imported', + confirmed: false, + score: 0, + tags: fieldMapping.tags ? JSON.stringify(parseTagsField(item[fieldMapping.tags])) : '[]', + note: '', + other: JSON.stringify(getOtherFields(item, fieldMapping)) + }; + + // 不在前端抛错,由后端负责校验并统计 skipped + return converted; + }); + + setProgress(25); + setCurrentStep(t('import.uploadingData', '上传数据...')); + + // 分批上传数据 + const batchSize = 500; + let processed = 0; + let success = 0; + let failed = 0; + let skipped = 0; + const errors = []; + + for (let i = 0; i < convertedData.length; i += batchSize) { + const batch = convertedData.slice(i, i + batchSize); + + try { + const response = await fetch(`/api/projects/${projectId}/datasets/import`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + datasets: batch, + sourceInfo + }) + }); + + if (!response.ok) { + throw new Error(`批次上传失败: ${response.statusText}`); + } + + const result = await response.json(); + success += result.success || 0; + failed += typeof result.failed === 'number' ? result.failed : result.errors?.length || 0; + skipped += result.skipped || 0; + processed += batch.length; + + if (result.errors && result.errors.length > 0) { + errors.push(...result.errors); + } + } catch (error) { + failed += batch.length; + processed += batch.length; + errors.push(`批次 ${Math.floor(i / batchSize) + 1}: ${error.message}`); + } + + // 更新进度 + const progressPercent = 25 + (processed / convertedData.length) * 70; + setProgress(progressPercent); + setImportStats({ + total: convertedData.length, + processed, + success, + failed, + skipped, + errors + }); + + setCurrentStep( + t('import.processing', '处理中... {{processed}}/{{total}}', { + processed, + total: convertedData.length + }) + ); + } + + setProgress(100); + setCurrentStep(t('import.completed', '导入完成')); + setCompleted(true); + + // 延迟一下再调用完成回调,让用户看到完成状态 + setTimeout(() => { + onComplete(); + }, 2000); + } catch (error) { + onError(error.message); + setImportStats(prev => ({ + ...prev, + errors: [...prev.errors, error.message] + })); + } + }; + + // 解析标签字段 + const parseTagsField = tagsValue => { + if (!tagsValue) return []; + + if (Array.isArray(tagsValue)) { + return tagsValue; + } + + if (typeof tagsValue === 'string') { + return tagsValue + .split(',') + .map(tag => tag.trim()) + .filter(tag => tag); + } + + return []; + }; + + // 获取其他字段(兼容数组映射) + const getOtherFields = (item, mapping) => { + const used = []; + Object.values(mapping).forEach(field => { + if (!field) return; + if (Array.isArray(field)) used.push(...field); + else used.push(field); + }); + const mappedFields = new Set(used); + const otherFields = {}; + + Object.keys(item).forEach(key => { + if (!mappedFields.has(key)) { + otherFields[key] = item[key]; + } + }); + + return otherFields; + }; + + return ( + + + {t('import.importing', '正在导入数据集')} + + + {/* 进度条 */} + + + {currentStep} + + + + {Math.round(progress)}% {t('import.complete', '完成')} + + + + {/* 导入统计 */} + + + {t('import.importStats', '导入统计')} + + + + } + label={t('import.total', '总计: {{count}}', { count: importStats.total })} + variant="outlined" + /> + } + label={t('import.success', '成功: {{count}}', { count: importStats.success })} + color="success" + variant="outlined" + /> + {importStats.skipped > 0 && ( + } + label={t('import.skipped', '跳过: {{count}}', { count: importStats.skipped })} + color="warning" + variant="outlined" + /> + )} + {importStats.failed > 0 && ( + } + label={t('import.failed', '失败: {{count}}', { count: importStats.failed })} + color="error" + variant="outlined" + /> + )} + + + {sourceInfo && ( + + + {t('import.source', '数据源')}:{' '} + {sourceInfo.type === 'file' ? sourceInfo.fileName : sourceInfo.datasetName} + + {sourceInfo.description && ( + + {t('import.description', '描述')}: {sourceInfo.description} + + )} + + )} + + + {/* 错误列表 */} + {importStats.errors.length > 0 && ( + + + {t('import.errors', '错误信息')} + + + {importStats.errors.slice(0, 10).map((error, index) => ( + + + + + + + ))} + + {importStats.errors.length > 10 && ( + + {t('import.moreErrors', '还有 {{count}} 个错误未显示...', { + count: importStats.errors.length - 10 + })} + + )} + + )} + + {/* 完成提示 */} + {completed && ( + + {t('import.importSuccess', '数据集导入完成!成功导入 {{success}} 条记录。', { + success: importStats.success + })} + + )} + + ); +} diff --git a/easy-dataset-main/components/datasets/utils/ratingUtils.js b/easy-dataset-main/components/datasets/utils/ratingUtils.js new file mode 100644 index 0000000..9af83a1 --- /dev/null +++ b/easy-dataset-main/components/datasets/utils/ratingUtils.js @@ -0,0 +1,135 @@ +/** + * 评分相关的工具函数 + */ + +/** + * 根据评分获取对应的颜色和标签(不包含国际化) + * @param {number} score - 评分 (0-5) + * @returns {object} - 包含颜色、背景色和标签的对象 + */ +export const getRatingConfig = score => { + if (score >= 4.5) { + return { + color: '#2e7d32', // 深绿色 + backgroundColor: '#e8f5e8', + label: '优秀', + variant: 'excellent' + }; + } else if (score >= 3.5) { + return { + color: '#388e3c', // 绿色 + backgroundColor: '#f1f8e9', + label: '良好', + variant: 'good' + }; + } else if (score >= 2.5) { + return { + color: '#f57c00', // 橙色 + backgroundColor: '#fff3e0', + label: '一般', + variant: 'average' + }; + } else if (score >= 1.5) { + return { + color: '#f44336', // 红色 + backgroundColor: '#ffebee', + label: '较差', + variant: 'poor' + }; + } else if (score > 0) { + return { + color: '#d32f2f', // 深红色 + backgroundColor: '#ffebee', + label: '很差', + variant: 'very-poor' + }; + } else { + return { + color: '#757575', // 灰色 + backgroundColor: '#f5f5f5', + label: '未评分', + variant: 'unrated' + }; + } +}; + +/** + * 根据评分获取对应的颜色和国际化标签 + * @param {number} score - 评分 (0-5) + * @param {function} t - 国际化翻译函数 + * @returns {object} - 包含颜色、背景色和国际化标签的对象 + */ +export const getRatingConfigI18n = (score, t) => { + const baseConfig = getRatingConfig(score); + + // 根据variant获取对应的翻译键 + let translationKey; + let fallbackText; + + switch (baseConfig.variant) { + case 'excellent': + translationKey = 'datasets.ratingExcellent'; + fallbackText = '优秀'; + break; + case 'good': + translationKey = 'datasets.ratingGood'; + fallbackText = '良好'; + break; + case 'average': + translationKey = 'datasets.ratingAverage'; + fallbackText = '一般'; + break; + case 'poor': + translationKey = 'datasets.ratingPoor'; + fallbackText = '较差'; + break; + case 'very-poor': + translationKey = 'datasets.ratingVeryPoor'; + fallbackText = '很差'; + break; + case 'unrated': + translationKey = 'datasets.ratingUnrated'; + fallbackText = '未评分'; + break; + default: + translationKey = 'datasets.ratingUnrated'; + fallbackText = '未评分'; + } + + return { + ...baseConfig, + label: t(translationKey, fallbackText) + }; +}; + +/** + * 格式化评分显示 + * @param {number} score - 评分 + * @returns {string} - 格式化后的评分字符串 + */ +export const formatScore = score => { + if (score === 0) return ''; + return score.toFixed(1); +}; + +/** + * 获取评分范围的描述 + * @param {number} score - 评分 + * @returns {string} - 评分范围描述 + */ +export const getScoreDescription = score => { + const config = getRatingConfig(score); + return `${formatScore(score)} - ${config.label}`; +}; + +/** + * 评分范围常量 + */ +export const SCORE_RANGES = { + EXCELLENT: { min: 4.5, max: 5.0, label: '优秀' }, + GOOD: { min: 3.5, max: 4.4, label: '良好' }, + AVERAGE: { min: 2.5, max: 3.4, label: '一般' }, + POOR: { min: 1.5, max: 2.4, label: '较差' }, + VERY_POOR: { min: 0.1, max: 1.4, label: '很差' }, + UNRATED: { min: 0, max: 0, label: '未评分' } +}; diff --git a/easy-dataset-main/components/distill/AutoDistillDialog.js b/easy-dataset-main/components/distill/AutoDistillDialog.js new file mode 100644 index 0000000..3f45a7f --- /dev/null +++ b/easy-dataset-main/components/distill/AutoDistillDialog.js @@ -0,0 +1,325 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + TextField, + Button, + Typography, + Box, + Alert, + Paper, + Divider, + FormControl, + FormLabel, + RadioGroup, + FormControlLabel, + Radio +} from '@mui/material'; + +/** + * 全自动蒸馏数据集配置弹框 + * @param {Object} props + * @param {boolean} props.open - 对话框是否打开 + * @param {Function} props.onClose - 关闭对话框的回调 + * @param {Function} props.onStart - 开始蒸馏任务的回调 + * @param {Function} props.onStartBackground - 开始后台蒸馏任务的回调 + * @param {string} props.projectId - 项目ID + * @param {Object} props.project - 项目信息 + * @param {Object} props.stats - 当前统计信息 + */ +export default function AutoDistillDialog({ + open, + onClose, + onStart, + onStartBackground, + projectId, + project, + stats = {} +}) { + const { t } = useTranslation(); + + // 表单状态 + const [topic, setTopic] = useState(''); + const [levels, setLevels] = useState(2); + const [tagsPerLevel, setTagsPerLevel] = useState(10); + const [questionsPerTag, setQuestionsPerTag] = useState(10); + const [datasetType, setDatasetType] = useState('single-turn'); // 'single-turn' | 'multi-turn' | 'both' + + // 计算信息 + const [estimatedTags, setEstimatedTags] = useState(0); // 所有标签总数(包括根节点和中间节点) + const [leafTags, setLeafTags] = useState(0); // 叶子节点数量(即最后一层标签数) + const [estimatedQuestions, setEstimatedQuestions] = useState(0); + const [newTags, setNewTags] = useState(0); + const [newQuestions, setNewQuestions] = useState(0); + const [error, setError] = useState(''); + + // 初始化默认主题 + useEffect(() => { + if (project && project.name) { + setTopic(project.name); + } + }, [project]); + + // 计算预估标签和问题数量 + useEffect(() => { + /* + * 根据公式:总问题数 = \left( \prod_{i=1}^{n} L_i \right) \times Q + * 当每层标签数量相同(L)时:总问题数 = L^n \times Q + */ + + const leafTags = Math.pow(tagsPerLevel, levels); + + // 总问题数 = 叶子节点数 * 每个节点的问题数 + const totalQuestions = leafTags * questionsPerTag; + + let totalTags; + if (tagsPerLevel === 1) { + // 如果每层只有1个标签,总数就是 levels+1 + totalTags = levels + 1; + } else { + // 使用等比数列求和公式 + totalTags = (1 - Math.pow(tagsPerLevel, levels + 1)) / (1 - tagsPerLevel); + } + + setLeafTags(leafTags); + setEstimatedTags(leafTags); // 改为只显示叶子节点数量,而非所有节点数量 + setEstimatedQuestions(totalQuestions); + + // 计算新增标签和问题数量 + const currentTags = stats.tagsCount || 0; + const currentQuestions = stats.questionsCount || 0; + + // 只考虑最后一层的标签数量 + setNewTags(Math.max(0, leafTags - currentTags)); + setNewQuestions(Math.max(0, totalQuestions - currentQuestions)); + + // 验证是否可以执行任务 + if (leafTags <= currentTags && totalQuestions <= currentQuestions) { + setError(t('distill.autoDistillInsufficientError')); + } else { + setError(''); + } + }, [levels, tagsPerLevel, questionsPerTag, stats, t]); + + // 处理开始任务 + const handleStart = () => { + if (error) return; + + onStart({ + topic, + levels, + tagsPerLevel, + questionsPerTag, + estimatedTags, + estimatedQuestions, + datasetType + }); + }; + + // 处理开始后台任务 + const handleStartBackground = () => { + if (error) return; + + onStartBackground({ + topic, + levels, + tagsPerLevel, + questionsPerTag, + estimatedTags, + estimatedQuestions, + datasetType + }); + }; + + return ( + + {t('distill.autoDistillTitle')} + + + {/* 左侧:输入区域 */} + + setTopic(e.target.value)} + fullWidth + margin="normal" + required + disabled + helperText={t('distill.rootTopicHelperText')} + /> + + + {t('distill.tagLevels')} + { + const value = Math.min(5, Math.max(1, Number(e.target.value))); + setLevels(value); + }} + helperText={t('distill.tagLevelsHelper', { max: 5 })} + /> + + + + {t('distill.tagsPerLevel')} + { + const value = Math.min(50, Math.max(1, Number(e.target.value))); + setTagsPerLevel(value); + }} + helperText={t('distill.tagsPerLevelHelper', { max: 50 })} + /> + + + + {t('distill.questionsPerTag')} + { + const value = Math.min(50, Math.max(1, Number(e.target.value))); + setQuestionsPerTag(value); + }} + helperText={t('distill.questionsPerTagHelper', { max: 50 })} + /> + + + + + + {t('distill.datasetType', { defaultValue: '数据集类型' })} + + setDatasetType(e.target.value)}> + } + label={t('distill.singleTurnDataset', { defaultValue: '单轮对话数据集' })} + /> + } + label={t('distill.multiTurnDataset', { defaultValue: '多轮对话数据集' })} + /> + } + label={t('distill.bothDatasetTypes', { defaultValue: '两种数据集都生成' })} + /> + + + + + + {/* 右侧:预估信息区域 */} + + + + {t('distill.estimationInfo')} + + + + + + {t('distill.estimatedTags')}: + + {estimatedTags} + + + + + {t('distill.estimatedQuestions')}: + + {estimatedQuestions} + + + + + + + {t('distill.currentTags')}: + + {stats.tagsCount || 0} + + + + + {t('distill.currentQuestions')}: + + {stats.questionsCount || 0} + + + + + + + + {t('distill.newTags')}: + + + {newTags} + + + + + + {t('distill.newQuestions')}: + + + {newQuestions} + + + + + + + {error && ( + + {error} + + )} + + + + + + + + + + ); +} diff --git a/easy-dataset-main/components/distill/AutoDistillProgress.js b/easy-dataset-main/components/distill/AutoDistillProgress.js new file mode 100644 index 0000000..2207e4f --- /dev/null +++ b/easy-dataset-main/components/distill/AutoDistillProgress.js @@ -0,0 +1,212 @@ +'use client'; + +import { useState, useEffect, useRef } from 'react'; +import { useTranslation } from 'react-i18next'; +import { + Dialog, + DialogTitle, + DialogContent, + Box, + Typography, + LinearProgress, + Paper, + Divider, + IconButton, + Button +} from '@mui/material'; +import CloseIcon from '@mui/icons-material/Close'; + +/** + * 全自动蒸馏进度组件 + * @param {Object} props + * @param {boolean} props.open - 对话框是否打开 + * @param {Function} props.onClose - 关闭对话框的回调 + * @param {Object} props.progress - 进度信息 + */ +export default function AutoDistillProgress({ open, onClose, progress = {} }) { + const { t } = useTranslation(); + const logContainerRef = useRef(null); + + // 自动滚动到底部 + useEffect(() => { + if (logContainerRef.current) { + logContainerRef.current.scrollTop = logContainerRef.current.scrollHeight; + } + }, [progress.logs]); + + const getStageText = () => { + const { stage } = progress; + switch (stage) { + case 'level1': + return t('distill.stageBuildingLevel1'); + case 'level2': + return t('distill.stageBuildingLevel2'); + case 'level3': + return t('distill.stageBuildingLevel3'); + case 'level4': + return t('distill.stageBuildingLevel4'); + case 'level5': + return t('distill.stageBuildingLevel5'); + case 'questions': + return t('distill.stageBuildingQuestions'); + case 'datasets': + return t('distill.stageBuildingDatasets'); + case 'multi-turn-datasets': + return t('distill.stageBuildingMultiTurnDatasets', { defaultValue: '生成多轮对话数据集中...' }); + case 'completed': + return t('distill.stageCompleted'); + default: + return t('distill.stageInitializing'); + } + }; + + const getOverallProgress = () => { + const { tagsBuilt, tagsTotal, questionsBuilt, questionsTotal, datasetsBuilt, datasetsTotal } = progress; + + // 整体进度按比例计算:标签构建占30%,问题生成占35%,数据集生成占35% + let tagProgress = tagsTotal ? (tagsBuilt / tagsTotal) * 30 : 0; + let questionProgress = questionsTotal ? (questionsBuilt / questionsTotal) * 35 : 0; + let datasetProgress = datasetsTotal ? (datasetsBuilt / datasetsTotal) * 35 : 0; + + return Math.min(100, Math.round(tagProgress + questionProgress + datasetProgress)); + }; + + return ( + + + + {t('distill.autoDistillProgress')} + {(progress.stage === 'completed' || !progress.stage) && ( + + + + )} + + + + + {/* 整体进度 */} + + + {t('distill.overallProgress')} + + + + + + + {getOverallProgress()}% + + + + + 0 ? 'repeat(4, 1fr)' : 'repeat(3, 1fr)', + gap: 2 + }} + > + + + {t('distill.tagsProgress')} + + + {progress.tagsBuilt || 0} / {progress.tagsTotal || 0} + + + + + + {t('distill.questionsProgress')} + + + {progress.questionsBuilt || 0} / {progress.questionsTotal || 0} + + + + + + {t('distill.datasetsProgress')} + + + {progress.datasetsBuilt || 0} / {progress.datasetsTotal || 0} + + + + {progress.multiTurnDatasetsTotal > 0 && ( + + + {t('distill.multiTurnDatasetsProgress', { defaultValue: '多轮对话进度' })} + + + {progress.multiTurnDatasetsBuilt || 0} / {progress.multiTurnDatasetsTotal || 0} + + + )} + + + + {/* 当前阶段 */} + + + {t('distill.currentStage')} + + + + {getStageText()} + + + + {/* 实时日志 */} + + + {t('distill.realTimeLogs')} + + + + {progress.logs?.length > 0 ? ( + progress.logs.map((log, index) => { + // 检测成功日志,显示为绿色 Successfully + let color = 'inherit'; + if (log.includes('成功') || log.includes('完成') || log.includes('Successfully')) { + color = '#4caf50'; + } + if (log.includes('失败') || log.toLowerCase().includes('error')) { + color = '#f44336'; + } + return ( + + {log} + + ); + }) + ) : ( + + {t('distill.waitingForLogs')} + + )} + + + + + + ); +} diff --git a/easy-dataset-main/components/distill/ConfirmDialog.js b/easy-dataset-main/components/distill/ConfirmDialog.js new file mode 100644 index 0000000..2180b9a --- /dev/null +++ b/easy-dataset-main/components/distill/ConfirmDialog.js @@ -0,0 +1,37 @@ +'use client'; + +import { Dialog, DialogActions, DialogTitle, Button } from '@mui/material'; + +/** + * 通用确认对话框组件 + * @param {Object} props + * @param {boolean} props.open - 对话框是否打开 + * @param {Function} props.onClose - 关闭对话框的回调 + * @param {Function} props.onConfirm - 确认操作的回调 + * @param {string} props.title - 对话框标题 + * @param {string} props.cancelText - 取消按钮文本 + * @param {string} props.confirmText - 确认按钮文本 + */ +export default function ConfirmDialog({ + open, + onClose, + onConfirm, + title, + cancelText = '取消', + confirmText = '确认', + confirmColor = 'error' +}) { + return ( + + {title} + + + + + + ); +} diff --git a/easy-dataset-main/components/distill/DistillTreeView.js b/easy-dataset-main/components/distill/DistillTreeView.js new file mode 100644 index 0000000..12cd6f2 --- /dev/null +++ b/easy-dataset-main/components/distill/DistillTreeView.js @@ -0,0 +1,515 @@ +'use client'; + +import { useState, useEffect, useCallback, useMemo, forwardRef, useImperativeHandle } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Box, Typography, List } from '@mui/material'; +import axios from 'axios'; +import { useAtomValue } from 'jotai'; +import { selectedModelInfoAtom } from '@/lib/store'; +import { useGenerateDataset } from '@/hooks/useGenerateDataset'; +import { toast } from 'sonner'; + +// 导入子组件 +import TagTreeItem from './TagTreeItem'; +import TagMenu from './TagMenu'; +import TagEditDialog from './TagEditDialog'; +import ConfirmDialog from './ConfirmDialog'; +import { sortTagsByNumber } from './utils'; + +/** + * 蒸馏树形视图组件 + * @param {Object} props + * @param {string} props.projectId - 项目ID + * @param {Array} props.tags - 标签列表 + * @param {Function} props.onGenerateSubTags - 生成子标签的回调函数 + * @param {Function} props.onGenerateQuestions - 生成问题的回调函数 + * @param {Function} props.onTagsUpdate - 标签更新的回调函数 + */ +const DistillTreeView = forwardRef(function DistillTreeView( + { projectId, tags = [], onGenerateSubTags, onGenerateQuestions, onTagsUpdate }, + ref +) { + const { t } = useTranslation(); + const selectedModel = useAtomValue(selectedModelInfoAtom); + const [expandedTags, setExpandedTags] = useState({}); + const [tagQuestions, setTagQuestions] = useState({}); + const [loadingTags, setLoadingTags] = useState({}); + const [loadingQuestions, setLoadingQuestions] = useState({}); + const [menuAnchorEl, setMenuAnchorEl] = useState(null); + const [selectedTagForMenu, setSelectedTagForMenu] = useState(null); + const [allQuestions, setAllQuestions] = useState([]); + const [loading, setLoading] = useState(false); + const [processingQuestions, setProcessingQuestions] = useState({}); + const [processingMultiTurnQuestions, setProcessingMultiTurnQuestions] = useState({}); + const [deleteQuestionConfirmOpen, setDeleteQuestionConfirmOpen] = useState(false); + const [questionToDelete, setQuestionToDelete] = useState(null); + const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false); + const [tagToDelete, setTagToDelete] = useState(null); + const [editDialogOpen, setEditDialogOpen] = useState(false); + const [tagToEdit, setTagToEdit] = useState(null); + const [project, setProject] = useState(null); + const [projectName, setProjectName] = useState(''); + + // 使用生成数据集的hook + const { generateSingleDataset } = useGenerateDataset(); + + // 获取问题统计信息 + const fetchQuestionsStats = useCallback(async () => { + try { + setLoading(true); + const response = await axios.get(`/api/projects/${projectId}/questions/tree?isDistill=true`); + setAllQuestions(response.data); + console.log('获取问题统计信息成功:', { totalQuestions: response.data.length }); + } catch (error) { + console.error('获取问题统计信息失败:', error); + } finally { + setLoading(false); + } + }, [projectId]); + + // 暴露方法给父组件 + useImperativeHandle(ref, () => ({ + fetchQuestionsStats + })); + + // 获取标签下的问题 + const fetchQuestionsByTag = useCallback( + async tagId => { + try { + setLoadingQuestions(prev => ({ ...prev, [tagId]: true })); + const response = await axios.get(`/api/projects/${projectId}/distill/questions/by-tag?tagId=${tagId}`); + setTagQuestions(prev => ({ + ...prev, + [tagId]: response.data + })); + } catch (error) { + console.error('获取标签问题失败:', error); + } finally { + setLoadingQuestions(prev => ({ ...prev, [tagId]: false })); + } + }, + [projectId] + ); + + // 获取项目信息,获取项目名称 + useEffect(() => { + if (projectId) { + axios + .get(`/api/projects/${projectId}`) + .then(response => { + setProject(response.data); + setProjectName(response.data.name || ''); + }) + .catch(error => { + console.error('获取项目信息失败:', error); + }); + } + }, [projectId]); + + // 初始化时获取问题统计信息 + useEffect(() => { + fetchQuestionsStats(); + }, [fetchQuestionsStats]); + + // 构建标签树 + const tagTree = useMemo(() => { + const rootTags = []; + const tagMap = {}; + + // 创建标签映射 + tags.forEach(tag => { + tagMap[tag.id] = { ...tag, children: [] }; + }); + + // 构建树结构 + tags.forEach(tag => { + if (tag.parentId && tagMap[tag.parentId]) { + tagMap[tag.parentId].children.push(tagMap[tag.id]); + } else { + rootTags.push(tagMap[tag.id]); + } + }); + + return rootTags; + }, [tags]); + + // 切换标签展开/折叠状态 + const toggleTag = useCallback( + tagId => { + setExpandedTags(prev => ({ + ...prev, + [tagId]: !prev[tagId] + })); + + // 如果展开且还没有加载过问题,则加载问题 + if (!expandedTags[tagId] && !tagQuestions[tagId]) { + fetchQuestionsByTag(tagId); + } + }, + [expandedTags, tagQuestions, fetchQuestionsByTag] + ); + + // 处理菜单打开 + const handleMenuOpen = (event, tag) => { + event.stopPropagation(); + setMenuAnchorEl(event.currentTarget); + setSelectedTagForMenu(tag); + }; + + // 处理菜单关闭 + const handleMenuClose = () => { + setMenuAnchorEl(null); + setSelectedTagForMenu(null); + }; + + // 打开编辑标签对话框 + const openEditDialog = () => { + setTagToEdit(selectedTagForMenu); + setEditDialogOpen(true); + handleMenuClose(); + }; + + // 关闭编辑标签对话框 + const closeEditDialog = () => { + setEditDialogOpen(false); + setTagToEdit(null); + }; + + // 处理编辑标签成功 + const handleEditTagSuccess = updatedTag => { + // 更新标签数据,不刷新页面 + const updateTagInTree = tagList => { + return tagList.map(tag => { + if (tag.id === updatedTag.id) { + return { ...tag, label: updatedTag.label }; + } + if (tag.children && tag.children.length > 0) { + return { ...tag, children: updateTagInTree(tag.children) }; + } + return tag; + }); + }; + + // 调用父组件的回调更新标签列表 + const updatedTags = updateTagInTree(tags); + onTagsUpdate?.(updatedTags); + }; + + // 打开删除确认对话框 + const openDeleteConfirm = () => { + console.log('打开删除确认对话框', selectedTagForMenu); + // 保存要删除的标签 + setTagToDelete(selectedTagForMenu); + setDeleteConfirmOpen(true); + handleMenuClose(); + }; + + // 关闭删除确认对话框 + const closeDeleteConfirm = () => { + setDeleteConfirmOpen(false); + }; + + // 处理删除标签 + const handleDeleteTag = () => { + if (!tagToDelete) { + console.log('没有要删除的标签信息'); + return; + } + + console.log('开始删除标签:', tagToDelete.id, tagToDelete.label); + + // 先关闭确认对话框 + closeDeleteConfirm(); + + // 执行删除操作 + const deleteTagAction = async () => { + try { + console.log('发送删除请求:', `/api/projects/${projectId}/tags?id=${tagToDelete.id}`); + + // 发送删除请求 + const response = await axios.delete(`/api/projects/${projectId}/tags?id=${tagToDelete.id}`); + + console.log('删除标签成功:', response.data); + + // 刷新页面 + window.location.reload(); + } catch (error) { + console.error('删除标签失败:', error); + console.error('错误详情:', error.response ? error.response.data : '无响应数据'); + alert(`删除标签失败: ${error.message}`); + } + }; + + // 立即执行删除操作 + deleteTagAction(); + }; + + // 打开删除问题确认对话框 + const openDeleteQuestionConfirm = (questionId, event) => { + event.stopPropagation(); + setQuestionToDelete(questionId); + setDeleteQuestionConfirmOpen(true); + }; + + // 关闭删除问题确认对话框 + const closeDeleteQuestionConfirm = () => { + setDeleteQuestionConfirmOpen(false); + setQuestionToDelete(null); + }; + + // 处理删除问题 + const handleDeleteQuestion = async () => { + if (!questionToDelete) return; + + try { + await axios.delete(`/api/projects/${projectId}/questions/${questionToDelete}`); + // 更新问题列表 + setTagQuestions(prev => { + const newQuestions = { ...prev }; + Object.keys(newQuestions).forEach(tagId => { + newQuestions[tagId] = newQuestions[tagId].filter(q => q.id !== questionToDelete); + }); + return newQuestions; + }); + // 关闭确认对话框 + closeDeleteQuestionConfirm(); + } catch (error) { + console.error('删除问题失败:', error); + } + }; + + // 处理生成数据集 + const handleGenerateDataset = async (questionId, questionInfo, event) => { + event.stopPropagation(); + // 设置处理状态 + setProcessingQuestions(prev => ({ + ...prev, + [questionId]: true + })); + await generateSingleDataset({ projectId, questionId, questionInfo }); + // 重置处理状态 + setProcessingQuestions(prev => ({ + ...prev, + [questionId]: false + })); + }; + + // 处理生成多轮对话数据集 + const handleGenerateMultiTurnDataset = async (questionId, questionInfo, event) => { + event.stopPropagation(); + + try { + // 设置处理状态 + setProcessingMultiTurnQuestions(prev => ({ + ...prev, + [questionId]: true + })); + + // 首先检查项目是否配置了多轮对话设置 + const configResponse = await axios.get(`/api/projects/${projectId}/tasks`); + if (configResponse.status !== 200) { + throw new Error('获取项目配置失败'); + } + + const config = configResponse.data; + const multiTurnConfig = { + systemPrompt: config.multiTurnSystemPrompt, + scenario: config.multiTurnScenario, + rounds: config.multiTurnRounds, + roleA: config.multiTurnRoleA, + roleB: config.multiTurnRoleB + }; + + // 检查是否已配置必要的多轮对话设置 + if ( + !multiTurnConfig.scenario || + !multiTurnConfig.roleA || + !multiTurnConfig.roleB || + !multiTurnConfig.rounds || + multiTurnConfig.rounds < 1 + ) { + throw new Error('请先在项目设置中配置多轮对话相关参数'); + } + + // 检查是否选择了模型 + if (!selectedModel || Object.keys(selectedModel).length === 0) { + throw new Error('请先选择一个模型'); + } + + // 调用多轮对话生成API + const response = await axios.post(`/api/projects/${projectId}/dataset-conversations`, { + questionId, + ...multiTurnConfig, + model: selectedModel, + language: 'zh-CN' + }); + + if (response.status === 200) { + // 成功后刷新问题统计 + fetchQuestionsStats(); + toast.success(t('datasets.multiTurnGenerateSuccess', { defaultValue: '多轮对话数据集生成成功!' })); + + // 通知父组件刷新统计信息 + if (typeof window !== 'undefined') { + window.dispatchEvent(new CustomEvent('refreshDistillStats')); + } + } + } catch (error) { + console.error('生成多轮对话数据集失败:', error); + toast.error(error.message || t('datasets.multiTurnGenerateError', { defaultValue: '生成多轮对话数据集失败' })); + } finally { + // 重置处理状态 + setProcessingMultiTurnQuestions(prev => ({ + ...prev, + [questionId]: false + })); + } + }; + + // 获取标签路径 + const getTagPath = useCallback( + tag => { + if (!tag) return ''; + + const findPath = (currentTag, path = []) => { + const newPath = [currentTag.label, ...path]; + + if (!currentTag.parentId) { + // 如果是顶级标签,确保路径以项目名称开始 + if (projectName && !newPath.includes(projectName)) { + return [projectName, ...newPath]; + } + return newPath; + } + + const parentTag = tags.find(t => t.id === currentTag.parentId); + if (!parentTag) { + // 如果没有找到父标签,确保路径以项目名称开始 + if (projectName && !newPath.includes(projectName)) { + return [projectName, ...newPath]; + } + return newPath; + } + + return findPath(parentTag, newPath); + }; + + const path = findPath(tag); + + // 最终检查,确保路径以项目名称开始 + if (projectName && path.length > 0 && path[0] !== projectName) { + path.unshift(projectName); + } + + return path.join(' > '); + }, + [tags, projectName] + ); + + // 渲染标签树 + const renderTagTree = (tagList, level = 0) => { + // 对同级标签进行排序 + const sortedTagList = sortTagsByNumber(tagList); + + return ( + + {sortedTagList.map(tag => ( + { + // 包装函数,处理问题生成后的刷新 + const handleGenerateQuestionsWithRefresh = async () => { + // 调用父组件传入的函数生成问题 + await onGenerateQuestions(tag, getTagPath(tag)); + + // 生成问题后刷新数据 + await fetchQuestionsStats(); + + // 如果标签已展开,刷新该标签的问题详情 + if (expandedTags[tag.id]) { + await fetchQuestionsByTag(tag.id); + } + }; + + handleGenerateQuestionsWithRefresh(); + }} + onGenerateSubTags={tag => onGenerateSubTags(tag, getTagPath(tag))} + questions={tagQuestions[tag.id] || []} + loadingQuestions={loadingQuestions[tag.id]} + processingQuestions={processingQuestions} + processingMultiTurnQuestions={processingMultiTurnQuestions} + onDeleteQuestion={openDeleteQuestionConfirm} + onGenerateDataset={handleGenerateDataset} + onGenerateMultiTurnDataset={handleGenerateMultiTurnDataset} + allQuestions={allQuestions} + tagQuestions={tagQuestions} + > + {/* 递归渲染子标签 */} + {tag.children && tag.children.length > 0 && expandedTags[tag.id] && renderTagTree(tag.children, level + 1)} + + ))} + + ); + }; + + return ( + + {tagTree.length > 0 ? ( + renderTagTree(tagTree) + ) : ( + + + {t('distill.noTags')} + + + )} + + {/* 标签操作菜单 */} + + + {/* 编辑标签对话框 */} + + + {/* 删除标签确认对话框 */} + + + {/* 删除问题确认对话框 */} + + + ); +}); + +export default DistillTreeView; diff --git a/easy-dataset-main/components/distill/QuestionGenerationDialog.js b/easy-dataset-main/components/distill/QuestionGenerationDialog.js new file mode 100644 index 0000000..2c9f19c --- /dev/null +++ b/easy-dataset-main/components/distill/QuestionGenerationDialog.js @@ -0,0 +1,194 @@ +'use client'; + +import React, { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + TextField, + Typography, + Box, + CircularProgress, + Alert, + List, + ListItem, + ListItemText, + Paper, + IconButton, + Divider +} from '@mui/material'; +import CloseIcon from '@mui/icons-material/Close'; +import axios from 'axios'; +import i18n from '@/lib/i18n'; + +/** + * 问题生成对话框组件 + * @param {Object} props + * @param {boolean} props.open - 对话框是否打开 + * @param {Function} props.onClose - 关闭对话框的回调函数 + * @param {Function} props.onGenerated - 问题生成完成的回调函数 + * @param {string} props.projectId - 项目ID + * @param {Object} props.tag - 标签对象 + * @param {string} props.tagPath - 标签路径 + * @param {Object} props.model - 选择的模型配置 + */ +export default function QuestionGenerationDialog({ open, onClose, onGenerated, projectId, tag, tagPath, model }) { + const { t } = useTranslation(); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(''); + const [count, setCount] = useState(5); + const [generatedQuestions, setGeneratedQuestions] = useState([]); + + // 处理生成问题 + const handleGenerateQuestions = async () => { + try { + setLoading(true); + setError(''); + + const response = await axios.post(`/api/projects/${projectId}/distill/questions`, { + tagPath, + currentTag: tag.label, + tagId: tag.id, + count, + model, + language: i18n.language + }); + + setGeneratedQuestions(response.data); + } catch (error) { + console.error('生成问题失败:', error); + setError(error.response?.data?.error || t('distill.generateQuestionsError')); + } finally { + setLoading(false); + } + }; + + // 处理生成完成 + const handleGenerateComplete = async () => { + if (onGenerated) { + onGenerated(generatedQuestions); + } + handleClose(); + }; + + // 处理关闭对话框 + const handleClose = () => { + setGeneratedQuestions([]); + setError(''); + setCount(5); + if (onClose) { + onClose(); + } + }; + + // 处理数量变化 + const handleCountChange = event => { + const value = parseInt(event.target.value); + if (!isNaN(value) && value >= 1 && value <= 100) { + setCount(value); + } + }; + + return ( + + + + {t('distill.generateQuestionsTitle', { tag: tag?.label || t('distill.unknownTag') })} + + + + + + + + {error && ( + + {error} + + )} + + + + {t('distill.tagPath')}: + + + {tagPath || tag?.label || t('distill.unknownTag')} + + + + + + {t('distill.questionCount')}: + + + + + {generatedQuestions.length > 0 && ( + + + {t('distill.generatedQuestions')}: + + + + {generatedQuestions.map((question, index) => ( + + {index > 0 && } + + + + + ))} + + + + )} + + + + + {generatedQuestions.length > 0 ? ( + + ) : ( + + )} + + + ); +} diff --git a/easy-dataset-main/components/distill/QuestionListItem.js b/easy-dataset-main/components/distill/QuestionListItem.js new file mode 100644 index 0000000..607a78b --- /dev/null +++ b/easy-dataset-main/components/distill/QuestionListItem.js @@ -0,0 +1,121 @@ +'use client'; + +import { useState } from 'react'; +import { + ListItem, + ListItemIcon, + ListItemText, + Box, + Typography, + Chip, + IconButton, + Tooltip, + CircularProgress +} from '@mui/material'; +import HelpOutlineIcon from '@mui/icons-material/HelpOutline'; +import DeleteIcon from '@mui/icons-material/Delete'; +import AutoFixHighIcon from '@mui/icons-material/AutoFixHigh'; +import ChatIcon from '@mui/icons-material/Chat'; +import { useTranslation } from 'react-i18next'; + +/** + * 问题列表项组件 + * @param {Object} props + * @param {Object} props.question - 问题对象 + * @param {number} props.level - 缩进级别 + * @param {Function} props.onDelete - 删除问题的回调 + * @param {Function} props.onGenerateDataset - 生成数据集的回调 + * @param {Function} props.onGenerateMultiTurnDataset - 生成多轮对话数据集的回调 + * @param {boolean} props.processing - 是否正在处理 + * @param {boolean} props.processingMultiTurn - 是否正在生成多轮对话 + */ +export default function QuestionListItem({ + question, + level, + onDelete, + onGenerateDataset, + onGenerateMultiTurnDataset, + processing = false, + processingMultiTurn = false +}) { + const { t } = useTranslation(); + + return ( + + + onGenerateDataset(e)} + disabled={processing || processingMultiTurn} + > + {processing ? : } + + + + onGenerateMultiTurnDataset && onGenerateMultiTurnDataset(e)} + disabled={processing || processingMultiTurn || !onGenerateMultiTurnDataset} + > + {processingMultiTurn ? : } + + + + onDelete(e)} + disabled={processing || processingMultiTurn} + > + + + + + } + > + + + + + + {question.question} + + {question.answered && ( + + )} + + } + /> + + ); +} diff --git a/easy-dataset-main/components/distill/TagEditDialog.js b/easy-dataset-main/components/distill/TagEditDialog.js new file mode 100644 index 0000000..800f961 --- /dev/null +++ b/easy-dataset-main/components/distill/TagEditDialog.js @@ -0,0 +1,115 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + TextField, + Button, + CircularProgress, + Alert +} from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import axios from 'axios'; +import { toast } from 'sonner'; + +/** + * 标签编辑对话框组件 + * @param {Object} props + * @param {boolean} props.open - 对话框是否打开 + * @param {Object} props.tag - 要编辑的标签对象 + * @param {string} props.projectId - 项目ID + * @param {Function} props.onClose - 关闭对话框的回调 + * @param {Function} props.onSuccess - 编辑成功的回调 + */ +export default function TagEditDialog({ open, tag, projectId, onClose, onSuccess }) { + const { t } = useTranslation(); + const [newLabel, setNewLabel] = useState(''); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(''); + + useEffect(() => { + if (open && tag) { + setNewLabel(tag.label); + setError(''); + } + }, [open, tag]); + + const handleConfirm = async () => { + if (!newLabel.trim()) { + setError(t('distill.labelRequired')); + return; + } + + if (newLabel === tag.label) { + onClose(); + return; + } + + try { + setLoading(true); + setError(''); + + const response = await axios.put(`/api/projects/${projectId}/distill/tags/${tag.id}`, { label: newLabel.trim() }); + + if (response.status === 200) { + toast.success(t('distill.tagUpdateSuccess')); + onSuccess?.(response.data); + onClose(); + } + } catch (err) { + console.error('更新标签失败:', err); + setError(err.response?.data?.error || t('distill.tagUpdateFailed')); + toast.error(err.response?.data?.error || t('distill.tagUpdateFailed')); + } finally { + setLoading(false); + } + }; + + const handleClose = () => { + if (!loading) { + onClose(); + } + }; + + return ( + + {t('distill.editTagTitle')} + + {error && ( + + {error} + + )} + setNewLabel(e.target.value)} + disabled={loading} + autoFocus + onKeyPress={e => { + if (e.key === 'Enter' && !loading) { + handleConfirm(); + } + }} + /> + + + + + + + ); +} diff --git a/easy-dataset-main/components/distill/TagGenerationDialog.js b/easy-dataset-main/components/distill/TagGenerationDialog.js new file mode 100644 index 0000000..386efc6 --- /dev/null +++ b/easy-dataset-main/components/distill/TagGenerationDialog.js @@ -0,0 +1,230 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + TextField, + Typography, + Box, + CircularProgress, + Alert, + Chip, + Paper, + IconButton +} from '@mui/material'; +import CloseIcon from '@mui/icons-material/Close'; +import axios from 'axios'; +import i18n from '@/lib/i18n'; + +/** + * 标签生成对话框组件 + * @param {Object} props + * @param {boolean} props.open - 对话框是否打开 + * @param {Function} props.onClose - 关闭对话框的回调函数 + * @param {Function} props.onGenerated - 标签生成完成的回调函数 + * @param {string} props.projectId - 项目ID + * @param {Object} props.parentTag - 父标签对象,为null时表示生成根标签 + * @param {string} props.tagPath - 标签链路 + * @param {Object} props.model - 选择的模型配置 + */ +export default function TagGenerationDialog({ open, onClose, onGenerated, projectId, parentTag, tagPath, model }) { + const { t } = useTranslation(); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(''); + const [count, setCount] = useState(5); + const [generatedTags, setGeneratedTags] = useState([]); + const [parentTagName, setParentTagName] = useState(''); + const [project, setProject] = useState(null); + + // 获取项目信息,如果是顶级标签,默认填写项目名称 + useEffect(() => { + if (projectId && !parentTag) { + axios + .get(`/api/projects/${projectId}`) + .then(response => { + setProject(response.data); + setParentTagName(response.data.name || ''); + }) + .catch(error => { + console.error('获取项目信息失败:', error); + }); + } else if (parentTag) { + setParentTagName(parentTag.label || ''); + } + }, [projectId, parentTag]); + + // 处理生成标签 + const handleGenerateTags = async () => { + try { + setLoading(true); + setError(''); + + const response = await axios.post(`/api/projects/${projectId}/distill/tags`, { + parentTag: parentTagName, + parentTagId: parentTag ? parentTag.id : null, + tagPath: tagPath || parentTagName, + count, + model, + language: i18n.language + }); + + setGeneratedTags(response.data); + } catch (error) { + console.error('生成标签失败:', error); + setError(error.response?.data?.error || t('distill.generateTagsError')); + } finally { + setLoading(false); + } + }; + + // 处理生成完成 + const handleGenerateComplete = async () => { + if (onGenerated) { + onGenerated(generatedTags); + } + handleClose(); + }; + + // 处理关闭对话框 + const handleClose = () => { + setGeneratedTags([]); + setError(''); + setCount(5); + if (onClose) { + onClose(); + } + }; + + // 处理数量变化 + const handleCountChange = event => { + const value = parseInt(event.target.value); + if (!isNaN(value) && value >= 1 && value <= 100) { + setCount(value); + } + }; + + return ( + + + + {parentTag + ? t('distill.generateSubTagsTitle', { parentTag: parentTag.label }) + : t('distill.generateRootTagsTitle')} + + + + + + + + {error && ( + + {error} + + )} + + {/* 标签路径显示 */} + {parentTag && tagPath && ( + + + {t('distill.tagPath')}: + + + {tagPath || parentTag.label} + + + )} + + + + {t('distill.parentTag')}: + + + setParentTagName(e.target.value)} + placeholder={t('distill.parentTagPlaceholder')} + disabled={loading || !parentTag} + // 如果是顶级标签,设置为只读 + InputProps={{ + readOnly: !parentTag + }} + // 显示适当的帮助文本 + helperText={ + !parentTag + ? t('distill.rootTopicHelperText', { defaultValue: '使用项目名称作为顶级主题' }) + : t('distill.parentTagHelp') + } + /> + + + + + {t('distill.tagCount')}: + + + + + {generatedTags.length > 0 && ( + + + {t('distill.generatedTags')}: + + + + {generatedTags.map((tag, index) => ( + + ))} + + + + )} + + + + + {generatedTags.length > 0 ? ( + + ) : ( + + )} + + + ); +} diff --git a/easy-dataset-main/components/distill/TagMenu.js b/easy-dataset-main/components/distill/TagMenu.js new file mode 100644 index 0000000..f5f31a3 --- /dev/null +++ b/easy-dataset-main/components/distill/TagMenu.js @@ -0,0 +1,46 @@ +'use client'; + +import { Menu, MenuItem, ListItemIcon, ListItemText } from '@mui/material'; +import DeleteIcon from '@mui/icons-material/Delete'; +import EditIcon from '@mui/icons-material/Edit'; +import { useTranslation } from 'react-i18next'; + +/** + * 标签操作菜单组件 + * @param {Object} props + * @param {HTMLElement} props.anchorEl - 菜单锚点元素 + * @param {boolean} props.open - 菜单是否打开 + * @param {Function} props.onClose - 关闭菜单的回调 + * @param {Function} props.onEdit - 编辑操作的回调 + * @param {Function} props.onDelete - 删除操作的回调 + */ +export default function TagMenu({ anchorEl, open, onClose, onEdit, onDelete }) { + const { t } = useTranslation(); + + const handleEdit = () => { + onEdit?.(); + onClose(); + }; + + const handleDelete = () => { + onDelete?.(); + onClose(); + }; + + return ( + + + + + + {t('common.edit')} + + + + + + {t('common.delete')} + + + ); +} diff --git a/easy-dataset-main/components/distill/TagTreeItem.js b/easy-dataset-main/components/distill/TagTreeItem.js new file mode 100644 index 0000000..b2ebb9c --- /dev/null +++ b/easy-dataset-main/components/distill/TagTreeItem.js @@ -0,0 +1,240 @@ +'use client'; + +import { useState } from 'react'; +import { + Box, + Typography, + ListItem, + ListItemButton, + ListItemIcon, + ListItemText, + IconButton, + Collapse, + Chip, + Tooltip, + List, + CircularProgress +} from '@mui/material'; +import FolderIcon from '@mui/icons-material/Folder'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import ExpandLessIcon from '@mui/icons-material/ExpandLess'; +import AddIcon from '@mui/icons-material/Add'; +import QuestionMarkIcon from '@mui/icons-material/QuestionMark'; +import MoreVertIcon from '@mui/icons-material/MoreVert'; +import { useTranslation } from 'react-i18next'; +import QuestionListItem from './QuestionListItem'; + +/** + * 标签树项组件 + * @param {Object} props + * @param {Object} props.tag - 标签对象 + * @param {number} props.level - 缩进级别 + * @param {boolean} props.expanded - 是否展开 + * @param {Function} props.onToggle - 切换展开/折叠的回调 + * @param {Function} props.onMenuOpen - 打开菜单的回调 + * @param {Function} props.onGenerateQuestions - 生成问题的回调 + * @param {Function} props.onGenerateSubTags - 生成子标签的回调 + * @param {Array} props.questions - 标签下的问题列表 + * @param {boolean} props.loadingQuestions - 是否正在加载问题 + * @param {Object} props.processingQuestions - 正在处理的问题ID映射 + * @param {Function} props.onDeleteQuestion - 删除问题的回调 + * @param {Function} props.onGenerateDataset - 生成数据集的回调 + * @param {Function} props.onGenerateMultiTurnDataset - 生成多轮对话数据集的回调 + * @param {Object} props.processingMultiTurnQuestions - 正在生成多轮对话的问题ID映射 + * @param {Array} props.allQuestions - 所有问题列表(用于计算问题数量) + * @param {Object} props.tagQuestions - 标签问题映射 + * @param {React.ReactNode} props.children - 子标签内容 + */ +export default function TagTreeItem({ + tag, + level = 0, + expanded = false, + onToggle, + onMenuOpen, + onGenerateQuestions, + onGenerateSubTags, + questions = [], + loadingQuestions = false, + processingQuestions = {}, + onDeleteQuestion, + onGenerateDataset, + onGenerateMultiTurnDataset, + processingMultiTurnQuestions = {}, + allQuestions = [], + tagQuestions = {}, + children +}) { + const { t } = useTranslation(); + + // 递归计算所有层级的子标签数量 + const getTotalSubTagsCount = childrenTags => { + let count = childrenTags.length; + childrenTags.forEach(childTag => { + if (childTag.children && childTag.children.length > 0) { + count += getTotalSubTagsCount(childTag.children); + } + }); + return count; + }; + + // 递归获取所有子标签的问题数量 + const getChildrenQuestionsCount = childrenTags => { + let count = 0; + childrenTags.forEach(childTag => { + // 子标签的问题 + if (tagQuestions[childTag.id] && tagQuestions[childTag.id].length > 0) { + count += tagQuestions[childTag.id].length; + } else { + count += allQuestions.filter(q => q.label === childTag.label).length; + } + + // 子标签的子标签的问题 + if (childTag.children && childTag.children.length > 0) { + count += getChildrenQuestionsCount(childTag.children); + } + }); + return count; + }; + + // 计算当前标签的问题数量 + const getCurrentTagQuestionsCount = () => { + let currentTagQuestions = 0; + if (tagQuestions[tag.id] && tagQuestions[tag.id].length > 0) { + currentTagQuestions = tagQuestions[tag.id].length; + } else { + currentTagQuestions = allQuestions.filter(q => q.label === tag.label).length; + } + return currentTagQuestions; + }; + + // 总问题数量 = 当前标签的问题 + 所有子标签的问题 + const totalQuestions = + getCurrentTagQuestionsCount() + (tag.children ? getChildrenQuestionsCount(tag.children || []) : 0); + + return ( + + 0 ? '1px dashed rgba(0, 0, 0, 0.1)' : 'none', + ml: level > 0 ? 2 : 0 + }} + > + onToggle(tag.id)} sx={{ borderRadius: 1, py: 0.5 }}> + + + + + {tag.label} + {tag.children && tag.children.length > 0 && ( + + )} + {totalQuestions > 0 && ( + + )} + + } + primaryTypographyProps={{ component: 'div' }} + /> + + + + { + e.stopPropagation(); + onGenerateQuestions(tag); + }} + > + + + + + + { + e.stopPropagation(); + onGenerateSubTags(tag); + }} + > + + + + + onMenuOpen(e, tag)}> + + + + {tag.children && tag.children.length > 0 ? ( + expanded ? ( + + ) : ( + + ) + ) : null} + + + + + {/* 子标签 */} + {tag.children && tag.children.length > 0 && ( + + {children} + + )} + + {/* 标签下的问题 */} + {expanded && ( + + + {loadingQuestions ? ( + + + + {t('common.loading')} + + + ) : questions && questions.length > 0 ? ( + questions.map(question => ( + onDeleteQuestion(question.id, e)} + onGenerateDataset={e => onGenerateDataset(question.id, question.question, e)} + onGenerateMultiTurnDataset={ + onGenerateMultiTurnDataset ? e => onGenerateMultiTurnDataset(question.id, question, e) : undefined + } + /> + )) + ) : ( + + + {t('distill.noQuestions')} + + + )} + + + )} + + ); +} diff --git a/easy-dataset-main/components/distill/utils.js b/easy-dataset-main/components/distill/utils.js new file mode 100644 index 0000000..1665206 --- /dev/null +++ b/easy-dataset-main/components/distill/utils.js @@ -0,0 +1,72 @@ +'use client'; + +/** + * 按照标签前面的序号对标签进行排序 + * @param {Array} tags - 标签数组 + * @returns {Array} 排序后的标签数组 + */ +export const sortTagsByNumber = tags => { + return [...tags].sort((a, b) => { + // 提取标签前面的序号 + const getNumberPrefix = label => { + // 匹配形如 1, 1.1, 1.1.2 的序号 + const match = label.match(/^([\d.]+)\s/); + if (match) { + return match[1]; // 返回完整的序号字符串,如 "1.10" + } + return null; // 没有序号 + }; + + const aPrefix = getNumberPrefix(a.label); + const bPrefix = getNumberPrefix(b.label); + + // 如果两个标签都有序号,按序号比较 + if (aPrefix && bPrefix) { + // 将序号分解为数组,然后按数值比较 + const aParts = aPrefix.split('.').map(num => parseInt(num, 10)); + const bParts = bPrefix.split('.').map(num => parseInt(num, 10)); + + // 比较序号数组 + for (let i = 0; i < Math.min(aParts.length, bParts.length); i++) { + if (aParts[i] !== bParts[i]) { + return aParts[i] - bParts[i]; // 数值比较,确保 1.2 排在 1.10 前面 + } + } + // 如果前面的数字都相同,则较短的序号在前 + return aParts.length - bParts.length; + } + // 如果只有一个标签有序号,则有序号的在前 + else if (aPrefix) { + return -1; + } else if (bPrefix) { + return 1; + } + // 如果都没有序号,则按原来的字母序排序 + else { + return a.label.localeCompare(b.label, 'zh-CN'); + } + }); +}; + +/** + * 获取标签的完整路径 + * @param {Object} tag - 标签对象 + * @param {Array} allTags - 所有标签数组 + * @returns {string} 标签路径,如 "标签1 > 标签2 > 标签3" + */ +export const getTagPath = (tag, allTags) => { + if (!tag) return ''; + + const findPath = (currentTag, path = []) => { + const newPath = [currentTag.label, ...path]; + + if (!currentTag.parentId) return newPath; + + const parentTag = allTags.find(t => t.id === currentTag.parentId); + if (!parentTag) return newPath; + + return findPath(parentTag, newPath); + }; + + return findPath(tag).join(' > '); +}; diff --git a/easy-dataset-main/components/export/HuggingFaceTab.js b/easy-dataset-main/components/export/HuggingFaceTab.js new file mode 100644 index 0000000..7e677b5 --- /dev/null +++ b/easy-dataset-main/components/export/HuggingFaceTab.js @@ -0,0 +1,245 @@ +// HuggingFaceTab.js 组件 +import React, { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { + Typography, + Box, + TextField, + Button, + FormControlLabel, + Checkbox, + Alert, + CircularProgress, + Divider, + Paper, + Grid, + Tooltip, + IconButton, + Link +} from '@mui/material'; +import InfoIcon from '@mui/icons-material/Info'; +import HelpOutlineIcon from '@mui/icons-material/HelpOutline'; +import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline'; + +const HuggingFaceTab = ({ + projectId, + systemPrompt, + reasoningLanguage, + confirmedOnly, + includeCOT, + formatType, + fileFormat, + customFields, + handleSystemPromptChange, + handleReasoningLanguageChange, + handleConfirmedOnlyChange, + handleIncludeCOTChange +}) => { + const { t } = useTranslation(); + const [token, setToken] = useState(''); + const [datasetName, setDatasetName] = useState(''); + const [isPrivate, setIsPrivate] = useState(false); + const [uploading, setUploading] = useState(false); + const [error, setError] = useState(''); + const [success, setSuccess] = useState(false); + const [datasetUrl, setDatasetUrl] = useState(''); + const [hasToken, setHasToken] = useState(false); + const [loading, setLoading] = useState(true); + + // 从配置中获取 huggingfaceToken + useEffect(() => { + if (projectId) { + setLoading(true); + fetch(`/api/projects/${projectId}/config`) + .then(res => res.json()) + .then(data => { + if (data.huggingfaceToken) { + setToken(data.huggingfaceToken); + setHasToken(true); + } + setLoading(false); + }) + .catch(err => { + console.error('获取 HuggingFace Token 失败:', err); + setLoading(false); + }); + } + }, [projectId]); + + // 处理上传数据集到 HuggingFace + const handleUpload = async () => { + if (!hasToken) { + return; + } + + if (!datasetName) { + setError('请输入数据集名称'); + return; + } + + try { + setUploading(true); + setError(''); + setSuccess(false); + + const response = await fetch(`/api/projects/${projectId}/huggingface/upload`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + token, + datasetName, + isPrivate, + formatType, + systemPrompt, + reasoningLanguage, + confirmedOnly, + includeCOT, + fileFormat, + customFields: formatType === 'custom' ? customFields : undefined + }) + }); + + const data = await response.json(); + if (!response.ok) { + throw new Error(data.error || '上传失败'); + } + + setSuccess(true); + setDatasetUrl(data.url); + } catch (err) { + setError(err.message); + } finally { + setUploading(false); + } + }; + + return ( + + {error && ( + + {error} + + )} + + {success && ( + }> + {t('export.uploadSuccess')} + {datasetUrl && ( + + + {t('export.viewOnHuggingFace')} + + + )} + + )} + + {!hasToken ? ( + + {t('export.noTokenWarning')} + + + + + ) : null} + + + + + + {t('export.datasetSettings')} + + + + + setDatasetName(e.target.value)} + helperText={t('export.datasetNameHelp')} + sx={{ mb: 2 }} + /> + + + + setIsPrivate(e.target.checked)} />} + label={t('export.privateDataset')} + /> + + + + + + + {t('export.exportOptions')} + + + + + {t('export.systemPrompt')} + + + + {/* Reasoning language – only for multilingual‑thinking */} + {formatType === 'multilingualthinking' && ( + + + {t('export.reasoningLanguage')} + + + + )} + + } + label={t('export.onlyConfirmed')} + /> + + } + label={t('export.includeCOT')} + /> + + + + + + + + ); +}; + +export default HuggingFaceTab; diff --git a/easy-dataset-main/components/export/LlamaFactoryTab.js b/easy-dataset-main/components/export/LlamaFactoryTab.js new file mode 100644 index 0000000..d7edb33 --- /dev/null +++ b/easy-dataset-main/components/export/LlamaFactoryTab.js @@ -0,0 +1,184 @@ +// LlamaFactoryTab.js 组件 +import React, { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { + Button, + FormControlLabel, + Checkbox, + Typography, + Box, + TextField, + Alert, + CircularProgress, + IconButton, + Tooltip +} from '@mui/material'; +import ContentCopyIcon from '@mui/icons-material/ContentCopy'; +import CheckIcon from '@mui/icons-material/Check'; + +const LlamaFactoryTab = ({ + projectId, + systemPrompt, + reasoningLanguage, + confirmedOnly, + includeCOT, + formatType, + handleSystemPromptChange, + handleReasoningLanguageChange, + handleConfirmedOnlyChange, + handleIncludeCOTChange +}) => { + const { t } = useTranslation(); + const [configExists, setConfigExists] = useState(false); + const [configPath, setConfigPath] = useState(''); + const [generating, setGenerating] = useState(false); + const [error, setError] = useState(''); + const [copied, setCopied] = useState(false); + + // 检查配置文件是否存在 + useEffect(() => { + if (projectId) { + fetch(`/api/projects/${projectId}/llamaFactory/checkConfig`) + .then(res => res.json()) + .then(data => { + setConfigExists(data.exists); + if (data.exists) { + setConfigPath(data.configPath); + } + }) + .catch(err => { + setError(err.message); + }); + } + }, [projectId, configExists]); + + // 复制路径到剪贴板 + const handleCopyPath = () => { + const path = configPath.replace('dataset_info.json', ''); + navigator.clipboard.writeText(path).then(() => { + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }); + }; + + // 处理生成 Llama Factory 配置 + const handleGenerateConfig = async () => { + try { + setGenerating(true); + setError(''); + + const response = await fetch(`/api/projects/${projectId}/llamaFactory/generate`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + formatType, + systemPrompt, + reasoningLanguage, + confirmedOnly, + includeCOT + }) + }); + + const data = await response.json(); + if (!response.ok) { + throw new Error(data.error); + } + + setConfigExists(true); + } catch (err) { + setError(err.message); + } finally { + setGenerating(false); + } + }; + + return ( + + {error && ( + + {error} + + )} + + + + {t('export.systemPrompt')} + + + + {/* Reasoning language – only for multilingual‑thinking */} + {formatType === 'multilingualthinking' && ( + + + {t('export.reasoningLanguage')} + + + + )} + + } + label={t('export.onlyConfirmed')} + /> + + } + label={t('export.includeCOT')} + /> + + + {configExists ? ( + <> + + {t('export.configExists')} + + + + {t('export.configPath')}: {configPath.replace('dataset_info.json', '')} + + + + {copied ? : } + + + + + ) : ( + + {t('export.noConfig')} + + )} + + + + + + ); +}; + +export default LlamaFactoryTab; diff --git a/easy-dataset-main/components/export/LocalExportTab.js b/easy-dataset-main/components/export/LocalExportTab.js new file mode 100644 index 0000000..0c303e7 --- /dev/null +++ b/easy-dataset-main/components/export/LocalExportTab.js @@ -0,0 +1,777 @@ +// LocalExportTab.js 组件 +import React, { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { + Button, + FormControl, + FormControlLabel, + RadioGroup, + Radio, + TextField, + Checkbox, + Typography, + Box, + Paper, + useTheme, + Grid, + Table, + TableRow, + TableHead, + TableBody, + TableCell, + TableContainer, + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Chip, + Alert, + CircularProgress +} from '@mui/material'; + +const LocalExportTab = ({ + fileFormat, + formatType, + systemPrompt, + confirmedOnly, + includeCOT, + customFields, + alpacaFieldType, + customInstruction, + reasoningLanguage, + handleFileFormatChange, + handleFormatChange, + handleSystemPromptChange, + handleReasoningLanguageChange, + handleConfirmedOnlyChange, + handleIncludeCOTChange, + handleCustomFieldChange, + handleIncludeLabelsChange, + handleIncludeChunkChange, + handleQuestionOnlyChange, + handleAlpacaFieldTypeChange, + handleCustomInstructionChange, + handleExport, + projectId +}) => { + const theme = useTheme(); + const { t } = useTranslation(); + + // Balance export related state + const [balanceDialogOpen, setBalanceDialogOpen] = useState(false); + const [tagStats, setTagStats] = useState([]); + const [balanceConfig, setBalanceConfig] = useState([]); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(''); + const [totalCount, setTotalCount] = useState(0); + + // Get label statistics (changed to GET + query parameters) + const fetchTagStats = async () => { + try { + setLoading(true); + const url = `/api/projects/${projectId}/datasets/export?confirmed=${confirmedOnly ? 'true' : 'false'}`; + const response = await fetch(url, { method: 'GET' }); + + if (!response.ok) { + throw new Error(t('errors.getTagStatsFailed')); + } + + const stats = await response.json(); + setTagStats(stats); + + // 初始化平衡配置 + const initialConfig = stats.map(stat => ({ + tagLabel: stat.tagLabel, + maxCount: Math.min(stat.datasetCount, 100), // 默认最多100条 + availableCount: stat.datasetCount + })); + + setBalanceConfig(initialConfig); + + // 计算总数 + const total = initialConfig.reduce((sum, config) => sum + config.maxCount, 0); + setTotalCount(total); + } catch (err) { + setError(err.message); + } finally { + setLoading(false); + } + }; + + // 打开平衡导出对话框 + const handleOpenBalanceDialog = () => { + setBalanceDialogOpen(true); + fetchTagStats(); + }; + + // 更新单个标签的数量配置 + const updateBalanceConfig = (tagLabel, newCount) => { + const newConfig = balanceConfig.map(config => { + if (config.tagLabel === tagLabel) { + const count = Math.min(Math.max(0, parseInt(newCount) || 0), config.availableCount); + return { ...config, maxCount: count }; + } + return config; + }); + + setBalanceConfig(newConfig); + + // 重新计算总数 + const total = newConfig.reduce((sum, config) => sum + config.maxCount, 0); + setTotalCount(total); + }; + + // 一键设置所有标签为相同数量 + const setAllToSameCount = count => { + const newConfig = balanceConfig.map(config => ({ + ...config, + maxCount: Math.min(Math.max(0, parseInt(count) || 0), config.availableCount) + })); + + setBalanceConfig(newConfig); + + const total = newConfig.reduce((sum, config) => sum + config.maxCount, 0); + setTotalCount(total); + }; + + // 处理平衡导出 + const handleBalancedExport = () => { + // 过滤出数量大于0的配置 + const validConfig = balanceConfig.filter(config => config.maxCount > 0); + + if (validConfig.length === 0) { + setError(t('export.balancedExport.atLeastOneTag', '请至少为一个标签设置大于0的数量')); + return; + } + + // 调用原有的导出函数,但传递平衡配置 + handleExport({ + balanceMode: true, + balanceConfig: validConfig, + formatType, + systemPrompt, + reasoningLanguage, + confirmedOnly, + fileFormat, + includeCOT, + alpacaFieldType, + customInstruction, + customFields: formatType === 'custom' ? customFields : undefined + }); + + setBalanceDialogOpen(false); + }; + + // 自定义格式的示例 + const getCustomFormatExample = () => { + const { questionField, answerField, cotField, includeLabels, includeChunk } = customFields; + const example = { + [questionField]: t('sampleData.questionContent'), + [answerField]: t('sampleData.answerContent') + }; + + // 如果包含思维链字段,添加到示例中 + if (includeCOT) { + example[cotField] = t('sampleData.cotContent'); + } + + if (includeLabels) { + example.labels = [t('sampleData.domainLabel')]; + } + + if (includeChunk) { + example.chunk = t('sampleData.textChunk'); + } + + return fileFormat === 'json' ? JSON.stringify([example], null, 2) : JSON.stringify(example); + }; + + // CSV 自定义格式化示例 + const getPreviewData = () => { + if (formatType === 'alpaca') { + // 根据选择的字段类型生成不同的示例 + if (alpacaFieldType === 'instruction') { + return { + headers: ['instruction', 'input', 'output', 'system'], + rows: [ + { + instruction: t('export.sampleInstruction', '人类指令(必填)'), + input: '', + output: t('export.sampleOutput', '模型回答(必填)'), + system: t('export.sampleSystem', '系统提示词(选填)') + }, + { + instruction: t('export.sampleInstruction2', '第二个指令'), + input: '', + output: t('export.sampleOutput2', '第二个回答'), + system: t('export.sampleSystemShort', '系统提示词') + } + ] + }; + } else { + // input + return { + headers: ['instruction', 'input', 'output', 'system'], + rows: [ + { + instruction: customInstruction || t('export.fixedInstruction', '固定的指令内容'), + input: t('export.sampleInput', '人类问题(必填)'), + output: t('export.sampleOutput', '模型回答(必填)'), + system: t('export.sampleSystem', '系统提示词(选填)') + }, + { + instruction: customInstruction || t('export.fixedInstruction', '固定的指令内容'), + input: t('export.sampleInput2', '第二个问题'), + output: t('export.sampleOutput2', '第二个回答'), + system: t('export.sampleSystemShort', '系统提示词') + } + ] + }; + } + } else if (formatType === 'sharegpt') { + return { + headers: ['messages'], + rows: [ + { + messages: JSON.stringify( + [ + { + messages: [ + { + role: 'system', + content: t('export.sampleSystem', '系统提示词(选填)') + }, + { + role: 'user', + content: t('export.sampleUserMessage', '人类指令') // 映射到 question 字段 + }, + { + role: 'assistant', + content: t('export.sampleAssistantMessage', '模型回答') // 映射到 cot+answer 字段 + } + ] + } + ], + null, + 2 + ) + } + ] + }; + } else if (formatType === 'multilingualthinking') { + return { + headers: 'messages', + rows: { + messages: JSON.stringify( + { + reasoning_language: 'English', + developer: t('export.sampleSystem', '系统提示词(选填)'), + user: t('export.sampleUserMessage', '人类指令'), // 映射到 question 字段 + analysis: t('export.sampleAnalysis', '模型的思维链内容'), // 映射到 cot 字段 + final: t('export.sampleFinal', '模型回答'), // 映射到 answer 字段 + messages: [ + { + role: 'system', + content: '系统提示词(选填)', + thinking: 'null' + }, + { + role: 'user', + content: '人类指令', // 映射到 question 字段 + thinking: 'null' + }, + { + role: 'assistant', + content: '模型回答', // 映射到 answer 字段 + thinking: '模型的思维链内容' // 映射到 cot 字段 + } + ] + }, + null, + 2 + ) + } + }; + } else if (formatType === 'custom') { + // 如果选择仅导出问题,只包含问题字段 + if (customFields.questionOnly) { + const headers = [customFields.questionField]; + if (customFields.includeLabels) headers.push('labels'); + if (customFields.includeChunk) headers.push('chunk'); + + const row = { + [customFields.questionField]: t('sampleData.questionContent') + }; + if (customFields.includeLabels) row.labels = t('sampleData.domainLabel'); + if (customFields.includeChunk) row.chunk = t('sampleData.textChunk'); + return { + headers, + rows: [row] + }; + } else { + // 正常的自定义格式 + const headers = [customFields.questionField, customFields.answerField]; + if (includeCOT) headers.push(customFields.cotField); + if (customFields.includeLabels) headers.push('labels'); + if (customFields.includeChunk) headers.push('chunk'); + + const row = { + [customFields.questionField]: t('sampleData.questionContent'), + [customFields.answerField]: t('sampleData.answerContent') + }; + if (includeCOT) row[customFields.cotField] = t('sampleData.cotContent'); + if (customFields.includeLabels) row.labels = t('sampleData.domainLabel'); + if (customFields.includeChunk) row.chunk = t('sampleData.textChunk'); + return { + headers, + rows: [row] + }; + } + } + }; + + return ( + <> + + + {t('export.fileFormat')} + + + + } label="JSON" /> + } label="JSONL" /> + {/* } label="CSV" /> */} + } + label="CSV" + /> + + + + + {/* 数据集风格 */} + + + {t('export.format')} + + + + } label="Alpaca" /> + } label="ShareGPT" /> + {/* NEW: Multilingual‑Thinking format */} + } + label={t('export.multilingualThinkingFormat') || 'Multilingual‑Thinking'} + /> + } label={t('export.customFormat')} /> + + + + + {/* Alpaca 格式特有的设置 */} + {formatType === 'alpaca' && ( + + + {t('export.alpacaSettings', 'Alpaca 格式设置')} + + + + {t('export.questionFieldType', '问题字段类型')} + + + } + label={t('export.useInstruction', '使用 instruction 字段')} + /> + } label={t('export.useInput', '使用 input 字段')} /> + + + {alpacaFieldType === 'input' && ( + + )} + + + )} + + {/* 自定义格式选项 */} + {formatType === 'custom' && ( + + + {t('export.customFormatSettings')} + + + + + + + + + {/* 添加思维链字段名输入框 */} + + + + + + } + label={t('export.includeLabels')} + /> + } + label={t('export.includeChunk')} + /> + } + label={t('export.questionOnly')} + /> + + )} + + + + {t('export.example')} + + + {fileFormat === 'csv' ? ( + + {(() => { + const { headers, rows } = getPreviewData(); + const tableKey = `${formatType}-${fileFormat}-${JSON.stringify(customFields)}`; + return ( + + + + {headers.map(header => ( + {header} + ))} + + + + {rows.map((row, index) => ( + + {headers.map(header => ( + + {Array.isArray(row[header]) ? row[header].join(', ') : row[header] || ''} + + ))} + + ))} + +
+ ); + })()} +
+ ) : ( + +
+              {formatType === 'custom'
+                ? getCustomFormatExample()
+                : formatType === 'multilingualthinking'
+                  ? fileFormat === 'json'
+                    ? JSON.stringify(
+                        {
+                          reasoning_language: 'English',
+                          developer: '系统提示词(选填)',
+                          user: '人类指令', // 映射到 question 字段
+                          analysis: '模型的思维链内容', // 映射到 cot 字段
+                          final: '模型回答', // 映射到 answer 字段
+                          messages: [
+                            {
+                              content: t('export.sampleSystem', '系统提示词(选填)'),
+                              role: 'system',
+                              thinking: null
+                            },
+                            {
+                              content: t('export.sampleUserMessage', '人类指令'),
+                              role: 'user',
+                              thinking: null
+                            },
+                            {
+                              content: t('export.sampleAssistantMessage', '模型回答'),
+                              role: 'assistant',
+                              thinking: t('export.sampleThinking', '模型的思维链内容')
+                            }
+                          ]
+                        },
+                        null,
+                        2
+                      )
+                    : '{"reasoning_language": "English","developer": "系统提示词(选填)", "user": "人类指令", "analysis": "模型的思维链内容", "final": "模型回答", "messages": [{"role": "user", "content": "人类指令", "thinking": "null"}, {"role": "assistant", "content": "模型回答", "thinking": "模型的思维链内容"}]}'
+                  : formatType === 'alpaca'
+                    ? fileFormat === 'json'
+                      ? JSON.stringify(
+                          [
+                            {
+                              instruction: t('export.sampleInstruction', '人类指令(必填)'), // 映射到 question 字段
+                              input: t('export.sampleInputOptional', '人类输入(选填)'),
+                              output: t('export.sampleOutput', '模型回答(必填)'), // 映射到 cot+answer 字段
+                              system: t('export.sampleSystem', '系统提示词(选填)')
+                            }
+                          ],
+                          null,
+                          2
+                        )
+                      : '{"instruction": "人类指令(必填)", "input": "人类输入(选填)", "output": "模型回答(必填)", "system": "系统提示词(选填)"}\n{"instruction": "第二个指令", "input": "", "output": "第二个回答", "system": "系统提示词"}'
+                    : fileFormat === 'json'
+                      ? JSON.stringify(
+                          [
+                            {
+                              messages: [
+                                {
+                                  role: 'system',
+                                  content: t('export.sampleSystem', '系统提示词(选填)')
+                                },
+                                {
+                                  role: 'user',
+                                  content: t('export.sampleUserMessage', '人类指令') // 映射到 question 字段
+                                },
+                                {
+                                  role: 'assistant',
+                                  content: t('export.sampleAssistantMessage', '模型回答') // 映射到 cot+answer 字段
+                                }
+                              ]
+                            }
+                          ],
+                          null,
+                          2
+                        )
+                      : '{"messages": [{"role": "system", "content": "系统提示词(选填)"}, {"role": "user", "content": "人类指令"}, {"role": "assistant", "content": "模型回答"}]}\n{"messages": [{"role": "user", "content": "第二个问题"}, {"role": "assistant", "content": "第二个回答"}]}'}
+            
+
+ )} +
+ + + + {t('export.systemPrompt')} + + + + {/* Reasoning language – only for multilingual‑thinking */} + {formatType === 'multilingualthinking' && ( + + + {t('export.Reasoninglanguage')} + + + + )} + + } + label={t('export.onlyConfirmed')} + /> + + } + label={t('export.includeCOT')} + /> + + + + + + + + {/* 平衡导出对话框 */} + setBalanceDialogOpen(false)} + maxWidth="md" + fullWidth + PaperProps={{ + sx: { + borderRadius: 2 + } + }} + > + {t('exportDialog.balancedExportTitle')} + + + {t('exportDialog.balancedExportDescription')} + + + {error && ( + + {error} + + )} + + {loading ? ( + + + + ) : ( + <> + {/* 批量设置 */} + + + {t('exportDialog.quickSettings')} + + + + + + { + if (e.key === 'Enter') { + setAllToSameCount(e.target.value); + e.target.value = ''; + } + }} + /> + + + + {/* 标签配置表格 */} + + + + + {t('exportDialog.tagName')} + {t('exportDialog.availableCount')} + {t('exportDialog.exportCount')} + {t('exportDialog.settings')} + + + + {balanceConfig.map(config => ( + + + + + {config.availableCount} + + {config.maxCount} + + + updateBalanceConfig(config.tagLabel, e.target.value)} + inputProps={{ + min: 0, + max: config.availableCount, + style: { textAlign: 'right' } + }} + sx={{ width: 80 }} + /> + + + ))} + +
+
+ + {/* 统计信息 */} + + + + {t('exportDialog.totalExportCount')}: {totalCount} + {' '} + | {t('exportDialog.tagCount')}: {balanceConfig.filter(c => c.maxCount > 0).length} /{' '} + {balanceConfig.length} + + + + )} +
+ + + + +
+ + ); +}; + +export default LocalExportTab; diff --git a/easy-dataset-main/components/home/CreateProjectDialog.js b/easy-dataset-main/components/home/CreateProjectDialog.js new file mode 100644 index 0000000..cca5d06 --- /dev/null +++ b/easy-dataset-main/components/home/CreateProjectDialog.js @@ -0,0 +1,173 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + TextField, + Box, + Typography, + useTheme, + CircularProgress, + FormControl, + InputLabel, + Select, + MenuItem +} from '@mui/material'; +import { useRouter } from 'next/navigation'; +import { useTranslation } from 'react-i18next'; + +export default function CreateProjectDialog({ open, onClose }) { + const { t } = useTranslation(); + const theme = useTheme(); + const router = useRouter(); + const [loading, setLoading] = useState(false); + const [projects, setProjects] = useState([]); + const [formData, setFormData] = useState({ + name: '', + description: '', + reuseConfigFrom: '' + }); + const [error, setError] = useState(null); + + // 获取项目列表 + useEffect(() => { + const fetchProjects = async () => { + try { + const response = await fetch('/api/projects'); + if (response.ok) { + const data = await response.json(); + setProjects(data); + } + } catch (error) { + console.error('获取项目列表失败:', error); + } + }; + + fetchProjects(); + }, []); + + const handleChange = e => { + const { name, value } = e.target; + setFormData(prev => ({ + ...prev, + [name]: value + })); + }; + + const handleSubmit = async e => { + e.preventDefault(); + setLoading(true); + setError(null); + + try { + const response = await fetch('/api/projects', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(formData) + }); + + if (!response.ok) { + throw new Error(t('projects.createFailed')); + } + + const data = await response.json(); + + router.push(`/projects/${data.id}/settings?tab=model`); + } catch (err) { + console.error(t('projects.createError'), err); + setError(err.message); + } finally { + setLoading(false); + } + }; + + return ( + + + + {t('projects.createNew')} + + +
+ + + + + + {t('projects.reuseConfig')} + + + + {error && ( + + {error} + + )} + + + + + +
+
+ ); +} diff --git a/easy-dataset-main/components/home/HeroSection.js b/easy-dataset-main/components/home/HeroSection.js new file mode 100644 index 0000000..74a50c0 --- /dev/null +++ b/easy-dataset-main/components/home/HeroSection.js @@ -0,0 +1,135 @@ +'use client'; + +import { Box, Container, Typography, Button, useMediaQuery } from '@mui/material'; +import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline'; +import SearchIcon from '@mui/icons-material/Search'; +import { styles } from '@/styles/home'; +import { useTheme } from '@mui/material'; +import { motion } from 'framer-motion'; +import ParticleBackground from './ParticleBackground'; +import { useTranslation } from 'react-i18next'; + +export default function HeroSection({ onCreateProject }) { + const { t } = useTranslation(); + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('sm')); + + return ( + + {/* 添加粒子背景 */} + + + + + + + + + {t('home.title')} + + + + {t('home.subtitle')} + + + + + + + + + + ); +} diff --git a/easy-dataset-main/components/home/MigrationDialog.js b/easy-dataset-main/components/home/MigrationDialog.js new file mode 100644 index 0000000..3a04b8e --- /dev/null +++ b/easy-dataset-main/components/home/MigrationDialog.js @@ -0,0 +1,300 @@ +'use client'; + +import { useState } from 'react'; +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + Box, + Typography, + CircularProgress, + List, + ListItem, + ListItemText, + ListItemSecondaryAction, + IconButton, + Alert, + Paper, + useTheme, + Tooltip +} from '@mui/material'; +import WarningAmberIcon from '@mui/icons-material/WarningAmber'; +import FolderOpenIcon from '@mui/icons-material/FolderOpen'; +import DeleteIcon from '@mui/icons-material/Delete'; +import { useTranslation } from 'react-i18next'; + +/** + * 项目迁移对话框组件 + * @param {Object} props - 组件属性 + * @param {boolean} props.open - 对话框是否打开 + * @param {Function} props.onClose - 关闭对话框的回调函数 + * @param {Array} props.projectIds - 需要迁移的项目ID列表 + */ +export default function MigrationDialog({ open, onClose, projectIds = [] }) { + const { t } = useTranslation(); + const theme = useTheme(); + const [migrating, setMigrating] = useState(false); + const [success, setSuccess] = useState(false); + const [error, setError] = useState(null); + const [migratedCount, setMigratedCount] = useState(0); + const [taskId, setTaskId] = useState(null); + const [progress, setProgress] = useState(0); + const [statusText, setStatusText] = useState(''); + const [processingIds, setProcessingIds] = useState([]); + + // 打开项目目录 + const handleOpenDirectory = async projectId => { + try { + setProcessingIds(prev => [...prev, projectId]); + + const response = await fetch('/api/projects/open-directory', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ projectId }) + }); + + if (!response.ok) { + const data = await response.json(); + throw new Error(data.error || t('migration.openDirectoryFailed')); + } + + // 成功打开目录,不需要特别处理 + } catch (err) { + console.error('打开目录错误:', err); + setError(err.message); + } finally { + setProcessingIds(prev => prev.filter(id => id !== projectId)); + } + }; + + // 删除项目目录 + const handleDeleteDirectory = async projectId => { + try { + if (!window.confirm(t('migration.confirmDelete'))) { + return; + } + + setProcessingIds(prev => [...prev, projectId]); + + const response = await fetch('/api/projects/delete-directory', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ projectId }) + }); + + if (!response.ok) { + const data = await response.json(); + throw new Error(data.error || t('migration.deleteDirectoryFailed')); + } + + // 从列表中移除已删除的项目 + const updatedProjectIds = projectIds.filter(id => id !== projectId); + // 这里我们不能直接修改 projectIds,因为它是从父组件传入的 + // 但我们可以通知用户界面刷新 + window.location.reload(); + } catch (err) { + console.error('删除目录错误:', err); + setError(err.message); + } finally { + setProcessingIds(prev => prev.filter(id => id !== projectId)); + } + }; + + // 处理迁移操作 + const handleMigration = async () => { + try { + setMigrating(true); + setError(null); + setSuccess(false); + setProgress(0); + setStatusText(t('migration.starting')); + + // 调用异步迁移接口启动迁移任务 + const response = await fetch('/api/projects/migrate', { + method: 'POST' + }); + + if (!response.ok) { + throw new Error(t('migration.failed')); + } + + const { success, taskId: newTaskId } = await response.json(); + + if (!success || !newTaskId) { + throw new Error(t('migration.startFailed')); + } + + // 保存任务ID + setTaskId(newTaskId); + setStatusText(t('migration.processing')); + + // 开始轮询任务状态 + await pollMigrationStatus(newTaskId); + } catch (err) { + console.error('迁移错误:', err); + setError(err.message); + setMigrating(false); + } + }; + + // 轮询迁移任务状态 + const pollMigrationStatus = async id => { + try { + // 定义轮询间隔(毫秒) + const pollInterval = 1000; + + // 发送请求获取任务状态 + const response = await fetch(`/api/projects/migrate?taskId=${id}`); + + if (!response.ok) { + throw new Error(t('migration.statusFailed')); + } + + const { success, task } = await response.json(); + + if (!success || !task) { + throw new Error(t('migration.taskNotFound')); + } + + // 更新进度 + setProgress(task.progress || 0); + + // 根据任务状态更新UI + if (task.status === 'completed') { + // 任务完成 + setMigratedCount(task.completed); + setSuccess(true); + setMigrating(false); + setStatusText(t('migration.completed')); + + // 迁移成功后,延迟关闭对话框并刷新页面 + setTimeout(() => { + onClose(); + window.location.reload(); + }, 2000); + } else if (task.status === 'failed') { + // 任务失败 + throw new Error(task.error || t('migration.failed')); + } else { + // 任务仍在进行中,继续轮询 + setTimeout(() => pollMigrationStatus(id), pollInterval); + + // 更新状态文本 + if (task.total > 0) { + setStatusText( + t('migration.progressStatus', { + completed: task.completed || 0, + total: task.total + }) + ); + } + } + } catch (err) { + console.error('获取迁移状态错误:', err); + setError(err.message); + setMigrating(false); + } + }; + + return ( + + + + {t('migration.title')} + + + + {success ? ( + + {t('migration.success', { count: migratedCount })} + + ) : error ? ( + + {error} + + ) : null} + + + {t('migration.description')} + + + {projectIds.length > 0 && ( + + + {t('migration.projectsList')}: + + + + {projectIds.map(id => ( + + + + + handleOpenDirectory(id)} + disabled={processingIds.includes(id)} + size="small" + > + + + + + handleDeleteDirectory(id)} + disabled={processingIds.includes(id)} + size="small" + sx={{ ml: 1, color: 'error.main' }} + > + + + + + + ))} + + + + )} + + {migrating && ( + + 0 ? 'determinate' : 'indeterminate'} value={progress} /> + + {statusText || t('migration.migrating')} + + {progress > 0 && ( + + {progress}% + + )} + + )} + + + + + + + + ); +} diff --git a/easy-dataset-main/components/home/ParticleBackground.js b/easy-dataset-main/components/home/ParticleBackground.js new file mode 100644 index 0000000..80b9569 --- /dev/null +++ b/easy-dataset-main/components/home/ParticleBackground.js @@ -0,0 +1,251 @@ +'use client'; + +import { useEffect, useRef } from 'react'; +import { useTheme } from '@mui/material'; + +export default function ParticleBackground() { + const canvasRef = useRef(null); + const theme = useTheme(); + + useEffect(() => { + const canvas = canvasRef.current; + const ctx = canvas.getContext('2d'); + let animationFrameId; + let particles = []; + let mousePosition = { x: 0, y: 0 }; + let hoverRadius = 150; // 增加鼠标影响范围 + let mouseSpeed = { x: 0, y: 0 }; // 跟踪鼠标速度 + let lastMousePosition = { x: 0, y: 0 }; // 上一帧鼠标位置 + + // 设置画布大小为窗口大小 + const handleResize = () => { + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + initParticles(); + }; + + // 跟踪鼠标位置和速度 + const handleMouseMove = event => { + // 计算鼠标速度 + mouseSpeed.x = event.clientX - mousePosition.x; + mouseSpeed.y = event.clientY - mousePosition.y; + + // 更新鼠标位置 + lastMousePosition.x = mousePosition.x; + lastMousePosition.y = mousePosition.y; + mousePosition.x = event.clientX; + mousePosition.y = event.clientY; + }; + + // 触摸设备支持 + const handleTouchMove = event => { + if (event.touches.length > 0) { + // 计算触摸速度 + mouseSpeed.x = event.touches[0].clientX - mousePosition.x; + mouseSpeed.y = event.touches[0].clientY - mousePosition.y; + + // 更新触摸位置 + lastMousePosition.x = mousePosition.x; + lastMousePosition.y = mousePosition.y; + mousePosition.x = event.touches[0].clientX; + mousePosition.y = event.touches[0].clientY; + } + }; + + // 生成随机颜色 + const getRandomColor = () => { + // 主题色调 + const colors = + theme.palette.mode === 'dark' + ? [ + 'rgba(255, 255, 255, 0.5)', // 白色 + 'rgba(100, 181, 246, 0.5)', // 蓝色 + 'rgba(156, 39, 176, 0.4)', // 紫色 + 'rgba(121, 134, 203, 0.5)' // 靛蓝色 + ] + : [ + 'rgba(42, 92, 170, 0.5)', // 主蓝色 + 'rgba(66, 165, 245, 0.4)', // 浅蓝色 + 'rgba(94, 53, 177, 0.3)', // 深紫色 + 'rgba(3, 169, 244, 0.4)' // 天蓝色 + ]; + + return colors[Math.floor(Math.random() * colors.length)]; + }; + + // 初始化粒子 + const initParticles = () => { + particles = []; + // 增加粒子数量,但保持性能平衡 + const particleCount = Math.min(Math.floor(window.innerWidth / 8), 150); + + for (let i = 0; i < particleCount; i++) { + // 创建不同大小和速度的粒子 + const size = Math.random(); + const speedFactor = Math.max(0.1, size); // 较大的粒子移动较慢 + + particles.push({ + x: Math.random() * canvas.width, + y: Math.random() * canvas.height, + // 粒子大小更加多样化 + radius: size * 3 + 0.5, + // 使用随机颜色 + color: getRandomColor(), + // 添加发光效果 + glow: Math.random() * 10 + 5, + // 调整速度范围,使运动更加自然 + speedX: (Math.random() * 0.6 - 0.3) * speedFactor, + speedY: (Math.random() * 0.6 - 0.3) * speedFactor, + originalSpeedX: (Math.random() * 0.6 - 0.3) * speedFactor, + originalSpeedY: (Math.random() * 0.6 - 0.3) * speedFactor, + // 添加脉动效果 + pulseSpeed: Math.random() * 0.02 + 0.01, + pulseDirection: Math.random() > 0.5 ? 1 : -1, + pulseAmount: 0, + // 粒子透明度 + opacity: Math.random() * 0.5 + 0.5 + }); + } + }; + + // 绘制粒子 + const drawParticles = () => { + ctx.clearRect(0, 0, canvas.width, canvas.height); + + // 计算鼠标速度衰减 + mouseSpeed.x *= 0.95; + mouseSpeed.y *= 0.95; + + // 绘制粒子之间的连线 + drawLines(); + + particles.forEach(particle => { + // 计算粒子与鼠标的距离 + const dx = mousePosition.x - particle.x; + const dy = mousePosition.y - particle.y; + const distance = Math.sqrt(dx * dx + dy * dy); + + // 脉动效果 + particle.pulseAmount += particle.pulseSpeed * particle.pulseDirection; + if (Math.abs(particle.pulseAmount) > 0.5) { + particle.pulseDirection *= -1; + } + + // 如果粒子在鼠标影响范围内,调整其速度 + if (distance < hoverRadius) { + const angle = Math.atan2(dy, dx); + const force = (hoverRadius - distance) / hoverRadius; + const mouseFactor = 3; // 增强鼠标影响力度 + + // 粒子远离鼠标,并受鼠标速度影响 + particle.speedX = -Math.cos(angle) * force * mouseFactor + particle.originalSpeedX + mouseSpeed.x * 0.05; + particle.speedY = -Math.sin(angle) * force * mouseFactor + particle.originalSpeedY + mouseSpeed.y * 0.05; + } else { + // 逐渐恢复原始速度 + particle.speedX = particle.speedX * 0.95 + particle.originalSpeedX * 0.05; + particle.speedY = particle.speedY * 0.95 + particle.originalSpeedY * 0.05; + } + + // 更新粒子位置 + particle.x += particle.speedX; + particle.y += particle.speedY; + + // 边界检查 + if (particle.x < 0) particle.x = canvas.width; + if (particle.x > canvas.width) particle.x = 0; + if (particle.y < 0) particle.y = canvas.height; + if (particle.y > canvas.height) particle.y = 0; + + // 应用脉动效果到粒子大小 + const currentRadius = particle.radius * (1 + particle.pulseAmount * 0.2); + + // 绘制发光效果 + const gradient = ctx.createRadialGradient(particle.x, particle.y, 0, particle.x, particle.y, particle.glow); + gradient.addColorStop(0, particle.color); + gradient.addColorStop(1, 'rgba(0, 0, 0, 0)'); + + // 绘制粒子 + ctx.beginPath(); + ctx.arc(particle.x, particle.y, currentRadius, 0, Math.PI * 2); + ctx.fillStyle = particle.color; + ctx.fill(); + + // 添加发光效果 + ctx.beginPath(); + ctx.arc(particle.x, particle.y, particle.glow, 0, Math.PI * 2); + ctx.fillStyle = gradient; + ctx.globalAlpha = 0.3 * particle.opacity; + ctx.fill(); + ctx.globalAlpha = 1.0; + }); + + animationFrameId = requestAnimationFrame(drawParticles); + }; + + // 绘制粒子之间的连线 + const drawLines = () => { + for (let i = 0; i < particles.length; i++) { + for (let j = i + 1; j < particles.length; j++) { + const dx = particles[i].x - particles[j].x; + const dy = particles[i].y - particles[j].y; + const distance = Math.sqrt(dx * dx + dy * dy); + + // 增加连线的最大距离 + const maxDistance = 120; + + if (distance < maxDistance) { + // 只在粒子距离小于maxDistance时绘制连线 + ctx.beginPath(); + ctx.moveTo(particles[i].x, particles[i].y); + ctx.lineTo(particles[j].x, particles[j].y); + + // 根据距离设置线条透明度 + const opacity = 1 - distance / maxDistance; + + // 根据主题设置线条颜色 + const lineColor = + theme.palette.mode === 'dark' + ? `rgba(255, 255, 255, ${opacity * 0.2})` + : `rgba(42, 92, 170, ${opacity * 0.2})`; + + ctx.strokeStyle = lineColor; + ctx.lineWidth = opacity * 1.5; // 根据距离调整线宽 + ctx.stroke(); + } + } + } + }; + + // 初始化 + handleResize(); + window.addEventListener('resize', handleResize); + window.addEventListener('mousemove', handleMouseMove); + window.addEventListener('touchmove', handleTouchMove); + + // 开始动画 + drawParticles(); + + // 清理函数 + return () => { + window.removeEventListener('resize', handleResize); + window.removeEventListener('mousemove', handleMouseMove); + window.removeEventListener('touchmove', handleTouchMove); + cancelAnimationFrame(animationFrameId); + }; + }, [theme.palette.mode]); + + return ( + + ); +} diff --git a/easy-dataset-main/components/home/ProjectCard.js b/easy-dataset-main/components/home/ProjectCard.js new file mode 100644 index 0000000..e3f86ee --- /dev/null +++ b/easy-dataset-main/components/home/ProjectCard.js @@ -0,0 +1,252 @@ +'use client'; + +import { + Card, + Box, + CardActionArea, + CardContent, + Typography, + Avatar, + Divider, + IconButton, + Menu, + MenuItem, + ListItemIcon +} from '@mui/material'; +import Link from 'next/link'; +import { styles } from '@/styles/home'; +import { useTheme, alpha } from '@mui/material/styles'; +import DataObjectIcon from '@mui/icons-material/DataObject'; +import DeleteIcon from '@mui/icons-material/Delete'; +import FolderOpenIcon from '@mui/icons-material/FolderOpen'; +import TokenIcon from '@mui/icons-material/Token'; +import AssessmentIcon from '@mui/icons-material/Assessment'; +import QuizIcon from '@mui/icons-material/Quiz'; +import MoreVertIcon from '@mui/icons-material/MoreVert'; +import { useTranslation } from 'react-i18next'; +import { useState } from 'react'; + +/** + * 统计项组件 + */ +const StatItem = ({ icon: Icon, count, label, color, isToken }) => { + const theme = useTheme(); + + // 格式化数字 + const displayCount = isToken ? (count || 0).toLocaleString() : count || 0; + + return ( + + + + + + + {displayCount} + + + {label} + + + + ); +}; + +/** + * 项目卡片组件 + * @param {Object} props - 组件属性 + * @param {Object} props.project - 项目数据 + * @param {Function} props.onDeleteClick - 删除按钮点击事件处理函数 + */ +export default function ProjectCard({ project, onDeleteClick }) { + const { t } = useTranslation(); + const theme = useTheme(); + const [processingId, setProcessingId] = useState(false); + + // 菜单状态 + const [anchorEl, setAnchorEl] = useState(null); + const open = Boolean(anchorEl); + + // 打开项目目录 + const handleOpenDirectory = async event => { + event.stopPropagation(); + event.preventDefault(); + + if (processingId) return; + + try { + setProcessingId(true); + + const response = await fetch('/api/projects/open-directory', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ projectId: project.id }) + }); + + if (!response.ok) { + const data = await response.json(); + throw new Error(data.error || t('migration.openDirectoryFailed')); + } + + // 成功打开目录,不需要特别处理 + } catch (error) { + console.error('打开目录错误:', error); + alert(error.message); + } finally { + setProcessingId(false); + } + }; + + // 处理菜单打开 + const handleMenuClick = event => { + event.stopPropagation(); + event.preventDefault(); + setAnchorEl(event.currentTarget); + }; + + // 处理菜单关闭 + const handleMenuClose = event => { + if (event) { + event.stopPropagation(); + event.preventDefault(); + } + setAnchorEl(null); + }; + + // 处理打开目录点击 + const handleOpenDirectoryClick = event => { + handleMenuClose(event); + handleOpenDirectory(event); + }; + + // 处理删除点击 + const handleDeleteClick = event => { + handleMenuClose(event); + onDeleteClick(event, project); + }; + + return ( + + + + + {/* 头部:Avatar + Title + Menu */} + + + + {project.name.charAt(0).toUpperCase()} + + + + {project.name} + + + ID: {project.id} + + + + + + + + + {/* 描述 */} + + {project.description || t('projects.noDescription', { defaultValue: '暂无描述' })} + + + {/* 统计数据 */} + + + + + + + + + + + {/* 操作菜单 */} + { + e.preventDefault(); + e.stopPropagation(); + }} + transformOrigin={{ horizontal: 'right', vertical: 'top' }} + anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }} + PaperProps={{ + elevation: 3, + sx: { + borderRadius: '12px', + minWidth: 160, + mt: 0.5 + } + }} + > + + + + + {t('projects.openDirectory')} + + + + + + + + + {t('common.delete')} + + + + ); +} diff --git a/easy-dataset-main/components/home/ProjectList.js b/easy-dataset-main/components/home/ProjectList.js new file mode 100644 index 0000000..aa37453 --- /dev/null +++ b/easy-dataset-main/components/home/ProjectList.js @@ -0,0 +1,117 @@ +'use client'; + +import { + Grid, + Paper, + Button, + Dialog, + DialogTitle, + DialogContent, + DialogContentText, + DialogActions, + Typography +} from '@mui/material'; +import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline'; +import { useTranslation } from 'react-i18next'; +import { useState } from 'react'; +import ProjectCard from './ProjectCard'; + +export default function ProjectList({ projects, onCreateProject }) { + const { t } = useTranslation(); + const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); + const [projectToDelete, setProjectToDelete] = useState(null); + const [loading, setLoading] = useState(false); + // 打开删除确认对话框 + const handleOpenDeleteDialog = (event, project) => { + setProjectToDelete(project); + setDeleteDialogOpen(true); + }; + + // 关闭删除确认对话框 + const handleCloseDeleteDialog = () => { + setDeleteDialogOpen(false); + setProjectToDelete(null); + }; + + // 删除项目 + const handleDeleteProject = async () => { + if (!projectToDelete) return; + + try { + setLoading(true); + const response = await fetch(`/api/projects/${projectToDelete.id}`, { + method: 'DELETE' + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.error || t('projects.deleteFailed')); + } + + // 刷新页面以更新项目列表 + window.location.reload(); + } catch (error) { + console.error('删除项目失败:', error); + alert(error.message || t('projects.deleteFailed')); + } finally { + setLoading(false); + handleCloseDeleteDialog(); + } + }; + + return ( + <> + + {projects.length === 0 ? ( + + + + {t('projects.noProjects')} + + + + + ) : ( + projects.map(project => ( + + + + )) + )} + + + {/* 删除确认对话框 */} + + {t('projects.deleteConfirmTitle')} + + + {projectToDelete && ( + <> + {t('projects.deleteConfirm')} +
+ + {projectToDelete.name} + + + )} +
+
+ + + + +
+ + ); +} diff --git a/easy-dataset-main/components/home/StatsCard.js b/easy-dataset-main/components/home/StatsCard.js new file mode 100644 index 0000000..ab28f34 --- /dev/null +++ b/easy-dataset-main/components/home/StatsCard.js @@ -0,0 +1,118 @@ +'use client'; + +import { Paper, Grid, Box, Typography, useMediaQuery, Avatar } from '@mui/material'; +import { styles } from '@/styles/home'; +import { useTheme } from '@mui/material'; +import { motion } from 'framer-motion'; +import FolderOpenIcon from '@mui/icons-material/FolderOpen'; +import QuestionAnswerIcon from '@mui/icons-material/QuestionAnswer'; +import StorageIcon from '@mui/icons-material/Storage'; +import MemoryIcon from '@mui/icons-material/Memory'; + +// 默认模型列表 +const mockModels = [ + { id: 'deepseek-r1', provider: 'Ollama', name: 'DeepSeek-R1' }, + { id: 'gpt-3.5-turbo-openai', provider: 'OpenAI', name: 'gpt-3.5-turbo' }, + { id: 'gpt-3.5-turbo-guiji', provider: 'Guiji', name: 'gpt-3.5-turbo' }, + { id: 'glm-4-flash', provider: 'Zhipu AI', name: 'GLM-4-Flash' } +]; + +export default function StatsCard({ projects }) { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('sm')); + + // 统计卡片数据 + const statsItems = [ + { + value: projects.length, + label: t('stats.ongoingProjects'), + color: 'primary', + icon: + }, + { + value: projects.reduce((sum, project) => sum + (project.questionsCount || 0), 0), + label: t('stats.questionCount'), + color: 'secondary', + icon: + }, + { + value: projects.reduce((sum, project) => sum + (project.datasetsCount || 0), 0), + label: t('stats.generatedDatasets'), + color: 'success', + icon: + }, + { + value: mockModels.length, + label: t('stats.supportedModels'), + color: 'warning', + icon: + } + ]; + + return ( + + + {statsItems.map((item, index) => ( + + + + {item.icon} + + + {item.value} + + + {item.label} + + + + ))} + + + ); +} diff --git a/easy-dataset-main/components/mga/GaPairsIndicator.js b/easy-dataset-main/components/mga/GaPairsIndicator.js new file mode 100644 index 0000000..d2329ab --- /dev/null +++ b/easy-dataset-main/components/mga/GaPairsIndicator.js @@ -0,0 +1,151 @@ +'use client'; + +import { useState, useEffect, useCallback, useRef } from 'react'; +import { + Box, + Chip, + Typography, + IconButton, + Tooltip, + CircularProgress, + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button +} from '@mui/material'; +import { Psychology as PsychologyIcon, AutoAwesome as AutoFixIcon } from '@mui/icons-material'; +import { useTranslation } from 'react-i18next'; +import GaPairsManager from './GaPairsManager'; + +/** + * GA Pairs Indicator Component - Shows GA pairs status for a file + * @param {Object} props + * @param {string} props.projectId - Project ID + * @param {string} props.fileId - File ID + * @param {string} props.fileName - File name for display + */ +export default function GaPairsIndicator({ projectId, fileId, fileName = '未命名文件' }) { + const { t } = useTranslation(); + const [gaPairs, setGaPairs] = useState([]); + const [loading, setLoading] = useState(false); + const [detailsOpen, setDetailsOpen] = useState(false); + + // 获取GA对状态的函数 + const fetchGaPairsStatus = useCallback(async () => { + try { + setLoading(true); + + const response = await fetch(`/api/projects/${projectId}/files/${fileId}/ga-pairs`); + + if (!response.ok) { + if (response.status === 404) { + setGaPairs([]); + return; + } + throw new Error(`HTTP ${response.status}: Failed to load GA pairs`); + } + + const result = await response.json(); + + // 处理响应格式 + let newGaPairs = []; + if (Array.isArray(result)) { + newGaPairs = result; + } else if (result?.data) { + newGaPairs = result.data; + } + + setGaPairs(newGaPairs); + } catch (error) { + console.error('获取GA对状态失败:', error); + setGaPairs([]); + } finally { + setLoading(false); + } + }, [projectId, fileId]); + + // 初始加载 + useEffect(() => { + if (projectId && fileId) { + fetchGaPairsStatus(); + } + }, [projectId, fileId, fetchGaPairsStatus]); + + //监听外部事件 + useEffect(() => { + const handleRefresh = event => { + const { projectId: eventProjectId, fileIds } = event.detail || {}; + + if (eventProjectId === projectId && fileIds?.includes(String(fileId))) { + fetchGaPairsStatus(); + } + }; + + window.addEventListener('refreshGaPairsIndicators', handleRefresh); + return () => window.removeEventListener('refreshGaPairsIndicators', handleRefresh); + }, [projectId, fileId, fetchGaPairsStatus]); + + // 计算激活的GA对数量 + const activePairs = gaPairs.filter(pair => pair.isActive); + const hasGaPairs = gaPairs.length > 0; + + //GA对变化回调处理 + const handleGaPairsChange = useCallback(newGaPairs => { + setGaPairs(newGaPairs || []); + }, []); + + const handleOpenDialog = useCallback(() => { + setDetailsOpen(true); + }, []); + + const handleCloseDialog = useCallback(() => { + setDetailsOpen(false); + }, []); + + //加载状态显示 + if (loading) { + return ( + + + + Loading... + + + ); + } + + return ( + + {hasGaPairs ? ( + } + label={`${activePairs.length}/${gaPairs.length} GA Pairs`} + size="small" + color={activePairs.length > 0 ? 'primary' : 'default'} + variant={activePairs.length > 0 ? 'filled' : 'outlined'} + onClick={handleOpenDialog} + /> + ) : ( + + + + + + )} + + {/* Details Dialog */} + + GA Pairs for {fileName} + + {detailsOpen && ( + + )} + + + + + + + ); +} diff --git a/easy-dataset-main/components/mga/GaPairsManager.js b/easy-dataset-main/components/mga/GaPairsManager.js new file mode 100644 index 0000000..8ded38b --- /dev/null +++ b/easy-dataset-main/components/mga/GaPairsManager.js @@ -0,0 +1,610 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { + Box, + Typography, + Button, + Card, + CardContent, + Switch, + FormControlLabel, + TextField, + IconButton, + Tooltip, + Divider, + Alert, + CircularProgress, + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Grid +} from '@mui/material'; +import { + Add as AddIcon, + Delete as DeleteIcon, + AutoFixHigh as AutoFixHighIcon, + Save as SaveIcon +} from '@mui/icons-material'; +import { useTranslation } from 'react-i18next'; +import i18n from '@/lib/i18n'; + +/** + * GA Pairs Manager Component + * @param {Object} props + * @param {string} props.projectId - Project ID + * @param {string} props.fileId - File ID + * @param {Function} props.onGaPairsChange - Callback when GA pairs change + */ +export default function GaPairsManager({ projectId, fileId, onGaPairsChange }) { + const { t } = useTranslation(); + const [gaPairs, setGaPairs] = useState([]); + const [backupGaPairs, setBackupGaPairs] = useState([]); // 备份状态 + const [loading, setLoading] = useState(false); + const [generating, setGenerating] = useState(false); + const [saving, setSaving] = useState(false); + const [error, setError] = useState(null); + const [success, setSuccess] = useState(null); + const [addDialogOpen, setAddDialogOpen] = useState(false); + const [newGaPair, setNewGaPair] = useState({ + genreTitle: '', + genreDesc: '', + audienceTitle: '', + audienceDesc: '', + isActive: true + }); + + useEffect(() => { + loadGaPairs(); + }, [projectId, fileId]); + + const loadGaPairs = async () => { + try { + setLoading(true); + setError(null); + + const response = await fetch(`/api/projects/${projectId}/files/${fileId}/ga-pairs`); + + // 检查响应状态 + if (!response.ok) { + if (response.status === 404) { + console.warn('GA Pairs API not found, using empty data'); + setGaPairs([]); + setBackupGaPairs([]); + return; + } + throw new Error(`HTTP ${response.status}: Failed to load GA pairs`); + } + + const result = await response.json(); + console.log('Load GA pairs result:', result); + + if (result.success) { + const loadedData = result.data || []; + setGaPairs(loadedData); + setBackupGaPairs([...loadedData]); // 创建备份 + onGaPairsChange?.(loadedData); + } else { + throw new Error(result.error || 'Failed to load GA pairs'); + } + } catch (error) { + console.error('Load GA pairs error:', error); + setError(t('gaPairs.loadError', { error: error.message })); + } finally { + setLoading(false); + } + }; + + const generateGaPairs = async () => { + try { + setGenerating(true); + setError(null); + + console.log('Starting GA pairs generation...'); + + // Get current language from i18n + const currentLanguage = i18n.language === 'en' ? 'en' : '中文'; + + const response = await fetch(`/api/projects/${projectId}/files/${fileId}/ga-pairs`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + regenerate: false, + appendMode: true, // 新增:启用追加模式 + language: currentLanguage + }) + }); + + if (!response.ok) { + let errorMessage = t('gaPairs.generateError'); + + if (response.status === 404) { + errorMessage = t('gaPairs.serviceNotAvailable'); + } else if (response.status === 400) { + try { + const errorResult = await response.json(); + if (errorResult.error?.includes('No active AI model')) { + errorMessage = t('gaPairs.noActiveModel'); + } else if (errorResult.error?.includes('content might be too short')) { + errorMessage = t('gaPairs.contentTooShort'); + } else { + errorMessage = errorResult.error || errorMessage; + } + } catch (parseError) { + errorMessage = t('gaPairs.requestFailed', { status: response.status }); + } + } else if (response.status === 500) { + try { + const errorResult = await response.json(); + if (errorResult.error?.includes('model configuration') || errorResult.error?.includes('Module not found')) { + errorMessage = t('gaPairs.configError'); + } else { + errorMessage = errorResult.error || 'Internal server error occurred.'; + } + } catch (parseError) { + console.error('Failed to parse error response:', parseError); + errorMessage = errorResult.error || t('gaPairs.internalServerError'); + } + } + + throw new Error(errorMessage); + } + + // 处理成功响应 + const responseText = await response.text(); + if (!responseText || responseText.trim() === '') { + throw new Error(t('gaPairs.emptyResponse')); + } + + const result = JSON.parse(responseText); + console.log('Generate GA pairs result:', result); + + if (result.success) { + // 在追加模式下,后端只返回新生成的GA对 + const newGaPairs = result.data || []; + + // 将新生成的GA对追加到现有的GA对 + const updatedGaPairs = [...gaPairs, ...newGaPairs]; + + setGaPairs(updatedGaPairs); + setBackupGaPairs([...updatedGaPairs]); // 更新备份 + onGaPairsChange?.(updatedGaPairs); + setSuccess( + t('gaPairs.additionalPairsGenerated', { + count: newGaPairs.length, + total: updatedGaPairs.length + }) + ); + } else { + throw new Error(result.error || t('gaPairs.generationFailed')); + } + } catch (error) { + console.error('Generate GA pairs error:', error); + setError(error.message); + } finally { + setGenerating(false); + } + }; + + const saveGaPairs = async () => { + try { + setSaving(true); + setError(null); + + // 验证GA对数据 + const validatedGaPairs = gaPairs.map((pair, index) => { + // 处理不同的数据格式 + let genreTitle, genreDesc, audienceTitle, audienceDesc; + + if (pair.genre && typeof pair.genre === 'object') { + genreTitle = pair.genre.title; + genreDesc = pair.genre.description; + } else { + genreTitle = pair.genreTitle || pair.genre; + genreDesc = pair.genreDesc || ''; + } + + if (pair.audience && typeof pair.audience === 'object') { + audienceTitle = pair.audience.title; + audienceDesc = pair.audience.description; + } else { + audienceTitle = pair.audienceTitle || pair.audience; + audienceDesc = pair.audienceDesc || ''; + } + + // 验证必填字段 + if (!genreTitle || !audienceTitle) { + throw new Error(t('gaPairs.validationError', { number: index + 1 })); + } + + return { + id: pair.id, + genreTitle: genreTitle.trim(), + genreDesc: genreDesc.trim(), + audienceTitle: audienceTitle.trim(), + audienceDesc: audienceDesc.trim(), + isActive: pair.isActive !== undefined ? pair.isActive : true + }; + }); + + console.log('Saving validated GA pairs:', validatedGaPairs); + + const response = await fetch(`/api/projects/${projectId}/files/${fileId}/ga-pairs`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + updates: validatedGaPairs + }) + }); + + if (!response.ok) { + let errorMessage = t('gaPairs.saveError'); + + if (response.status === 404) { + errorMessage = 'GA Pairs save service is not available.'; + } else { + try { + const errorResult = await response.json(); + errorMessage = errorResult.error || errorMessage; + } catch (parseError) { + errorMessage = t('gaPairs.serverError', { status: response.status }); + } + } + + throw new Error(errorMessage); + } + + const responseText = await response.text(); + const result = responseText ? JSON.parse(responseText) : { success: true }; + + if (result.success) { + // 更新本地状态为服务器返回的数据 + const savedData = result.data || validatedGaPairs; + setGaPairs(savedData); + + // 根据保存的GA对数量显示不同的成功消息 + if (savedData.length === 0) { + setSuccess(t('gaPairs.allPairsDeleted')); + } else { + setSuccess(t('gaPairs.pairsSaved', { count: savedData.length })); + } + + onGaPairsChange?.(savedData); + } else { + throw new Error(result.error || t('gaPairs.saveOperationFailed')); + } + } catch (error) { + console.error('Save GA pairs error:', error); + setError(error.message); + } finally { + setSaving(false); + } + }; + + const handleGaPairChange = (index, field, value) => { + const updatedGaPairs = [...gaPairs]; + + // 确保对象存在 + if (!updatedGaPairs[index]) { + console.error(`GA pair at index ${index} does not exist`); + return; + } + + updatedGaPairs[index] = { + ...updatedGaPairs[index], + [field]: value + }; + + setGaPairs(updatedGaPairs); + // 不立即调用 onGaPairsChange,等用户点击保存时再调用 + }; + + const handleDeleteGaPair = index => { + const updatedGaPairs = gaPairs.filter((_, i) => i !== index); + setGaPairs(updatedGaPairs); + onGaPairsChange?.(updatedGaPairs); + }; + + const handleAddGaPair = () => { + // 验证输入 + if (!newGaPair.genreTitle?.trim() || !newGaPair.audienceTitle?.trim()) { + setError(t('gaPairs.requiredFields')); + return; + } + + // 创建新的GA对对象 + const newPair = { + id: `temp_${Date.now()}`, // 临时ID + genreTitle: newGaPair.genreTitle.trim(), + genreDesc: newGaPair.genreDesc?.trim() || '', + audienceTitle: newGaPair.audienceTitle.trim(), + audienceDesc: newGaPair.audienceDesc?.trim() || '', + isActive: true + }; + + const updatedGaPairs = [...gaPairs, newPair]; + setGaPairs(updatedGaPairs); + onGaPairsChange?.(updatedGaPairs); + + // 重置表单并关闭对话框 + setNewGaPair({ + genreTitle: '', + genreDesc: '', + audienceTitle: '', + audienceDesc: '', + isActive: true + }); + setAddDialogOpen(false); + setError(null); + }; + + const resetMessages = () => { + setError(null); + setSuccess(null); + }; + + const recoverFromBackup = () => { + setGaPairs([...backupGaPairs]); + setError(null); + setSuccess(t('gaPairs.restoredFromBackup')); + }; + + useEffect(() => { + if (error || success) { + const timer = setTimeout(resetMessages, 5000); + return () => clearTimeout(timer); + } + }, [error, success]); + + if (loading) { + return ( + + + {t('gaPairs.loading')} + + ); + } + + return ( + + {/* Header with action buttons */} + + {t('gaPairs.title')} + + {/* 右上角按钮为手动添加GA对 */} + + + + + + {/* Error/Success Messages */} + {error && ( + 0 && ( + + ) + } + onClose={resetMessages} + > + {error} + + )} + {success && ( + + {success} + + )} + + {/* Generate GA Pairs Section - 只在没有GA对时显示 */} + {gaPairs.length === 0 && ( + + + + {t('gaPairs.noGaPairsTitle')} + + + {t('gaPairs.noGaPairsDescription')} + + + + + )} + + {/* GA Pairs List */} + {gaPairs.length > 0 && ( + + + {t('gaPairs.activePairs', { + active: gaPairs.filter(pair => pair.isActive).length, + total: gaPairs.length + })} + + + + {gaPairs.map((pair, index) => ( + + + + + + {t('gaPairs.pairNumber', { number: index + 1 })} + + + handleGaPairChange(index, 'isActive', e.target.checked)} + size="small" + /> + } + label={t('gaPairs.active')} + /> + {/* 添加删除按钮 */} + + handleDeleteGaPair(index)}> + + + + + + + + handleGaPairChange(index, 'genreTitle', e.target.value)} + multiline + rows={2} + fullWidth + disabled={!pair.isActive} + /> + handleGaPairChange(index, 'genreDesc', e.target.value)} + multiline + rows={2} + fullWidth + disabled={!pair.isActive} + /> + handleGaPairChange(index, 'audienceTitle', e.target.value)} + multiline + rows={2} + fullWidth + disabled={!pair.isActive} + /> + handleGaPairChange(index, 'audienceDesc', e.target.value)} + multiline + rows={2} + fullWidth + disabled={!pair.isActive} + /> + + + + + ))} + + + {/* 在GA对列表下方添加生成按钮 */} + + + + + )} + + {/* Add GA Pair Dialog */} + setAddDialogOpen(false)} maxWidth="md" fullWidth> + {t('gaPairs.addDialogTitle')} + + + setNewGaPair({ ...newGaPair, genreTitle: e.target.value })} + fullWidth + required + placeholder={t('gaPairs.genreTitlePlaceholder')} + /> + setNewGaPair({ ...newGaPair, genreDesc: e.target.value })} + multiline + rows={3} + fullWidth + placeholder={t('gaPairs.genreDescPlaceholder')} + /> + setNewGaPair({ ...newGaPair, audienceTitle: e.target.value })} + fullWidth + required + placeholder={t('gaPairs.audienceTitlePlaceholder')} + /> + setNewGaPair({ ...newGaPair, audienceDesc: e.target.value })} + multiline + rows={3} + fullWidth + placeholder={t('gaPairs.audienceDescPlaceholder')} + /> + setNewGaPair({ ...newGaPair, isActive: e.target.checked })} + /> + } + label={t('gaPairs.active')} + /> + + + + + + + + + ); +} diff --git a/easy-dataset-main/components/playground/ChatArea.js b/easy-dataset-main/components/playground/ChatArea.js new file mode 100644 index 0000000..927c5e9 --- /dev/null +++ b/easy-dataset-main/components/playground/ChatArea.js @@ -0,0 +1,83 @@ +'use client'; + +import React, { useRef, useEffect } from 'react'; +import { Box, Typography, Paper, Grid, CircularProgress } from '@mui/material'; +import { useTheme } from '@mui/material/styles'; +import ChatMessage from './ChatMessage'; +import { playgroundStyles } from '@/styles/playground'; +import { useTranslation } from 'react-i18next'; + +const ChatArea = ({ selectedModels, conversations, loading, getModelName }) => { + const theme = useTheme(); + const styles = playgroundStyles(theme); + const { t } = useTranslation(); + + // 为每个模型创建独立的引用 + const chatContainerRefs = { + model1: useRef(null), + model2: useRef(null), + model3: useRef(null) + }; + + // 为每个模型的聊天容器自动滚动到底部 + useEffect(() => { + Object.values(chatContainerRefs).forEach(ref => { + if (ref.current) { + ref.current.scrollTop = ref.current.scrollHeight; + } + }); + }, [conversations]); + + if (selectedModels.length === 0) { + return ( + + {t('playground.selectModelFirst')} + + ); + } + + return ( + + {selectedModels.map((modelId, index) => { + const modelConversation = conversations[modelId] || []; + const isLoading = loading[modelId]; + const refKey = `model${index + 1}`; + + return ( + 1 ? 12 / selectedModels.length : 12} + key={modelId} + style={{ maxHeight: 'calc(100vh - 300px)' }} + > + + + {getModelName(modelId)} + {isLoading && } + + + + {modelConversation.length === 0 ? ( + + + {t('playground.sendFirstMessage')} + + + ) : ( + modelConversation.map((message, msgIndex) => ( + + + + )) + )} + + + + ); + })} + + ); +}; + +export default ChatArea; diff --git a/easy-dataset-main/components/playground/ChatMessage.js b/easy-dataset-main/components/playground/ChatMessage.js new file mode 100644 index 0000000..faeb32a --- /dev/null +++ b/easy-dataset-main/components/playground/ChatMessage.js @@ -0,0 +1,215 @@ +import React, { useState } from 'react'; +import { Box, Paper, Typography, Alert, useTheme, IconButton, Collapse } from '@mui/material'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import ExpandLessIcon from '@mui/icons-material/ExpandLess'; +import PsychologyIcon from '@mui/icons-material/Psychology'; +import AutoFixHighIcon from '@mui/icons-material/AutoFixHigh'; +import { useTranslation } from 'react-i18next'; + +/** + * 聊天消息组件 + * @param {Object} props + * @param {Object} props.message - 消息对象 + * @param {string} props.message.role - 消息角色:'user'、'assistant' 或 'error' + * @param {string} props.message.content - 消息内容 + * @param {string} props.modelName - 模型名称(仅在 assistant 或 error 类型消息中显示) + */ +export default function ChatMessage({ message, modelName }) { + const theme = useTheme(); + const { t } = useTranslation(); + + // 用户消息 + if (message.role === 'user') { + return ( + + + {typeof message.content === 'string' ? ( + {message.content} + ) : ( + // 如果是数组类型(用于视觉模型的用户输入) + <> + {Array.isArray(message.content) && + message.content.map((item, i) => { + if (item.type === 'text') { + return ( + + {item.text} + + ); + } else if (item.type === 'image_url') { + return ( + + 上传图片 + + ); + } + return null; + })} + + )} + + + ); + } + + // 助手消息 + if (message.role === 'assistant') { + // 处理推理过程的展示状态 + const [showThinking, setShowThinking] = useState(message.showThinking || false); + const hasThinking = message.thinking && message.thinking.trim().length > 0; + + return ( + + + {modelName && ( + + {modelName} + + )} + + {/* 推理过程显示区域 */} + {hasThinking && ( + + + + {message.isStreaming ? ( + + ) : ( + + )} + + {t('playground.reasoningProcess', '推理过程')} + + + setShowThinking(!showThinking)} sx={{ p: 0 }}> + {showThinking ? : } + + + + + + + {message.thinking} + + + + + )} + + {/* 回答内容 */} + + {typeof message.content === 'string' ? ( + <> + {message.content} + {message.isStreaming && |} + + ) : ( + // 如果是数组类型(用于视觉模型的响应) + <> + {Array.isArray(message.content) && + message.content.map((item, i) => { + if (item.type === 'text') { + return {item.text}; + } else if (item.type === 'image_url') { + return ( + + 图片 + + ); + } + return null; + })} + {message.isStreaming && |} + + )} + + + + ); + } + + // 错误消息 + if (message.role === 'error') { + return ( + + + {modelName && ( + + {modelName} + + )} + {message.content} + + + ); + } + + return null; +} diff --git a/easy-dataset-main/components/playground/MessageInput.js b/easy-dataset-main/components/playground/MessageInput.js new file mode 100644 index 0000000..ddfe18c --- /dev/null +++ b/easy-dataset-main/components/playground/MessageInput.js @@ -0,0 +1,104 @@ +'use client'; + +import React, { useState } from 'react'; +import { Box, TextField, Button, IconButton, Badge, Tooltip } from '@mui/material'; +import SendIcon from '@mui/icons-material/Send'; +import ImageIcon from '@mui/icons-material/Image'; +import CancelIcon from '@mui/icons-material/Cancel'; +import { useTheme } from '@mui/material/styles'; +import { playgroundStyles } from '@/styles/playground'; +import { useTranslation } from 'react-i18next'; + +const MessageInput = ({ + userInput, + handleInputChange, + handleSendMessage, + loading, + selectedModels, + uploadedImage, + handleImageUpload, + handleRemoveImage, + availableModels +}) => { + const theme = useTheme(); + const styles = playgroundStyles(theme); + const { t } = useTranslation(); + + const isDisabled = Object.values(loading).some(value => value) || selectedModels.length === 0; + const isSendDisabled = isDisabled || (!userInput.trim() && !uploadedImage); + + // 检查是否有视觉模型被选中 + const hasVisionModel = selectedModels.some(modelId => { + const model = availableModels.find(m => m.id === modelId); + return model && model.type === 'vision'; + }); + + return ( + + {uploadedImage && ( + + + + + } + sx={{ width: '100%' }} + overlap="rectangular" + anchorOrigin={{ vertical: 'top', horizontal: 'right' }} + > + 上传图片 + + + )} + + { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + handleSendMessage(); + } + }} + multiline + maxRows={4} + /> + {hasVisionModel && ( + + + + + + + + + )} + + + + ); +}; + +export default MessageInput; diff --git a/easy-dataset-main/components/playground/ModelSelector.js b/easy-dataset-main/components/playground/ModelSelector.js new file mode 100644 index 0000000..f34a794 --- /dev/null +++ b/easy-dataset-main/components/playground/ModelSelector.js @@ -0,0 +1,81 @@ +import React from 'react'; +import { + FormControl, + InputLabel, + Select, + MenuItem, + OutlinedInput, + Box, + Chip, + Checkbox, + ListItemText +} from '@mui/material'; +import { useTranslation } from 'react-i18next'; + +const ITEM_HEIGHT = 48; +const ITEM_PADDING_TOP = 8; +const MenuProps = { + PaperProps: { + style: { + maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP, + width: 250 + } + } +}; + +/** + * 模型选择组件 + * @param {Object} props + * @param {Array} props.models - 可用模型列表 + * @param {Array} props.selectedModels - 已选择的模型ID列表 + * @param {Function} props.onChange - 选择改变时的回调函数 + */ +export default function ModelSelector({ models, selectedModels, onChange }) { + // 获取模型名称 + const getModelName = modelId => { + const model = models.find(m => m.id === modelId); + return model ? `${model.providerName}: ${model.modelName}` : modelId; + }; + const { t } = useTranslation(); + + return ( + + {t('playground.selectModelMax3')} + + + ); +} diff --git a/easy-dataset-main/components/playground/PlaygroundHeader.js b/easy-dataset-main/components/playground/PlaygroundHeader.js new file mode 100644 index 0000000..75cb9ca --- /dev/null +++ b/easy-dataset-main/components/playground/PlaygroundHeader.js @@ -0,0 +1,66 @@ +'use client'; + +import React from 'react'; +import { Grid, Button, Divider, FormControl, InputLabel, Select, MenuItem } from '@mui/material'; +import DeleteIcon from '@mui/icons-material/Delete'; +import { useTheme } from '@mui/material/styles'; +import ModelSelector from './ModelSelector'; +import { playgroundStyles } from '@/styles/playground'; +import { useTranslation } from 'react-i18next'; + +const PlaygroundHeader = ({ + availableModels, + selectedModels, + handleModelSelection, + handleClearConversations, + conversations, + outputMode, + handleOutputModeChange +}) => { + const theme = useTheme(); + const styles = playgroundStyles(theme); + const { t } = useTranslation(); + + const isClearDisabled = selectedModels.length === 0 || Object.values(conversations).every(conv => conv.length === 0); + + return ( + <> + + + + + + + {t('playground.outputMode')} + + + + + + + + + + + ); +}; + +export default PlaygroundHeader; diff --git a/easy-dataset-main/components/questions/QuestionListView.js b/easy-dataset-main/components/questions/QuestionListView.js new file mode 100644 index 0000000..0d1bf56 --- /dev/null +++ b/easy-dataset-main/components/questions/QuestionListView.js @@ -0,0 +1,374 @@ +'use client'; + +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { + Box, + Typography, + Checkbox, + IconButton, + Chip, + Tooltip, + Pagination, + Divider, + Paper, + CircularProgress, + TextField +} from '@mui/material'; +import DeleteIcon from '@mui/icons-material/Delete'; +import AutoFixHighIcon from '@mui/icons-material/AutoFixHigh'; +import EditIcon from '@mui/icons-material/Edit'; +import ChatIcon from '@mui/icons-material/Chat'; +import { useGenerateDataset } from '@/hooks/useGenerateDataset'; +import { toast } from 'sonner'; +import { useAtomValue } from 'jotai'; +import { selectedModelInfoAtom } from '@/lib/store'; + +export default function QuestionListView({ + questions = [], + currentPage, + totalQuestions = 0, + handlePageChange, + selectedQuestions = [], + onSelectQuestion, + onDeleteQuestion, + projectId, + onEditQuestion, + refreshQuestions +}) { + const { t } = useTranslation(); + // 处理状态 + const [processingQuestions, setProcessingQuestions] = useState({}); + const { generateSingleDataset } = useGenerateDataset(); + // 获取当前选中的模型 + const selectedModelInfo = useAtomValue(selectedModelInfoAtom); + + // 获取文本块的标题 + const getChunkTitle = content => { + const firstLine = content ? content.split('\n')[0].trim() : ''; + if (firstLine.startsWith('# ')) { + return firstLine.substring(2); + } else if (firstLine.length > 0) { + return firstLine.length > 200 ? firstLine.substring(0, 200) + '...' : firstLine; + } + return ''; + }; + + // 检查问题是否被选中 + const isQuestionSelected = questionId => { + return selectedQuestions.includes(questionId); + }; + + // 处理生成数据集 + const handleGenerateDataset = async (questionId, questionInfo, imageId, imageName) => { + // 设置处理状态 + setProcessingQuestions(prev => ({ + ...prev, + [questionId]: true + })); + await generateSingleDataset({ + projectId, + questionId, + questionInfo, + imageId, + imageName + }); + // 重置处理状态 + setProcessingQuestions(prev => ({ + ...prev, + [questionId]: false + })); + refreshQuestions(); + }; + + // 处理生成多轮对话数据集 + const handleGenerateMultiTurnDataset = async (questionId, questionInfo) => { + try { + // 设置处理状态 + setProcessingQuestions(prev => ({ + ...prev, + [`${questionId}_multi`]: true + })); + + // 首先检查项目是否配置了多轮对话设置 + const configResponse = await fetch(`/api/projects/${projectId}/tasks`); + if (!configResponse.ok) { + throw new Error('获取项目配置失败'); + } + + const config = await configResponse.json(); + const multiTurnConfig = { + systemPrompt: config.multiTurnSystemPrompt, + scenario: config.multiTurnScenario, + rounds: config.multiTurnRounds, + roleA: config.multiTurnRoleA, + roleB: config.multiTurnRoleB + }; + + console.log('multiTurnConfig:', multiTurnConfig); + + // 检查是否已配置必要的多轮对话设置 + // 系统提示词是可选的,但场景、角色A、角色B和轮数是必需的 + if ( + !multiTurnConfig.scenario || + !multiTurnConfig.roleA || + !multiTurnConfig.roleB || + !multiTurnConfig.rounds || + multiTurnConfig.rounds < 1 + ) { + toast.error(t('questions.multiTurnNotConfigured', '请先在项目设置中配置多轮对话相关参数')); + return; + } + + // 检查是否选中了模型 + if (!selectedModelInfo) { + toast.error(t('datasets.selectModelFirst', '请先选择模型')); + return; + } + + // 调用多轮对话生成API + const response = await fetch(`/api/projects/${projectId}/dataset-conversations`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + questionId, + ...multiTurnConfig, + model: selectedModelInfo + }) + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.message || '生成多轮对话数据集失败'); + } + + const result = await response.json(); + toast.success(t('questions.multiTurnGenerated', '多轮对话数据集生成成功!')); + } catch (error) { + console.error('生成多轮对话数据集失败:', error); + toast.error(error.message || '生成多轮对话数据集失败'); + } finally { + // 重置处理状态 + setProcessingQuestions(prev => ({ + ...prev, + [`${questionId}_multi`]: false + })); + } + }; + + return ( + + {/* 问题列表 */} + + + + {t('datasets.question')} + + + + {t('common.label')} + + + {t('common.dataSource')} + + + {t('common.actions')} + + + + + + + {questions.map((question, index) => { + const isSelected = isQuestionSelected(question.id); + const questionKey = question.id; + return ( + + + { + onSelectQuestion(questionKey); + }} + size="small" + /> + + + + {question.question} + {question.datasetCount > 0 ? ( + + ) : null} + + + {question.label || t('datasets.noTag')} • ID: {(question.question || '').substring(0, 8)} + + + + + {question.label ? ( + + ) : ( + + {t('datasets.noTag')} + + )} + + + + + + + + + + + onEditQuestion(question)} + disabled={processingQuestions[questionKey]} + > + + + + + + handleGenerateDataset(question.id, question.question, question.imageId, question.imageName) + } + disabled={processingQuestions[questionKey]} + > + {processingQuestions[questionKey] ? ( + + ) : ( + + )} + + + + {!question.imageId && ( + + handleGenerateMultiTurnDataset(question.id, question.question)} + disabled={processingQuestions[`${questionKey}_multi`]} + > + {processingQuestions[`${questionKey}_multi`] ? ( + + ) : ( + + )} + + + )} + + + onDeleteQuestion(question.id)} + disabled={processingQuestions[questionKey]} + > + + + + + + {index < questions.length - 1 && } + + ); + })} + + + {/* 分页 */} + {totalQuestions > 1 && ( + + + + {t('common.jumpTo')}: + { + if (e.key === 'Enter') { + const pageNum = parseInt(e.target.value, 10); + if (pageNum >= 1 && pageNum <= totalQuestions) { + handlePageChange(null, pageNum); + e.target.value = ''; + } + } + }} + /> + + + )} + + ); +} diff --git a/easy-dataset-main/components/questions/QuestionTreeView.js b/easy-dataset-main/components/questions/QuestionTreeView.js new file mode 100644 index 0000000..6688742 --- /dev/null +++ b/easy-dataset-main/components/questions/QuestionTreeView.js @@ -0,0 +1,565 @@ +'use client'; + +import { useState, useEffect, useCallback, useMemo, memo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { + Box, + Typography, + Paper, + List, + ListItem, + ListItemText, + Checkbox, + IconButton, + Collapse, + Chip, + Tooltip, + Divider, + CircularProgress +} from '@mui/material'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import ExpandLessIcon from '@mui/icons-material/ExpandLess'; +import DeleteIcon from '@mui/icons-material/Delete'; +import AutoFixHighIcon from '@mui/icons-material/AutoFixHigh'; +import EditIcon from '@mui/icons-material/Edit'; +import FolderIcon from '@mui/icons-material/Folder'; +import QuestionMarkIcon from '@mui/icons-material/QuestionMark'; +import { useGenerateDataset } from '@/hooks/useGenerateDataset'; +import axios from 'axios'; + +/** + * 问题树视图组件 + * @param {Object} props + * @param {Array} props.tags - 标签树 + * @param {Array} props.selectedQuestions - 已选择的问题ID列表 + * @param {Function} props.onSelectQuestion - 选择问题的回调函数 + * @param {Function} props.onDeleteQuestion - 删除问题的回调函数 + */ +export default function QuestionTreeView({ + tags = [], + selectedQuestions = [], + onSelectQuestion, + onDeleteQuestion, + onEditQuestion, + projectId, + searchTerm +}) { + const { t } = useTranslation(); + const [expandedTags, setExpandedTags] = useState({}); + const [questionsByTag, setQuestionsByTag] = useState({}); + const [processingQuestions, setProcessingQuestions] = useState({}); + const { generateSingleDataset } = useGenerateDataset(); + const [questions, setQuestions] = useState([]); + const [loadedTags, setLoadedTags] = useState({}); + // 初始化时,将所有标签设置为收起状态(而不是展开状态) + useEffect(() => { + async function fetchTagsInfo() { + try { + // 获取标签信息,仅用于标签统计 + const response = await axios.get(`/api/projects/${projectId}/questions/tree?tagsOnly=true&input=${searchTerm}`); + setQuestions(response.data); // 设置数据仅用于标签统计 + + // 当搜索条件变化时,重新加载已展开标签的问题数据 + const expandedTagLabels = Object.entries(expandedTags) + .filter(([_, isExpanded]) => isExpanded) + .map(([label]) => label); + + // 重新加载已展开标签的数据 + for (const label of expandedTagLabels) { + fetchTagQuestions(label); + } + } catch (error) { + console.error('获取标签信息失败:', error); + } + } + + if (projectId) { + fetchTagsInfo(); + } + + const initialExpandedState = {}; + const processTag = tag => { + // 将默认状态改为 false(收起)而不是 true(展开) + initialExpandedState[tag.label] = false; + if (tag.child && tag.child.length > 0) { + tag.child.forEach(processTag); + } + }; + + tags.forEach(processTag); + // 未分类问题也默认收起 + initialExpandedState['uncategorized'] = false; + setExpandedTags(initialExpandedState); + }, [tags]); + + // 根据标签对问题进行分类 + useEffect(() => { + const taggedQuestions = {}; + + // 初始化标签映射 + const initTagMap = tag => { + taggedQuestions[tag.label] = []; + if (tag.child && tag.child.length > 0) { + tag.child.forEach(initTagMap); + } + }; + + tags.forEach(initTagMap); + + // 将问题分配到对应的标签下 + questions.forEach(question => { + // 如果问题没有标签,添加到"未分类" + if (!question.label) { + if (!taggedQuestions['uncategorized']) { + taggedQuestions['uncategorized'] = []; + } + taggedQuestions['uncategorized'].push(question); + return; + } + + // 将问题添加到匹配的标签下 + const questionLabel = question.label; + + // 查找最精确匹配的标签 + // 使用一个数组来存储所有匹配的标签路径,以便找到最精确的匹配 + const findAllMatchingTags = (tag, path = []) => { + const currentPath = [...path, tag.label]; + + // 存储所有匹配结果 + const matches = []; + + // 精确匹配当前标签 + if (tag.label === questionLabel) { + matches.push({ label: tag.label, depth: currentPath.length }); + } + + // 检查子标签 + if (tag.child && tag.child.length > 0) { + for (const childTag of tag.child) { + const childMatches = findAllMatchingTags(childTag, currentPath); + matches.push(...childMatches); + } + } + + return matches; + }; + + // 在所有根标签中查找所有匹配 + let allMatches = []; + for (const rootTag of tags) { + const matches = findAllMatchingTags(rootTag); + allMatches.push(...matches); + } + + // 找到深度最大的匹配(最精确的匹配) + let matchedTagLabel = null; + if (allMatches.length > 0) { + // 按深度排序,深度最大的是最精确的匹配 + allMatches.sort((a, b) => b.depth - a.depth); + matchedTagLabel = allMatches[0].label; + } + + if (matchedTagLabel) { + // 如果找到匹配的标签,将问题添加到该标签下 + if (!taggedQuestions[matchedTagLabel]) { + taggedQuestions[matchedTagLabel] = []; + } + taggedQuestions[matchedTagLabel].push(question); + } else { + // 如果找不到匹配的标签,添加到"未分类" + if (!taggedQuestions['uncategorized']) { + taggedQuestions['uncategorized'] = []; + } + taggedQuestions['uncategorized'].push(question); + } + }); + + setQuestionsByTag(taggedQuestions); + }, [questions, tags]); + + // 处理展开/折叠标签 - 使用 useCallback 优化 + const handleToggleExpand = useCallback( + tagLabel => { + // 检查是否需要加载此标签的问题数据 + const shouldExpand = !expandedTags[tagLabel]; + + if (shouldExpand && !loadedTags[tagLabel]) { + // 如果要展开且尚未加载数据,则加载数据 + fetchTagQuestions(tagLabel); + } + + setExpandedTags(prev => ({ + ...prev, + [tagLabel]: shouldExpand + })); + }, + [expandedTags, loadedTags, projectId] + ); + + // 获取特定标签的问题数据 + const fetchTagQuestions = useCallback( + async tagLabel => { + try { + const response = await axios.get( + `/api/projects/${projectId}/questions/tree?tag=${encodeURIComponent(tagLabel)}${searchTerm ? `&input=${searchTerm}` : ''}` + ); + + // 更新问题数据,合并新获取的数据 + setQuestions(prev => { + // 创建一个新数组,包含现有数据 + const updatedQuestions = [...prev]; + + // 添加新获取的问题数据 + response.data.forEach(newQuestion => { + // 检查是否已存在相同 ID 的问题 + const existingIndex = updatedQuestions.findIndex(q => q.id === newQuestion.id); + if (existingIndex === -1) { + // 如果不存在,添加到数组 + updatedQuestions.push(newQuestion); + } else { + // 如果已存在,更新数据 + updatedQuestions[existingIndex] = newQuestion; + } + }); + + return updatedQuestions; + }); + + // 标记该标签已加载数据 + setLoadedTags(prev => ({ + ...prev, + [tagLabel]: true + })); + } catch (error) { + console.error(`获取标签 "${tagLabel}" 的问题失败:`, error); + } + }, + [projectId, searchTerm, expandedTags] + ); + + // 检查问题是否被选中 - 使用 useCallback 优化 + const isQuestionSelected = useCallback( + questionKey => { + return selectedQuestions.includes(questionKey); + }, + [selectedQuestions] + ); + + // 处理生成数据集 - 使用 useCallback 优化 + const handleGenerateDataset = async (questionId, questionInfo) => { + // 设置处理状态 + setProcessingQuestions(prev => ({ + ...prev, + [questionId]: true + })); + await generateSingleDataset({ projectId, questionId, questionInfo }); + // 重置处理状态 + setProcessingQuestions(prev => ({ + ...prev, + [questionId]: false + })); + }; + + // 渲染单个问题项 - 使用 useCallback 优化 + const renderQuestionItem = useCallback( + (question, index, total) => { + const questionKey = question.id; + return ( + + ); + }, + [isQuestionSelected, onSelectQuestion, onDeleteQuestion, handleGenerateDataset, processingQuestions, t] + ); + + // 计算标签及其子标签下的所有问题数量 - 使用 useMemo 缓存计算结果 + const tagQuestionCounts = useMemo(() => { + const counts = {}; + + const countQuestions = tag => { + const directQuestions = questionsByTag[tag.label] || []; + let total = directQuestions.length; + + if (tag.child && tag.child.length > 0) { + for (const childTag of tag.child) { + total += countQuestions(childTag); + } + } + + counts[tag.label] = total; + return total; + }; + + tags.forEach(countQuestions); + return counts; + }, [questionsByTag, tags]); + + // 递归渲染标签树 - 使用 useCallback 优化 + const renderTagTree = useCallback( + (tag, level = 0) => { + const questions = questionsByTag[tag.label] || []; + const hasQuestions = questions.length > 0; + const hasChildren = tag.child && tag.child.length > 0; + const isExpanded = expandedTags[tag.label]; + const totalQuestions = tagQuestionCounts[tag.label] || 0; + + return ( + + + + {/* 只有当标签展开时才渲染子内容,减少不必要的渲染 */} + {isExpanded && ( + + {hasChildren && ( + {tag.child.map(childTag => renderTagTree(childTag, level + 1))} + )} + + {hasQuestions && ( + + {questions.map((question, index) => renderQuestionItem(question, index, questions.length))} + + )} + + )} + + ); + }, + [questionsByTag, expandedTags, tagQuestionCounts, handleToggleExpand, renderQuestionItem, t] + ); + + // 渲染未分类问题 + const renderUncategorizedQuestions = () => { + const uncategorizedQuestions = questionsByTag['uncategorized'] || []; + if (uncategorizedQuestions.length === 0) return null; + + return ( + + handleToggleExpand('uncategorized')} + sx={{ + py: 1, + bgcolor: 'primary.light', + color: 'primary.contrastText', + '&:hover': { + bgcolor: 'primary.main' + }, + borderRadius: '4px', + mb: 0.5, + pr: 1 + }} + > + + + + {t('datasets.uncategorized')} + + + + } + /> + + {expandedTags['uncategorized'] ? : } + + + + + + {uncategorizedQuestions.map((question, index) => + renderQuestionItem(question, index, uncategorizedQuestions.length) + )} + + +
+ ); + }; + + // 如果没有标签和问题,显示空状态 + if (tags.length === 0 && Object.keys(questionsByTag).length === 0) { + return ( + + + {t('datasets.noTagsAndQuestions')} + + + ); + } + + return ( + + + {renderUncategorizedQuestions()} + {tags.map(tag => renderTagTree(tag))} + + + ); +} + +// 使用 memo 优化问题项渲染 +const QuestionItem = memo( + ({ question, index, total, isSelected, onSelect, onDelete, onGenerate, onEdit, isProcessing, t }) => { + const questionKey = question.id; + return ( + + + onSelect(questionKey)} size="small" /> + + + {question.question} + {question.dataSites && question.dataSites.length > 0 && ( + + )} + + } + secondary={ + + {t('datasets.source')}: {question.chunk?.name || question.chunkId || t('common.unknown')} + + } + /> + + + + onEdit({ + question: question.question, + chunkId: question.chunkId, + label: question.label || 'other' + }) + } + disabled={isProcessing} + > + + + + + onGenerate(question.id, question.question)} + disabled={isProcessing} + > + {isProcessing ? : } + + + + onDelete(question.id)}> + + + + + + {index < total - 1 && } + + ); + } +); + +// 使用 memo 优化标签项渲染 +const TagItem = memo(({ tag, level, isExpanded, totalQuestions, onToggle, t }) => { + return ( + onToggle(tag.label)} + sx={{ + pl: level * 2 + 1, + py: 1, + bgcolor: level === 0 ? 'primary.light' : 'background.paper', + color: level === 0 ? 'primary.contrastText' : 'inherit', + '&:hover': { + bgcolor: level === 0 ? 'primary.main' : 'action.hover' + }, + borderRadius: '4px', + mb: 0.5, + pr: 1 + }} + > + {/* 内部内容保持不变 */} + + + + {tag.label} + + {totalQuestions > 0 && ( + + )} +
+ } + /> + + {isExpanded ? : } + + + ); +}); diff --git a/easy-dataset-main/components/settings/BasicSettings.js b/easy-dataset-main/components/settings/BasicSettings.js new file mode 100644 index 0000000..831d447 --- /dev/null +++ b/easy-dataset-main/components/settings/BasicSettings.js @@ -0,0 +1,153 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { Typography, Box, Button, TextField, Grid, Card, CardContent, Alert, Snackbar } from '@mui/material'; +import SaveIcon from '@mui/icons-material/Save'; +import { useTranslation } from 'react-i18next'; + +export default function BasicSettings({ projectId }) { + const { t } = useTranslation(); + const [projectInfo, setProjectInfo] = useState({ + id: '', + name: '', + description: '' + }); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [success, setSuccess] = useState(false); + + useEffect(() => { + async function fetchProjectInfo() { + try { + setLoading(true); + const response = await fetch(`/api/projects/${projectId}`); + + if (!response.ok) { + throw new Error(t('projects.fetchFailed')); + } + + const data = await response.json(); + setProjectInfo(data); + } catch (error) { + console.error('获取项目信息出错:', error); + setError(error.message); + } finally { + setLoading(false); + } + } + + fetchProjectInfo(); + }, [projectId, t]); + + // 处理项目信息变更 + const handleProjectInfoChange = e => { + const { name, value } = e.target; + setProjectInfo(prev => ({ + ...prev, + [name]: value + })); + }; + + // 保存项目信息 + const handleSaveProjectInfo = async () => { + try { + const response = await fetch(`/api/projects/${projectId}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + name: projectInfo.name, + description: projectInfo.description + }) + }); + + if (!response.ok) { + throw new Error(t('projects.saveFailed')); + } + + setSuccess(true); + } catch (error) { + console.error('保存项目信息出错:', error); + setError(error.message); + } + }; + + const handleCloseSnackbar = () => { + setSuccess(false); + setError(null); + }; + + if (loading) { + return {t('common.loading')}; + } + + return ( + + + + {t('settings.basicInfo')} + + + + + + + + + + + + + + + + + + + + {t('settings.saveSuccess')} + + + + + + {error} + + + + ); +} diff --git a/easy-dataset-main/components/settings/ModelSettings.js b/easy-dataset-main/components/settings/ModelSettings.js new file mode 100644 index 0000000..a20d308 --- /dev/null +++ b/easy-dataset-main/components/settings/ModelSettings.js @@ -0,0 +1,1056 @@ +'use client'; + +import { useState, useEffect, useMemo } from 'react'; +import { + Typography, + Box, + Button, + TextField, + Grid, + Card, + CardContent, + Dialog, + DialogTitle, + DialogContent, + DialogActions, + FormControl, + Autocomplete, + Slider, + InputLabel, + Select, + MenuItem, + Stack, + Paper, + Tooltip, + IconButton, + Chip, + Divider, + CircularProgress +} from '@mui/material'; +import AddIcon from '@mui/icons-material/Add'; +import CheckCircleIcon from '@mui/icons-material/CheckCircle'; +import ErrorIcon from '@mui/icons-material/Error'; +import { DEFAULT_MODEL_SETTINGS } from '@/constant/model'; +import { useTranslation } from 'react-i18next'; +import axios from 'axios'; +import { toast } from 'sonner'; +import EditIcon from '@mui/icons-material/Edit'; +import DeleteIcon from '@mui/icons-material/Delete'; +import ScienceIcon from '@mui/icons-material/Science'; +import HealthAndSafetyIcon from '@mui/icons-material/HealthAndSafety'; +import { useRouter } from 'next/navigation'; +import { useAtom } from 'jotai'; +import { modelConfigListAtom, selectedModelInfoAtom } from '@/lib/store'; +import { getProviderLogo, sortProvidersByPriority } from '@/lib/util/providerLogo'; + +export default function ModelSettings({ projectId }) { + const { t } = useTranslation(); + const router = useRouter(); + // 展示端点的最大长度 + const MAX_ENDPOINT_DISPLAY = 80; + const MAX_GENERATION_TOKENS = 131072; + // 模型对话框状态 + const [openModelDialog, setOpenModelDialog] = useState(false); + const [editingModel, setEditingModel] = useState(null); + const [loading, setLoading] = useState(true); + const [providerList, setProviderList] = useState([]); + const [providerOptions, setProviderOptions] = useState([]); + const [selectedProvider, setSelectedProvider] = useState({}); + const [models, setModels] = useState([]); + const [modelConfigList, setModelConfigList] = useAtom(modelConfigListAtom); + const [selectedModelInfo, setSelectedModelInfo] = useAtom(selectedModelInfoAtom); + const orderedModelConfigList = useMemo( + () => sortProvidersByPriority(modelConfigList, item => item.providerId), + [modelConfigList] + ); + const [modelConfigForm, setModelConfigForm] = useState({ + id: '', + providerId: '', + providerName: '', + endpoint: '', + apiKey: '', + modelId: '', + modelName: '', + type: 'text', + temperature: 0.0, + maxTokens: DEFAULT_MODEL_SETTINGS.maxTokens, + topP: 0, + topK: 0, + status: 1 + }); + const [healthStatusMap, setHealthStatusMap] = useState({}); + const [batchCheckingHealth, setBatchCheckingHealth] = useState(false); + + const isModelConfigured = model => { + if (!model) return false; + const hasEndpoint = Boolean(String(model.endpoint || '').trim()); + const hasModel = Boolean(String(model.modelId || model.modelName || '').trim()); + const providerId = String(model.providerId || '').toLowerCase(); + + if (providerId === 'ollama') { + return hasEndpoint && hasModel; + } + + const hasApiKey = Boolean(String(model.apiKey || '').trim()); + return hasEndpoint && hasApiKey && hasModel; + }; + + const configuredModelList = useMemo(() => orderedModelConfigList.filter(isModelConfigured), [orderedModelConfigList]); + + const unconfiguredModelList = useMemo( + () => orderedModelConfigList.filter(model => !isModelConfigured(model)), + [orderedModelConfigList] + ); + + const normalizePositiveInteger = value => { + const parsedValue = Number(value); + if (!Number.isInteger(parsedValue) || parsedValue < 1) { + return null; + } + return parsedValue; + }; + + const getSafeMaxTokensValue = value => { + return normalizePositiveInteger(value) ?? DEFAULT_MODEL_SETTINGS.maxTokens; + }; + + useEffect(() => { + getProvidersList(); + getModelConfigList(); + }, []); + + // 获取提供商列表 + const getProvidersList = () => { + axios.get('/api/llm/providers').then(response => { + console.log('获取的模型列表', response.data); + const sortedProviders = sortProvidersByPriority(response.data, item => item.id); + setProviderList(sortedProviders); + const providerOptions = sortedProviders.map(provider => ({ + id: provider.id, + label: provider.name + })); + if (sortedProviders.length > 0) { + setSelectedProvider(sortedProviders[0]); + getProviderModels(sortedProviders[0].id); + } + setProviderOptions(providerOptions); + }); + }; + + // 裁剪端点展示长度(不改变实际值,仅用于 UI 展示) + const formatEndpoint = model => { + if (!model?.endpoint) return ''; + const base = model.endpoint.replace(/^https?:\/\//, ''); + if (base.length > MAX_ENDPOINT_DISPLAY) { + return base.slice(0, MAX_ENDPOINT_DISPLAY) + '...'; + } + return base; + }; + + // 获取模型配置列表 + const getModelConfigList = () => { + axios + .get(`/api/projects/${projectId}/model-config`) + .then(response => { + setModelConfigList(sortProvidersByPriority(response.data.data, item => item.providerId)); + setLoading(false); + }) + .catch(error => { + setLoading(false); + toast.error('Fetch model list Error'); + }); + }; + + const onChangeProvider = (event, newValue) => { + console.log('选择提供商', newValue, typeof newValue); + if (typeof newValue === 'string') { + // 用户手动输入了自定义提供商 + setModelConfigForm(prev => ({ + ...prev, + providerId: 'custom', + endpoint: '', + providerName: '' + })); + } else if (newValue && newValue.id) { + // 用户从下拉列表中选择了一个提供商 + const selectedProvider = providerList.find(p => p.id === newValue.id); + if (selectedProvider) { + setSelectedProvider(selectedProvider); + setModelConfigForm(prev => ({ + ...prev, + providerId: selectedProvider.id, + endpoint: selectedProvider.apiUrl, + providerName: selectedProvider.name, + modelName: '' + })); + getProviderModels(newValue.id); + } + } + }; + + // 获取提供商的模型列表(DB) + const getProviderModels = providerId => { + axios + .get(`/api/llm/model?providerId=${providerId}`) + .then(response => { + setModels(response.data); + }) + .catch(error => { + toast.error('Get Models Error'); + }); + }; + + // 同步模型列表 + const refreshProviderModels = async () => { + let data = await getNewModels(); + if (!data) return; + if (data.length > 0) { + setModels(data); + toast.success('Refresh Success'); + const newModelsData = await axios.post('/api/llm/model', { + newModels: data, + providerId: selectedProvider.id + }); + if (newModelsData.status === 200) { + toast.success('Get Model Success'); + } + } else { + toast.info('No Models Need Refresh'); + } + }; + + // 获取最新模型列表 + async function getNewModels() { + try { + if (!modelConfigForm || !modelConfigForm.endpoint) { + return null; + } + const providerId = modelConfigForm.providerId; + console.log(providerId, 'getNewModels providerId'); + + // 使用后端 API 代理请求 + const res = await axios.post('/api/llm/fetch-models', { + endpoint: modelConfigForm.endpoint, + providerId: providerId, + apiKey: modelConfigForm.apiKey + }); + + return res.data; + } catch (err) { + if (err.response && err.response.status === 401) { + toast.error('API Key Invalid'); + } else { + toast.error('Get Model List Error'); + } + return null; + } + } + + const getHealthCheckErrorMessage = error => { + if (error?.response?.data?.error) return String(error.response.data.error); + if (error?.response?.data?.message) return String(error.response.data.message); + if (error?.message) return String(error.message); + return t('models.endpointCheckFailed', { defaultValue: 'Endpoint check failed' }); + }; + + const checkModelEndpointHealth = async (model, { silent = false } = {}) => { + if (!model?.id) return false; + + const endpoint = String(model.endpoint || '').trim(); + if (!endpoint) { + setHealthStatusMap(prev => ({ + ...prev, + [model.id]: { + status: 'error', + message: t('models.endpointMissing', { defaultValue: 'Endpoint is empty' }) + } + })); + if (!silent) { + toast.error(t('models.endpointMissing', { defaultValue: 'Endpoint is empty' })); + } + return false; + } + + setHealthStatusMap(prev => ({ + ...prev, + [model.id]: { + status: 'checking', + message: t('models.checking', { defaultValue: 'Checking...' }) + } + })); + + try { + const response = await axios.post('/api/llm/fetch-models', { + endpoint, + providerId: model.providerId, + apiKey: model.apiKey + }); + + const resultList = Array.isArray(response.data) ? response.data : []; + const currentModelId = String(model.modelId || model.modelName || '').trim(); + const hasMatchedModel = + !currentModelId || + resultList.some(item => { + return item?.modelId === currentModelId || item?.modelName === currentModelId; + }); + + if (!hasMatchedModel) { + setHealthStatusMap(prev => ({ + ...prev, + [model.id]: { + status: 'warning', + message: t('models.endpointReachableModelMissing', { + defaultValue: 'Endpoint reachable, but current model is not in the returned model list' + }), + checkedAt: Date.now() + } + })); + if (!silent) { + toast.warning( + t('models.endpointReachableModelMissing', { + defaultValue: 'Endpoint reachable, but current model is not in the returned model list' + }) + ); + } + return true; + } + + setHealthStatusMap(prev => ({ + ...prev, + [model.id]: { + status: 'success', + message: t('models.endpointHealthy', { defaultValue: 'Endpoint is healthy' }), + checkedAt: Date.now() + } + })); + if (!silent) { + toast.success(t('models.endpointHealthy', { defaultValue: 'Endpoint is healthy' })); + } + return true; + } catch (error) { + const message = getHealthCheckErrorMessage(error); + setHealthStatusMap(prev => ({ + ...prev, + [model.id]: { + status: 'error', + message, + checkedAt: Date.now() + } + })); + if (!silent) { + toast.error(message); + } + return false; + } + }; + + const checkAllConfiguredModelHealth = async () => { + if (configuredModelList.length === 0) { + toast.info(t('models.noConfiguredModels', { defaultValue: 'No configured models to check' })); + return; + } + + setBatchCheckingHealth(true); + let okCount = 0; + let failCount = 0; + + for (const model of configuredModelList) { + const isHealthy = await checkModelEndpointHealth(model, { silent: true }); + if (isHealthy) { + okCount += 1; + } else { + failCount += 1; + } + } + + setBatchCheckingHealth(false); + toast.success( + t('models.healthCheckSummary', { + defaultValue: `Health check completed: ${okCount} healthy, ${failCount} failed`, + okCount, + failCount + }) + ); + }; + + const getHealthStatusInfo = model => { + const status = healthStatusMap[model.id]?.status || 'idle'; + const message = healthStatusMap[model.id]?.message; + + if (status === 'checking') { + return { + color: 'default', + icon: , + label: t('models.checking', { defaultValue: 'Checking...' }), + message + }; + } + + if (status === 'success') { + return { + color: 'success', + icon: , + label: t('models.healthy', { defaultValue: 'Healthy' }), + message + }; + } + + if (status === 'warning') { + return { + color: 'warning', + icon: , + label: t('models.reachable', { defaultValue: 'Reachable' }), + message + }; + } + + if (status === 'error') { + return { + color: 'error', + icon: , + label: t('models.unhealthy', { defaultValue: 'Unhealthy' }), + message + }; + } + + return { + color: 'default', + icon: , + label: t('models.notChecked', { defaultValue: 'Not checked' }), + message: t('models.notChecked', { defaultValue: 'Not checked' }) + }; + }; + + // 打开模型对话框 + const handleOpenModelDialog = (model = null) => { + if (model) { + setEditingModel(model); + console.log('handleOpenModelDialog', model); + // 兼容逻辑:如果 modelId 为空,则用 modelName 作为 modelId + const initialForm = { ...model }; + if (!initialForm.modelId && initialForm.modelName) { + initialForm.modelId = initialForm.modelName; + } + + // 编辑现有模型时,为未设置的参数应用默认值 + setModelConfigForm({ + ...initialForm, + temperature: model.temperature !== undefined ? model.temperature : DEFAULT_MODEL_SETTINGS.temperature, + maxTokens: model.maxTokens !== undefined ? model.maxTokens : DEFAULT_MODEL_SETTINGS.maxTokens, + topP: model.topP !== undefined && model.topP !== 0 ? model.topP : DEFAULT_MODEL_SETTINGS.topP + }); + getProviderModels(model.providerId); + } else { + setEditingModel(null); + // 添加新模型时,完全重置表单 + setModelConfigForm({ + providerId: selectedProvider?.id || '', + providerName: selectedProvider?.name || '', + endpoint: selectedProvider?.apiUrl || '', + apiKey: '', + modelId: '', + modelName: '', + type: 'text', + ...DEFAULT_MODEL_SETTINGS, + id: '' + }); + if (selectedProvider?.id) { + getProviderModels(selectedProvider.id); + } + } + setOpenModelDialog(true); + }; + + // 关闭模型对话框 + const handleCloseModelDialog = () => { + setEditingModel(null); + setOpenModelDialog(false); + }; + + // 处理模型表单变更 + const handleModelFormChange = e => { + const { name, value } = e.target; + console.log('handleModelFormChange', name, value); + setModelConfigForm(prev => ({ + ...prev, + [name]: value + })); + }; + + const handleMaxTokensSliderChange = (event, newValue) => { + const value = Array.isArray(newValue) ? newValue[0] : newValue; + const normalizedValue = normalizePositiveInteger(value); + if (normalizedValue === null) { + return; + } + setModelConfigForm(prev => ({ + ...prev, + maxTokens: normalizedValue + })); + }; + + const handleMaxTokensInputChange = e => { + const { value } = e.target; + if (value === '') { + setModelConfigForm(prev => ({ + ...prev, + maxTokens: '' + })); + return; + } + const normalizedValue = normalizePositiveInteger(value); + if (normalizedValue === null) { + return; + } + setModelConfigForm(prev => ({ + ...prev, + maxTokens: normalizedValue + })); + }; + + const handleMaxTokensInputBlur = () => { + const normalizedValue = normalizePositiveInteger(modelConfigForm.maxTokens); + if (normalizedValue !== null) { + return; + } + setModelConfigForm(prev => ({ + ...prev, + maxTokens: DEFAULT_MODEL_SETTINGS.maxTokens + })); + }; + + // 保存模型 + const handleSaveModel = () => { + // 确保有模型 ID + const normalizedModelId = String(modelConfigForm.modelId || '').trim(); + const normalizedModelName = String(modelConfigForm.modelName || '').trim(); + const isEditingExistingModel = Boolean(modelConfigForm.id || editingModel?.id); + + if (!isEditingExistingModel && !normalizedModelId) { + toast.error(t('models.modelIdPlaceholder')); + return; + } + + const normalizedMaxTokens = normalizePositiveInteger(modelConfigForm.maxTokens); + if (normalizedMaxTokens === null) { + toast.error(t('models.maxTokensPositiveError', { defaultValue: 'Max Tokens must be a positive integer' })); + return; + } + + // 如果模型名称为空,则默认为模型 ID + const dataToSave = { + ...modelConfigForm, + modelId: normalizedModelId, + maxTokens: normalizedMaxTokens, + modelName: normalizedModelName || normalizedModelId + }; + + axios + .post(`/api/projects/${projectId}/model-config`, dataToSave) + .then(response => { + if (selectedModelInfo && selectedModelInfo.id === response.data.id) { + setSelectedModelInfo(response.data); + } + toast.success(t('settings.saveSuccess')); + getModelConfigList(); + handleCloseModelDialog(); + }) + .catch(error => { + toast.error(t('settings.saveFailed')); + console.error(error); + }); + }; + + // 删除模型 + const handleDeleteModel = id => { + axios + .delete(`/api/projects/${projectId}/model-config/${id}`) + .then(response => { + toast.success(t('settings.deleteSuccess')); + getModelConfigList(); + }) + .catch(error => { + toast.error(t('settings.deleteFailed')); + }); + }; + + // 获取模型状态图标和颜色 + const getModelStatusInfo = model => { + const providerId = String(model?.providerId || '').toLowerCase(); + if (providerId === 'ollama') { + return { + icon: , + color: 'success', + text: t('models.localModel') + }; + } else if (model.apiKey) { + return { + icon: , + color: 'success', + text: t('models.apiKeyConfigured') + }; + } else { + return { + icon: , + color: 'warning', + text: t('models.apiKeyNotConfigured') + }; + } + }; + + const renderModelCard = model => { + const modelStatus = getModelStatusInfo(model); + const healthStatus = getHealthStatusInfo(model); + const providerId = String(model?.providerId || '').toLowerCase(); + const endpointLabel = `${formatEndpoint(model)}${ + providerId !== 'ollama' && !model.apiKey ? ' (' + t('models.unconfiguredAPIKey') + ')' : '' + }`; + + return ( + + + + { + e.target.src = '/imgs/models/default.svg'; + }} + /> + + + {model.modelName ? model.modelName : t('models.unselectedModel')} + + + {model.providerName} + + + + + + + + + + + + + + + + checkModelEndpointHealth(model)} + disabled={healthStatusMap[model.id]?.status === 'checking'} + > + + + + + + + + + + router.push(`/projects/${projectId}/playground?modelId=${model.id}`)} + color="secondary" + > + + + + + + handleOpenModelDialog(model)} color="primary"> + + + + + + handleDeleteModel(model.id)} + disabled={modelConfigList.length <= 1} + color="error" + > + + + + + + + ); + }; + + if (loading) { + return {t('textSplit.loading')}; + } + + return ( + + + + + {t('settings.modelConfig')} + + + + + + + + + + + + + {t('models.configuredModels', { defaultValue: 'Configured Models' })} + + + + + {configuredModelList.map(renderModelCard)} + {configuredModelList.length === 0 && ( + + {t('models.noConfiguredModels', { defaultValue: 'No configured models' })} + + )} + + + + + + + + + {t('models.unconfiguredModels', { defaultValue: 'Unconfigured Models' })} + + + + + {unconfiguredModelList.map(renderModelCard)} + {unconfiguredModelList.length === 0 && ( + + {t('models.noUnconfiguredModels', { defaultValue: 'No unconfigured models' })} + + )} + + + + + + {/* 模型表单对话框 */} + + {editingModel ? t('models.edit') : t('models.add')} + + + {/* provider */} + + + option.label} + value={ + providerOptions.find(p => p.id === modelConfigForm.providerId) || { + id: 'custom', + label: modelConfigForm.providerName || '' + } + } + onChange={onChangeProvider} + renderInput={params => ( + { + // 当用户手动输入时,更新 provider 字段 + setModelConfigForm(prev => ({ + ...prev, + providerId: 'custom', + providerName: e.target.value + })); + }} + /> + )} + renderOption={(props, option) => { + return ( +
+
+ { + e.target.src = '/imgs/models/default.svg'; + }} + /> + {option.label} +
+
+ ); + }} + /> +
+
+ {/* 接口地址 */} + + + + {/* API Key */} + + + + {/* 模型 ID */} + + + model && model.modelId) + .map(model => ({ + label: `${model.modelName} (${model.modelId})`, + modelName: model.modelName, + modelId: model.modelId, + providerId: model.providerId + }))} + value={modelConfigForm.modelId} + onChange={(event, newValue) => { + console.log('newValue', newValue); + const newId = newValue?.modelId || newValue || ''; + const newName = newValue?.modelName || newValue?.modelId || newValue || ''; + setModelConfigForm(prev => ({ + ...prev, + modelId: newId, + // 如果当前名称为空或与旧 ID 一致,则同步更新名称 + modelName: !prev.modelName || prev.modelName === prev.modelId ? newName : prev.modelName + })); + }} + renderInput={params => ( + { + setModelConfigForm(prev => ({ + ...prev, + modelId: e.target.value + })); + }} + /> + )} + /> + + + + {/* 模型名称 */} + + + + {/* 新增:视觉模型选择项 */} + + + {t('models.type')} + + + + + + {t('models.temperature')} + + + + + + {modelConfigForm.temperature} + + + + + + {t('models.maxTokens')} + + + + + + + + {t('models.maxTokensInputTip', { + defaultValue: `Slider range: 1-${MAX_GENERATION_TOKENS}. You can also input any positive integer.` + })} + + + + + {t('models.topP', { defaultValue: 'Top P' })} + + + + + + {modelConfigForm.topP} + + + +
+
+ + + + +
+
+ ); +} diff --git a/easy-dataset-main/components/settings/TaskSettings.js b/easy-dataset-main/components/settings/TaskSettings.js new file mode 100644 index 0000000..81bf4d1 --- /dev/null +++ b/easy-dataset-main/components/settings/TaskSettings.js @@ -0,0 +1,709 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { + Typography, + Box, + Button, + TextField, + Grid, + Card, + CardContent, + Slider, + InputAdornment, + Alert, + Snackbar, + FormControl, + Select, + InputLabel, + MenuItem, + Chip, + FormHelperText +} from '@mui/material'; +import { useTranslation } from 'react-i18next'; + +import SaveIcon from '@mui/icons-material/Save'; +import useTaskSettings from '@/hooks/useTaskSettings'; + +export default function TaskSettings({ projectId }) { + const { t } = useTranslation(); + const { taskSettings, setTaskSettings, loading, error, success, setSuccess } = useTaskSettings(projectId); + + // 确保 multiTurnRounds 有正确的初始值 + useEffect(() => { + if ( + !loading && + taskSettings && + (taskSettings.multiTurnRounds === undefined || taskSettings.multiTurnRounds === null) + ) { + setTaskSettings(prev => ({ + ...prev, + multiTurnRounds: 3 // 默认值 + })); + } + }, [loading, taskSettings, setTaskSettings]); + + // 处理设置变更 + const handleSettingChange = e => { + const { name, value } = e.target; + setTaskSettings(prev => ({ + ...prev, + [name]: value + })); + }; + + // 处理滑块变更 + const handleSliderChange = name => (event, newValue) => { + setTaskSettings(prev => ({ + ...prev, + [name]: newValue + })); + }; + + // 保存任务配置 + const handleSaveTaskSettings = async () => { + try { + // 确保数组类型的数据被正确处理 + const settingsToSave = { ...taskSettings }; + + // 确保递归分块的分隔符数组存在 + if (settingsToSave.splitType === 'recursive' && settingsToSave.separatorsInput) { + if (!settingsToSave.separators || !Array.isArray(settingsToSave.separators)) { + settingsToSave.separators = settingsToSave.separatorsInput.split(',').map(item => item.trim()); + } + } + + console.log('Saving settings:', settingsToSave); + + const response = await fetch(`/api/projects/${projectId}/tasks`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(settingsToSave) + }); + + if (!response.ok) { + throw new Error(t('settings.saveTasksFailed')); + } + + setSuccess(true); + } catch (error) { + console.error('保存任务配置出错:', error); + //setError(error.message); + } + }; + + const handleCloseSnackbar = () => { + setSuccess(false); + //setError(null); + }; + + if (loading) { + return {t('common.loading')}; + } + + return ( + + {' '} + {/* 添加底部填充,为固定按钮留出空间 */} + + + + + + {t('settings.textSplitSettings')} + + + {/* 分块策略选择 */} + + {t('settings.splitType')} + + + + {/* Markdown模式设置 */} + {(!taskSettings.splitType || taskSettings.splitType === 'markdown') && ( + <> + + {t('settings.minLength')}: {taskSettings.textSplitMinLength} + + + + + {t('settings.maxLength')}: {taskSettings.textSplitMaxLength} + + + + )} + + {/* 通用 LangChain 参数设置 */} + {taskSettings.splitType && taskSettings.splitType !== 'markdown' && ( + <> + + {t('settings.chunkSize')}: {taskSettings.chunkSize || 3000} + + + + + {t('settings.chunkOverlap')}: {taskSettings.chunkOverlap || 200} + + + + )} + + {/* Text 分块器特殊设置 */} + {taskSettings.splitType === 'text' && ( + + )} + + {/* 自定义符号分块器特殊设置 */} + {taskSettings.splitType === 'custom' && ( + + )} + + {/* Code 分块器特殊设置 */} + {taskSettings.splitType === 'code' && ( + + {t('settings.codeLanguage')} + + {t('settings.codeLanguageHelper')} + + )} + + {/* Recursive 分块器特殊设置 */} + {taskSettings.splitType === 'recursive' && ( + + {t('settings.separators')} + ,-'} + onChange={e => { + const value = e.target.value; + // 同时更新输入框值和分隔符数组 + setTaskSettings(prev => ({ + ...prev, + separatorsInput: value, + separators: value.split(',').map(item => item.trim()) + })); + }} + helperText={t('settings.separatorsHelper')} + /> + + {(taskSettings.separators || ['|', '##', '>', '-']).map((sep, index) => ( + + ))} + + + )} + + + {t('settings.textSplitDescription')} + + + + + + + + + + + + {t('settings.questionGenSettings')} + + + + {t('settings.questionGenLength', { length: taskSettings.questionGenerationLength })} + + + + {t('settings.questionGenDescription')} + + + + {t('settings.questionMaskRemovingProbability', { + probability: taskSettings.questionMaskRemovingProbability + })} + + + + + + + + + + + + + + + {t('settings.pdfSettings')} + + + + + + + + + + + + + {/* 多轮对话数据集设置 */} + + + + + + {t('settings.multiTurnSettings')} + + + {/* 系统提示词 */} + + + {/* 对话场景 */} + + + {/* 对话轮数 */} + + {t('settings.multiTurnRounds', { rounds: taskSettings.multiTurnRounds || 3 })} + + + + {/* 角色A设定 */} + + + {/* 角色B设定 */} + + + + {t('settings.multiTurnDescription')} + + + + + + + {/* 测试集生成设置 */} + + + + + + {t('settings.evalQuestionSettings')} + + + {t('settings.evalQuestionSettingsDescription')} + + + + + { + const value = Math.max(0, parseInt(e.target.value) || 0); + setTaskSettings(prev => ({ + ...prev, + evalQuestionTypeRatios: { + ...prev.evalQuestionTypeRatios, + true_false: value + } + })); + }} + InputProps={{ inputProps: { min: 0 } }} + /> + + + {/* 单选题 */} + + { + const value = Math.max(0, parseInt(e.target.value) || 0); + setTaskSettings(prev => ({ + ...prev, + evalQuestionTypeRatios: { + ...prev.evalQuestionTypeRatios, + single_choice: value + } + })); + }} + InputProps={{ inputProps: { min: 0 } }} + /> + + + {/* 多选题 */} + + { + const value = Math.max(0, parseInt(e.target.value) || 0); + setTaskSettings(prev => ({ + ...prev, + evalQuestionTypeRatios: { + ...prev.evalQuestionTypeRatios, + multiple_choice: value + } + })); + }} + InputProps={{ inputProps: { min: 0 } }} + /> + + + {/* 固定短答案 */} + + { + const value = Math.max(0, parseInt(e.target.value) || 0); + setTaskSettings(prev => ({ + ...prev, + evalQuestionTypeRatios: { + ...prev.evalQuestionTypeRatios, + short_answer: value + } + })); + }} + InputProps={{ inputProps: { min: 0 } }} + /> + + + {/* 开放式回答 */} + + { + const value = Math.max(0, parseInt(e.target.value) || 0); + setTaskSettings(prev => ({ + ...prev, + evalQuestionTypeRatios: { + ...prev.evalQuestionTypeRatios, + open_ended: value + } + })); + }} + InputProps={{ inputProps: { min: 0 } }} + /> + + + + + {t('settings.evalQuestionRatioHelper')} + + + + + + + + + + + {t('settings.huggingfaceSettings')} + + + + + + + + + {t('settings.saveSuccess')} + + + + + {error} + + + {/* 吸底保存按钮 */} + + + + + ); +} diff --git a/easy-dataset-main/components/tasks/TaskActions.js b/easy-dataset-main/components/tasks/TaskActions.js new file mode 100644 index 0000000..f74cf27 --- /dev/null +++ b/easy-dataset-main/components/tasks/TaskActions.js @@ -0,0 +1,27 @@ +'use client'; + +import React from 'react'; +import { IconButton, Tooltip } from '@mui/material'; +import DeleteIcon from '@mui/icons-material/Delete'; +import StopCircleIcon from '@mui/icons-material/StopCircle'; +import { useTranslation } from 'react-i18next'; + +// 任务操作组件 +export default function TaskActions({ task, onAbort, onDelete }) { + const { t } = useTranslation(); + + // 处理中的任务显示中断按钮,其他状态显示删除按钮 + return task.status === 0 ? ( + + onAbort(task.id)}> + + + + ) : ( + + onDelete(task.id)}> + + + + ); +} diff --git a/easy-dataset-main/components/tasks/TaskFilters.js b/easy-dataset-main/components/tasks/TaskFilters.js new file mode 100644 index 0000000..af94635 --- /dev/null +++ b/easy-dataset-main/components/tasks/TaskFilters.js @@ -0,0 +1,74 @@ +'use client'; + +import React from 'react'; +import { + Box, + FormControl, + InputLabel, + Select, + MenuItem, + OutlinedInput, + IconButton, + Tooltip, + CircularProgress +} from '@mui/material'; +import RefreshIcon from '@mui/icons-material/Refresh'; +import { useTranslation } from 'react-i18next'; + +export default function TaskFilters({ statusFilter, setStatusFilter, typeFilter, setTypeFilter, loading, onRefresh }) { + const { t } = useTranslation(); + + const taskTypeOptions = [ + 'text-processing', + 'file-processing', + 'pdf-processing', + 'question-generation', + 'answer-generation', + 'data-cleaning', + 'data-distillation', + 'eval-generation', + 'multi-turn-generation', + 'image-question-generation' + ]; + + return ( + + + {t('tasks.filters.status')} + + + + + {t('tasks.filters.type')} + + + + + + {loading ? : } + + + + ); +} diff --git a/easy-dataset-main/components/tasks/TaskProgress.js b/easy-dataset-main/components/tasks/TaskProgress.js new file mode 100644 index 0000000..2ac970d --- /dev/null +++ b/easy-dataset-main/components/tasks/TaskProgress.js @@ -0,0 +1,36 @@ +'use client'; + +import React from 'react'; +import { Stack, LinearProgress, Typography } from '@mui/material'; +import { useTranslation } from 'react-i18next'; + +// 任务进度组件 +export default function TaskProgress({ task }) { + const { t } = useTranslation(); + + // 如果没有总数,则不显示进度条 + if (task.totalCount === 0) return '-'; + + // 计算进度百分比 + const progress = (task.completedCount / task.totalCount) * 100; + + return ( + + + + {task.completedCount} / {task.totalCount} ({Math.round(progress)}%) + + + ); +} diff --git a/easy-dataset-main/components/tasks/TaskStatusChip.js b/easy-dataset-main/components/tasks/TaskStatusChip.js new file mode 100644 index 0000000..6f6111b --- /dev/null +++ b/easy-dataset-main/components/tasks/TaskStatusChip.js @@ -0,0 +1,48 @@ +'use client'; + +import React from 'react'; +import { Chip, CircularProgress, Box } from '@mui/material'; +import { useTranslation } from 'react-i18next'; + +// 任务状态显示组件 +export default function TaskStatusChip({ status }) { + const { t } = useTranslation(); + + // 状态映射配置 + const STATUS_CONFIG = { + 0: { + label: t('tasks.status.processing'), + color: 'warning', + loading: true + }, + 1: { + label: t('tasks.status.completed'), + color: 'success' + }, + 2: { + label: t('tasks.status.failed'), + color: 'error' + }, + 3: { + label: t('tasks.status.aborted'), + color: 'default' + } + }; + + const statusInfo = STATUS_CONFIG[status] || { + label: t('tasks.status.unknown'), + color: 'default' + }; + + // 处理中状态显示加载动画 + if (status === 0) { + return ( + + + + + ); + } + + return ; +} diff --git a/easy-dataset-main/components/tasks/TasksTable.js b/easy-dataset-main/components/tasks/TasksTable.js new file mode 100644 index 0000000..84eabbc --- /dev/null +++ b/easy-dataset-main/components/tasks/TasksTable.js @@ -0,0 +1,293 @@ +'use client'; + +import React from 'react'; +import { + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Paper, + Typography, + CircularProgress, + Box, + TablePagination, + Tooltip +} from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import { formatDistanceToNow } from 'date-fns'; +import { zhCN, enUS } from 'date-fns/locale'; + +import TaskStatusChip from './TaskStatusChip'; +import TaskProgress from './TaskProgress'; +import TaskActions from './TaskActions'; + +export default function TasksTable({ + tasks, + loading, + handleAbortTask, + handleDeleteTask, + page, + rowsPerPage, + handleChangePage, + handleChangeRowsPerPage, + totalCount +}) { + const { t, i18n } = useTranslation(); + + const formatDate = dateString => { + if (!dateString) return '-'; + const date = new Date(dateString); + return formatDistanceToNow(date, { + addSuffix: true, + locale: i18n.language === 'zh-CN' ? zhCN : enUS + }); + }; + + const calculateDuration = (startTimeStr, endTimeStr) => { + if (!startTimeStr || !endTimeStr) return '-'; + + try { + const startTime = new Date(startTimeStr); + const endTime = new Date(endTimeStr); + const duration = endTime - startTime; + const seconds = Math.floor(duration / 1000); + + if (seconds < 60) { + return t('tasks.duration.seconds', { seconds }); + } + if (seconds < 3600) { + const minutes = Math.floor(seconds / 60); + const remainingSeconds = seconds % 60; + return t('tasks.duration.minutes', { minutes, seconds: remainingSeconds }); + } + + const hours = Math.floor(seconds / 3600); + const remainingMinutes = Math.floor((seconds % 3600) / 60); + return t('tasks.duration.hours', { hours, minutes: remainingMinutes }); + } catch (error) { + console.error('Failed to calculate duration:', error); + return '-'; + } + }; + + const parseModelInfo = modelInfoString => { + let modelInfo = ''; + try { + const parsedModel = JSON.parse(modelInfoString); + modelInfo = parsedModel.modelName || parsedModel.name || '-'; + } catch { + modelInfo = modelInfoString || '-'; + } + return modelInfo; + }; + + const toTaskTypeLabel = taskType => { + if (!taskType) return '-'; + return String(taskType) + .split('-') + .map(word => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); + }; + + const getLocalizedTaskType = taskType => { + return t(`tasks.types.${taskType}`, { defaultValue: toTaskTypeLabel(taskType) }); + }; + + const parseJsonSafely = input => { + if (!input || typeof input !== 'string') return null; + try { + return JSON.parse(input); + } catch { + return null; + } + }; + + const formatTaskNote = task => { + const note = String(task?.note || '').trim(); + if (!note) return '-'; + + const noteJson = parseJsonSafely(note); + if (noteJson) { + if (Array.isArray(noteJson.chunkIds)) { + return t('tasks.notes.selectedChunks', { count: noteJson.chunkIds.length }); + } + if (Array.isArray(noteJson.fileList)) { + return t('tasks.notes.fileBatch', { + count: noteJson.fileList.length, + strategy: noteJson.strategy || '-' + }); + } + return t('tasks.notes.jsonParams'); + } + + if (note === 'No chunks require question generation' || note.startsWith('No chunks require question gen')) { + return t('tasks.notes.noChunksQuestion'); + } + if (note === 'No chunks require cleaning' || note.startsWith('No chunks require clean')) { + return t('tasks.notes.noChunksCleaning'); + } + if (note.startsWith('Processing failed:')) { + return t('tasks.notes.processingFailed', { + error: note.replace('Processing failed:', '').trim() + }); + } + + const summaryMatch = note.match(/Processed:\s*(\d+)\/(\d+),\s*succeeded:\s*(\d+),\s*failed:\s*(\d+)/i); + if (summaryMatch) { + const [, processed, total, succeeded, failed] = summaryMatch; + + const questionMatch = note.match(/questions generated:\s*(\d+)/i); + if (questionMatch) { + return t('tasks.notes.questionSummary', { + processed, + total, + succeeded, + failed, + generated: questionMatch[1] + }); + } + + const datasetMatch = note.match(/datasets generated:\s*(\d+)/i); + if (datasetMatch) { + return t('tasks.notes.datasetSummary', { + processed, + total, + succeeded, + failed, + generated: datasetMatch[1] + }); + } + + const cleaningMatch = note.match(/total original length:\s*(\d+),\s*total cleaned length:\s*(\d+)/i); + if (cleaningMatch) { + return t('tasks.notes.cleaningSummary', { + processed, + total, + succeeded, + failed, + original: cleaningMatch[1], + cleaned: cleaningMatch[2] + }); + } + + return t('tasks.notes.genericSummary', { + processed, + total, + succeeded, + failed + }); + } + + return note; + }; + + const truncateNote = (note, maxLength = 48) => { + if (!note) return '-'; + if (note.length <= maxLength) return note; + return `${note.substring(0, maxLength)}...`; + }; + + return ( + + + + + + {t('tasks.table.type')} + {t('tasks.table.status')} + {t('tasks.table.progress')} + {t('tasks.table.createTime')} + {t('tasks.table.duration')} + {t('tasks.table.model')} + {t('tasks.table.note')} + {t('tasks.table.actions')} + + + + {loading && tasks.length === 0 ? ( + + + + + + {t('tasks.loading')} + + + + + ) : tasks.length === 0 ? ( + + + {t('tasks.empty')} + + + ) : ( + tasks.map(task => { + const noteText = formatTaskNote(task); + return ( + + {getLocalizedTaskType(task.taskType)} + + + + + + + + {formatDate(task.createAt)} + {task.endTime ? calculateDuration(task.startTime, task.endTime) : '-'} + {parseModelInfo(task.modelInfo)} + + {noteText !== '-' ? ( + + + {truncateNote(noteText)} + + + ) : ( + '-' + )} + + + + + + ); + }) + )} + +
+
+ + {tasks.length > 0 && ( + { + const calculatedFrom = page * rowsPerPage + 1; + const calculatedTo = Math.min((page + 1) * rowsPerPage, count); + return t('datasets.pagination', { + from: calculatedFrom, + to: calculatedTo, + count + }); + }} + /> + )} +
+ ); +} diff --git a/easy-dataset-main/components/text-split/BatchEditChunkDialog.js b/easy-dataset-main/components/text-split/BatchEditChunkDialog.js new file mode 100644 index 0000000..a094ab6 --- /dev/null +++ b/easy-dataset-main/components/text-split/BatchEditChunkDialog.js @@ -0,0 +1,180 @@ +'use client'; + +import { useState } from 'react'; +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + TextField, + RadioGroup, + FormControlLabel, + Radio, + FormControl, + FormLabel, + Box, + Typography, + Alert, + CircularProgress +} from '@mui/material'; +import { useTranslation } from 'react-i18next'; + +/** + * 批量编辑文本块对话框 + * @param {Object} props + * @param {boolean} props.open - 对话框是否打开 + * @param {Function} props.onClose - 关闭对话框的回调 + * @param {Function} props.onConfirm - 确认编辑的回调 + * @param {Array} props.selectedChunks - 选中的文本块ID数组 + * @param {number} props.totalChunks - 文本块总数 + * @param {boolean} props.loading - 是否正在处理 + */ +export default function BatchEditChunksDialog({ + open, + onClose, + onConfirm, + selectedChunks = [], + totalChunks = 0, + loading = false +}) { + const { t } = useTranslation(); + const [position, setPosition] = useState('start'); // 'start' 或 'end' + const [content, setContent] = useState(''); + const [error, setError] = useState(''); + + // 处理位置变更 + const handlePositionChange = event => { + setPosition(event.target.value); + }; + + // 处理内容变更 + const handleContentChange = event => { + setContent(event.target.value); + if (error) setError(''); + }; + + // 处理确认 + const handleConfirm = () => { + if (!content.trim()) { + setError(t('batchEdit.contentRequired')); + return; + } + + onConfirm({ + position, + content: content.trim(), + chunkIds: selectedChunks + }); + }; + + // 处理关闭 + const handleClose = () => { + if (!loading) { + setContent(''); + setError(''); + setPosition('start'); + onClose(); + } + }; + + return ( + + {t('batchEdit.title')} + + + + {/* 选择提示 */} + + + {selectedChunks.length === totalChunks + ? t('batchEdit.allChunksSelected', { count: totalChunks }) + : t('batchEdit.selectedChunks', { + selected: selectedChunks.length, + total: totalChunks + })} + + + + {/* 位置选择 */} + + + {t('batchEdit.position')} + + + } label={t('batchEdit.atBeginning')} /> + } label={t('batchEdit.atEnd')} /> + + + + {/* 内容输入 */} + + + {/* 预览示例 */} + {content.trim() && ( + + + {t('batchEdit.preview')}: + + + {position === 'start' ? ( + <> + {content} + {'\n\n[原始文本块内容...]'} + + ) : ( + <> + {'[原始文本块内容...]\n\n'} + {content} + + )} + + + )} + + + + + + + + + ); +} diff --git a/easy-dataset-main/components/text-split/ChunkBatchDeleteDialog.js b/easy-dataset-main/components/text-split/ChunkBatchDeleteDialog.js new file mode 100644 index 0000000..68bbf07 --- /dev/null +++ b/easy-dataset-main/components/text-split/ChunkBatchDeleteDialog.js @@ -0,0 +1,45 @@ +'use client'; + +import { + Dialog, + DialogTitle, + DialogContent, + DialogContentText, + DialogActions, + Button, + CircularProgress +} from '@mui/material'; +import { useTranslation } from 'react-i18next'; + +export default function ChunkBatchDeleteDialog({ open, onClose, onConfirm, loading, count }) { + const { t } = useTranslation(); + + return ( + + + {t('textSplit.batchDeleteChunksConfirmTitle', { defaultValue: '确认批量删除' })} + + + + {t('textSplit.batchDeleteChunksConfirmMessage', { + count, + defaultValue: `您确定要删除选中的 ${count} 个文本块吗?此操作不可恢复。` + })} + + + + + + + + ); +} diff --git a/easy-dataset-main/components/text-split/ChunkCard.js b/easy-dataset-main/components/text-split/ChunkCard.js new file mode 100644 index 0000000..3c9d0bc --- /dev/null +++ b/easy-dataset-main/components/text-split/ChunkCard.js @@ -0,0 +1,449 @@ +'use client'; + +import { useRouter } from 'next/navigation'; +import { useState, useEffect } from 'react'; +import { + Box, + Typography, + IconButton, + Chip, + Checkbox, + Tooltip, + Card, + CardContent, + CardActions, + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + TextField, + CircularProgress +} from '@mui/material'; +import DeleteIcon from '@mui/icons-material/Delete'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import QuizIcon from '@mui/icons-material/Quiz'; +import EditIcon from '@mui/icons-material/Edit'; +import CleaningServicesIcon from '@mui/icons-material/CleaningServices'; +import AssignmentIcon from '@mui/icons-material/Assignment'; +import { useTheme } from '@mui/material/styles'; +import { useTranslation } from 'react-i18next'; + +// 编辑文本块对话框组件 +const EditChunkDialog = ({ open, chunk, onClose, onSave }) => { + const [content, setContent] = useState(chunk?.content || ''); + const { t } = useTranslation(); + + // 当文本块变化时更新内容 + useEffect(() => { + if (chunk?.content) { + setContent(chunk.content); + } + }, [chunk]); + + const handleSave = () => { + onSave(content); + onClose(); + }; + + return ( + + {t('textSplit.editChunk', { chunkId: chunk?.name })} + + setContent(e.target.value)} + variant="outlined" + sx={{ mt: 1 }} + /> + + + + + + + ); +}; + +export default function ChunkCard({ + chunk, + selected, + onSelect, + onView, + onDelete, + onGenerateQuestions, + onDataCleaning, + onEdit, + onGenerateEvalQuestions, // 新增:生成测评题目的回调 + projectId, + selectedModel // 添加selectedModel参数 +}) { + const theme = useTheme(); + const { t } = useTranslation(); + const router = useRouter(); + const [editDialogOpen, setEditDialogOpen] = useState(false); + const [chunkForEdit, setChunkForEdit] = useState(null); + const [generatingQuestions, setGeneratingQuestions] = useState(false); + const [generatingEval, setGeneratingEval] = useState(false); + + // 获取文本预览 + const getTextPreview = (content, maxLength = 150) => { + if (!content) return ''; + return content.length > maxLength ? `${content.substring(0, maxLength)}...` : content; + }; + + // 检查是否有已生成的问题 + const hasQuestions = chunk.questions && chunk.questions.length > 0; + + // 处理编辑按钮点击 + const handleEditClick = async () => { + try { + // 显示加载状态 + console.log('正在获取文本块完整内容...'); + console.log('projectId:', projectId, 'chunkId:', chunk.id); + + // 先获取完整的文本块内容,使用从外部传入的 projectId + const response = await fetch(`/api/projects/${projectId}/chunks/${encodeURIComponent(chunk.id)}`); + + if (!response.ok) { + throw new Error(t('textSplit.fetchChunkFailed')); + } + + const data = await response.json(); + console.log('获取文本块完整内容成功:', data); + + // 先设置完整数据,再打开对话框(与 ChunkList.js 中的实现一致) + setChunkForEdit(data); + setEditDialogOpen(true); + } catch (error) { + console.error(t('textSplit.fetchChunkError'), error); + // 如果出错,使用原始预览数据 + alert(t('textSplit.fetchChunkError')); + } + }; + + // 处理保存编辑内容 + const handleSaveEdit = newContent => { + if (onEdit) { + onEdit(chunk.id, newContent); + } + }; + + // 处理生成单个问题 - 后台执行,不阻塞UI + const handleGenerateQuestionsClick = async () => { + setGeneratingQuestions(true); + try { + await onGenerateQuestions([chunk.id]); + } finally { + // Always release loading state, even when generation fails. + setTimeout(() => { + setGeneratingQuestions(false); + }, 500); + } + }; + + // 处理生成测评题目 + const handleGenerateEvalQuestionsClick = async () => { + if (!onGenerateEvalQuestions) return; + + setGeneratingEval(true); + try { + await onGenerateEvalQuestions(chunk.id); + } finally { + // 延迟关闭加载状态 + setTimeout(() => { + setGeneratingEval(false); + }, 500); + } + }; + + return ( + <> + + + + + + + + {chunk.name} + + + + + {chunk.Questions.length > 0 && ( + + {chunk.Questions.map((q, index) => ( + + {index + 1}. {q.question} + + ))} + + } + arrow + placement="top" + > + { + if (!projectId) return; + router.push(`/projects/${projectId}/questions`); + }} + /> + + )} + {chunk.EvalDatasets && chunk.EvalDatasets.length > 0 && ( + { + if (!projectId) return; + router.push(`/projects/${projectId}/eval-datasets`); + }} + /> + )} + + + + + {getTextPreview(chunk.content)} + + + + + + + + + + + + + + + + {generatingQuestions ? : } + + + + + + + + {generatingEval ? : } + + + + + + + + + + + + + + + + + + + + + + + + + + + {/* 编辑文本块对话框 */} + { + setEditDialogOpen(false); + setChunkForEdit(null); + }} + onSave={handleSaveEdit} + /> + + ); +} diff --git a/easy-dataset-main/components/text-split/ChunkDeleteDialog.js b/easy-dataset-main/components/text-split/ChunkDeleteDialog.js new file mode 100644 index 0000000..434fd89 --- /dev/null +++ b/easy-dataset-main/components/text-split/ChunkDeleteDialog.js @@ -0,0 +1,27 @@ +'use client'; + +import { Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions, Button } from '@mui/material'; +import { useTranslation } from 'react-i18next'; + +export default function ChunkDeleteDialog({ open, onClose, onConfirm }) { + const { t } = useTranslation(); + return ( + + {t('common.confirmDelete')}? + + {t('common.confirmDelete')}? + + + + + + + ); +} diff --git a/easy-dataset-main/components/text-split/ChunkFilterDialog.js b/easy-dataset-main/components/text-split/ChunkFilterDialog.js new file mode 100644 index 0000000..222c02a --- /dev/null +++ b/easy-dataset-main/components/text-split/ChunkFilterDialog.js @@ -0,0 +1,124 @@ +'use client'; + +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + Box, + TextField, + Typography, + Slider, + FormControlLabel, + Checkbox +} from '@mui/material'; +import { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; + +export default function ChunkFilterDialog({ open, onClose, onApply, initialFilters = {} }) { + const { t } = useTranslation(); + const [contentKeyword, setContentKeyword] = useState(initialFilters.contentKeyword || ''); + const [sizeRange, setSizeRange] = useState(initialFilters.sizeRange || [0, 10000]); + const [hasQuestions, setHasQuestions] = useState(initialFilters.hasQuestions || null); + + // 重置筛选条件 + const handleReset = () => { + setContentKeyword(''); + setSizeRange([0, 10000]); + setHasQuestions(null); + }; + + // 应用筛选 + const handleApply = () => { + onApply({ + contentKeyword, + sizeRange, + hasQuestions + }); + onClose(); + }; + + // 处理大小范围变化 + const handleSizeRangeChange = (event, newValue) => { + setSizeRange(newValue); + }; + + return ( + + {t('datasets.moreFilters', { defaultValue: '更多筛选' })} + + {/* 文本块内容筛选 */} + + + {t('textSplit.contentKeyword', { defaultValue: '文本块内容' })} + + setContentKeyword(e.target.value)} + variant="outlined" + /> + + + {/* 字数范围筛选 */} + + + + {t('textSplit.characterRange', { defaultValue: '字数范围' })} + + + {sizeRange[0]} - {sizeRange[1]} + + + + + + {/* 是否有问题的筛选 */} + + + {t('textSplit.questionStatus', { defaultValue: '问题状态' })} + + + setHasQuestions(null)} />} + label={t('textSplit.allChunks', { defaultValue: '全部' })} + /> + setHasQuestions(true)} />} + label={t('textSplit.generatedQuestions2', { defaultValue: '已生成问题' })} + /> + setHasQuestions(false)} />} + label={t('textSplit.ungeneratedQuestions', { defaultValue: '未生成问题' })} + /> + + + + + + + + + + ); +} diff --git a/easy-dataset-main/components/text-split/ChunkList.js b/easy-dataset-main/components/text-split/ChunkList.js new file mode 100644 index 0000000..cc30f82 --- /dev/null +++ b/easy-dataset-main/components/text-split/ChunkList.js @@ -0,0 +1,413 @@ +'use client'; + +import { useState, useEffect, useMemo } from 'react'; +import { Box, Paper, Typography, CircularProgress, Pagination, Grid } from '@mui/material'; +import ChunkListHeader from './ChunkListHeader'; +import ChunkCard from './ChunkCard'; +import ChunkViewDialog from './ChunkViewDialog'; +import ChunkDeleteDialog from './ChunkDeleteDialog'; +import BatchEditChunksDialog from './BatchEditChunkDialog'; +import ChunkBatchDeleteDialog from './ChunkBatchDeleteDialog'; +import { useTheme } from '@mui/material/styles'; +import { useTranslation } from 'react-i18next'; + +/** + * Chunk list component + * @param {Object} props + * @param {string} props.projectId - Project ID + * @param {Array} props.chunks - Chunk array + * @param {Function} props.onDelete - Delete callback + * @param {Function} props.onEdit - Edit callback + * @param {Function} props.onGenerateQuestions - Generate questions callback + * @param {Function} props.onDataCleaning - Data cleaning callback + * @param {string} props.questionFilter - Question filter + * @param {Function} props.onQuestionFilterChange - Question filter change callback + * @param {Object} props.selectedModel - 閫変腑鐨勬ā鍨嬩俊鎭? + */ +export default function ChunkList({ + projectId, + chunks = [], + onDelete, + onEdit, + onGenerateQuestions, + onGenerateEvalQuestions, + onDataCleaning, + loading = false, + questionFilter, + setQuestionFilter, + selectedModel, + onChunksUpdate +}) { + const theme = useTheme(); + const [page, setPage] = useState(1); + const [selectedChunks, setSelectedChunks] = useState([]); + const [viewChunk, setViewChunk] = useState(null); + const [viewDialogOpen, setViewDialogOpen] = useState(false); + const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); + const [chunkToDelete, setChunkToDelete] = useState(null); + const [batchEditDialogOpen, setBatchEditDialogOpen] = useState(false); + const [batchEditLoading, setBatchEditLoading] = useState(false); + const [batchDeleteDialogOpen, setBatchDeleteDialogOpen] = useState(false); + const [batchDeleteLoading, setBatchDeleteLoading] = useState(false); + + // 娣诲姞楂樼骇绛涢€夌姸鎬? + const [advancedFilters, setAdvancedFilters] = useState({ + contentKeyword: '', + sizeRange: [0, 10000], + hasQuestions: null + }); + + // 璁$畻娲昏穬绛涢€夋潯浠舵暟 + const activeFilterCount = useMemo(() => { + let count = 0; + if (advancedFilters.contentKeyword) count++; + if (advancedFilters.sizeRange[0] > 0 || advancedFilters.sizeRange[1] < 10000) count++; + if (advancedFilters.hasQuestions !== null) count++; + return count; + }, [advancedFilters]); + + const sortedChunks = useMemo( + () => + [...chunks].sort((a, b) => { + if (a.fileId !== b.fileId) { + return a.fileId.localeCompare(b.fileId); + } + + const getPartNumber = name => { + const match = name.match(/part-(\d+)/); + return match ? parseInt(match[1], 10) : 0; + }; + + const numA = getPartNumber(a.name); + const numB = getPartNumber(b.name); + + return numA - numB; + }), + [chunks] + ); + + const filteredChunks = useMemo(() => { + return sortedChunks.filter(chunk => { + if (advancedFilters.contentKeyword) { + const keyword = advancedFilters.contentKeyword.toLowerCase(); + if (!chunk.content?.toLowerCase().includes(keyword)) { + return false; + } + } + + const size = chunk.size || 0; + if (size < advancedFilters.sizeRange[0] || size > advancedFilters.sizeRange[1]) { + return false; + } + + if (advancedFilters.hasQuestions !== null) { + const hasQuestions = chunk.Questions && chunk.Questions.length > 0; + if (advancedFilters.hasQuestions !== hasQuestions) { + return false; + } + } + + return true; + }); + }, [sortedChunks, advancedFilters]); + + // 褰撶瓫閫夋潯浠跺彉鍖栨椂锛屾竻闄や笉鍦ㄧ瓫閫夌粨鏋滀腑鐨勯€変腑椤? + useEffect(() => { + const filteredChunkIds = filteredChunks.map(chunk => chunk.id); + setSelectedChunks(prev => prev.filter(id => filteredChunkIds.includes(id))); + }, [filteredChunks]); + + const itemsPerPage = 5; + const displayedChunks = useMemo(() => { + const startIndex = (page - 1) * itemsPerPage; + return filteredChunks.slice(startIndex, startIndex + itemsPerPage); + }, [filteredChunks, page]); + const totalPages = useMemo(() => Math.ceil(filteredChunks.length / itemsPerPage), [filteredChunks.length]); + const { t } = useTranslation(); + + const handlePageChange = (event, value) => { + setPage(value); + }; + + const handleViewChunk = async chunkId => { + try { + const response = await fetch(`/api/projects/${projectId}/chunks/${chunkId}`); + if (!response.ok) { + throw new Error(t('textSplit.fetchChunksFailed')); + } + + const data = await response.json(); + setViewChunk(data); + setViewDialogOpen(true); + } catch (error) { + console.error(t('textSplit.fetchChunksError'), error); + } + }; + + const handleCloseViewDialog = () => { + setViewDialogOpen(false); + }; + + const handleOpenDeleteDialog = chunkId => { + setChunkToDelete(chunkId); + setDeleteDialogOpen(true); + }; + + const handleCloseDeleteDialog = () => { + setDeleteDialogOpen(false); + setChunkToDelete(null); + }; + + const handleConfirmDelete = () => { + if (chunkToDelete && onDelete) { + onDelete(chunkToDelete); + } + handleCloseDeleteDialog(); + }; + + // 澶勭悊缂栬緫鏂囨湰鍧? + const handleEditChunk = async (chunkId, newContent) => { + if (onEdit) { + onEdit(chunkId, newContent); + onChunksUpdate(); + } + }; + + // 澶勭悊閫夋嫨鏂囨湰鍧? + const handleSelectChunk = chunkId => { + setSelectedChunks(prev => { + if (prev.includes(chunkId)) { + return prev.filter(id => id !== chunkId); + } else { + return [...prev, chunkId]; + } + }); + }; + + const handleSelectAll = () => { + if (selectedChunks.length === filteredChunks.length) { + setSelectedChunks([]); + } else { + setSelectedChunks(filteredChunks.map(chunk => chunk.id)); + } + }; + + const handleBatchGenerateQuestions = () => { + if (onGenerateQuestions && selectedChunks.length > 0) { + onGenerateQuestions(selectedChunks); + } + }; + + const handleBatchEdit = async editData => { + try { + setBatchEditLoading(true); + + // 璋冪敤鎵归噺缂栬緫API + const response = await fetch(`/api/projects/${projectId}/chunks/batch-edit`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + position: editData.position, + content: editData.content, + chunkIds: editData.chunkIds + }) + }); + + if (!response.ok) { + throw new Error('鎵归噺缂栬緫澶辫触'); + } + + const result = await response.json(); + + if (result.success) { + // 缂栬緫鎴愬姛鍚庯紝鍒锋柊鏂囨湰鍧楁暟鎹? + if (onChunksUpdate) { + onChunksUpdate(); + } + + // 娓呯┖閫変腑鐘舵€? + setSelectedChunks([]); + + // 鍏抽棴瀵硅瘽妗? + setBatchEditDialogOpen(false); + + // 鏄剧ず鎴愬姛娑堟伅 + console.log(`鎴愬姛鏇存柊浜?${result.updatedCount} 涓枃鏈潡`); + } else { + throw new Error(result.message || '鎵归噺缂栬緫澶辫触'); + } + } catch (error) { + console.error('鎵归噺缂栬緫澶辫触:', error); + // 杩欓噷鍙互娣诲姞閿欒鎻愮ず + } finally { + setBatchEditLoading(false); + } + }; + + // 鎵撳紑鎵归噺缂栬緫瀵硅瘽妗? + const handleOpenBatchEdit = () => { + setBatchEditDialogOpen(true); + }; + + // 鍏抽棴鎵归噺缂栬緫瀵硅瘽妗? + const handleCloseBatchEdit = () => { + setBatchEditDialogOpen(false); + }; + + if (loading) { + return ( + + + + ); + } + + // 澶勭悊绛涢€夊彉鍖? + const handleFilterChange = filters => { + setAdvancedFilters(filters); + setPage(1); // 閲嶇疆鍒扮涓€椤? + }; + + // 鎵撳紑鎵归噺鍒犻櫎瀵硅瘽妗? + const handleOpenBatchDelete = () => { + setBatchDeleteDialogOpen(true); + }; + + // 鍏抽棴鎵归噺鍒犻櫎瀵硅瘽妗? + const handleCloseBatchDelete = () => { + setBatchDeleteDialogOpen(false); + }; + + // 纭鎵归噺鍒犻櫎 + const handleConfirmBatchDelete = async () => { + if (selectedChunks.length === 0) return; + + try { + setBatchDeleteLoading(true); + + let successCount = 0; + let failCount = 0; + + // 寰幆璋冪敤鍗曚釜鍒犻櫎鎺ュ彛 + for (const chunkId of selectedChunks) { + try { + await onDelete(chunkId); + successCount++; + } catch (error) { + console.error(`鍒犻櫎鏂囨湰鍧?${chunkId} 澶辫触:`, error); + failCount++; + } + } + + // 鏄剧ず鍒犻櫎缁撴灉 + if (failCount === 0) { + console.log(`鎴愬姛鍒犻櫎 ${successCount} 涓枃鏈潡`); + } else { + console.log(`删除完成:成功 ${successCount} 个,失败 ${failCount} 个`); + } + + // 娓呯┖閫変腑鐘舵€? + setSelectedChunks([]); + + // 鍒锋柊鏁版嵁 + if (onChunksUpdate) { + onChunksUpdate(); + } + + // 鍏抽棴瀵硅瘽妗? + setBatchDeleteDialogOpen(false); + } catch (error) { + console.error('鎵归噺鍒犻櫎澶辫触:', error); + } finally { + setBatchDeleteLoading(false); + } + }; + + return ( + + setQuestionFilter(event.target.value)} + chunks={chunks} + selectedModel={selectedModel} + onFilterChange={handleFilterChange} + activeFilterCount={activeFilterCount} + /> + + + {displayedChunks.map(chunk => ( + + handleSelectChunk(chunk.id)} + onView={() => handleViewChunk(chunk.id)} + onDelete={() => handleOpenDeleteDialog(chunk.id)} + onEdit={handleEditChunk} + onGenerateQuestions={() => onGenerateQuestions && onGenerateQuestions([chunk.id])} + onGenerateEvalQuestions={() => onGenerateEvalQuestions && onGenerateEvalQuestions(chunk.id)} + onDataCleaning={() => onDataCleaning && onDataCleaning([chunk.id])} + projectId={projectId} + selectedModel={selectedModel} + /> + + ))} + + + {chunks.length === 0 && ( + + + {t('textSplit.noChunks')} + + + )} + + {totalPages > 1 && ( + + + + )} + + {/* 鏂囨湰鍧楄鎯呭璇濇 */} + + + {/* 鍒犻櫎纭瀵硅瘽妗?*/} + + + {/* 鎵归噺缂栬緫瀵硅瘽妗?*/} + + + {/* 鎵归噺鍒犻櫎纭瀵硅瘽妗?*/} + + + ); +} diff --git a/easy-dataset-main/components/text-split/ChunkListHeader.js b/easy-dataset-main/components/text-split/ChunkListHeader.js new file mode 100644 index 0000000..89e9fb7 --- /dev/null +++ b/easy-dataset-main/components/text-split/ChunkListHeader.js @@ -0,0 +1,400 @@ +'use client'; + +import { Box, Typography, Checkbox, Button, Select, MenuItem, Tooltip, Menu, IconButton, Badge } from '@mui/material'; +import QuizIcon from '@mui/icons-material/Quiz'; +import DownloadIcon from '@mui/icons-material/Download'; +import AutoFixHighIcon from '@mui/icons-material/AutoFixHigh'; +import CleaningServicesIcon from '@mui/icons-material/CleaningServices'; +import AssessmentIcon from '@mui/icons-material/Assessment'; +import MoreVertIcon from '@mui/icons-material/MoreVert'; +import EditIcon from '@mui/icons-material/Edit'; +import DeleteIcon from '@mui/icons-material/Delete'; +import FilterListIcon from '@mui/icons-material/FilterList'; +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import axios from 'axios'; +import { toast } from 'sonner'; +import { useTranslation } from 'react-i18next'; +import { useState } from 'react'; +import ChunkFilterDialog from './ChunkFilterDialog'; + +export default function ChunkListHeader({ + projectId, + totalChunks, + selectedChunks, + onSelectAll, + onBatchGenerateQuestions, + onBatchEditChunks, + onBatchDeleteChunks, + questionFilter, + setQuestionFilter, + chunks = [], // 添加chunks参数,用于导出文本块 + selectedModel = {}, + onFilterChange = null, + activeFilterCount = 0 +}) { + const { t, i18n } = useTranslation(); + + // 添加更多菜单的状态和锚点 + const [moreMenuAnchorEl, setMoreMenuAnchorEl] = useState(null); + const isMoreMenuOpen = Boolean(moreMenuAnchorEl); + + // 添加筛选对话框状态 + const [filterDialogOpen, setFilterDialogOpen] = useState(false); + + // 自动任务菜单状态 + const [autoTasksMenuAnchorEl, setAutoTasksMenuAnchorEl] = useState(null); + const isAutoTasksMenuOpen = Boolean(autoTasksMenuAnchorEl); + + const handleAutoTasksClick = event => { + setAutoTasksMenuAnchorEl(event.currentTarget); + }; + + const handleAutoTasksClose = () => { + setAutoTasksMenuAnchorEl(null); + }; + + // 打开更多菜单 + const handleMoreMenuClick = event => { + setMoreMenuAnchorEl(event.currentTarget); + }; + + // 关闭更多菜单 + const handleMoreMenuClose = () => { + setMoreMenuAnchorEl(null); + }; + + // 处理批量编辑,关闭菜单并调用原有函数 + const handleBatchEdit = () => { + handleMoreMenuClose(); + onBatchEditChunks(); + }; + + // 处理批量删除,关闭菜单并调用原有函数 + const handleBatchDelete = () => { + handleMoreMenuClose(); + onBatchDeleteChunks(); + }; + + // 处理导出文本块,关闭菜单并调用原有函数 + const handleExport = () => { + handleMoreMenuClose(); + handleExportChunks(); + }; + + // 创建自动提取问题任务 + const handleCreateAutoQuestionTask = async () => { + if (!projectId || !selectedModel?.id) { + toast.error(t('textSplit.selectModelFirst', { defaultValue: '请先选择模型' })); + return; + } + + try { + // 调用创建任务接口 + const response = await axios.post(`/api/projects/${projectId}/tasks`, { + taskType: 'question-generation', + modelInfo: selectedModel, + language: i18n.language, + detail: '批量生成问题任务' + }); + + if (response.data?.code === 0) { + toast.success(t('tasks.createSuccess', { defaultValue: '后台任务已创建,系统将自动处理未生成问题的文本块' })); + } else { + toast.error(t('tasks.createFailed', { defaultValue: '创建任务失败' }) + ': ' + response.data?.message); + } + } catch (error) { + console.error('创建自动提取问题任务失败:', error); + toast.error(t('tasks.createFailed', { defaultValue: '创建任务失败' }) + ': ' + error.message); + } + }; + + // 创建自动数据清洗任务 + const handleCreateAutoDataCleaningTask = async () => { + if (!projectId || !selectedModel?.id) { + toast.error(t('textSplit.selectModelFirst', { defaultValue: '请先选择模型' })); + return; + } + + try { + // 调用创建任务接口 + const response = await axios.post(`/api/projects/${projectId}/tasks`, { + taskType: 'data-cleaning', + modelInfo: selectedModel, + language: i18n.language, + detail: '批量数据清洗任务' + }); + + if (response.data?.code === 0) { + toast.success( + t('tasks.createSuccess', { defaultValue: '后台任务已创建,系统将自动处理所有文本块进行数据清洗' }) + ); + } else { + toast.error(t('tasks.createFailed', { defaultValue: '创建任务失败' }) + ': ' + response.data?.message); + } + } catch (error) { + console.error('创建自动数据清洗任务失败:', error); + toast.error(t('tasks.createFailed', { defaultValue: '创建任务失败' }) + ': ' + error.message); + } + }; + + // 创建自动生成评估数据集任务 + const handleCreateAutoEvalGenerationTask = async () => { + if (!projectId || !selectedModel?.id) { + toast.error(t('textSplit.selectModelFirst', { defaultValue: '请先选择模型' })); + return; + } + + try { + // 调用创建任务接口 + const response = await axios.post(`/api/projects/${projectId}/tasks`, { + taskType: 'eval-generation', + modelInfo: selectedModel, + language: i18n.language, + detail: '批量生成评估数据集任务' + }); + + if (response.data?.code === 0) { + toast.success( + t('tasks.createSuccess', { + defaultValue: '后台任务已创建,系统将自动为所有未生成评估题目的文本块生成评估数据集' + }) + ); + } else { + toast.error(t('tasks.createFailed', { defaultValue: '创建任务失败' }) + ': ' + response.data?.message); + } + } catch (error) { + console.error('创建自动生成评估数据集任务失败:', error); + toast.error(t('tasks.createFailed', { defaultValue: '创建任务失败' }) + ': ' + error.message); + } + }; + + // 导出文本块为JSON文件的函数 + const handleExportChunks = () => { + if (!chunks || chunks.length === 0) return; + + // 创建要导出的数据对象 + const exportData = chunks.map(chunk => ({ + name: chunk.name, + projectId: chunk.projectId, + fileName: chunk.fileName, + content: chunk.content, + summary: chunk.summary, + size: chunk.size + })); + + // 将数据转换为JSON字符串 + const jsonString = JSON.stringify(exportData, null, 2); + + // 创建Blob对象 + const blob = new Blob([jsonString], { type: 'application/json' }); + + // 创建下载链接 + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `text-chunks-export-${new Date().toISOString().split('T')[0]}.json`; + + // 触发下载 + document.body.appendChild(a); + a.click(); + + // 清理 + document.body.removeChild(a); + URL.revokeObjectURL(url); + }; + + return ( + + + 0 && selectedChunks.length < totalChunks} + onChange={onSelectAll} + /> + {t('textSplit.selectedCount', { count: selectedChunks.length })} + + + + {/* 更多筛选按钮 */} + + + + + + + + + + {/* 自动任务下拉菜单 */} + + + + + { + handleCreateAutoQuestionTask(); + handleAutoTasksClose(); + }} + > + + {t('textSplit.autoGenerateQuestions')} + + + + + { + handleCreateAutoEvalGenerationTask(); + handleAutoTasksClose(); + }} + > + + {t('textSplit.autoEvalGeneration', { defaultValue: '自动生成评估集' })} + + + + + { + handleCreateAutoDataCleaningTask(); + handleAutoTasksClose(); + }} + > + + {t('textSplit.autoDataCleaning', { defaultValue: '自动数据清洗' })} + + + + + {/* 更多菜单按钮 */} + + + + + + + {/* 更多操作下拉菜单 */} + + + + {t('batchEdit.batchEdit', { defaultValue: '批量编辑' })} + + + + {t('textSplit.batchDeleteChunks', { defaultValue: '批量删除' })} + + + + {t('textSplit.exportChunks', { defaultValue: '导出文本块' })} + + + + + + {/* 筛选对话框 */} + setFilterDialogOpen(false)} onApply={onFilterChange} /> + + ); +} diff --git a/easy-dataset-main/components/text-split/ChunkViewDialog.js b/easy-dataset-main/components/text-split/ChunkViewDialog.js new file mode 100644 index 0000000..9f24a2f --- /dev/null +++ b/easy-dataset-main/components/text-split/ChunkViewDialog.js @@ -0,0 +1,31 @@ +'use client'; + +import { Box, Button, Dialog, DialogTitle, DialogContent, DialogActions, CircularProgress } from '@mui/material'; +import ReactMarkdown from 'react-markdown'; +import { useTranslation } from 'react-i18next'; +import 'github-markdown-css/github-markdown-light.css'; + +export default function ChunkViewDialog({ open, chunk, onClose }) { + const { t } = useTranslation(); + return ( + + {t('textSplit.chunkDetails', { chunkId: chunk?.name })} + + {chunk ? ( + +
+ {chunk.content} +
+
+ ) : ( + + + + )} +
+ + + +
+ ); +} diff --git a/easy-dataset-main/components/text-split/DomainAnalysis.js b/easy-dataset-main/components/text-split/DomainAnalysis.js new file mode 100644 index 0000000..98150d5 --- /dev/null +++ b/easy-dataset-main/components/text-split/DomainAnalysis.js @@ -0,0 +1,560 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { + Box, + Paper, + Typography, + Divider, + CircularProgress, + Tabs, + Tab, + List, + ListItem, + ListItemText, + Collapse, + IconButton, + TextField, + Button, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, + Tooltip, + Menu, + MenuItem +} from '@mui/material'; +import { useTheme } from '@mui/material/styles'; +import TabPanel from './components/TabPanel'; +import ReactMarkdown from 'react-markdown'; +import ExpandLess from '@mui/icons-material/ExpandLess'; +import ExpandMore from '@mui/icons-material/ExpandMore'; +import AddIcon from '@mui/icons-material/Add'; +import EditIcon from '@mui/icons-material/Edit'; +import DeleteIcon from '@mui/icons-material/Delete'; +import MoreVertIcon from '@mui/icons-material/MoreVert'; +import axios from 'axios'; +import { toast } from 'sonner'; + +import 'github-markdown-css/github-markdown-light.css'; + +/** + * 领域分析组件 + * @param {Object} props + * @param {string} props.projectId - 项目ID + * @param {Array} props.toc - 目录结构数组 + * @param {Array} props.tags - 标签树数组 + * @param {boolean} props.loading - 是否加载中 + * @param {Function} props.onTagsUpdate - 标签更新回调 + */ + +// 领域树节点组件 +function TreeNode({ node, level = 0, onEdit, onDelete, onAddChild }) { + const [open, setOpen] = useState(true); + const theme = useTheme(); + const hasChildren = node.child && node.child.length > 0; + const [anchorEl, setAnchorEl] = useState(null); + const menuOpen = Boolean(anchorEl); + const { t } = useTranslation(); + + const handleClick = () => { + if (hasChildren) { + setOpen(!open); + } + }; + + const handleMenuOpen = event => { + event.stopPropagation(); + setAnchorEl(event.currentTarget); + }; + + const handleMenuClose = event => { + if (event) event.stopPropagation(); + setAnchorEl(null); + }; + + const handleEdit = event => { + event.stopPropagation(); + onEdit(node); + handleMenuClose(); + }; + + const handleDelete = event => { + event.stopPropagation(); + onDelete(node); + handleMenuClose(); + }; + + const handleAddChild = event => { + event.stopPropagation(); + onAddChild(node); + handleMenuClose(); + }; + + return ( + <> + + + + + + + {hasChildren && (open ? : )} + + + e.stopPropagation()}> + + + {t('textSplit.editTag')} + + + + {t('textSplit.deleteTag')} + + {level === 0 && ( + + + {t('textSplit.addTag')} + + )} + + + + {hasChildren && ( + + + {node.child.map((childNode, index) => ( + + ))} + + + )} + + ); +} + +// 领域树组件 +function DomainTree({ tags, onEdit, onDelete, onAddChild }) { + return ( + + {tags.map((node, index) => ( + + ))} + + ); +} + +export default function DomainAnalysis({ projectId, toc = '', loading = false }) { + const theme = useTheme(); + const { t } = useTranslation(); + const [activeTab, setActiveTab] = useState(0); + const [dialogOpen, setDialogOpen] = useState(false); + const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); + const [currentNode, setCurrentNode] = useState(null); + const [parentNode, setParentNode] = useState(''); + const [dialogMode, setDialogMode] = useState('add'); + const [labelValue, setLabelValue] = useState({}); + const [saving, setSaving] = useState(false); + const [tags, setTags] = useState([]); + const [snackbar, setSnackbar] = useState({ + open: false, + message: '', + severity: 'success' + }); + + const handleCloseSnackbar = () => { + setSnackbar(prev => ({ ...prev, open: false })); + }; + + useEffect(() => { + getTags(); + }, []); + const getTags = async () => { + const response = await axios.get(`/api/projects/${projectId}/tags`); + setTags(response.data.tags); + }; + // 处理标签切换 + const handleTabChange = (event, newValue) => { + setActiveTab(newValue); + }; + + // 打开添加标签对话框 + const handleAddTag = () => { + setDialogMode('add'); + setCurrentNode(null); + setParentNode(null); + setLabelValue({}); + setDialogOpen(true); + }; + + // 打开编辑标签对话框 + const handleEditTag = node => { + setDialogMode('edit'); + setCurrentNode({ id: node.id, label: node.label }); + setLabelValue({ id: node.id, label: node.label }); + setDialogOpen(true); + }; + + // 打开添加子标签对话框 + const handleAddChildTag = parentNode => { + setDialogMode('addChild'); + setParentNode(parentNode.label); + setLabelValue({ parentId: parentNode.id }); + setDialogOpen(true); + }; + + // 打开删除标签对话框 + const handleDeleteTag = node => { + setCurrentNode(node); + setDeleteDialogOpen(true); + }; + + // 关闭对话框 + const handleCloseDialog = () => { + setDialogOpen(false); + setDeleteDialogOpen(false); + }; + + // 查找并更新节点 + const findAndUpdateNode = (nodes, targetNode, newLabel) => { + return nodes.map(node => { + if (node === targetNode) { + return { ...node, label: newLabel }; + } + if (node.child && node.child.length > 0) { + return { ...node, child: findAndUpdateNode(node.child, targetNode, newLabel) }; + } + return node; + }); + }; + + // 查找并删除节点 + const findAndDeleteNode = (nodes, targetNode) => { + return nodes + .filter(node => node !== targetNode) + .map(node => { + if (node.child && node.child.length > 0) { + return { ...node, child: findAndDeleteNode(node.child, targetNode) }; + } + return node; + }); + }; + + // 查找并添加子节点 + const findAndAddChildNode = (nodes, parentNode, childLabel) => { + return nodes.map(node => { + if (node === parentNode) { + const childArray = node.child || []; + return { + ...node, + child: [...childArray, { label: childLabel, child: [] }] + }; + } + if (node.child && node.child.length > 0) { + return { ...node, child: findAndAddChildNode(node.child, parentNode, childLabel) }; + } + return node; + }); + }; + + // 保存标签更改 + const saveTagChanges = async updatedTags => { + console.log('保存标签更改:', updatedTags); + setSaving(true); + try { + const response = await fetch(`/api/projects/${projectId}/tags`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ tags: updatedTags }) + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.error || t('domain.errors.saveFailed')); + } + getTags(); + setSnackbar({ + open: true, + message: t('domain.messages.updateSuccess'), + severity: 'success' + }); + } catch (error) { + console.error('保存标签失败:', error); + setSnackbar({ + open: true, + message: error.message || '保存标签失败', + severity: 'error' + }); + } finally { + setSaving(false); + } + }; + + // 提交表单 + const handleSubmit = async () => { + if (!labelValue.label.trim()) { + setSnackbar({ + open: true, + message: '标签名称不能为空', + severity: 'error' + }); + return; + } + + await saveTagChanges(labelValue); + handleCloseDialog(); + }; + + const handleConfirmDelete = async () => { + if (!currentNode) return; + + const res = await axios.delete(`/api/projects/${projectId}/tags?id=${currentNode.id}`); + if (res.status === 200) { + toast.success('删除成功'); + getTags(); + } + + setDeleteDialogOpen(false); + }; + + if (loading) { + return ( + + + + ); + } + + if (toc.length === 0) { + return ( + + + {t('domain.noToc')} + + + ); + } + + return ( + + + + + + + + + + + + {t('domain.tabs.tree')} + + + + + + + {tags && tags.length > 0 ? ( + + ) : ( + + + {t('domain.noTags')} + + + + )} + + + + + + + {t('domain.docStructure')} + + + +
+ ( +
+ {children} +
+ ) + }} + > + {toc} +
+
+
+
+
+
+
+ + {/* 添加/编辑标签对话框 */} + + + {dialogMode === 'add' + ? t('domain.dialog.addTitle') + : dialogMode === 'edit' + ? t('domain.dialog.editTitle') + : t('domain.dialog.addChildTitle')} + + + + {dialogMode === 'add' + ? t('domain.dialog.inputRoot') + : dialogMode === 'edit' + ? t('domain.dialog.inputEdit') + : t('domain.dialog.inputChild', { label: parentNode })} + + setLabelValue({ ...labelValue, label: e.target.value })} + /> + + + + + + + + {/* 删除确认对话框 */} + + {t('common.confirmDelete')} + + + {t('domain.dialog.deleteConfirm', { label: currentNode?.label })} + {currentNode?.child && currentNode.child.length > 0 && t('domain.dialog.deleteWarning')} + + + + + + + +
+ ); +} diff --git a/easy-dataset-main/components/text-split/FileUploader.js b/easy-dataset-main/components/text-split/FileUploader.js new file mode 100644 index 0000000..380588e --- /dev/null +++ b/easy-dataset-main/components/text-split/FileUploader.js @@ -0,0 +1,360 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Paper, Grid } from '@mui/material'; +import { useTheme } from '@mui/material/styles'; +import { useAtomValue } from 'jotai/index'; +import { selectedModelInfoAtom } from '@/lib/store'; +import UploadArea from './components/UploadArea'; +import FileList from './components/FileList'; +import DeleteConfirmDialog from './components/DeleteConfirmDialog'; +import PdfProcessingDialog from './components/PdfProcessingDialog'; +import DomainTreeActionDialog from './components/DomainTreeActionDialog'; +import FileLoadingProgress from './components/FileLoadingProgress'; +import { fileApi, taskApi } from '@/lib/api'; +import { getContent, checkMaxSize, checkInvalidFiles, getvalidFiles } from '@/lib/file/file-process'; +import { toast } from 'sonner'; + +export default function FileUploader({ + projectId, + onUploadSuccess, + onFileDeleted, + sendToPages, + setPdfStrategy, + pdfStrategy, + selectedViosnModel, + setSelectedViosnModel, + setPageLoading, + taskFileProcessing, + fileTask +}) { + const theme = useTheme(); + const { t } = useTranslation(); + const [files, setFiles] = useState([]); + const [pdfFiles, setPdfFiles] = useState([]); + const [uploadedFiles, setUploadedFiles] = useState({}); + const selectedModelInfo = useAtomValue(selectedModelInfoAtom); + const [uploading, setUploading] = useState(false); + const [loading, setLoading] = useState(false); + const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false); + const [pdfProcessConfirmOpen, setpdfProcessConfirmOpen] = useState(false); + const [fileToDelete, setFileToDelete] = useState({}); + const [domainTreeActionOpen, setDomainTreeActionOpen] = useState(false); + const [domainTreeAction, setDomainTreeAction] = useState(''); + const [isFirstUpload, setIsFirstUpload] = useState(false); + const [pendingAction, setPendingAction] = useState(null); + const [taskSettings, setTaskSettings] = useState(null); + const [visionModels, setVisionModels] = useState([]); + const [currentPage, setCurrentPage] = useState(1); + const [pageSize] = useState(10); + const [searchFileName, setSearchFileName] = useState(''); + + useEffect(() => { + fetchUploadedFiles(); + }, [currentPage, searchFileName]); + + /** + * 处理 PDF 处理方式选择 + */ + const handleRadioChange = event => { + const modelId = event.target.selectedVision; + setPdfStrategy(event.target.value); + + if (event.target.value === 'mineru') { + toast.success(t('textSplit.mineruSelected')); + } else if (event.target.value === 'mineru-local') { + toast.success(t('textSplit.mineruLocalSelected')); + } else if (event.target.value === 'vision') { + const model = visionModels.find(item => item.id === modelId); + toast.success( + t('textSplit.customVisionModelSelected', { + name: model.modelName, + provider: model.projectName + }) + ); + } else { + toast.success(t('textSplit.defaultSelected')); + } + }; + + /** + * 获取上传的文件列表 + * @param {*} page + * @param {*} size + * @param {*} fileName + */ + const fetchUploadedFiles = async (page = currentPage, size = pageSize, fileName = searchFileName) => { + try { + setLoading(true); + const data = await fileApi.getFiles({ projectId, page, size, fileName, t }); + setUploadedFiles(data); + + setIsFirstUpload(data.total === 0); + + const taskData = await taskApi.getProjectTasks(projectId); + setTaskSettings(taskData); + + //使用Jotai会出现数据获取的延迟,导致这里模型获取不到,改用localStorage获取模型信息 + const model = JSON.parse(localStorage.getItem('modelConfigList')); + + //过滤出视觉模型 + const visionItems = model.filter(item => item.type === 'vision'); + + //先默认选择第一个配置的视觉模型 + if (visionItems.length > 0) { + setSelectedViosnModel(visionItems[0].id); + } + + setVisionModels(visionItems); + } catch (error) { + toast.error(error.message); + } finally { + setLoading(false); + } + }; + + /** + * 处理文件选择 + */ + const handleFileSelect = event => { + const selectedFiles = Array.from(event.target.files); + + checkMaxSize(selectedFiles); + checkInvalidFiles(selectedFiles); + + const validFiles = getvalidFiles(selectedFiles); + + if (validFiles.length > 0) { + setFiles(prev => [...prev, ...validFiles]); + } + const hasPdfFiles = selectedFiles.filter(file => file.name.endsWith('.pdf')); + if (hasPdfFiles.length > 0) { + setpdfProcessConfirmOpen(true); + setPdfFiles(hasPdfFiles); + } + }; + + /** + * 从待上传文件列表中移除文件 + */ + const removeFile = index => { + const fileToRemove = files[index]; + setFiles(prev => prev.filter((_, i) => i !== index)); + if (fileToRemove && fileToRemove.name.toLowerCase().endsWith('.pdf')) { + setPdfFiles(prevPdfFiles => prevPdfFiles.filter(pdfFile => pdfFile.name !== fileToRemove.name)); + } + }; + + /** + * 上传文件 + */ + const uploadFiles = async () => { + if (files.length === 0) return; + + // 如果是第一次上传,直接走默认逻辑 + if (isFirstUpload) { + handleStartUpload('rebuild'); + return; + } + + // 否则打开领域树操作选择对话框 + setDomainTreeAction('upload'); + setPendingAction({ type: 'upload' }); + setDomainTreeActionOpen(true); + }; + + /** + * 处理领域树操作选择 + */ + const handleDomainTreeAction = action => { + setDomainTreeActionOpen(false); + + // 执行挂起的操作 + if (pendingAction && pendingAction.type === 'upload') { + handleStartUpload(action); + } else if (pendingAction && pendingAction.type === 'delete') { + handleDeleteFile(action); + } + + // 清除挂起的操作 + setPendingAction(null); + }; + + /** + * 开始上传文件 + */ + const handleStartUpload = async domainTreeActionType => { + setUploading(true); + try { + const uploadedFileInfos = []; + for (const file of files) { + const { fileContent, fileName } = await getContent(file); + const data = await fileApi.uploadFile({ file, projectId, fileContent, fileName, t }); + uploadedFileInfos.push({ fileName: data.fileName, fileId: data.fileId }); + } + toast.success(t('textSplit.uploadSuccess', { count: files.length })); + setFiles([]); + setCurrentPage(1); + await fetchUploadedFiles(); + if (onUploadSuccess) { + await onUploadSuccess(uploadedFileInfos, pdfFiles, domainTreeActionType); + } + } catch (err) { + toast.error(err.message || t('textSplit.uploadFailed')); + } finally { + setUploading(false); + } + }; + + // 打开删除确认对话框 + const openDeleteConfirm = (fileId, fileName) => { + setFileToDelete({ fileId, fileName }); + setDeleteConfirmOpen(true); + }; + + // 关闭删除确认对话框 + const closeDeleteConfirm = () => { + setDeleteConfirmOpen(false); + setFileToDelete(null); + }; + + // 删除文件前确认领域树操作 + const confirmDeleteFile = () => { + setDeleteConfirmOpen(false); + + // 如果没有其他文件了(删除后会变为空),直接删除 + if (uploadedFiles.total <= 1) { + handleDeleteFile('keep'); + return; + } + + // 否则打开领域树操作选择对话框 + setDomainTreeAction('delete'); + setPendingAction({ type: 'delete' }); + setDomainTreeActionOpen(true); + }; + + // 处理删除文件 + const handleDeleteFile = async domainTreeActionType => { + if (!fileToDelete) return; + + try { + setLoading(true); + closeDeleteConfirm(); + + await fileApi.deleteFile({ + fileToDelete, + projectId, + domainTreeActionType, + modelInfo: selectedModelInfo || {}, + t + }); + await fetchUploadedFiles(); + + if (onFileDeleted) { + const filesLength = uploadedFiles.total; + onFileDeleted(fileToDelete, filesLength); + } + + if (uploadedFiles.data && uploadedFiles.data.length <= 1 && currentPage > 1) { + setCurrentPage(1); + } + + toast.success(t('textSplit.deleteSuccess', { fileName: fileToDelete.fileName })); + } catch (error) { + console.error('Error deleting file:', error); + toast.error(error.message); + } finally { + setLoading(false); + setFileToDelete(null); + } + }; + + return ( + + {taskFileProcessing ? ( + + ) : ( + <> + + {/* 左侧:上传文件区域 */} + + + + + {/* 右侧:已上传文件列表 */} + + sendToPages(array)} + onDeleteFile={openDeleteConfirm} + projectId={projectId} + currentPage={currentPage} + onPageChange={(page, fileName) => { + if (fileName !== undefined) { + // 搜索时更新搜索关键词和页码 + setSearchFileName(fileName); + setCurrentPage(page); + } else { + // 翻页时只更新页码 + setCurrentPage(page); + } + }} + onRefresh={fetchUploadedFiles} // 传递刷新函数 + /> + + + + + + {/* 领域树操作选择对话框 */} + setDomainTreeActionOpen(false)} + onConfirm={handleDomainTreeAction} + isFirstUpload={isFirstUpload} + action={domainTreeAction} + /> + {/* 检测到pdf的处理框 */} + setpdfProcessConfirmOpen(false)} + onRadioChange={handleRadioChange} + value={pdfStrategy} + projectId={projectId} + taskSettings={taskSettings} + visionModels={visionModels} + selectedViosnModel={selectedViosnModel} + setSelectedViosnModel={setSelectedViosnModel} + /> + + )} + + ); +} diff --git a/easy-dataset-main/components/text-split/LoadingBackdrop.js b/easy-dataset-main/components/text-split/LoadingBackdrop.js new file mode 100644 index 0000000..3ba4a3c --- /dev/null +++ b/easy-dataset-main/components/text-split/LoadingBackdrop.js @@ -0,0 +1,114 @@ +'use client'; + +import { Backdrop, Paper, CircularProgress, Typography, Box, LinearProgress } from '@mui/material'; + +export default function LoadingBackdrop({ open, title, description, progress = null }) { + return ( + theme.zIndex.drawer + 1, + position: 'fixed', + backdropFilter: 'blur(5px)', + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + }} + open={open} + > + + theme.palette.primary.main + }} + /> + + + {title} + + + + {description} + + + {progress && progress.total > 0 && ( + + + + {progress.completed}/{progress.total} ({progress.percentage}%) + + + {progress.questionCount > 0 && ( + + 已生成问题数: {progress.questionCount} + + )} + + + + + )} + + + ); +} diff --git a/easy-dataset-main/components/text-split/MarkdownViewDialog.js b/easy-dataset-main/components/text-split/MarkdownViewDialog.js new file mode 100644 index 0000000..fbbc325 --- /dev/null +++ b/easy-dataset-main/components/text-split/MarkdownViewDialog.js @@ -0,0 +1,433 @@ +'use client'; + +import React, { useState, useEffect, useRef } from 'react'; +import { + Box, + Button, + Dialog, + DialogTitle, + DialogContent, + DialogActions, + CircularProgress, + Typography, + Divider, + Chip, + Switch, + FormControlLabel, + Alert, + DialogContentText +} from '@mui/material'; +import DeleteIcon from '@mui/icons-material/Delete'; +import SaveIcon from '@mui/icons-material/Save'; +import ReactMarkdown from 'react-markdown'; +import { useTranslation } from 'react-i18next'; +import 'github-markdown-css/github-markdown-light.css'; + +export default function MarkdownViewDialog({ open, text, onClose, projectId, onSaveSuccess }) { + const { t } = useTranslation(); + const [customSplitMode, setCustomSplitMode] = useState(false); + const [splitPoints, setSplitPoints] = useState([]); + const [selectedText, setSelectedText] = useState(''); + const [savedMessage, setSavedMessage] = useState(''); + const [confirmDialogOpen, setConfirmDialogOpen] = useState(false); + const [saving, setSaving] = useState(false); + const [error, setError] = useState(''); + const contentRef = useRef(null); + const [chunksPreview, setChunksPreview] = useState([]); + + // 根据分块点计算每个块的字数 + const calculateChunksPreview = points => { + if (!text || !text.content) return []; + + const content = text.content; + const sortedPoints = [...points].sort((a, b) => a.position - b.position); + + const chunks = []; + let startPos = 0; + + // 计算每个分块 + for (let i = 0; i < sortedPoints.length; i++) { + const endPos = sortedPoints[i].position; + const chunkContent = content.substring(startPos, endPos); + + if (chunkContent.trim().length > 0) { + chunks.push({ + index: i + 1, + length: chunkContent.length, + preview: chunkContent.substring(0, 20) + (chunkContent.length > 20 ? '...' : '') + }); + } + + startPos = endPos; + } + + // 添加最后一个分块 + const lastChunkContent = content.substring(startPos); + if (lastChunkContent.trim().length > 0) { + chunks.push({ + index: chunks.length + 1, + length: lastChunkContent.length, + preview: lastChunkContent.substring(0, 20) + (lastChunkContent.length > 20 ? '...' : '') + }); + } + + return chunks; + }; + + // 重置组件状态 + useEffect(() => { + if (!open) { + setSplitPoints([]); + setCustomSplitMode(false); + setSelectedText(''); + setSavedMessage(''); + } + }, [open]); + + // 当分块点变化时更新预览 + useEffect(() => { + if (splitPoints.length > 0 && text?.content) { + const preview = calculateChunksPreview(splitPoints); + setChunksPreview(preview); + } else { + setChunksPreview([]); + } + }, [splitPoints, text?.content]); + + // 处理用户选择文本事件 + const handleTextSelection = () => { + if (!customSplitMode) return; + + const selection = window.getSelection(); + if (!selection.toString().trim()) return; + + // 获取选择的文本内容和位置 + const selectedContent = selection.toString(); + + // 计算选择位置在文档中的偏移量 + const range = selection.getRangeAt(0); + const preCaretRange = range.cloneRange(); + preCaretRange.selectNodeContents(contentRef.current); + preCaretRange.setEnd(range.endContainer, range.endOffset); + const position = preCaretRange.toString().length; + + // 添加到分割点列表 + const newPoint = { + id: Date.now(), + position, + preview: selectedContent.substring(0, 40) + (selectedContent.length > 40 ? '...' : '') + }; + + setSplitPoints(prev => [...prev, newPoint].sort((a, b) => a.position - b.position)); + setSelectedText(''); + }; + + // 删除分割点 + const handleDeletePoint = id => { + setSplitPoints(prev => prev.filter(point => point.id !== id)); + }; + + // 弹出确认对话框 + const handleConfirmSave = () => { + setConfirmDialogOpen(true); + }; + + // 取消保存 + const handleCancelSave = () => { + setConfirmDialogOpen(false); + }; + + // 确认并执行保存 + const handleSavePoints = async () => { + // 输出调试信息 + console.log('保存分块点时的数据:', { + projectId, + text: text + ? { + fileId: text.fileId, + fileName: text.fileName, + contentLength: text.content ? text.content.length : 0 + } + : null, + splitPointsCount: splitPoints.length + }); + + if (!text) { + setError(t('textSplit.missingRequiredData') + ': text 为空'); + return; + } + + if (!text.fileId) { + setError(t('textSplit.missingRequiredData') + ': fileId 不存在'); + return; + } + + if (!text.fileName) { + setError(t('textSplit.missingRequiredData') + ': fileName 不存在'); + return; + } + + if (!text.content) { + setError(t('textSplit.missingRequiredData') + ': content 不存在'); + return; + } + + if (!projectId) { + setError(t('textSplit.missingRequiredData') + ': projectId 不存在'); + return; + } + + setConfirmDialogOpen(false); + setSaving(true); + setError(''); + + try { + // 准备要发送的数据 + const customSplitData = { + fileId: text.fileId, + fileName: text.fileName, + content: text.content, + splitPoints: splitPoints.map(point => ({ + position: point.position, + preview: point.preview + })) + }; + + // 发送请求到待创建的API接口 + const response = await fetch(`/api/projects/${projectId}/custom-split`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(customSplitData) + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.error || t('textSplit.customSplitFailed')); + } + + // 保存成功 + setSavedMessage(t('textSplit.customSplitSuccess')); + + // 短暂显示成功消息后关闭对话框并刷新列表 + setTimeout(() => { + setSavedMessage(''); + + // 关闭对话框 + onClose(); + + // 调用父组件的刷新方法(如果提供了) + if (typeof onSaveSuccess === 'function') { + onSaveSuccess(); + } + }, 1500); + } catch (err) { + console.error('保存自定义分块出错:', err); + setError(err.message || t('textSplit.customSplitFailed')); + } finally { + setSaving(false); + } + }; + + return ( + + + {text ? text.fileName : ''} + setCustomSplitMode(e.target.checked)} color="primary" /> + } + label={t('textSplit.customSplitMode')} + sx={{ ml: 2 }} + /> + + + {customSplitMode && ( + + + {t('textSplit.customSplitInstructions')} + + + {/* 分割点列表 */} + {splitPoints.length > 0 && ( + + + {t('textSplit.splitPointsList')} ({splitPoints.length}): + + + {splitPoints.map((point, index) => ( + handleDeletePoint(point.id)} + deleteIcon={} + color="primary" + variant="outlined" + /> + ))} + + + {/* 文本块字数预览 */} + {chunksPreview.length > 0 && ( + + + {t('textSplit.chunksPreview')} + + + {chunksPreview.map(chunk => ( + + ))} + + + )} + + )} + + {/* 保存按钮 */} + + + + + {/* 提示消息 */} + {savedMessage && ( + + {savedMessage} + + )} + + {error && ( + + {error} + + )} + + )} + + + + + {text ? ( + + {/* 渲染带有分割点标记的内容 */} + {customSplitMode && splitPoints.length > 0 ? ( + +
+                  {text.content.split('').map((char, index) => {
+                    const isSplitPoint = splitPoints.some(point => point.position === index);
+                    const splitPointIndex = splitPoints.findIndex(point => point.position === index);
+
+                    if (isSplitPoint) {
+                      return (
+                        
+                          
+                            
+                              {splitPointIndex + 1}
+                            
+                          
+                          {char}
+                        
+                      );
+                    }
+                    return char;
+                  })}
+                
+
+ ) : ( + +
+ {text.content} +
+
+ )} +
+ ) : ( + + + + )} +
+ + + + + + {/* 确认对话框 */} + + {t('textSplit.confirmCustomSplitTitle')} + + + {t('textSplit.confirmCustomSplitMessage')} + + + + + + + +
+ ); +} diff --git a/easy-dataset-main/components/text-split/PdfSettings.js b/easy-dataset-main/components/text-split/PdfSettings.js new file mode 100644 index 0000000..ae257e1 --- /dev/null +++ b/easy-dataset-main/components/text-split/PdfSettings.js @@ -0,0 +1,43 @@ +'use client'; + +import { Box, Select, MenuItem, Typography, FormControl, InputLabel } from '@mui/material'; +import { useTranslation } from 'react-i18next'; + +export default function PdfSettings({ pdfStrategy, setPdfStrategy, selectedViosnModel, setSelectedViosnModel }) { + const { t } = useTranslation(); + + return ( + + + {t('textSplit.pdfStrategy')} + + + + {pdfStrategy === 'vision' && ( + + {t('textSplit.visionModel')} + + + )} + + ); +} diff --git a/easy-dataset-main/components/text-split/components/DeleteConfirmDialog.js b/easy-dataset-main/components/text-split/components/DeleteConfirmDialog.js new file mode 100644 index 0000000..353aee9 --- /dev/null +++ b/easy-dataset-main/components/text-split/components/DeleteConfirmDialog.js @@ -0,0 +1,60 @@ +'use client'; + +import { + Dialog, + DialogTitle, + DialogContent, + DialogContentText, + DialogActions, + Button, + Typography, + Box, + Alert +} from '@mui/material'; +import { useTranslation } from 'react-i18next'; + +export default function DeleteConfirmDialog({ open, fileName, onClose, onConfirm }) { + const { t } = useTranslation(); + return ( + + + {t('common.confirmDelete')}「{fileName}」? + + + {t('common.confirmDeleteDescription')} + + + + {t('textSplit.deleteFileWarning')} + + + + • {t('textSplit.deleteFileWarningChunks')} + + + • {t('textSplit.deleteFileWarningQuestions')} + + + • {t('textSplit.deleteFileWarningDatasets')} + + + + + + + + + + ); +} diff --git a/easy-dataset-main/components/text-split/components/DirectoryView.js b/easy-dataset-main/components/text-split/components/DirectoryView.js new file mode 100644 index 0000000..3913fd4 --- /dev/null +++ b/easy-dataset-main/components/text-split/components/DirectoryView.js @@ -0,0 +1,73 @@ +'use client'; + +import { Box, List, ListItem, ListItemIcon, ListItemText, Collapse, IconButton } from '@mui/material'; +import FolderIcon from '@mui/icons-material/Folder'; +import ArticleIcon from '@mui/icons-material/Article'; +import ExpandLess from '@mui/icons-material/ExpandLess'; +import ExpandMore from '@mui/icons-material/ExpandMore'; +import { useTheme } from '@mui/material/styles'; + +/** + * 目录结构组件 + * @param {Object} props + * @param {Array} props.items - 目录项数组 + * @param {Object} props.expandedItems - 展开状态对象 + * @param {Function} props.onToggleItem - 展开/折叠回调 + * @param {number} props.level - 当前层级 + * @param {string} props.parentId - 父级ID + */ +export default function DirectoryView({ items, expandedItems, onToggleItem, level = 0, parentId = '' }) { + const theme = useTheme(); + + if (!items || items.length === 0) return null; + + return ( + 0 ? 2 : 0 }}> + {items.map((item, index) => { + const itemId = `${parentId}-${index}`; + const hasChildren = item.children && item.children.length > 0; + const isExpanded = expandedItems[itemId] || false; + + return ( + + 0 ? `1px solid ${theme.palette.divider}` : 'none', + ml: level > 0 ? 1 : 0 + }} + > + + {hasChildren ? : } + + + {hasChildren && ( + onToggleItem(itemId)}> + {isExpanded ? : } + + )} + + + {hasChildren && ( + + + + )} + + ); + })} + + ); +} diff --git a/easy-dataset-main/components/text-split/components/DomainTreeActionDialog.js b/easy-dataset-main/components/text-split/components/DomainTreeActionDialog.js new file mode 100644 index 0000000..2ef8bcf --- /dev/null +++ b/easy-dataset-main/components/text-split/components/DomainTreeActionDialog.js @@ -0,0 +1,101 @@ +'use client'; + +import { useState } from 'react'; +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + Radio, + RadioGroup, + FormControlLabel, + FormControl, + Typography +} from '@mui/material'; +import { useTranslation } from 'react-i18next'; + +/** + * 领域树操作选择对话框 + * 提供三种选项:修订领域树、重建领域树、不更改领域树 + */ +export default function DomainTreeActionDialog({ open, onClose, onConfirm, isFirstUpload, action }) { + const { t } = useTranslation(); + const [value, setValue] = useState(isFirstUpload ? 'rebuild' : 'revise'); + + // 处理选项变更 + const handleChange = event => { + setValue(event.target.value); + }; + + // 确认选择 + const handleConfirm = () => { + onConfirm(value); + }; + + // 获取对话框标题 + const getDialogTitle = () => { + if (isFirstUpload) { + return t('textSplit.domainTree.firstUploadTitle'); + } + return action === 'upload' ? t('textSplit.domainTree.uploadTitle') : t('textSplit.domainTree.deleteTitle'); + }; + + return ( + + {getDialogTitle()} + + + + {!isFirstUpload && ( + } + label={ + <> + {t('textSplit.domainTree.reviseOption')} + + {t('textSplit.domainTree.reviseDesc')} + + + } + /> + )} + } + label={ + <> + {t('textSplit.domainTree.rebuildOption')} + + {t('textSplit.domainTree.rebuildDesc')} + + + } + /> + {!isFirstUpload && ( + } + label={ + <> + {t('textSplit.domainTree.keepOption')} + + {t('textSplit.domainTree.keepDesc')} + + + } + /> + )} + + + + + + + + + ); +} diff --git a/easy-dataset-main/components/text-split/components/DomainTreeView.js b/easy-dataset-main/components/text-split/components/DomainTreeView.js new file mode 100644 index 0000000..b0e2a1e --- /dev/null +++ b/easy-dataset-main/components/text-split/components/DomainTreeView.js @@ -0,0 +1,33 @@ +'use client'; + +import { Box } from '@mui/material'; +import { TreeView, TreeItem } from '@mui/lab'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import ChevronRightIcon from '@mui/icons-material/ChevronRight'; + +/** + * 领域知识树组件 + * @param {Object} props + * @param {Array} props.nodes - 树节点数组 + */ +export default function DomainTreeView({ nodes = [] }) { + if (!nodes || nodes.length === 0) return null; + + const renderTreeItems = nodes => { + return nodes.map((node, index) => ( + + {node.children && node.children.length > 0 && renderTreeItems(node.children)} + + )); + }; + + return ( + } + defaultExpandIcon={} + sx={{ flexGrow: 1, overflowY: 'auto' }} + > + {renderTreeItems(nodes)} + + ); +} diff --git a/easy-dataset-main/components/text-split/components/FileList.js b/easy-dataset-main/components/text-split/components/FileList.js new file mode 100644 index 0000000..e494e2b --- /dev/null +++ b/easy-dataset-main/components/text-split/components/FileList.js @@ -0,0 +1,1068 @@ +'use client'; + +import { + Box, + Typography, + List, + ListItem, + ListItemText, + IconButton, + Tooltip, + Divider, + CircularProgress, + Checkbox, + Button, + Dialog, + DialogTitle, + DialogContent, + DialogContentText, + DialogActions, + FormControlLabel, + Switch, + Pagination, + TextField, + InputAdornment, + Grid, + Alert +} from '@mui/material'; +import { + Visibility as VisibilityIcon, + Download, + Delete as DeleteIcon, + FilePresent as FileIcon, + Psychology as PsychologyIcon, + CheckBox as SelectAllIcon, + CheckBoxOutlineBlank as DeselectAllIcon, + Search as SearchIcon, + Clear as ClearIcon +} from '@mui/icons-material'; +import { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useAtomValue } from 'jotai'; +import { selectedModelInfoAtom } from '@/lib/store'; +import MarkdownViewDialog from '../MarkdownViewDialog'; +import GaPairsIndicator from '../../mga/GaPairsIndicator'; +import DomainTreeActionDialog from './DomainTreeActionDialog'; +import i18n from '@/lib/i18n'; +import { toast } from 'sonner'; + +export default function FileList({ + theme, + files = {}, + loading = false, + onDeleteFile, + sendToFileUploader, + projectId, + setPageLoading, + currentPage = 1, + onPageChange, + onRefresh, // 新增:刷新文件列表的回调函数 + isFullscreen = false // 新增参数,用于控制是否处于全屏状态 +}) { + const { t } = useTranslation(); + + // 现有的状态 + const [array, setArray] = useState([]); + const [viewDialogOpen, setViewDialogOpen] = useState(false); + const [viewContent, setViewContent] = useState(''); + + // 新增的批量生成GA对相关状态 + const [batchGenDialogOpen, setBatchGenDialogOpen] = useState(false); + const [generating, setGenerating] = useState(false); + const [genError, setGenError] = useState(null); + const [genResult, setGenResult] = useState(null); + const [projectModel, setProjectModel] = useState(null); + const [loadingModel, setLoadingModel] = useState(false); + const [appendMode, setAppendMode] = useState(false); + const [generationMode, setGenerationMode] = useState('ai'); // 'ai' 或 'manual' + const [manualGaPair, setManualGaPair] = useState({ + genreTitle: '', + genreDesc: '', + audienceTitle: '', + audienceDesc: '' + }); + + // 批量删除相关状态 + const [batchDeleteDialogOpen, setBatchDeleteDialogOpen] = useState(false); + const [domainTreeActionOpen, setDomainTreeActionOpen] = useState(false); + const [deleting, setDeleting] = useState(false); + + // 搜索相关状态 + const [searchTerm, setSearchTerm] = useState(''); + const [searchLoading, setSearchLoading] = useState(false); + + // 获取当前选中的模型信息 + const selectedModelInfo = useAtomValue(selectedModelInfoAtom); + + // 后端搜索功能 + const handleSearch = async searchValue => { + if (typeof onPageChange === 'function') { + setSearchLoading(true); + try { + // 调用父组件的页面变更函数,传递搜索参数 + await onPageChange(1, searchValue); // 搜索时重置到第一页 + } catch (error) { + console.error('搜索失败:', error); + } finally { + setSearchLoading(false); + } + } + }; + + // 防抖搜索 + useEffect(() => { + const timer = setTimeout(() => { + handleSearch(searchTerm); + }, 500); // 500ms 防抖 + + return () => clearTimeout(timer); + }, [searchTerm]); + + // 清空搜索 + const handleClearSearch = () => { + setSearchTerm(''); + // 清空搜索时立即触发搜索 + handleSearch(''); + }; + + const handleCheckboxChange = (fileId, isChecked) => { + setArray(prevArray => { + let newArray; + const stringFileId = String(fileId); + + if (isChecked) { + newArray = prevArray.includes(stringFileId) ? prevArray : [...prevArray, stringFileId]; + } else { + newArray = prevArray.filter(item => item !== stringFileId); + } + + if (typeof sendToFileUploader === 'function') { + sendToFileUploader(newArray); + } + return newArray; + }); + }; + + // 全选文件(包括所有页面的文件) + const handleSelectAll = async () => { + try { + // 获取项目中所有文件的ID + const response = await fetch(`/api/projects/${projectId}/files?getAllIds=true`); + if (!response.ok) { + throw new Error('获取文件列表失败'); + } + + const data = await response.json(); + const allFileIds = data.allFileIds || []; + + setArray(allFileIds); + if (typeof sendToFileUploader === 'function') { + sendToFileUploader(allFileIds); + } + } catch (error) { + console.error('全选文件失败:', error); + // 如果API调用失败,回退到选择当前页面的文件 + if (files?.data?.length > 0) { + const currentPageFileIds = files.data.map(file => String(file.id)); + setArray(currentPageFileIds); + if (typeof sendToFileUploader === 'function') { + sendToFileUploader(currentPageFileIds); + } + } + } + }; + // 取消全选 + const handleDeselectAll = () => { + setArray([]); + if (typeof sendToFileUploader === 'function') { + sendToFileUploader([]); + } + }; + + const handleCloseViewDialog = () => { + setViewDialogOpen(false); + }; + + // 刷新文本块列表 + const refreshTextChunks = () => { + if (typeof setPageLoading === 'function') { + setPageLoading(true); + setTimeout(() => { + // 可能需要调用父组件的刷新方法 + sendToFileUploader(array); + setPageLoading(false); + }, 500); + } + }; + + const handleViewContent = async fileId => { + getFileContent(fileId); + setViewDialogOpen(true); + }; + + const handleDownload = async (fileId, fileName) => { + setPageLoading(true); + const text = await getFileContent(fileId); + + // Modify the filename if it ends with .pdf + let downloadName = fileName || 'download.txt'; + if (downloadName.toLowerCase().endsWith('.pdf')) { + downloadName = downloadName.slice(0, -4) + '.md'; + } + + const blob = new Blob([text.content], { type: 'text/plain' }); + const url = URL.createObjectURL(blob); + + const a = document.createElement('a'); + a.href = url; + a.download = downloadName; + + document.body.appendChild(a); + a.click(); + + document.body.removeChild(a); + URL.revokeObjectURL(url); + + setPageLoading(false); + }; + + const getFileContent = async fileId => { + try { + const response = await fetch(`/api/projects/${projectId}/preview/${fileId}`); + if (!response.ok) { + throw new Error(t('textSplit.fetchChunksFailed')); + } + const data = await response.json(); + setViewContent(data); + return data; + } catch (error) { + console.error(t('textSplit.fetchChunksError'), error); + } + }; + + const formatFileSize = size => { + if (size < 1024) { + return size + 'B'; + } else if (size < 1024 * 1024) { + return (size / 1024).toFixed(2) + 'KB'; + } else if (size < 1024 * 1024 * 1024) { + return (size / 1024 / 1024).toFixed(2) + 'MB'; + } else { + return (size / 1024 / 1024 / 1024).toFixed(2) + 'GB'; + } + }; + + // 新增:获取项目特定的默认模型信息 + const fetchProjectModel = async () => { + try { + setLoadingModel(true); + + // 首先获取项目信息 + const response = await fetch(`/api/projects/${projectId}`); + if (!response.ok) { + throw new Error(t('gaPairs.fetchProjectInfoFailed', { status: response.status })); + } + + const projectData = await response.json(); + + // 获取模型配置 + const modelResponse = await fetch(`/api/projects/${projectId}/model-config`); + if (!modelResponse.ok) { + throw new Error(t('gaPairs.fetchModelConfigFailed', { status: modelResponse.status })); + } + + const modelConfigData = await modelResponse.json(); + + if (modelConfigData.data && Array.isArray(modelConfigData.data)) { + // 优先使用项目默认模型 + let targetModel = null; + + if (projectData.defaultModelConfigId) { + targetModel = modelConfigData.data.find(model => model.id === projectData.defaultModelConfigId); + } + + // 如果没有默认模型,使用第一个可用的模型 + if (!targetModel) { + targetModel = modelConfigData.data.find( + m => m.modelName && m.endpoint && (m.providerId === 'ollama' || m.apiKey) + ); + } + + if (targetModel) { + setProjectModel(targetModel); + } + } + } catch (error) { + console.error(t('gaPairs.fetchProjectModelError'), error); + } finally { + setLoadingModel(false); + } + }; + + // 新增:批量生成GA对的处理函数 + const handleBatchGenerateGAPairs = async () => { + if (array.length === 0) { + setGenError(t('gaPairs.selectAtLeastOneFile')); + return; + } + + // 如果是手动添加模式,验证手动输入的 GA 对 + if (generationMode === 'manual') { + if (!manualGaPair.genreTitle || !manualGaPair.audienceTitle) { + setGenError(t('gaPairs.manualGaPairRequired')); + return; + } + + try { + setGenerating(true); + setGenError(null); + setGenResult(null); + + const stringFileIds = array.map(id => String(id)); + + const requestData = { + fileIds: stringFileIds, + gaPair: manualGaPair, + appendMode: appendMode + }; + + const response = await fetch(`/api/projects/${projectId}/batch-add-manual-ga`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(requestData) + }); + + const responseText = await response.text(); + + if (!response.ok) { + const errorData = await response + .json() + .catch(() => ({ error: t('gaPairs.requestFailed', { status: response.status }) })); + throw new Error(errorData.error || t('gaPairs.requestFailed', { status: response.status })); + } + + const result = JSON.parse(responseText); + + if (result.success) { + setGenResult({ + total: result.data?.length || 0, + success: result.data?.filter(r => r.success).length || 0 + }); + + // 成功后清空选择状态和表单 + setArray([]); + if (typeof sendToFileUploader === 'function') { + sendToFileUploader([]); + } + setManualGaPair({ + genreTitle: '', + genreDesc: '', + audienceTitle: '', + audienceDesc: '' + }); + + // 发送全局刷新事件 + const successfulFileIds = result.data?.filter(item => item.success)?.map(item => String(item.fileId)) || []; + + if (successfulFileIds.length > 0) { + window.dispatchEvent( + new CustomEvent('refreshGaPairsIndicators', { + detail: { + projectId, + fileIds: successfulFileIds + } + }) + ); + } + } else { + setGenError(result.error || t('gaPairs.generationFailed')); + } + } catch (error) { + console.error(t('gaPairs.batchGenerationFailed'), error); + setGenError(t('gaPairs.generationError', { error: error.message || t('common.unknownError') })); + } finally { + setGenerating(false); + } + return; + } + + // AI 生成模式 + const modelToUse = projectModel || selectedModelInfo; + + if (!modelToUse || !modelToUse.id) { + setGenError(t('gaPairs.noDefaultModel')); + return; + } + + // 检查模型配置是否完整 + if (!modelToUse.modelName || !modelToUse.endpoint) { + setGenError('模型配置不完整,请检查模型设置'); + return; + } + + // 检查API密钥(除了ollama模型) + if (modelToUse.providerId !== 'ollama' && !modelToUse.apiKey) { + setGenError(t('gaPairs.missingApiKey')); + return; + } + + try { + setGenerating(true); + setGenError(null); + setGenResult(null); + + const stringFileIds = array.map(id => String(id)); + + // 获取当前语言环境 + const currentLanguage = i18n.language === 'en' ? 'en' : '中文'; + + const requestData = { + fileIds: stringFileIds, + modelConfigId: modelToUse.id, + language: currentLanguage, + appendMode: appendMode + }; + + const response = await fetch(`/api/projects/${projectId}/batch-generateGA`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(requestData) + }); + + const responseText = await response.text(); + + if (!response.ok) { + const errorData = await response + .json() + .catch(() => ({ error: t('gaPairs.requestFailed', { status: response.status }) })); + throw new Error(errorData.error || t('gaPairs.requestFailed', { status: response.status })); + } + + const result = JSON.parse(responseText); + + if (result.success) { + setGenResult({ + total: result.data?.length || 0, + success: result.data?.filter(r => r.success).length || 0 + }); + + // 成功后清空选择状态 + setArray([]); + if (typeof sendToFileUploader === 'function') { + sendToFileUploader([]); + } + + console.log(t('gaPairs.batchGenerationSuccess', { count: result.summary?.success || 0 })); + + //发送全局刷新事件 + const successfulFileIds = result.data?.filter(item => item.success)?.map(item => String(item.fileId)) || []; + + if (successfulFileIds.length > 0) { + window.dispatchEvent( + new CustomEvent('refreshGaPairsIndicators', { + detail: { + projectId, + fileIds: successfulFileIds + } + }) + ); + } + } else { + setGenError(result.error || t('gaPairs.generationFailed')); + } + } catch (error) { + console.error(t('gaPairs.batchGenerationFailed'), error); + setGenError(t('gaPairs.generationError', { error: error.message || t('common.unknownError') })); + } finally { + setGenerating(false); + } + }; + + // 新增:打开批量生成对话框 + const openBatchGenDialog = () => { + // 如果没有选中文件,自动选中所有文件 + if (array.length === 0 && files?.data?.length > 0) { + const allFileIds = files.data.map(file => String(file.id)); + setArray(allFileIds); + if (typeof sendToFileUploader === 'function') { + sendToFileUploader(allFileIds); + } + } + + // 获取项目模型配置 + fetchProjectModel(); + setBatchGenDialogOpen(true); + }; + + // 新增:关闭批量生成对话框 + const closeBatchGenDialog = () => { + setBatchGenDialogOpen(false); + setGenError(null); + setGenResult(null); + setAppendMode(false); // 重置追加模式 + }; + + // 批量删除处理函数 - 第一步:打开确认对话框 + const handleBatchDelete = () => { + if (array.length === 0) { + return; + } + setBatchDeleteDialogOpen(true); + }; + + // 确认批量删除 - 第二步:打开领域树选择对话框 + const confirmBatchDelete = () => { + setBatchDeleteDialogOpen(false); + + // 检查是否还有其他文件 + const remainingFilesCount = files.total - array.length; + + // 如果删除后没有文件了,直接执行删除(keep 模式) + if (remainingFilesCount === 0) { + executeBatchDelete('keep'); + return; + } + + // 否则打开领域树操作选择对话框 + setDomainTreeActionOpen(true); + }; + + // 处理领域树操作选择 + const handleDomainTreeAction = action => { + setDomainTreeActionOpen(false); + executeBatchDelete(action); + }; + + // 执行批量删除 - 第三步:实际删除操作 + const executeBatchDelete = async domainTreeAction => { + if (array.length === 0) { + return; + } + + setDeleting(true); + // 设置页面 loading 状态 + if (typeof setPageLoading === 'function') { + setPageLoading(true); + } + + try { + const response = await fetch(`/api/projects/${projectId}/batch-delete-files`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + fileIds: array, + domainTreeAction, + model: selectedModelInfo || {}, + language: i18n.language === 'en' ? 'English' : '中文' + }) + }); + + if (!response.ok) { + throw new Error('批量删除失败'); + } + + const result = await response.json(); + + // 清空选择 + setArray([]); + if (typeof sendToFileUploader === 'function') { + sendToFileUploader([]); + } + + // 刷新文件列表 + if (typeof onRefresh === 'function') { + await onRefresh(); + } else if (typeof onPageChange === 'function') { + // 回退方案:如果没有 onRefresh,使用 onPageChange + await onPageChange(1); + } + + toast.success( + t('textSplit.batchDeleteSuccess', { + count: result.deletedCount || array.length, + defaultValue: `成功删除 ${result.deletedCount || array.length} 个文件` + }) + ); + } catch (error) { + console.error('批量删除文件失败:', error); + toast.error(t('textSplit.batchDeleteFailed', { defaultValue: '批量删除失败' })); + } finally { + setDeleting(false); + // 清除页面 loading 状态 + if (typeof setPageLoading === 'function') { + setPageLoading(false); + } + } + }; + + // 取消批量删除 + const cancelBatchDelete = () => { + setBatchDeleteDialogOpen(false); + }; + + return ( + + {/* 标题和按钮区域 */} + + {/* 第一行:标题和按钮 */} + 0 ? 2 : 0 + }} + > + {t('textSplit.uploadedDocuments', { count: files.total })} + + {/* 批量操作按钮 */} + {files.total > 0 && ( + + {/* 全选/取消全选按钮 */} + {array.length === files.total ? ( + + ) : ( + + )} + + {/* 批量删除按钮 */} + {array.length > 0 && ( + + )} + + {/* 批量生成GA对按钮 */} + + + )} + + + {/* 第二行:搜索框 - 在全屏展示时显示,或者有搜索内容时显示 */} + {isFullscreen && (files.total > 0 || searchTerm) && ( + + setSearchTerm(e.target.value)} + InputProps={{ + startAdornment: ( + + + + ), + endAdornment: searchTerm && ( + + + + + + ) + }} + sx={{ width: '100%', maxWidth: 400 }} + /> + {(searchTerm || searchLoading) && ( + + {searchLoading + ? '搜索中...' + : searchTerm + ? t('textSplit.searchResults', { + count: files?.data?.length || 0, + total: files.total, + defaultValue: `找到 ${files?.data?.length || 0} 个文件(共 ${files.total} 个)` + }) + : null} + + )} + + )} + + + {loading ? ( + + + + ) : files.total === 0 ? ( + + + {searchTerm + ? // 搜索无结果 + t('textSplit.noSearchResults', { + searchTerm, + defaultValue: `未找到包含 "${searchTerm}" 的文件` + }) + : // 真的没有上传文件 + t('textSplit.noFilesUploaded', { + defaultValue: '暂未上传文件' + })} + + + ) : !files?.data || files.data.length === 0 ? ( + + + {searchTerm + ? // 搜索有结果但当前页没数据 + t('textSplit.noResultsOnCurrentPage', { + defaultValue: '当前页面没有搜索结果,请返回第一页查看' + }) + : // 当前页没数据但总数不为0 + t('textSplit.noDataOnCurrentPage', { + defaultValue: '当前页面没有数据' + })} + + + ) : ( + <> + + {files?.data?.map((file, index) => ( + + + {/* 文件信息区域 */} + + + + handleViewContent(file.id)} + primary={ + + {file.fileName} + + } + secondary={ + + {`${formatFileSize(file.size)} · ${new Date(file.createAt).toLocaleString()}`} + + } + /> + + + + {/* 操作按钮区域 */} + + handleCheckboxChange(file.id, e.target.checked)} + /> + + + handleDownload(file.id, file.fileName)}> + + + + + onDeleteFile(file.id, file.fileName)}> + + + + + + {index < files.data.length - 1 && } + + ))} + + + {/* 分页控件 */} + {files.total > 10 && ( + + onPageChange && onPageChange(page)} + color="primary" + showFirstButton + showLastButton + /> + + )} + + )} + + {/* 现有的文本块详情对话框 */} + + + {/* 新增:批量生成GA对对话框 */} + + {t('gaPairs.batchGenerateTitle')} + + {!genResult && ( + + {t('gaPairs.batchGenerateDescription', { count: array.length })} + + {/* 生成方式选择 */} + + + {t('gaPairs.generationMode')} + + setGenerationMode(e.target.checked ? 'manual' : 'ai')} + color="primary" + /> + } + label={generationMode === 'manual' ? t('gaPairs.manualAddMode') : t('gaPairs.aiGenerateMode')} + /> + + + {/* AI 生成模式:显示模型信息 */} + {generationMode === 'ai' && ( + <> + {loadingModel ? ( + + + {t('gaPairs.loadingProjectModel')} + + ) : projectModel ? ( + + + {t('gaPairs.usingModel')}:{' '} + + {projectModel.providerName}: {projectModel.modelName} + + + + ) : ( + + + {t('gaPairs.noDefaultModel')} + + + )} + + )} + + {/* 手动添加模式:显示输入表单 */} + {generationMode === 'manual' && ( + + + + setManualGaPair({ ...manualGaPair, genreTitle: e.target.value })} + required + /> + + + setManualGaPair({ ...manualGaPair, genreDesc: e.target.value })} + multiline + rows={2} + /> + + + setManualGaPair({ ...manualGaPair, audienceTitle: e.target.value })} + required + /> + + + setManualGaPair({ ...manualGaPair, audienceDesc: e.target.value })} + multiline + rows={2} + /> + + + + )} + + {/* 追加模式选择 */} + + setAppendMode(e.target.checked)} color="primary" /> + } + label={`${t('gaPairs.appendMode')}(${t('gaPairs.appendModeDescription')})`} + /> + + + )} + + {genError && ( + + + {genError} + + + )} + + {genResult && ( + + + {t('gaPairs.batchGenCompleted', { success: genResult.success, total: genResult.total })} + + + )} + + + + {!genResult && ( + + )} + + + + {/* 批量删除确认对话框 */} + + {t('textSplit.batchDeleteTitle')} + + + {t('textSplit.batchDeleteConfirm', { + count: array.length, + defaultValue: `确定要删除选中的 ${array.length} 个文件吗?此操作不可恢复。` + })} + + + + {t('textSplit.deleteFileWarning')} + + + + • {t('textSplit.deleteFileWarningChunks')} + + + • {t('textSplit.deleteFileWarningQuestions')} + + + • {t('textSplit.deleteFileWarningDatasets')} + + + + + + + + + + + {/* 领域树操作选择对话框 */} + setDomainTreeActionOpen(false)} + onConfirm={handleDomainTreeAction} + isFirstUpload={false} + action="delete" + /> + + ); +} diff --git a/easy-dataset-main/components/text-split/components/FileLoadingProgress.js b/easy-dataset-main/components/text-split/components/FileLoadingProgress.js new file mode 100644 index 0000000..aeb1403 --- /dev/null +++ b/easy-dataset-main/components/text-split/components/FileLoadingProgress.js @@ -0,0 +1,208 @@ +'use client'; + +import { Box, Typography, keyframes, Paper } from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import { handleLongFileName } from '@/lib/file/file-process'; +import { useState, useEffect } from 'react'; + +// 定义动画效果 +const pulse = keyframes` + 0% { + box-shadow: 0 0 0 0 rgba(32, 76, 255, 0.2); + } + 70% { + box-shadow: 0 0 0 15px rgba(32, 76, 255, 0); + } + 100% { + box-shadow: 0 0 0 0 rgba(32, 76, 255, 0); + } +`; + +const rotateAnimation = keyframes` + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +`; + +const shimmer = keyframes` + 0% { background-position: -200% 0; } + 100% { background-position: 200% 0; } +`; + +/** + * 文件处理进度展示组件 - 美化版 + * + * @param {Object} props + * @param {Object} props.fileTask - 文件处理任务信息 + */ +export default function FileLoadingProgress({ fileTask }) { + const { t } = useTranslation(); + const [animationStep, setAnimationStep] = useState(0); + + // 创建动态效果 + useEffect(() => { + const interval = setInterval(() => { + setAnimationStep(prev => (prev + 1) % 4); + }, 600); + return () => clearInterval(interval); + }, []); + + if (!fileTask) { + return null; + } + + const pageProgress = (fileTask.current.processedPage / fileTask.current.totalPage) * 100; + const filesProgress = (fileTask.processedFiles / fileTask.totalFiles) * 100; + + // 生成进度指示器文本 + const getProgressIndicator = () => { + const dots = '.'; + return dots.repeat(animationStep + 1); + }; + + return ( + + {/* 背景动画元素 */} + + + {/* 主标题 */} + + {t('textSplit.pdfProcessingLoading')} + {getProgressIndicator()} + + + {/* 处理进度显示区域 */} + + {/* 当前文件进度 */} + + + {/* 总文件进度 */} + + + + ); +} + +/** + * 进度条区域组件 + */ +function ProgressSection({ label, progress, color, mt = 0 }) { + return ( + + + + {label} + + + {Math.round(progress)}% + + + + {/* 自定义进度条 */} + + + + + ); +} diff --git a/easy-dataset-main/components/text-split/components/PdfProcessingDialog.js b/easy-dataset-main/components/text-split/components/PdfProcessingDialog.js new file mode 100644 index 0000000..782cffd --- /dev/null +++ b/easy-dataset-main/components/text-split/components/PdfProcessingDialog.js @@ -0,0 +1,188 @@ +'use client'; + +import { + Dialog, + DialogTitle, + DialogContent, + Card, + CardContent, + Typography, + Box, + Stack, + FormControl, + InputLabel, + Select, + MenuItem +} from '@mui/material'; +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { styled } from '@mui/material/styles'; +import ArticleOutlinedIcon from '@mui/icons-material/ArticleOutlined'; +import ScienceOutlinedIcon from '@mui/icons-material/ScienceOutlined'; +import LaunchOutlinedIcon from '@mui/icons-material/LaunchOutlined'; +import SmartToyOutlinedIcon from '@mui/icons-material/SmartToyOutlined'; +import ChangeCircleOutlinedIcon from '@mui/icons-material/ChangeCircleOutlined'; + +const StyledCard = styled(Card)(({ theme, disabled }) => ({ + cursor: disabled ? 'not-allowed' : 'pointer', + opacity: disabled ? 0.6 : 1, + transition: 'transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out', + '&:hover': disabled + ? {} + : { + transform: 'translateY(-4px)', + boxShadow: theme.shadows[4] + } +})); + +const OptionCard = ({ + icon, + title, + description, + disabled, + onClick, + selected, + isVisionEnabled, + visionModels, + selectorName, + handleSettingChange, + selectedViosnModel +}) => ( + + + + {icon} + + {title} + + + {description} + + {isVisionEnabled && ( + + {selectorName} + + + )} + + + +); + +export default function PdfProcessingDialog({ + open, + onClose, + onRadioChange, + value, + taskSettings, + visionModels, + selectedViosnModel, + setSelectedViosnModel +}) { + const { t } = useTranslation(); + + //检查配置中是否启用MinerU + const isMinerUEnabled = taskSettings && taskSettings.minerUToken ? true : false; + + const isMinerULocalEnabled = taskSettings && taskSettings.minerULocalUrl ? true : false; + + //检查配置中是否启用Vision策略 + const isVisionEnabled = visionModels.length > 0 ? true : false; + + //用于传递到父组件,显示当前选中的模型 + let selectedModel = selectedViosnModel; + + const handleOptionClick = optionValue => { + if (optionValue === 'mineru-web') { + window.open('https://mineru.net/OpenSourceTools/Extractor', '_blank'); + } else { + onRadioChange({ target: { value: optionValue, selectedVision: selectedModel } }); + onClose(); + } + }; + + // 处理设置变更 + const handleSettingChange = e => { + const { value } = e.target; + selectedModel = value; + setSelectedViosnModel(value); + }; + + return ( + + {t('textSplit.pdfProcess')} + + + } + title={t('textSplit.basicPdfParsing')} + description={t('textSplit.basicPdfParsingDesc')} + onClick={() => handleOptionClick('default')} + selected={value === 'default'} + /> + } + title="MinerU API" + description={isMinerUEnabled ? t('textSplit.mineruApiDesc') : t('textSplit.mineruApiDescDisabled')} + disabled={!isMinerUEnabled} + onClick={() => handleOptionClick('mineru')} + selected={value === 'mineru'} + /> + } + title="MinerU Local" + description={isMinerULocalEnabled ? t('textSplit.mineruLocalDesc') : t('textSplit.mineruLocalDisabled')} + disabled={!isMinerULocalEnabled} + onClick={() => handleOptionClick('mineru-local')} + selected={value === 'mineru-local'} + /> + } + title={t('textSplit.mineruWebPlatform')} + description={t('textSplit.mineruWebPlatformDesc')} + onClick={() => handleOptionClick('mineru-web')} + /> + } + title={t('textSplit.customVisionModel')} + description={t('textSplit.customVisionModelDesc')} + disabled={!isVisionEnabled} + onClick={() => handleOptionClick('vision')} + selected={value === 'vision'} + isVisionEnabled={isVisionEnabled} + visionModels={visionModels} + selectorName={t('settings.vision')} + selectedViosnModel={selectedViosnModel} + handleSettingChange={handleSettingChange} + /> + + + + ); +} diff --git a/easy-dataset-main/components/text-split/components/TabPanel.js b/easy-dataset-main/components/text-split/components/TabPanel.js new file mode 100644 index 0000000..2f2b7e5 --- /dev/null +++ b/easy-dataset-main/components/text-split/components/TabPanel.js @@ -0,0 +1,24 @@ +'use client'; + +import { Box } from '@mui/material'; + +/** + * 标签页面板组件 + * @param {Object} props + * @param {number} props.value - 当前激活的标签索引 + * @param {number} props.index - 当前面板对应的索引 + * @param {ReactNode} props.children - 子组件 + */ +export default function TabPanel({ value, index, children }) { + return ( + + ); +} diff --git a/easy-dataset-main/components/text-split/components/UploadArea.js b/easy-dataset-main/components/text-split/components/UploadArea.js new file mode 100644 index 0000000..3e98cfd --- /dev/null +++ b/easy-dataset-main/components/text-split/components/UploadArea.js @@ -0,0 +1,207 @@ +'use client'; + +import { + Box, + Button, + Typography, + List, + ListItem, + ListItemText, + Divider, + CircularProgress, + Tooltip +} from '@mui/material'; +import UploadFileIcon from '@mui/icons-material/UploadFile'; +import DeleteIcon from '@mui/icons-material/Delete'; +import { alpha } from '@mui/material/styles'; +import { useTranslation } from 'react-i18next'; +import React, { useRef, useState } from 'react'; + +export default function UploadArea({ + theme, + files, + uploading, + uploadedFiles, + onFileSelect, + onRemoveFile, + onUpload, + selectedModel +}) { + const { t } = useTranslation(); + const [dragActive, setDragActive] = useState(false); + const inputRef = useRef(null); + + // 拖拽进入 + const handleDragOver = e => { + e.preventDefault(); + e.stopPropagation(); + if (!dragActive) setDragActive(true); + }; + // 拖拽离开 + const handleDragLeave = e => { + e.preventDefault(); + e.stopPropagation(); + setDragActive(false); + }; + // 拖拽释放 + const handleDrop = e => { + e.preventDefault(); + e.stopPropagation(); + setDragActive(false); + if (!selectedModel?.id || uploading) return; + const files = e.dataTransfer.files; + if (files && files.length > 0) { + // 构造一个模拟的 event 以复用 onFileSelect + const event = { target: { files } }; + onFileSelect(event); + } + }; + + return ( + + {dragActive && ( + + + + + {t('textSplit.dragToUpload', { defaultValue: '拖拽文件到此处上传' })} + + + + )} + + {t('textSplit.uploadNewDocument')} + + + + + + + + + + {uploadedFiles.total > 0 ? t('textSplit.mutilFileMessage') : t('textSplit.supportedFormats')} + + + {files.length > 0 && ( + + + {t('textSplit.selectedFiles', { count: files.length })} + + + + {files.map((file, index) => ( + + } + onClick={() => onRemoveFile(index)} + disabled={uploading} + > + {t('common.delete')} + + } + > + + + {index < files.length - 1 && } + + ))} + + + + + + + + + + + )} + + ); +} diff --git a/easy-dataset-main/constant/index.js b/easy-dataset-main/constant/index.js new file mode 100644 index 0000000..9a41497 --- /dev/null +++ b/easy-dataset-main/constant/index.js @@ -0,0 +1,15 @@ +/** + * 全局常量 + */ + +export const FILE = { + MAX_FILE_SIZE: 300 * 1024 * 1024 // 300MB in bytes +}; + +export const TASK = { + STATUS: { + PROCESSING: 0, + COMPLETED: 1, + FAILED: 2 + } +}; diff --git a/easy-dataset-main/constant/model.js b/easy-dataset-main/constant/model.js new file mode 100644 index 0000000..dfc2351 --- /dev/null +++ b/easy-dataset-main/constant/model.js @@ -0,0 +1,83 @@ +export const MODEL_PROVIDERS = [ + { + id: 'ollama', + name: 'Ollama', + defaultEndpoint: 'http://127.0.0.1:11434/api', + defaultModels: [] + }, + { + id: 'openai', + name: 'OpenAI', + defaultEndpoint: 'https://api.openai.com/v1/', + defaultModels: ['gpt-4o', 'gpt-4o-mini', 'o1-mini'] + }, + { + id: 'siliconcloud', + name: '硅基流动', + defaultEndpoint: 'https://api.siliconflow.cn/v1/', + defaultModels: [ + 'deepseek-ai/DeepSeek-R1', + 'deepseek-ai/DeepSeek-V3', + 'Qwen2.5-7B-Instruct', + 'meta-llama/Llama-3.3-70B-Instruct' + ] + }, + { + id: 'deepseek', + name: 'DeepSeek', + defaultEndpoint: 'https://api.deepseek.com/v1/', + defaultModels: ['deepseek-chat', 'deepseek-reasoner'] + }, + { + id: '302ai', + name: '302.AI', + defaultEndpoint: 'https://api.302.ai/v1/', + defaultModels: ['Doubao-pro-128k', 'deepseek-r1', 'kimi-latest', 'qwen-max'] + }, + { + id: 'zhipu', + name: '智谱AI', + defaultEndpoint: 'https://open.bigmodel.cn/api/paas/v4/', + defaultModels: ['glm-4-flash', 'glm-4-flashx', 'glm-4-plus', 'glm-4-long'] + }, + { + id: 'Doubao', + name: '火山引擎', + defaultEndpoint: 'https://ark.cn-beijing.volces.com/api/v3/', + defaultModels: [] + }, + { + id: 'groq', + name: 'Groq', + defaultEndpoint: 'https://api.groq.com/openai', + defaultModels: ['Gemma 7B', 'LLaMA3 8B', 'LLaMA3 70B'] + }, + { + id: 'grok', + name: 'Grok', + defaultEndpoint: 'https://api.x.ai/v1', + defaultModels: ['Grok'] + }, + { + id: 'OpenRouter', + name: 'OpenRouter', + defaultEndpoint: 'https://openrouter.ai/api/v1/', + defaultModels: [ + 'google/gemma-2-9b-it:free', + 'meta-llama/llama-3-8b-instruct:free', + 'microsoft/phi-3-mini-128k-instruct:free' + ] + }, + { + id: 'alibailian', + name: '阿里云百炼', + defaultEndpoint: 'https://dashscope.aliyuncs.com/compatible-mode/v1', + defaultModels: ['qwen-max-latest', 'qwen-max-2025-01-25'] + } +]; + +export const DEFAULT_MODEL_SETTINGS = { + temperature: 0.7, + maxTokens: 8192, + topP: 0.9 +}; diff --git a/easy-dataset-main/constant/setting.js b/easy-dataset-main/constant/setting.js new file mode 100644 index 0000000..894c631 --- /dev/null +++ b/easy-dataset-main/constant/setting.js @@ -0,0 +1,24 @@ +// 默认项目任务配置 +export const DEFAULT_SETTINGS = { + textSplitMinLength: 2500, + textSplitMaxLength: 4000, + questionGenerationLength: 240, + questionMaskRemovingProbability: 60, + huggingfaceToken: '', + concurrencyLimit: 5, + visionConcurrencyLimit: 5, + // 多轮对话数据集默认配置 + multiTurnSystemPrompt: '', + multiTurnScenario: '', + multiTurnRounds: 3, + multiTurnRoleA: '', + multiTurnRoleB: '', + // 测试集生成配置 + evalQuestionTypeRatios: { + true_false: 1, + single_choice: 1, + multiple_choice: 1, + short_answer: 1, + open_ended: 1 + } +}; diff --git a/easy-dataset-main/constant/sites.json b/easy-dataset-main/constant/sites.json new file mode 100644 index 0000000..f7907fc --- /dev/null +++ b/easy-dataset-main/constant/sites.json @@ -0,0 +1,286 @@ +[ + { + "name": "HuggingFace开源数据集", + "link": "https://huggingface.co/datasets", + "image": "/imgs/huggingface.png", + "description": "提供了丰富的开源数据集,涵盖多种领域和语言,支持自然语言处理、计算机视觉等多种任务。", + "labels": ["热门推荐", "多模态"] + }, + { + "name": "OpenDataLab开源数据集", + "link": "https://opendatalab.com/", + "image": "/imgs/opendatalab.png", + "description": "致力于收集和整理高质量的开源数据集,方便研究人员和开发者使用。", + "labels": ["热门推荐"] + }, + { + "name": "谷歌开源数据集", + "link": "https://datasetsearch.research.google.com", + "image": "/imgs/google.png", + "description": "谷歌提供的数据集搜索工具,可帮助用户找到来自不同来源的公开数据集。", + "labels": ["热门推荐", "英文资源"] + }, + { + "name": "kaggle开源数据集", + "link": "https://www.kaggle.com/datasets", + "image": "/imgs/kaggle.png", + "description": "Kaggle平台上的开源数据集,涉及各种领域和任务,常用于数据竞赛和实践。", + "labels": ["热门推荐", "英文资源"] + }, + { + "name": "ModelScope开源数据集", + "link": "https://modelscope.cn/datasets", + "image": "/imgs/modelscope.png", + "description": "提供了多种开源数据集,支持模型的训练和评估,涵盖多个领域。", + "labels": ["中文资源"] + }, + { + "name": "LUGE千言开源数据集", + "link": "https://www.luge.ai/", + "image": "/imgs/lluga.png", + "description": "专注于中文领域的开源数据集,包括自然语言处理、语音识别等方向。", + "labels": ["中文资源"] + }, + { + "name": "GitHub开源数据集", + "link": "https://github.com/awesomedata/awesome-public-datasets", + "image": "/imgs/github.png", + "description": "在GitHub上整理的优秀的公开数据集资源,涉及多个领域和方向。", + "labels": ["热门推荐"] + }, + { + "name": "AWS亚马逊开源数据集", + "link": "https://registry.opendata.aws/", + "image": "/imgs/aws.png", + "description": "提供了大量的公开数据集,涵盖多个领域,可在亚马逊云服务上直接访问和使用。", + "labels": ["英文资源"] + }, + { + "name": "TIANCHI天池开源数据集", + "link": "https://tianchi.aliyun.com/dataset/", + "description": "阿里云天池平台提供的开源数据集,涵盖多个领域的竞赛数据和公开数据。", + "labels": ["中文资源"] + }, + { + "name": "UCI开源数据集", + "link": "https://archive.ics.uci.edu/datasets", + "description": "加州大学欧文分校提供的开源数据集,涵盖多个领域,常用于机器学习研究。", + "labels": ["研究数据", "英文资源"] + }, + { + "name": "计算机视觉开源数据集", + "link": "https://visualdata.io/discovery", + "description": "专注于计算机视觉领域的开源数据集,支持相关模型的训练和评估。", + "labels": ["多模态"] + }, + { + "name": "BAAI开源数据集", + "link": "https://data.baai.ac.cn/data", + "description": "北京智源人工智能研究院提供的开源数据集,涵盖多个领域,支持大模型的训练。", + "labels": ["中文资源", "研究数据"] + }, + { + "name": "百度飞桨开源数据集", + "link": "https://aistudio.baidu.com/datasetoverview", + "description": "百度飞桨平台提供的开源数据集,支持深度学习模型的训练和评估。", + "labels": ["中文资源"] + }, + { + "name": "启智开源数据集", + "link": "https://openi.pcl.ac.cn/explore/datasets", + "description": "开源平台提供的多种开源数据集,涵盖多个领域,支持模型的训练和研究。", + "labels": ["中文资源"] + }, + { + "name": "LAION-2B-en", + "link": "https://laion.ai/", + "description": "包含25亿张图像和相应的文本描述,适用于多模态模型的训练。", + "labels": ["多模态"] + }, + { + "name": "Common Crawl", + "link": "https://commoncrawl.org/", + "description": "提供了大量的网页爬取数据,可用于语言模型的训练。", + "labels": ["英文资源", "研究数据"] + }, + { + "name": "The Pile", + "link": "https://github.com/EleutherAI/the-pile", + "description": "由多个数据集组成的大型语言模型训练数据集,涵盖多种文本类型。", + "labels": ["研究数据", "英文资源"] + }, + { + "name": "MuJoCo", + "link": "https://mujoco.org/", + "description": "用于物理模拟的机器人交互数据集,适用于强化学习和机器人控制任务。", + "labels": ["多模态"] + }, + { + "name": "Robotics Datasets", + "link": "https://roboticsdatasets.github.io/", + "description": "提供了多种机器人交互数据集,支持机器人学习和控制任务。", + "labels": ["多模态"] + }, + { + "name": "Atari Games", + "link": "https://www.atari.com/games", + "description": "经典的Atari游戏数据集,用于强化学习算法的基准测试。", + "labels": ["多模态"] + }, + { + "name": "Web-crawled Interactions", + "link": "https://commoncrawl.org/", + "description": "从网络平台上爬取的用户行为数据,适用于训练交互式代理。", + "labels": ["研究数据"] + }, + { + "name": "AI2 ARC Dataset", + "link": "https://allenai.org/data/arc", + "description": "用于评估AI常识推理和解决问题能力的多选题数据集。", + "labels": ["研究数据"] + }, + { + "name": "Speech Commands Dataset", + "link": "https://www.tensorflow.org/datasets/catalog/speech_commands", + "description": "包含数千个语音命令的音频数据集,适用于语音识别任务。", + "labels": ["多模态"] + }, + { + "name": "Environmental Audio Datasets", + "link": "https://www.tensorflow.org/datasets/catalog/audioset", + "description": "包含环境声音事件的音频数据集,适用于音频场景分类任务。", + "labels": ["多模态"] + }, + { + "name": "COVID-19 Open Research Dataset", + "link": "https://www.kaggle.com/allenai/cord-19-research-challenge", + "description": "包含45,000篇关于COVID-19的学术文章,适用于医疗AI研究。", + "labels": ["研究数据"] + }, + { + "name": "Waymo Open Dataset", + "link": "https://waymo.com/open/", + "description": "由Waymo发布的最多样化的自动驾驶数据集。", + "labels": ["多模态"] + }, + { + "name": "Labelme", + "link": "http://labelme.csail.mit.edu/Release3.0/", + "description": "包含大量标注图像的数据集,适用于计算机视觉任务。", + "labels": ["多模态"] + }, + { + "name": "Stanford Dogs Dataset", + "link": "http://vision.stanford.edu/aditya86/ImageNetDogs/", + "description": "包含20,500多张不同狗品种的图像数据集。", + "labels": ["多模态"] + }, + { + "name": "Flickr Audio Caption Corpus", + "link": "https://www.multispeech.org/2018/challenge.html", + "description": "包含超过40,000个口语描述的音频数据集。", + "labels": ["多模态"] + }, + { + "name": "Data.gov", + "link": "https://www.data.gov/", + "description": "美国政府开放数据平台,涵盖农业、气候、教育、能源等领域的公开数据集。", + "labels": ["政府数据", "英文资源"] + }, + { + "name": "Eurostat", + "link": "https://ec.europa.eu/eurostat", + "description": "欧盟统计局提供的经济、人口、社会等多领域统计数据。", + "labels": ["研究数据", "英文资源"] + }, + { + "name": "ImageNet", + "link": "https://www.image-net.org/", + "description": "大型图像数据集,包含数百万张标注图像,广泛用于计算机视觉任务。", + "labels": ["多模态", "计算机视觉"] + }, + { + "name": "COCO Dataset", + "link": "https://cocodataset.org/", + "description": "通用物体识别与分割数据集,适用于目标检测和图像分割任务。", + "labels": ["多模态"] + }, + { + "name": "World Bank Open Data", + "link": "https://data.worldbank.org/", + "description": "世界银行提供的全球经济指标、发展数据及统计报告。", + "labels": ["研究数据", "英文资源"] + }, + { + "name": "NASA Earth Data", + "link": "https://earthdata.nasa.gov/", + "description": "NASA地球科学数据,涵盖气候、地质、环境等领域的遥感数据。", + "labels": ["研究数据", "地球科学"] + }, + { + "name": "Yelp Open Dataset", + "link": "https://www.yelp.com/dataset", + "description": "包含商家信息、用户评论和图片数据,适用于商业分析和NLP任务。", + "labels": ["商业", "英文资源"] + }, + { + "name": "CIFAR-10/100", + "link": "https://www.cs.toronto.edu/~kriz/cifar.html", + "description": "经典的小规模图像分类数据集,包含10或100个类别的标注图像。", + "labels": ["多模态"] + }, + { + "name": "Global Health Observatory (WHO)", + "link": "https://www.who.int/data/gho", + "description": "世界卫生组织提供的全球公共卫生统计数据,包括疾病、营养等主题。", + "labels": ["医疗健康", "研究数据"] + }, + { + "name": "arXiv Dataset", + "link": "https://www.kaggle.com/Cornell-University/arxiv", + "description": "包含数百万篇arXiv学术论文的元数据和全文,适用于文本挖掘研究。", + "labels": ["研究数据", "英文资源"] + }, + { + "name": "LibriSpeech", + "link": "https://www.openslr.org/12", + "description": "包含1000小时英语语音数据,适用于语音识别模型训练。", + "labels": ["多模态", "语音识别"] + }, + { + "name": "KITTI Vision Benchmark", + "link": "http://www.cvlibs.net/datasets/kitti/", + "description": "自动驾驶领域经典数据集,包含立体视觉、激光雷达等多模态数据。", + "labels": ["多模态", "自动驾驶"] + }, + { + "name": "Cityscapes Dataset", + "link": "https://www.cityscapes-dataset.com/", + "description": "城市街景语义分割数据集,支持自动驾驶和计算机视觉研究。", + "labels": ["多模态"] + }, + { + "name": "CDC Data", + "link": "https://data.cdc.gov/", + "description": "美国疾病控制与预防中心发布的公共卫生数据集,涵盖疾病追踪和健康统计。", + "labels": ["医疗健康", "政府数据"] + }, + { + "name": "OpenStreetMap", + "link": "https://www.openstreetmap.org/", + "description": "开源地理数据协作项目,提供全球范围的道路、建筑等地理信息数据。", + "labels": ["地理信息", "众包数据"] + }, + { + "name": "FiveThirtyEight Datasets", + "link": "https://data.fivethirtyeight.com/", + "description": "涵盖政治、体育、文化等领域的数据集,常用于数据新闻分析。", + "labels": ["社会趋势", "英文资源"] + }, + { + "name": "Human Protein Atlas", + "link": "https://www.proteinatlas.org/", + "description": "包含人体蛋白质分布的组织图像数据,支持生物医学研究。", + "labels": ["医疗健康", "研究数据"] + } +] diff --git a/easy-dataset-main/docker-compose.yml b/easy-dataset-main/docker-compose.yml new file mode 100644 index 0000000..8417bd3 --- /dev/null +++ b/easy-dataset-main/docker-compose.yml @@ -0,0 +1,10 @@ +services: + easy-dataset: + image: ghcr.io/conardli/easy-dataset + container_name: easy-dataset + ports: + - '1717:1717' + volumes: + - ./local-db:/app/local-db + - ./prisma:/app/prisma + restart: unless-stopped diff --git a/easy-dataset-main/docker-entrypoint.sh b/easy-dataset-main/docker-entrypoint.sh new file mode 100644 index 0000000..610b939 --- /dev/null +++ b/easy-dataset-main/docker-entrypoint.sh @@ -0,0 +1,70 @@ +#!/bin/sh +set -e + +# Colors for output +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +NC='\033[0m' # No Color + +# Define paths +PRISMA_DIR="/app/prisma" +PRISMA_TEMPLATE_DIR="/app/prisma-template" +DB_FILE="$PRISMA_DIR/db.sqlite" +LOCAL_DB_DIR="/app/local-db" + +echo "${GREEN}=== Easy Dataset Database Initialization ===${NC}" + +# Create prisma directory if it doesn't exist +if [ ! -d "$PRISMA_DIR" ]; then + echo "${YELLOW}Creating prisma directory...${NC}" + mkdir -p "$PRISMA_DIR" +fi + +# Check if database file exists +if [ ! -f "$DB_FILE" ]; then + echo "${YELLOW}Database file not found at: $DB_FILE${NC}" + + # Check if local-db has files (possible configuration issue) + if [ -d "$LOCAL_DB_DIR" ] && [ -n "$(ls -A $LOCAL_DB_DIR 2>/dev/null | grep -v 'empty.txt')" ]; then + echo "${YELLOW}Note: local-db contains files but database is missing.${NC}" + echo "${YELLOW}If you have existing data, ensure prisma volume is mounted.${NC}" + fi + + # Safety check: only initialize if directory is completely empty + if [ -z "$(ls -A $PRISMA_DIR 2>/dev/null)" ]; then + # Directory is completely empty - safe to initialize + echo "${GREEN}Prisma directory is empty. Initializing from template...${NC}" + + if [ -d "$PRISMA_TEMPLATE_DIR" ]; then + cp -r "$PRISMA_TEMPLATE_DIR"/* "$PRISMA_DIR/" + echo "${GREEN}Database initialized from template!${NC}" + else + echo "${YELLOW}No template found. Running prisma db push...${NC}" + cd /app + pnpm prisma db push --accept-data-loss + echo "${GREEN}Database created successfully!${NC}" + fi + else + # Directory is not empty but database is missing - error out + echo "${RED}ERROR: Database file missing but prisma directory is not empty!${NC}" + echo "${YELLOW}This may indicate a configuration problem.${NC}" + echo "" + echo "${YELLOW}Files in $PRISMA_DIR:${NC}" + ls -lh "$PRISMA_DIR" + echo "" + echo "${YELLOW}Please either:${NC}" + echo " 1. Remove all files in prisma directory to re-initialize" + echo " 2. Or run: pnpm prisma db push --accept-data-loss" + echo "" + exit 1 + fi +else + echo "${GREEN}Database file exists: $DB_FILE${NC}" +fi + +echo "${GREEN}=== Database Ready! Starting application... ===${NC}" +echo "" + +# Execute the command passed to the container +exec "$@" diff --git a/easy-dataset-main/electron/entitlements.mac.plist b/easy-dataset-main/electron/entitlements.mac.plist new file mode 100644 index 0000000..066f489 --- /dev/null +++ b/easy-dataset-main/electron/entitlements.mac.plist @@ -0,0 +1,16 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.allow-dyld-environment-variables + + com.apple.security.network.client + + com.apple.security.files.user-selected.read-write + + + \ No newline at end of file diff --git a/easy-dataset-main/electron/loading.html b/easy-dataset-main/electron/loading.html new file mode 100644 index 0000000..fc5849d --- /dev/null +++ b/easy-dataset-main/electron/loading.html @@ -0,0 +1,124 @@ + + + + + + Easy Dataset Loading... + + + +
+ +

Easy Dataset

+

The first startup may take a bit longer to load. Please be patient. ...

+
+
+
+
+
+
+
+ + + + diff --git a/easy-dataset-main/electron/main.js b/easy-dataset-main/electron/main.js new file mode 100644 index 0000000..e114546 --- /dev/null +++ b/easy-dataset-main/electron/main.js @@ -0,0 +1,83 @@ +const { app, dialog, ipcMain } = require('electron'); +const { setupLogging, setupIpcLogging } = require('./modules/logger'); +const { createWindow, loadAppUrl, openDevTools, getMainWindow } = require('./modules/window-manager'); +const { createMenu } = require('./modules/menu'); +const { startNextServer } = require('./modules/server'); +const { setupAutoUpdater } = require('./modules/updater'); +const { initializeDatabase } = require('./modules/database'); +const { clearCache } = require('./modules/cache'); +const { setupIpcHandlers } = require('./modules/ipc-handlers'); + +// 是否是开发环境 +const isDev = process.env.NODE_ENV === 'development'; +const port = 1717; +let mainWindow; + +// 当 Electron 完成初始化时创建窗口 +app.whenReady().then(async () => { + try { + // 设置日志系统 + setupLogging(app); + + // 设置 IPC 处理程序 + setupIpcHandlers(app, isDev); + setupIpcLogging(ipcMain, app, isDev); + + // 初始化数据库 + await initializeDatabase(app); + + // 创建主窗口 + mainWindow = createWindow(isDev, port); + + // 创建菜单 + createMenu(mainWindow, () => clearCache(app)); + + // 在开发环境中加载 localhost URL + if (isDev) { + loadAppUrl(`http://localhost:${port}`); + openDevTools(); + } else { + // 在生产环境中启动 Next.js 服务 + const appUrl = await startNextServer(port, app); + loadAppUrl(appUrl); + } + + // 设置自动更新 + setupAutoUpdater(mainWindow); + + // 应用启动完成后的一段时间后自动检查更新 + setTimeout(() => { + if (!isDev) { + const { autoUpdater } = require('electron-updater'); + autoUpdater.checkForUpdates().catch(err => { + console.error('Automatic update check failed:', err); + }); + } + }, 10000); // Check for updates after 10 seconds + } catch (error) { + console.error('An error occurred during application initialization:', error); + dialog.showErrorBox( + 'Application Initialization Error', + `An error occurred during startup, which may affect application functionality. + Error details: ${error.message}` + ); + } +}); + +// 当所有窗口关闭时退出应用 +app.on('window-all-closed', () => { + if (process.platform !== 'darwin') { + app.quit(); + } +}); + +app.on('activate', () => { + if (BrowserWindow.getAllWindows().length === 0) { + mainWindow = createWindow(isDev, port); + } +}); + +// 应用退出前清理 +app.on('before-quit', () => { + console.log('应用正在退出...'); +}); diff --git a/easy-dataset-main/electron/modules/cache.js b/easy-dataset-main/electron/modules/cache.js new file mode 100644 index 0000000..ed11100 --- /dev/null +++ b/easy-dataset-main/electron/modules/cache.js @@ -0,0 +1,21 @@ +const { clearLogs } = require('./logger'); +const { clearDatabaseCache } = require('./database'); + +/** + * 清除缓存函数 - 清理logs和local-db目录 + * @param {Object} app Electron app 对象 + * @returns {Promise} 操作是否成功 + */ +async function clearCache(app) { + // 清理日志目录 + await clearLogs(app); + + // 清理数据库缓存 + await clearDatabaseCache(app); + + return true; +} + +module.exports = { + clearCache +}; diff --git a/easy-dataset-main/electron/modules/database.js b/easy-dataset-main/electron/modules/database.js new file mode 100644 index 0000000..c8d9ad6 --- /dev/null +++ b/easy-dataset-main/electron/modules/database.js @@ -0,0 +1,147 @@ +const fs = require('fs'); +const path = require('path'); +const { dialog } = require('electron'); +const { updateDatabase } = require('./db-updater'); + +/** + * 清除数据库缓存 + * @param {Object} app Electron app 对象 + * @returns {Promise} 操作是否成功 + */ +async function clearDatabaseCache(app) { + // 清理local-db目录,保留db.sqlite文件 + const localDbDir = path.join(app.getPath('userData'), 'local-db'); + if (fs.existsSync(localDbDir)) { + // 读取目录下所有文件 + const files = await fs.promises.readdir(localDbDir); + // 删除除了db.sqlite之外的所有文件 + for (const file of files) { + if (file !== 'db.sqlite') { + const filePath = path.join(localDbDir, file); + const stat = await fs.promises.stat(filePath); + if (stat.isFile()) { + await fs.promises.unlink(filePath); + global.appLog(`已删除数据库缓存文件: ${filePath}`); + } else if (stat.isDirectory()) { + // 如果是目录,可能需要递归删除,根据需求决定 + global.appLog(`跳过目录: ${filePath}`); + } + } + } + } + return true; +} + +/** + * 初始化数据库 + * @param {Object} app Electron app 对象 + * @returns {Promise} 数据库配置信息 + */ +async function initializeDatabase(app) { + try { + // 设置数据库路径 + const userDataPath = app.getPath('userData'); + const dataDir = path.join(userDataPath, 'local-db'); + const dbFilePath = path.join(dataDir, 'db.sqlite'); + const dbJSONPath = path.join(dataDir, 'db.json'); + fs.writeFileSync(path.join(process.resourcesPath, 'root-path.txt'), dataDir); + + // 确保数据目录存在 + if (!fs.existsSync(dataDir)) { + fs.mkdirSync(dataDir, { recursive: true }); + console.log(`数据目录已创建: ${dataDir}`); + } + + // 设置数据库连接字符串 (Prisma 格式) + const dbConnectionString = `file:${dbFilePath}`; + process.env.DATABASE_URL = dbConnectionString; + + // 仅在开发环境记录日志 + const logs = { + userDataPath, + dataDir, + dbFilePath, + dbConnectionString, + dbExists: fs.existsSync(dbFilePath) + }; + global.appLog(`数据库配置: ${JSON.stringify(logs)}`); + + if (!fs.existsSync(dbFilePath)) { + global.appLog('数据库文件不存在,正在初始化...'); + + try { + const resourcePath = + process.env.NODE_ENV === 'development' + ? path.join(__dirname, '../..', 'prisma', 'template.sqlite') + : path.join(process.resourcesPath, 'prisma', 'template.sqlite'); + + const resourceJSONPath = + process.env.NODE_ENV === 'development' + ? path.join(__dirname, '../..', 'prisma', 'sql.json') + : path.join(process.resourcesPath, 'prisma', 'sql.json'); + + global.appLog(`resourcePath: ${resourcePath}`); + + if (fs.existsSync(resourcePath)) { + fs.copyFileSync(resourcePath, dbFilePath); + global.appLog(`数据库已从模板初始化: ${dbFilePath}`); + } + + if (fs.existsSync(resourceJSONPath)) { + fs.copyFileSync(resourceJSONPath, dbJSONPath); + global.appLog(`数据库SQL配置已初始化: ${dbJSONPath}`); + } + } catch (error) { + console.error('数据库初始化失败:', error); + dialog.showErrorBox('数据库初始化失败', `应用无法初始化数据库,可能需要重新安装。\n错误详情: ${error.message}`); + throw error; + } + } else { + // 数据库文件存在,检查是否需要更新 + global.appLog('检查数据库是否需要更新...'); + try { + const resourcesPath = + process.env.NODE_ENV === 'development' ? path.join(__dirname, '../..') : process.resourcesPath; + + const isDev = process.env.NODE_ENV === 'development'; + + // 更新数据库 + const result = await updateDatabase(userDataPath, resourcesPath, isDev, global.appLog); + + if (result.updated) { + global.appLog(`数据库更新成功: ${result.message}`); + global.appLog(`执行的版本: ${result.executedVersions.join(', ')}`); + } else { + global.appLog(`数据库无需更新: ${result.message}`); + } + } catch (error) { + console.error('数据库更新失败:', error); + global.appLog(`数据库更新失败: ${error.message}`, 'error'); + + // 非致命错误,只提示但不阻止应用启动 + dialog.showMessageBox({ + type: 'warning', + title: '数据库更新警告', + message: '数据库更新过程中出现错误,部分功能可能受影响。', + detail: `错误详情: ${error.message}\n\n您可以继续使用应用,但如果遇到问题,请重新安装应用。`, + buttons: ['继续'] + }); + } + } + + return { + userDataPath, + dataDir, + dbFilePath, + dbConnectionString + }; + } catch (error) { + console.error('初始化数据库时发生错误:', error); + throw error; + } +} + +module.exports = { + clearDatabaseCache, + initializeDatabase +}; diff --git a/easy-dataset-main/electron/modules/db-updater.js b/easy-dataset-main/electron/modules/db-updater.js new file mode 100644 index 0000000..11702ea --- /dev/null +++ b/easy-dataset-main/electron/modules/db-updater.js @@ -0,0 +1,179 @@ +const fs = require('fs'); +const path = require('path'); +const { PrismaClient } = require('@prisma/client'); + +/** + * 执行SQL命令 + * @param {string} dbUrl 数据库连接 URL + * @param {string} sql SQL命令 + * @returns {Promise} + */ +async function executeSql(dbUrl, sql) { + // 允许多条SQL语句分开执行,支持分号和空行分隔 + const statements = sql + .split(';') + .map(stmt => stmt.trim()) + .filter(stmt => stmt.length > 0); + + if (statements.length === 0) { + return; + } + + // 设置环境变量 + process.env.DATABASE_URL = dbUrl; + + // 创建Prisma实例 + const prisma = new PrismaClient(); + + try { + // 执行每条SQL语句 + for (const statement of statements) { + await prisma.$executeRawUnsafe(statement); + } + } finally { + // 关闭连接 + await prisma.$disconnect(); + } +} + +/** + * 获取本地和应用的SQL配置文件 + * @param {string} userDataPath 用户数据目录 + * @param {string} resourcesPath 应用资源目录 + * @param {boolean} isDev 是否开发环境 + * @returns {Promise<{userSqlConfig: Array, appSqlConfig: Array}>} + */ +async function getSqlConfigs(userDataPath, resourcesPath, isDev, logger = console.log) { + // 用户SQL配置文件路径 + const userSqlPath = path.join(userDataPath, 'sql.json'); + + // 应用SQL配置文件路径 + const appSqlPath = isDev + ? path.join(__dirname, '..', 'prisma', 'sql.json') + : path.join(resourcesPath, 'prisma', 'sql.json'); + + let userSqlConfig = []; + let appSqlConfig = []; + + // 读取应用SQL配置 + try { + if (fs.existsSync(appSqlPath)) { + const appSqlContent = fs.readFileSync(appSqlPath, 'utf8'); + appSqlConfig = JSON.parse(appSqlContent); + } + } catch (error) { + throw new Error(`读取应用SQL配置文件失败: ${error.message}`); + } + + // 读取用户SQL配置(如果存在) + try { + if (fs.existsSync(userSqlPath)) { + const userSqlContent = fs.readFileSync(userSqlPath, 'utf8'); + userSqlConfig = JSON.parse(userSqlContent); + } + } catch (error) { + // 如果用户SQL配置不存在或无法解析,使用空数组 + userSqlConfig = []; + } + + logger(appSqlPath); + // logger(JSON.stringify(appSqlConfig, null, 2)); + logger(userSqlPath); + // logger(JSON.stringify(userSqlConfig, null, 2)); + + return { userSqlConfig, appSqlConfig }; +} + +/** + * 更新用户SQL配置文件 + * @param {string} userDataPath 用户数据目录 + * @param {Array} sqlConfig 新的SQL配置 + */ +function updateUserSqlConfig(userDataPath, sqlConfig) { + const userSqlPath = path.join(userDataPath, 'sql.json'); + fs.writeFileSync(userSqlPath, JSON.stringify(sqlConfig, null, 4), 'utf8'); +} + +// 不再需要版本比较功能 + +/** + * 获取需要执行的SQL命令 + * @param {Array} userSqlConfig 用户SQL配置 + * @param {Array} appSqlConfig 应用SQL配置 + * @returns {Array} 需要执行的SQL命令 + */ +function getSqlsToExecute(userSqlConfig, appSqlConfig) { + // 创建用户已执行的SQL集合 (使用 version + sql 的组合作为唯一标识) + const userExecutedSqlSet = new Set(); + userSqlConfig.forEach(item => { + const key = `${item.version}:${item.sql}`; + userExecutedSqlSet.add(key); + }); + + // 过滤出用户需要执行的SQL (即应用SQL配置中存在但用户尚未执行的SQL) + return appSqlConfig.filter(item => { + const key = `${item.version}:${item.sql}`; + return !userExecutedSqlSet.has(key); + }); +} + +/** + * 更新数据库 + * @param {string} userDataPath 用户数据目录 + * @param {string} resourcesPath 应用资源目录 + * @param {boolean} isDev 是否开发环境 + * @param {function} logger 日志函数 + */ +async function updateDatabase(userDataPath, resourcesPath, isDev, logger = console.log) { + const dbPath = path.join(userDataPath, 'local-db', 'db.sqlite'); + + try { + // 获取SQL配置 + const { userSqlConfig, appSqlConfig } = await getSqlConfigs(userDataPath, resourcesPath, isDev, logger); + + // 获取需要执行的SQL + const sqlsToExecute = getSqlsToExecute(userSqlConfig, appSqlConfig); + + if (sqlsToExecute.length === 0) { + logger('数据库已是最新版本,无需更新'); + return { updated: false, message: '数据库已是最新版本' }; + } + + // 设置数据库URL + const dbUrl = `file:${dbPath}`; + + // 执行SQL更新 + logger(`发现 ${sqlsToExecute.length} 个数据库更新,开始执行...`); + for (const item of sqlsToExecute) { + try { + logger(`执行版本 ${item.version} 的SQL更新: ${item.sql.substring(0, 100)}...`); + await executeSql(dbUrl, item.sql); + // 添加到用户SQL配置 + userSqlConfig.push(item); + } catch (error) { + logger(`执行版本 ${item.version} 的SQL更新失败: ${error.message}`); + } + } + + // 更新用户SQL配置文件 + updateUserSqlConfig(userDataPath, userSqlConfig); + + logger('数据库更新完成'); + return { + updated: true, + message: `成功执行了 ${sqlsToExecute.length} 个数据库更新`, + executedVersions: sqlsToExecute.map(item => item.version) + }; + } catch (error) { + logger(`数据库更新失败: ${error.message}`); + return { updated: false, error: error.message }; + } +} + +module.exports = { + updateDatabase, + executeSql, + getSqlConfigs, + updateUserSqlConfig, + getSqlsToExecute +}; diff --git a/easy-dataset-main/electron/modules/ipc-handlers.js b/easy-dataset-main/electron/modules/ipc-handlers.js new file mode 100644 index 0000000..8ec8007 --- /dev/null +++ b/easy-dataset-main/electron/modules/ipc-handlers.js @@ -0,0 +1,33 @@ +const { ipcMain } = require('electron'); +const { checkUpdate, downloadUpdate, installUpdate } = require('./updater'); + +/** + * 设置 IPC 处理程序 + * @param {Object} app Electron app 对象 + * @param {boolean} isDev 是否为开发环境 + */ +function setupIpcHandlers(app, isDev) { + // 获取用户数据路径 + ipcMain.on('get-user-data-path', event => { + event.returnValue = app.getPath('userData'); + }); + + // 检查更新 + ipcMain.handle('check-update', async () => { + return await checkUpdate(isDev); + }); + + // 下载更新 + ipcMain.handle('download-update', async () => { + return await downloadUpdate(); + }); + + // 安装更新 + ipcMain.handle('install-update', () => { + return installUpdate(); + }); +} + +module.exports = { + setupIpcHandlers +}; diff --git a/easy-dataset-main/electron/modules/logger.js b/easy-dataset-main/electron/modules/logger.js new file mode 100644 index 0000000..6da03fe --- /dev/null +++ b/easy-dataset-main/electron/modules/logger.js @@ -0,0 +1,84 @@ +const fs = require('fs'); +const path = require('path'); + +/** + * 设置应用日志系统 + * @param {Object} app Electron app 对象 + * @returns {string} 日志文件路径 + */ +function setupLogging(app) { + const logDir = path.join(app.getPath('userData'), 'logs'); + if (!fs.existsSync(logDir)) { + fs.mkdirSync(logDir, { recursive: true }); + } + + const logFilePath = path.join(logDir, `app-${new Date().toISOString().slice(0, 10)}.log`); + + // 创建自定义日志函数 + global.appLog = (message, level = 'info') => { + const timestamp = new Date().toISOString(); + const logEntry = `[${timestamp}] [${level.toUpperCase()}] ${message}\n`; + + // 同时输出到控制台和日志文件 + console.log(message); + fs.appendFileSync(logFilePath, logEntry); + }; + + // 捕获全局未处理异常并记录 + process.on('uncaughtException', error => { + global.appLog(`未捕获的异常: ${error.stack || error}`, 'error'); + }); + + return logFilePath; +} + +/** + * 设置 IPC 日志处理程序 + * @param {Object} ipcMain IPC 主进程对象 + * @param {Object} app Electron app 对象 + * @param {boolean} isDev 是否为开发环境 + */ +function setupIpcLogging(ipcMain, app, isDev) { + ipcMain.on('log', (event, { level, message }) => { + const timestamp = new Date().toISOString(); + const logEntry = `[${timestamp}] [${level.toUpperCase()}] ${message}\n`; + + // 只在客户端环境下写入文件 + if (!isDev || true) { + const logsDir = path.join(app.getPath('userData'), 'logs'); + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const logFile = path.join(logsDir, `${new Date().toISOString().split('T')[0]}.log`); + fs.appendFileSync(logFile, logEntry); + } + + // 同时输出到控制台 + console[level](message); + }); +} + +/** + * 清理日志文件 + * @param {Object} app Electron app 对象 + * @returns {Promise} + */ +async function clearLogs(app) { + const logsDir = path.join(app.getPath('userData'), 'logs'); + if (fs.existsSync(logsDir)) { + // 读取目录下所有文件 + const files = await fs.promises.readdir(logsDir); + // 删除所有文件 + for (const file of files) { + const filePath = path.join(logsDir, file); + await fs.promises.unlink(filePath); + global.appLog(`已删除日志文件: ${filePath}`); + } + } +} + +module.exports = { + setupLogging, + setupIpcLogging, + clearLogs +}; diff --git a/easy-dataset-main/electron/modules/menu.js b/easy-dataset-main/electron/modules/menu.js new file mode 100644 index 0000000..0ee9cec --- /dev/null +++ b/easy-dataset-main/electron/modules/menu.js @@ -0,0 +1,136 @@ +const { Menu, dialog, shell, app } = require('electron'); +const path = require('path'); +const fs = require('fs'); +const os = require('os'); +const { getAppVersion } = require('../util'); + +/** + * 创建应用菜单 + * @param {BrowserWindow} mainWindow 主窗口 + * @param {Function} clearCache 清除缓存函数 + */ +function createMenu(mainWindow, clearCache) { + const template = [ + { + label: 'File', + submenu: [{ role: 'quit', label: 'Quit' }] + }, + { + label: 'Edit', + submenu: [ + { role: 'undo', label: 'Undo' }, + { role: 'redo', label: 'Redo' }, + { type: 'separator' }, + { role: 'cut', label: 'Cut' }, + { role: 'copy', label: 'Copy' }, + { role: 'paste', label: 'Paste' } + ] + }, + { + label: 'View', + submenu: [ + { role: 'reload', label: 'Refresh' }, + { type: 'separator' }, + { role: 'resetzoom', label: 'Reset Zoom' }, + { role: 'zoomin', label: 'Zoom In' }, + { role: 'zoomout', label: 'Zoom Out' }, + { type: 'separator' }, + { role: 'togglefullscreen', label: 'Fullscreen' } + ] + }, + { + label: 'Help', + submenu: [ + { + label: 'About', + click: () => { + dialog.showMessageBox(mainWindow, { + title: 'About Easy Dataset', + message: `Easy Dataset v${getAppVersion()}`, + detail: 'An application for creating fine-tuning datasets for large models.', + buttons: ['OK'] + }); + } + }, + { + label: 'Visit GitHub', + click: () => { + shell.openExternal('https://github.com/ConardLi/easy-dataset'); + } + } + ] + }, + { + label: 'More', + submenu: [ + { role: 'toggledevtools', label: 'Developer Tools' }, + { + label: 'Open Logs Directory', + click: () => { + const logsDir = path.join(app.getPath('userData'), 'logs'); + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + shell.openPath(logsDir); + } + }, + { + label: 'Open Data Directory', + click: () => { + const dataDir = path.join(app.getPath('userData'), 'local-db'); + if (!fs.existsSync(dataDir)) { + fs.mkdirSync(dataDir, { recursive: true }); + } + shell.openPath(dataDir); + } + }, + { + label: 'Open Data Directory (History)', + click: () => { + const dataDir = path.join(os.homedir(), '.easy-dataset-db'); + if (!fs.existsSync(dataDir)) { + fs.mkdirSync(dataDir, { recursive: true }); + } + shell.openPath(dataDir); + } + }, + { + label: 'Clear Cache', + click: async () => { + try { + const response = await dialog.showMessageBox(mainWindow, { + type: 'question', + buttons: ['Cancel', 'Confirm'], + defaultId: 1, + title: 'Clear Cache', + message: 'Are you sure you want to clear the cache?', + detail: + 'This will delete all files in the logs directory and local database cache files (excluding main database files).' + }); + + if (response.response === 1) { + // User clicked confirm + await clearCache(); + dialog.showMessageBox(mainWindow, { + type: 'info', + title: 'Cleared Successfully', + message: 'Cache has been cleared successfully' + }); + } + } catch (error) { + global.appLog(`Failed to clear cache: ${error.message}`, 'error'); + dialog.showErrorBox('Failed to clear cache', error.message); + } + } + } + ] + } + ]; + + const menu = Menu.buildFromTemplate(template); + Menu.setApplicationMenu(menu); +} + +module.exports = { + createMenu +}; diff --git a/easy-dataset-main/electron/modules/server.js b/easy-dataset-main/electron/modules/server.js new file mode 100644 index 0000000..a0051bf --- /dev/null +++ b/easy-dataset-main/electron/modules/server.js @@ -0,0 +1,118 @@ +const http = require('http'); +const path = require('path'); +const fs = require('fs'); +const { dialog } = require('electron'); + +/** + * 检查端口是否被占用 + * @param {number} port 端口号 + * @returns {Promise} 端口是否被占用 + */ +function checkPort(port) { + return new Promise(resolve => { + const server = http.createServer(); + server.once('error', () => { + resolve(true); // 端口被占用 + }); + server.once('listening', () => { + server.close(); + resolve(false); // 端口未被占用 + }); + server.listen(port); + }); +} + +/** + * 启动 Next.js 服务 + * @param {number} port 端口号 + * @param {Object} app Electron app 对象 + * @returns {Promise} 服务URL + */ +async function startNextServer(port, app) { + console.log(`Easy Dataset 客户端启动中,当前版本: ${require('../util').getAppVersion()}`); + + // 设置日志文件路径 + const logDir = path.join(app.getPath('userData'), 'logs'); + if (!fs.existsSync(logDir)) { + fs.mkdirSync(logDir, { recursive: true }); + } + const logFile = path.join(logDir, `nextjs-${new Date().toISOString().replace(/:/g, '-')}.log`); + const logStream = fs.createWriteStream(logFile, { flags: 'a' }); + + // 重定向 console.log 和 console.error + const originalConsoleLog = console.log; + const originalConsoleError = console.error; + + console.log = function () { + const args = Array.from(arguments); + const logMessage = args.map(arg => (typeof arg === 'object' ? JSON.stringify(arg, null, 2) : arg)).join(' '); + + logStream.write(`[${new Date().toISOString()}] [LOG] ${logMessage}\n`); + originalConsoleLog.apply(console, args); + }; + + console.error = function () { + const args = Array.from(arguments); + const logMessage = args.map(arg => (typeof arg === 'object' ? JSON.stringify(arg, null, 2) : arg)).join(' '); + + logStream.write(`[${new Date().toISOString()}] [ERROR] ${logMessage}\n`); + originalConsoleError.apply(console, args); + }; + + // 检查端口是否被占用 + const isPortBusy = await checkPort(port); + if (isPortBusy) { + console.log(`端口 ${port} 已被占用,尝试直接连接...`); + return `http://localhost:${port}`; + } + + console.log(`启动 Next.js 服务,端口: ${port}`); + + try { + // 动态导入 Next.js + const next = require('next'); + const nextApp = next({ + dev: false, + dir: path.join(__dirname, '../..'), + conf: { + // 配置 Next.js 的日志输出 + onInfo: info => { + console.log(`[Next.js Info] ${info}`); + }, + onError: error => { + console.error(`[Next.js Error] ${error}`); + }, + onWarn: warn => { + console.log(`[Next.js Warning] ${warn}`); + } + } + }); + const handle = nextApp.getRequestHandler(); + + await nextApp.prepare(); + + const server = http.createServer((req, res) => { + // 记录请求日志 + console.log(`[Request] ${req.method} ${req.url}`); + handle(req, res); + }); + + return new Promise(resolve => { + server.listen(port, err => { + if (err) throw err; + console.log(`服务已启动,正在打开应用...`); + resolve(`http://localhost:${port}`); + }); + }); + } catch (error) { + console.error('启动服务失败:', error); + dialog.showErrorBox('启动失败', `无法启动 Next.js 服务: ${error.message}`); + app.quit(); + return ''; + } +} + +module.exports = { + checkPort, + startNextServer +}; diff --git a/easy-dataset-main/electron/modules/updater.js b/easy-dataset-main/electron/modules/updater.js new file mode 100644 index 0000000..eeff538 --- /dev/null +++ b/easy-dataset-main/electron/modules/updater.js @@ -0,0 +1,116 @@ +const { autoUpdater } = require('electron-updater'); +const { getAppVersion } = require('../util'); + +/** + * 设置自动更新 + * @param {BrowserWindow} mainWindow 主窗口 + */ +function setupAutoUpdater(mainWindow) { + autoUpdater.autoDownload = false; + autoUpdater.allowDowngrade = false; + + // 检查更新时出错 + autoUpdater.on('error', error => { + if (mainWindow) { + mainWindow.webContents.send('update-error', error.message); + } + }); + + // 检查到更新时 + autoUpdater.on('update-available', info => { + if (mainWindow) { + mainWindow.webContents.send('update-available', { + version: info.version, + releaseDate: info.releaseDate, + releaseNotes: info.releaseNotes + }); + } + }); + + // 没有可用更新 + autoUpdater.on('update-not-available', () => { + if (mainWindow) { + mainWindow.webContents.send('update-not-available'); + } + }); + + // 下载进度 + autoUpdater.on('download-progress', progressObj => { + if (mainWindow) { + mainWindow.webContents.send('download-progress', progressObj); + } + }); + + // 下载完成 + autoUpdater.on('update-downloaded', info => { + if (mainWindow) { + mainWindow.webContents.send('update-downloaded', { + version: info.version, + releaseDate: info.releaseDate, + releaseNotes: info.releaseNotes + }); + } + }); +} + +/** + * 检查更新 + * @param {boolean} isDev 是否为开发环境 + * @returns {Promise} 更新信息 + */ +async function checkUpdate(isDev) { + try { + if (isDev) { + // 开发环境下模拟更新检查 + return { + hasUpdate: false, + currentVersion: getAppVersion(), + message: '开发环境下不检查更新' + }; + } + + // 返回当前版本信息,并开始检查更新 + const result = await autoUpdater.checkForUpdates(); + return { + checking: true, + currentVersion: getAppVersion() + }; + } catch (error) { + console.error('检查更新失败:', error); + return { + hasUpdate: false, + currentVersion: getAppVersion(), + error: error.message + }; + } +} + +/** + * 下载更新 + * @returns {Promise} 下载状态 + */ +async function downloadUpdate() { + try { + autoUpdater.downloadUpdate(); + return { downloading: true }; + } catch (error) { + console.error('下载更新失败:', error); + return { error: error.message }; + } +} + +/** + * 安装更新 + * @returns {Object} 安装状态 + */ +function installUpdate() { + autoUpdater.quitAndInstall(false, true); + return { installing: true }; +} + +module.exports = { + setupAutoUpdater, + checkUpdate, + downloadUpdate, + installUpdate +}; diff --git a/easy-dataset-main/electron/modules/window-manager.js b/easy-dataset-main/electron/modules/window-manager.js new file mode 100644 index 0000000..fc01329 --- /dev/null +++ b/easy-dataset-main/electron/modules/window-manager.js @@ -0,0 +1,113 @@ +const { BrowserWindow, shell } = require('electron'); +const path = require('path'); +const url = require('url'); +const { getAppVersion } = require('../util'); + +let mainWindow; + +/** + * 创建主窗口 + * @param {boolean} isDev 是否为开发环境 + * @param {number} port 服务端口 + * @returns {BrowserWindow} 创建的主窗口 + */ +function createWindow(isDev, port) { + mainWindow = new BrowserWindow({ + width: 1200, + height: 800, + show: false, + frame: true, + webPreferences: { + nodeIntegration: false, + contextIsolation: true, + preload: path.join(__dirname, '..', 'preload.js') + }, + icon: path.join(__dirname, '../../public/imgs/logo.ico') + }); + + // 设置窗口标题 + mainWindow.setTitle(`Easy Dataset v${getAppVersion()}`); + const loadingPath = url.format({ + pathname: path.join(__dirname, '..', 'loading.html'), + protocol: 'file:', + slashes: true + }); + + // 加载 loading 页面时使用专门的 preload 脚本 + mainWindow.webContents.on('did-finish-load', () => { + mainWindow.show(); + }); + + mainWindow.loadURL(loadingPath); + + // 处理窗口导航事件,将外部链接在浏览器中打开 + mainWindow.webContents.on('will-navigate', (event, navigationUrl) => { + // 解析当前 URL 和导航 URL + const parsedUrl = new URL(navigationUrl); + const currentHostname = isDev ? 'localhost' : 'localhost'; + const currentPort = port.toString(); + + // 检查是否是外部链接 + if (parsedUrl.hostname !== currentHostname || (parsedUrl.port !== currentPort && parsedUrl.port !== '')) { + event.preventDefault(); + shell.openExternal(navigationUrl); + } + }); + + // 处理新窗口打开请求,将外部链接在浏览器中打开 + mainWindow.webContents.setWindowOpenHandler(({ url: navigationUrl }) => { + // 解析导航 URL + const parsedUrl = new URL(navigationUrl); + const currentHostname = isDev ? 'localhost' : 'localhost'; + const currentPort = port.toString(); + + // 检查是否是外部链接 + if (parsedUrl.hostname !== currentHostname || (parsedUrl.port !== currentPort && parsedUrl.port !== '')) { + shell.openExternal(navigationUrl); + return { action: 'deny' }; + } + return { action: 'allow' }; + }); + + mainWindow.on('closed', () => { + mainWindow = null; + }); + + mainWindow.maximize(); + + return mainWindow; +} + +/** + * 加载应用URL + * @param {string} appUrl 应用URL + */ +function loadAppUrl(appUrl) { + if (mainWindow) { + mainWindow.loadURL(appUrl); + } +} + +/** + * 在开发环境中打开开发者工具 + */ +function openDevTools() { + if (mainWindow) { + mainWindow.webContents.openDevTools(); + } +} + +/** + * 获取主窗口 + * @returns {BrowserWindow} 主窗口 + */ +function getMainWindow() { + return mainWindow; +} + +module.exports = { + createWindow, + loadAppUrl, + openDevTools, + getMainWindow +}; diff --git a/easy-dataset-main/electron/preload.js b/easy-dataset-main/electron/preload.js new file mode 100644 index 0000000..db62abb --- /dev/null +++ b/easy-dataset-main/electron/preload.js @@ -0,0 +1,73 @@ +const { contextBridge, ipcRenderer } = require('electron'); + +// 在渲染进程中暴露安全的 API +contextBridge.exposeInMainWorld('electron', { + // 获取应用版本 + getAppVersion: () => ipcRenderer.invoke('get-app-version'), + + // 获取当前语言 + getLanguage: () => { + // 尝试从本地存储获取语言设置 + const storedLang = localStorage.getItem('i18nextLng'); + // 如果存在则返回,否则返回系统语言或默认为中文 + return storedLang || navigator.language.startsWith('zh') ? 'zh' : 'en'; + }, + + // 获取用户数据目录 + getUserDataPath: () => { + try { + return ipcRenderer.sendSync('get-user-data-path'); + } catch (error) { + console.error('获取用户数据目录失败:', error); + return null; + } + }, + + // 更新相关 API + updater: { + // 检查更新 + checkForUpdates: () => ipcRenderer.invoke('check-update'), + + // 下载更新 + downloadUpdate: () => ipcRenderer.invoke('download-update'), + + // 安装更新 + installUpdate: () => ipcRenderer.invoke('install-update'), + + // 监听更新事件 + onUpdateAvailable: callback => { + const handler = (_, info) => callback(info); + ipcRenderer.on('update-available', handler); + return () => ipcRenderer.removeListener('update-available', handler); + }, + + onUpdateNotAvailable: callback => { + const handler = () => callback(); + ipcRenderer.on('update-not-available', handler); + return () => ipcRenderer.removeListener('update-not-available', handler); + }, + + onUpdateError: callback => { + const handler = (_, error) => callback(error); + ipcRenderer.on('update-error', handler); + return () => ipcRenderer.removeListener('update-error', handler); + }, + + onDownloadProgress: callback => { + const handler = (_, progress) => callback(progress); + ipcRenderer.on('download-progress', handler); + return () => ipcRenderer.removeListener('download-progress', handler); + }, + + onUpdateDownloaded: callback => { + const handler = (_, info) => callback(info); + ipcRenderer.on('update-downloaded', handler); + return () => ipcRenderer.removeListener('update-downloaded', handler); + } + } +}); + +// 通知渲染进程 preload 脚本已加载完成 +window.addEventListener('DOMContentLoaded', () => { + console.log('Electron preload script loaded'); +}); diff --git a/easy-dataset-main/electron/util.js b/easy-dataset-main/electron/util.js new file mode 100644 index 0000000..83288cb --- /dev/null +++ b/easy-dataset-main/electron/util.js @@ -0,0 +1,19 @@ +const path = require('path'); +const fs = require('fs'); + +// 获取应用版本 +const getAppVersion = () => { + try { + const packageJsonPath = path.join(__dirname, '../package.json'); + if (fs.existsSync(packageJsonPath)) { + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + return packageJson.version; + } + return '1.0.0'; + } catch (error) { + console.error('读取版本信息失败:', error); + return '1.0.0'; + } +}; + +module.exports = { getAppVersion }; diff --git a/easy-dataset-main/hooks/useDebounce.js b/easy-dataset-main/hooks/useDebounce.js new file mode 100644 index 0000000..38c81c5 --- /dev/null +++ b/easy-dataset-main/hooks/useDebounce.js @@ -0,0 +1,14 @@ +import { useEffect, useState } from 'react'; + +export function useDebounce(value, delay = 500) { + const [debouncedValue, setDebouncedValue] = useState(value); + useEffect(() => { + const timer = setTimeout(() => { + setDebouncedValue(value); + }, delay); + return () => { + clearTimeout(timer); + }; + }, [value, delay]); + return debouncedValue; +} diff --git a/easy-dataset-main/hooks/useFileProcessingStatus.js b/easy-dataset-main/hooks/useFileProcessingStatus.js new file mode 100644 index 0000000..e9fc49f --- /dev/null +++ b/easy-dataset-main/hooks/useFileProcessingStatus.js @@ -0,0 +1,57 @@ +import { useState, useEffect } from 'react'; + +// 存储文件处理状态的共享对象 +const fileProcessingSubscribers = { + value: false, + listeners: new Set() +}; + +// 存储文件任务信息的共享对象 +const fileTaskSubscribers = { + value: null, + listeners: new Set() +}; + +/** + * 自定义hook,用于在组件间共享文件处理任务的状态 + */ +export default function useFileProcessingStatus() { + const [taskFileProcessing, setTaskFileProcessing] = useState(fileProcessingSubscribers.value); + const [task, setTask] = useState(fileTaskSubscribers.value); + + useEffect(() => { + // 添加当前组件为订阅者 + const updateProcessingState = newValue => setTaskFileProcessing(newValue); + const updateTaskState = newTask => setTask(newTask); + + fileProcessingSubscribers.listeners.add(updateProcessingState); + fileTaskSubscribers.listeners.add(updateTaskState); + + // 组件卸载时清理 + return () => { + fileProcessingSubscribers.listeners.delete(updateProcessingState); + fileTaskSubscribers.listeners.delete(updateTaskState); + }; + }, []); + + // 共享的setState函数 + const setSharedFileProcessing = newValue => { + fileProcessingSubscribers.value = newValue; + // 通知所有订阅者 + fileProcessingSubscribers.listeners.forEach(listener => listener(newValue)); + }; + + // 共享的setTask函数 + const setSharedTask = newTask => { + fileTaskSubscribers.value = newTask; + // 通知所有订阅者 + fileTaskSubscribers.listeners.forEach(listener => listener(newTask)); + }; + + return { + taskFileProcessing, + task, + setTaskFileProcessing: setSharedFileProcessing, + setTask: setSharedTask + }; +} diff --git a/easy-dataset-main/hooks/useGenerateDataset.js b/easy-dataset-main/hooks/useGenerateDataset.js new file mode 100644 index 0000000..792be94 --- /dev/null +++ b/easy-dataset-main/hooks/useGenerateDataset.js @@ -0,0 +1,135 @@ +import { useCallback } from 'react'; +import { toast } from 'sonner'; +import i18n from '@/lib/i18n'; +import axios from 'axios'; +import { useAtomValue } from 'jotai/index'; +import { selectedModelInfoAtom } from '@/lib/store'; +import { useTranslation } from 'react-i18next'; + +export function useGenerateDataset() { + const model = useAtomValue(selectedModelInfoAtom); + const { t } = useTranslation(); + + const generateSingleDataset = useCallback( + async ({ projectId, questionId, questionInfo, imageId, imageName }) => { + // 获取模型参数 + if (!model) { + toast.error(t('models.configNotFound')); + return null; + } + + // 判断是否为图片问题 + const isImageQuestion = !!imageId; + + // 调用API生成数据集 + const currentLanguage = i18n.language === 'zh-CN' ? '中文' : 'en'; + + if (isImageQuestion) { + // 图片问题:调用图片数据集生成接口 + toast.promise( + axios.post(`/api/projects/${projectId}/images/datasets`, { + imageName, + question: { question: questionInfo, id: questionId }, + model, + language: currentLanguage + }), + { + loading: t('datasets.generating'), + description: `图片:【${imageName}】\n问题:【${questionInfo}】`, + position: 'top-right', + success: data => { + return '生成数据集成功'; + }, + error: error => { + return t('datasets.generateFailed', { error: error.response?.data?.error }); + } + } + ); + } else { + // 文本问题:调用普通数据集生成接口 + toast.promise( + axios.post(`/api/projects/${projectId}/datasets`, { + questionId, + model, + language: currentLanguage + }), + { + loading: t('datasets.generating'), + description: `问题:【${questionInfo}】`, + position: 'top-right', + success: data => { + return '生成数据集成功'; + }, + error: error => { + return t('datasets.generateFailed', { error: error.response?.data?.error }); + } + } + ); + } + }, + [model, t] + ); + + const generateMultipleDataset = useCallback( + async (projectId, questions) => { + let completed = 0; + const total = questions.length; + // 显示带进度的Loading + const loadingToastId = toast.loading(`正在处理请求 (${completed}/${total})...`, { position: 'top-right' }); + + // 处理每个请求 + const processRequest = async question => { + try { + const isImageQuestion = !!question.imageId; + let response; + + if (isImageQuestion) { + // 图片问题 + response = await axios.post(`/api/projects/${projectId}/images/datasets`, { + imageName: question.imageName, + question, + model, + language: i18n.language === 'zh-CN' ? '中文' : 'en' + }); + } else { + // 文本问题 + response = await axios.post(`/api/projects/${projectId}/datasets`, { + questionId: question.id, + model, + language: i18n.language === 'zh-CN' ? '中文' : 'en' + }); + } + + const data = response.data; + completed++; + toast.success(`${question.question} 完成`, { position: 'top-right' }); + toast.loading(`正在处理请求 (${completed}/${total})...`, { id: loadingToastId }); + return data; + } catch (error) { + completed++; + toast.error(`${question.question} 失败`, { + description: error.message, + position: 'top-right' + }); + toast.loading(`正在处理请求 (${completed}/${total})...`, { id: loadingToastId }); + throw error; + } + }; + + try { + const results = await Promise.allSettled(questions.map(req => processRequest(req))); + // 全部完成后更新Loading为完成状态 + toast.success(`全部请求处理完成 (成功: ${results.filter(r => r.status === 'fulfilled').length}/${total})`, { + id: loadingToastId, + position: 'top-right' + }); + return results; + } catch { + // Promise.allSettled不会进入catch,这里只是保险 + } + }, + [model, t] + ); + + return { generateSingleDataset, generateMultipleDataset }; +} diff --git a/easy-dataset-main/hooks/useModelPlayground.js b/easy-dataset-main/hooks/useModelPlayground.js new file mode 100644 index 0000000..5b53108 --- /dev/null +++ b/easy-dataset-main/hooks/useModelPlayground.js @@ -0,0 +1,406 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { useAtomValue } from 'jotai/index'; +import { modelConfigListAtom } from '@/lib/store'; + +export default function useModelPlayground(projectId, defaultModelId = null) { + // 状态管理 + const [selectedModels, setSelectedModels] = useState(defaultModelId ? [defaultModelId] : []); + const [loading, setLoading] = useState({}); + const [userInput, setUserInput] = useState(''); + const [conversations, setConversations] = useState({}); + const [error, setError] = useState(null); + const [outputMode, setOutputMode] = useState('normal'); // 'normal' 或 'streaming' + const [uploadedImage, setUploadedImage] = useState(null); // 存储上传的图片Base64 + + const availableModels = useAtomValue(modelConfigListAtom); + + // 初始化会话状态 + useEffect(() => { + if (selectedModels.length > 0) { + const initialConversations = {}; + selectedModels.forEach(modelId => { + if (!conversations[modelId]) { + initialConversations[modelId] = []; + } + }); + + if (Object.keys(initialConversations).length > 0) { + setConversations(prev => ({ + ...prev, + ...initialConversations + })); + } + } + }, [selectedModels]); + + // 处理模型选择 + const handleModelSelection = event => { + const { + target: { value } + } = event; + + // 限制最多选择 3 个模型 + const selectedValues = typeof value === 'string' ? value.split(',') : value; + const limitedSelection = selectedValues.slice(0, 3); + + setSelectedModels(limitedSelection); + }; + + // 处理用户输入 + const handleInputChange = e => { + setUserInput(e.target.value); + }; + + // 处理图片上传 + const handleImageUpload = e => { + const file = e.target.files[0]; + if (file) { + const reader = new FileReader(); + reader.onloadend = () => { + setUploadedImage(reader.result); + }; + reader.readAsDataURL(file); + } + }; + + // 删除已上传的图片 + const handleRemoveImage = () => { + setUploadedImage(null); + }; + + // 处理输出模式切换 + const handleOutputModeChange = event => { + setOutputMode(event.target.value); + }; + + // 发送消息给所有选中的模型 + const handleSendMessage = async () => { + if (!userInput.trim() || Object.values(loading).some(value => value) || selectedModels.length === 0) return; + + // 获取用户输入 + const input = userInput.trim(); + setUserInput(''); + + // 获取图片(如果有的话) + const image = uploadedImage; + setUploadedImage(null); // 清除图片 + + // 更新所有选中模型的对话 + const updatedConversations = { ...conversations }; + selectedModels.forEach(modelId => { + if (!updatedConversations[modelId]) { + updatedConversations[modelId] = []; + } + // 检查是否有图片并且当前模型是视觉模型 + const model = availableModels.find(m => m.id === modelId); + const isVisionModel = model && model.type === 'vision'; + + if (isVisionModel && image) { + // 如果是视觉模型并且有图片,使用复合格式 + updatedConversations[modelId].push({ + role: 'user', + content: [ + { type: 'text', text: input || '请描述这个图片' }, + { type: 'image_url', image_url: { url: image } } + ] + }); + } else { + // 其他情况使用纯文本 + updatedConversations[modelId].push({ + role: 'user', + content: input + }); + } + }); + + setConversations(updatedConversations); + + // 为每个模型设置独立的加载状态 + const updatedLoading = {}; + selectedModels.forEach(modelId => { + updatedLoading[modelId] = true; + }); + setLoading(updatedLoading); + + // 为每个模型单独发送请求 + selectedModels.forEach(async modelId => { + const model = availableModels.find(m => m.id === modelId); + if (!model) { + // 模型配置不存在 + const modelConversation = [...(updatedConversations[modelId] || [])]; + + // 更新对话状态 + setConversations(prev => ({ + ...prev, + [modelId]: [...modelConversation, { role: 'error', content: '模型配置不存在' }] + })); + + // 更新加载状态 + setLoading(prev => ({ ...prev, [modelId]: false })); + return; + } + + try { + // 检查是否是视觉模型且有图片 + const isVisionModel = model.type === 'vision'; + + // 构建请求消息 + let requestMessages = [...updatedConversations[modelId]]; // 复制当前消息历史 + + // 如果是vision模型并且有图片,将最后一条用户消息替换为包含图片的消息 + if (isVisionModel && image && requestMessages.length > 0) { + // 找到最后一条用户消息 + const lastUserMsgIndex = requestMessages.length - 1; + // 替换为包含图片的消息 + requestMessages[lastUserMsgIndex] = { + role: 'user', + content: [ + { type: 'text', text: input || '请描述这个图片' }, + { type: 'image_url', image_url: { url: image } } + ] + }; + } + + // 根据输出模式选择不同的处理方式 + if (outputMode === 'streaming') { + // 流式输出处理 + // 先添加一个空的助手回复,用于后续流式更新 + setConversations(prev => { + const modelConversation = [...(prev[modelId] || [])]; + return { + ...prev, + [modelId]: [ + ...modelConversation, + { + role: 'assistant', + content: '', + isStreaming: true, + thinking: '', // 添加推理过程字段 + showThinking: true // 默认显示推理过程 + } + ] + }; + }); + + const response = await fetch(`/api/projects/${projectId}/playground/chat/stream`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + model: model, + messages: requestMessages + }) + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const reader = response.body.getReader(); + const decoder = new TextDecoder('utf-8'); + let accumulatedContent = ''; + + // 状态变量,用于跟踪是否正在处理思维链 + let isInThinking = false; + let currentThinking = ''; + let currentContent = ''; + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + // 解码收到的数据块 + const chunk = decoder.decode(value, { stream: true }); + + // 处理当前数据块 + for (let i = 0; i < chunk.length; i++) { + const char = chunk[i]; + + // 检测开始标签 + if (i + 6 <= chunk.length && chunk.substring(i, i + 7) === '') { + isInThinking = true; + i += 6; // 跳过标签 + continue; + } + + // 检测结束标签 + if (i + 7 <= chunk.length && chunk.substring(i, i + 8) === '') { + isInThinking = false; + i += 7; // 跳过标签 + continue; + } + + // 根据当前状态添加到对应内容中 + if (isInThinking) { + currentThinking += char; + } else { + currentContent += char; + } + } + + // 累积全部内容以便最终处理 + accumulatedContent += chunk; + + // 更新对话内容 + setConversations(prev => { + const modelConversation = [...prev[modelId]]; + const lastIndex = modelConversation.length - 1; + + // 更新最后一条消息的内容,包括思维链 + modelConversation[lastIndex] = { + ...modelConversation[lastIndex], + content: currentContent, + thinking: currentThinking, + showThinking: currentThinking.length > 0 // 只要有思维链内容就显示 + }; + + return { + ...prev, + [modelId]: modelConversation + }; + }); + } + + // 完成流式传输,移除流式标记 + // 使用刚刚实时跟踪的 currentThinking 和 currentContent作为最终的思维链和内容 + let finalThinking = currentThinking; + let finalAnswer = currentContent; + + // 如果到流结束时还在思维链中,确保解析完整的思维链内容 + if (isInThinking) { + console.log('警告: 流结束时仍在思维链中,可能有标签不完整'); + isInThinking = false; + } + + setConversations(prev => { + const modelConversation = [...prev[modelId]]; + const lastIndex = modelConversation.length - 1; + + // 更新最后一条消息,移除流式标记 + modelConversation[lastIndex] = { + role: 'assistant', + content: finalAnswer, + thinking: finalThinking, + showThinking: finalThinking ? true : false, + isStreaming: false + }; + + return { + ...prev, + [modelId]: modelConversation + }; + }); + } else { + // 普通输出处理 + const response = await fetch(`/api/projects/${projectId}/playground/chat`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + model: { + ...model, + extra_body: { enable_thinking: true } // 启用思考链 + }, + messages: requestMessages + }) + }); + + // 获取响应数据 + const data = await response.json(); + + // 独立更新此模型的对话状态 + setConversations(prev => { + const modelConversation = [...(prev[modelId] || [])]; + + if (response.ok) { + // 处理可能包含思考链的内容 + let thinking = ''; + let content = data.response; + + // 检查是否包含思考链 + if (content && content.includes('')) { + const thinkParts = content.split(/(.*?)<\/think>/s); + if (thinkParts.length >= 3) { + thinking = thinkParts[1] || ''; + // 移除思考链部分,只保留最终回答 + content = thinkParts.filter((_, i) => i % 2 === 0).join(''); + } + } + + return { + ...prev, + [modelId]: [ + ...modelConversation, + { + role: 'assistant', + content: content, + thinking: thinking, + showThinking: thinking ? true : false + } + ] + }; + } else { + return { + ...prev, + [modelId]: [...modelConversation, { role: 'error', content: `错误: ${data.error || '请求失败'}` }] + }; + } + }); + } + } catch (error) { + console.error(`请求模型 ${model.name} 失败:`, error); + + // 独立更新此模型的对话状态 - 添加错误消息 + setConversations(prev => { + const modelConversation = [...(prev[modelId] || [])]; + return { + ...prev, + [modelId]: [...modelConversation, { role: 'error', content: `错误: ${error.message}` }] + }; + }); + } finally { + // 更新此模型的加载状态 + setLoading(prev => ({ ...prev, [modelId]: false })); + } + }); + }; + + // 清空所有对话 + const handleClearConversations = () => { + const clearedConversations = {}; + selectedModels.forEach(modelId => { + clearedConversations[modelId] = []; + }); + setConversations(clearedConversations); + setLoading({}); + }; + + // 获取模型名称 + const getModelName = modelId => { + const model = availableModels.find(m => m.id === modelId); + return model ? `${model.provider}: ${model.name}` : modelId; + }; + + return { + availableModels, + selectedModels, + loading, + userInput, + conversations, + error, + outputMode, + uploadedImage, + handleModelSelection, + handleInputChange, + handleImageUpload, + handleRemoveImage, + handleSendMessage, + handleClearConversations, + handleOutputModeChange, + getModelName + }; +} diff --git a/easy-dataset-main/hooks/useSnackbar.js b/easy-dataset-main/hooks/useSnackbar.js new file mode 100644 index 0000000..8651975 --- /dev/null +++ b/easy-dataset-main/hooks/useSnackbar.js @@ -0,0 +1,73 @@ +'use client'; + +import { useState, useCallback } from 'react'; +import { Snackbar, Alert } from '@mui/material'; + +export const useSnackbar = () => { + const [open, setOpen] = useState(false); + const [message, setMessage] = useState(''); + const [severity, setSeverity] = useState('info'); + + const showMessage = useCallback((newMessage, newSeverity = 'info') => { + setMessage(newMessage); + setSeverity(newSeverity); + setOpen(true); + }, []); + + const showSuccess = useCallback( + message => { + showMessage(message, 'success'); + }, + [showMessage] + ); + + const showError = useCallback( + message => { + showMessage(message, 'error'); + }, + [showMessage] + ); + + const showInfo = useCallback( + message => { + showMessage(message, 'info'); + }, + [showMessage] + ); + + const showWarning = useCallback( + message => { + showMessage(message, 'warning'); + }, + [showMessage] + ); + + const handleClose = useCallback(() => { + setOpen(false); + }, []); + + const SnackbarComponent = useCallback( + () => ( + + + {message} + + + ), + [open, message, severity, handleClose] + ); + + return { + showMessage, + showSuccess, + showError, + showInfo, + showWarning, + SnackbarComponent + }; +}; diff --git a/easy-dataset-main/hooks/useTaskSettings.js b/easy-dataset-main/hooks/useTaskSettings.js new file mode 100644 index 0000000..338b161 --- /dev/null +++ b/easy-dataset-main/hooks/useTaskSettings.js @@ -0,0 +1,63 @@ +import { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { DEFAULT_SETTINGS } from '@/constant/setting'; + +export default function useTaskSettings(projectId) { + const { t } = useTranslation(); + const [taskSettings, setTaskSettings] = useState({ + ...DEFAULT_SETTINGS + }); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [success, setSuccess] = useState(false); + + useEffect(() => { + async function fetchTaskSettings() { + try { + setLoading(true); + const response = await fetch(`/api/projects/${projectId}/tasks`); + if (!response.ok) { + throw new Error(t('settings.fetchTasksFailed')); + } + + const data = await response.json(); + + // 如果没有配置,使用默认值 + if (Object.keys(data).length === 0) { + setTaskSettings({ + ...DEFAULT_SETTINGS + }); + } else { + // 确保所有默认值都被正确设置,特别是数字类型的字段 + const mergedSettings = { + ...DEFAULT_SETTINGS, + ...data + }; + + // 确保 multiTurnRounds 是数字类型 + if (mergedSettings.multiTurnRounds !== undefined) { + mergedSettings.multiTurnRounds = Number(mergedSettings.multiTurnRounds); + } + + setTaskSettings(mergedSettings); + } + } catch (error) { + console.error('获取任务配置出错:', error); + setError(error.message); + } finally { + setLoading(false); + } + } + + fetchTaskSettings(); + }, [projectId, t]); + + return { + taskSettings, + setTaskSettings, + loading, + error, + success, + setSuccess + }; +} diff --git a/easy-dataset-main/jsconfig.json b/easy-dataset-main/jsconfig.json new file mode 100644 index 0000000..9c33383 --- /dev/null +++ b/easy-dataset-main/jsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["./*"] + } + } +} diff --git a/easy-dataset-main/locales/en/translation.json b/easy-dataset-main/locales/en/translation.json new file mode 100644 index 0000000..33e7eed --- /dev/null +++ b/easy-dataset-main/locales/en/translation.json @@ -0,0 +1,1871 @@ +{ + "language": { + "switchToEnglish": "Switch to English", + "switchToChinese": "Switch to Chinese", + "switcherTitle": "Change Language / 切换语言 / Dil Değiştir", + "english": "English", + "chineseSimplified": "Simplified Chinese", + "turkish": "Turkish", + "portugues": "Portuguese", + "en": "EN", + "zh": "中" + }, + "theme": { + "switchToLight": "Switch to Light Mode", + "switchToDark": "Switch to Dark Mode" + }, + "settings": { + "promptConfig": "Prompt Configuration", + "promptsDescription": "Configure prompt used in the project, supporting global prompts and scenario-specific prompts.", + "globalPrompt": "Global Prompt", + "questionPrompt": "Question Generation Prompt", + "answerPrompt": "Answer Generation Prompt", + "labelPrompt": "Question Labeling Prompt", + "domainTreePrompt": "Domain Tree Building Prompt", + "globalPromptPlaceholder": "Enter global prompt that will serve as the base prompt for all scenarios", + "questionPromptPlaceholder": "Enter prompt for generating questions", + "answerPromptPlaceholder": "Enter prompt for generating answers", + "labelPromptPlaceholder": "Enter prompt for question labeling (not supported currently)", + "domainTreePromptPlaceholder": "Enter prompt for building domain tree", + "cleanPrompt": "Data Cleaning Prompt", + "cleanPromptPlaceholder": "Enter custom prompt for data cleaning", + "loadPromptsFailed": "Failed to load prompt configurations", + "savePromptsSuccess": "Successfully saved prompt configurations", + "savePromptsFailed": "Failed to save prompt configurations", + "title": "Settings", + "basicInfo": "Basic Info", + "modelConfig": "Model Configuration", + "taskConfig": "Task Configuration", + "tabsAriaLabel": "Settings Tabs", + "idNotEditable": "Project ID is not editable", + "saveBasicInfo": "Save Basic Info", + "saveSuccess": "Save Successful", + "saveFailed": "Save Failed", + "deleteSuccess": "Delete Successful", + "deleteFailed": "Delete Failed", + "fetchTasksFailed": "Failed to fetch task settings", + "saveTasksFailed": "Failed to save task settings", + "textSplitSettings": "Text Split Settings", + "minLength": "Minimum Length", + "maxLength": "Maximum Split Length", + "textSplitDescription": "Adjust the text split length range", + "splitType": "Split Strategy", + "splitTypeMarkdown": "Document Structure Spliting (Markdown)", + "splitTypeMarkdownDesc": "Automatically split the text according to the titles in the document, maintaining semantic integrity. It is suitable for Markdown documents with a clear structure.", + "splitTypeRecursive": "Text Structure Spliting (Custom Delimiter)", + "splitTypeRecursiveDesc": "Recursively attempt multiple levels of delimiters (configurable). First, use delimiters with higher priority, and then use secondary delimiters. It is suitable for complex documents.", + "splitTypeText": "Fixed-length Spliting (Characters)", + "splitTypeTextDesc": "Split the text according to the specified delimiter (configurable), and then combine it according to the specified length. It is suitable for ordinary text files.", + "splitTypeToken": "Fixed-length Spliting (Tokens)", + "splitTypeTokenDesc": "Block based on the number of Tokens (not the number of characters).", + "splitTypeCode": "Intelligent Spliting of Program Code", + "splitTypeCodeDesc": "Intelligently block according to the syntax structure of different programming languages, avoiding splitting at places with incomplete syntax.", + "splitTypeCustom": "Custom Symbol Splitting", + "splitTypeCustomDesc": "Split documents based on custom symbols. The separator will be discarded and the split text chunks will not be affected by chunk size.", + "codeLanguage": "Programming Language", + "codeLanguageHelper": "Select the programming language for smarter code splitting based on language syntax.", + "chunkSize": "Chunk Size", + "chunkOverlap": "Chunk Overlap", + "separator": "Separator", + "separatorHelper": "Separator used for splitting text, e.g. \n\n for blank lines", + "customSeparator": "Custom Separator", + "customSeparatorHelper": "Custom separator used for splitting text, e.g. --- or ===", + "separators": "Separators List", + "separatorsInput": "Separators (comma separated)", + "separatorsHelper": "Comma-separated list of separators in priority order", + "questionGenSettings": "Question Generation Settings", + "questionGenLength": "Question Generation Length: {{length}}", + "questionMaskRemovingProbability": "Removing Question Marks Probability: {{probability}}%", + "questionGenDescription": "Set the maximum length for generated questions", + "huggingfaceSettings": "Hugging Face Settings", + "datasetUpload": "Dataset Upload Settings", + "huggingfaceToken": "Hugging Face Token", + "huggingfaceNotImplemented": "", + "concurrencyLimit": "Concurrency Limit", + "concurrencyLimitHelper": "Limit the number of tasks for generating questions and generating datasets simultaneously. ", + "saveTaskConfig": "Save Task Config", + "pdfSettings": "PDF file conversion configuration", + "minerUToken": "MinerU Token configuration", + "minerUHelper": "MinerU Token is valid for only 14 days. Please replace the Token in time", + "minerULocalUrl": "PDF Conversion (MinerU Local) URL Configuration", + "vision": "Custom large-scale vision model configuration", + "visionConcurrencyLimit": "Concurrency limit for custom large-scale vision models", + "prompts": { + "selectPromptFirst": "Please select a prompt on the left", + "customized": "Customized", + "editPrompt": "Edit Prompt", + "restoreDefault": "Restore Default", + "promptType": "Prompt Type", + "keyName": "Key Name", + "contentPlaceholder": "Please enter custom prompt content...", + "restoreDefaultContent": "Restore Default Content", + "noPromptsAvailable": "No prompts available", + "restoreSuccess": "Successfully restored to default prompt", + "restoreFailed": "Failed to restore default prompt", + "deleteError": "Error deleting prompt:", + "saveSuccess": "Prompt saved successfully", + "saveFailed": "Failed to save prompt", + "saveError": "Error saving prompt:", + "createCustomPrompt": "Create Custom Prompt", + "fetchContentError": "Failed to fetch latest prompt content:" + }, + "multiTurnSettings": "Multi-turn Conversation Settings", + "multiTurnSystemPrompt": "System Prompt", + "multiTurnSystemPromptHelper": "System prompt for multi-turn conversation generation", + "multiTurnScenario": "Conversation Scenario", + "multiTurnScenarioHelper": "Describe the conversation scenario or context", + "multiTurnRounds": "Number of Rounds", + "multiTurnRoleA": "Role A", + "multiTurnRoleAHelper": "Description of the first participant in the conversation", + "multiTurnRoleB": "Role B", + "multiTurnRoleBHelper": "Description of the second participant in the conversation", + "multiTurnDescription": "Multi-turn conversation generation configuration", + "evalQuestionSettings": "Evaluation Test Set Generation Settings", + "evalQuestionSettingsDescription": "Configure the ratio of each question type when generating test sets. A ratio of 0 means this type will not be generated", + "evalTrueFalseRatio": "True/False Ratio", + "evalSingleChoiceRatio": "Single Choice Ratio", + "evalMultipleChoiceRatio": "Multiple Choice Ratio", + "evalShortAnswerRatio": "Short Answer Ratio", + "evalOpenEndedRatio": "Open-ended Ratio", + "evalQuestionRatioHelper": "The system will automatically allocate the number of questions for each type based on the set ratios. The sum of all ratios does not need to equal a specific value" + }, + "questions": { + "autoGenerateDataset": "Auto Generate Dataset", + "autoGenerateDatasetTip": "Create background batch processing tasks: automatically query text blocks pending question generation and extract questions.", + "filterAll": "All Questions", + "filterAnswered": "With Answers", + "filterUnanswered": "Without Answers", + "filterChunkNamePlaceholder": "Filter by chunk name...", + "sourceTypeAll": "All Sources", + "sourceTypeText": "Text Source", + "sourceTypeImage": "Image Source", + "title": "Questions", + "confirmDeleteTitle": "Confirm Delete Question", + "confirmDeleteContent": "Are you sure you want to delete the question \"{{question}}\"? This action cannot be undone.", + "deleting": "Deleting question...", + "batchDeleteTitle": "Confirm Batch Delete", + "batchDeleting": "Deleting {{count}} questions...", + "deleteSuccess": "Question deleted successfully", + "deleteFailed": "Failed to delete question", + "batchDeleteSuccess": "Successfully deleted {{count}} questions", + "batchDeletePartial": "Delete completed, success: {{success}}, failed: {{failed}}", + "batchDeleteFailed": "Failed to batch delete questions", + "noQuestionsSelected": "Please select questions first", + "batchGenerateStart": "Starting to generate datasets for {{count}} questions", + "invalidQuestionKey": "Invalid question key", + "listView": "Question List", + "treeView": "Domain Tree", + "selectAll": "Select All", + "selectedCount": "Selected {{count}} questions", + "totalCount": "Total {{count}} questions", + "searchPlaceholder": "Search questions or tags...", + "searchMatch": "Match", + "searchNotMatch": "Not Match", + "deleteSelected": "Delete Selected", + "batchGenerate": "Batch Generate Datasets", + "generating": "Generating Dataset", + "generatingProgress": "Completed: {{completed}}/{{total}}", + "generatedCount": "Generated {{count}} datasets", + "pleaseWait": "Please wait...", + "selectAllLimitReached": "Selected {{count}} questions (maximum limit reached)", + "selectAllFailed": "Select all operation failed, please try again later", + "createSuccess": "Question created successfully", + "updateSuccess": "Question updated successfully", + "operationSuccess": "Operation successful", + "operationFailed": "Operation failed", + "editQuestion": "Edit Question", + "questionContent": "Question Content", + "sourceType": "Data Source Type", + "sourceType.text": "Text", + "sourceType.image": "Image", + "selectChunk": "Select Text Chunk", + "searchChunk": "Search text chunks...", + "selectImage": "Select Image", + "searchImage": "Search images...", + "selectTag": "Select Tag", + "searchTag": "Search tags...", + "createQuestion": "Create Question", + "createNormalQuestion": "Create Normal Question", + "createQuestionTemplate": "Create Question Template", + "questionPlaceholder": "Please enter your question", + "noChunkSelected": "Please select a text chunk first", + "noTagSelected": "Please select a tag", + "fetchTemplatesFailed": "Failed to fetch question templates", + "createTemplateSuccess": "Question template created successfully", + "createTemplateFailed": "Failed to create question template", + "updateTemplateSuccess": "Question template updated successfully", + "updateTemplateFailed": "Failed to update question template", + "deleteTemplateSuccess": "Question template deleted successfully", + "deleteTemplateFailed": "Failed to delete question template", + "exportQuestions": "Export Questions", + "exportScope": "Export Scope", + "exportAll": "Export All ({{count}} questions)", + "exportSelected": "Export Selected ({{count}} questions)", + "exportFormat": "Export Format", + "txtFormat": "Plain Text (Questions Only)", + "exportSuccess": "Questions exported successfully", + "exportFailed": "Failed to export questions", + "template": { + "management": "Question Templates", + "create": "Create Template", + "edit": "Edit Template", + "question": "Question Content", + "description": "Prompt", + "descriptionHelp": "Used to be included in the overall prompt when AI generates answers related to this question template, to influence the final answer generation results", + "noTemplates": "No question templates yet, click create button to add", + "deleteConfirm": "Are you sure you want to delete this question template?", + "used": "Used", + "addLabel": "Add Label", + "customFormat": "Custom Format", + "customFormatHelp": "Enter JSON format output constraint", + "customFormatInfo": "This format will be provided to the LLM as a prompt to constrain output format", + "sourceTypeInfo": "Data Source Type", + "sourceType": { + "label": "Data Source Type", + "image": "Image", + "text": "Text" + }, + "answerType": { + "label": "Answer Type", + "text": "Text", + "tags": "Tags", + "customFormat": "Custom Format" + }, + "errors": { + "questionRequired": "Please enter question content", + "labelsRequired": "Label type questions require at least one label", + "customFormatRequired": "Please enter custom format", + "invalidJson": "Invalid JSON format" + }, + "autoGenerate": "Auto-generate questions after creating template", + "autoGenerateHelpText": "Will automatically create questions based on this template for all text chunks in the project", + "autoGenerateHelpImage": "Will automatically create questions based on this template for all images in the project", + "confirmAutoGenerate": "Confirm Auto-generate Questions", + "confirmAutoGenerateTextMessage": "You have chosen to auto-generate questions. The system will create questions based on this template for all text chunks in the project.", + "confirmAutoGenerateImageMessage": "You have chosen to auto-generate questions. The system will create questions based on this template for all images in the project.", + "autoGenerateWarning": "This operation may create a large number of questions. Please confirm to continue.", + "autoGenerateSuccess": "Successfully created questions for {{count}} data sources", + "autoGeneratePartialFail": "Successfully created {{success}} questions, {{fail}} failed", + "autoGenerateFailed": "Failed to auto-generate questions" + }, + "generateSingleTurnDataset": "Generate Single-turn Dataset", + "generateSingleTurnDatasetDesc": "Generate Q&A dataset based on questions", + "generateMultiTurnDataset": "Generate Multi-Turn Dataset", + "generateImageDataset": "Generate Image Q&A Dataset", + "generateMultiTurnDatasetDesc": "Generate multi-turn conversation dataset based on questions", + "deleteConfirm": "Are you sure you want to delete this question? This action cannot be undone." + }, + "common": { + "dataSource": "Data Source", + "menu": "Menu", + "openMenu": "Open navigation menu", + "all": "All", + "jumpTo": "Jump To", + "unknownError": "Unknown Error", + "create": "Create", + "edit": "Edit", + "delete": "Delete", + "save": "Save", + "cancel": "Cancel", + "confirm": "Confirm", + "complete": "Complete", + "close": "Close", + "add": "Add", + "remove": "Remove", + "loading": "Loading...", + "yes": "Yes", + "no": "No", + "confirmDelete": "Confirm Delete", + "saving": "Saving...", + "deleting": "Deleting...", + "actions": "Actions", + "confirmDeleteDataSet": "Are you sure you want to delete this dataset? This action cannot be undone.", + "noData": "None", + "failed": "Failed", + "success": "Success", + "backToList": "Back to List", + "label": "Label", + "confirmDeleteDescription": "Are you sure you want to delete this File? This action cannot be undone.", + "more": "More", + "import": "Import", + "export": "Export", + "fetchError": "Fetching data failed", + "confirmDeleteQuestion": "Are you sure you want to delete this question? This action cannot be undone.", + "deleteSuccess": "Delete successful", + "visitGitHub": "Visit GitHub Repository", + "syncOldData": "Sync Old Data", + "copy": "Copy", + "enabled": "Enabled", + "disabled": "Disabled", + "copied": "Copied", + "generating": "Generating...", + "processing": "Processing...", + "items": "items", + "detailInfo": "Detail Info", + "reset": "Reset", + "apply": "Apply", + "mainNavigation": "Main Navigation", + "goHome": "Go to Home", + "goToHomePage": "Go to Home Page", + "mobileNavigation": "Mobile Navigation Menu", + "navigation": "Navigation", + "closeMenu": "Close Menu", + "documentation": "Documentation", + "viewOnGitHub": "View on GitHub", + "back": "Back", + "refresh": "Refresh", + "expand": "Expand All", + "collapse": "Collapse" + }, + "home": { + "title": "Easy Dataset", + "subtitle": "A powerful tool for creating fine-tuning datasets for Large Language Models", + "createProject": "Create Project", + "searchDataset": "Search Public Datasets" + }, + "projects": { + "reuseConfig": "Reuse Model Config", + "noReuse": "No Configuration Reuse", + "selectProject": "Select Project", + "fetchFailed": "Failed to fetch project list", + "fetchError": "Error fetching project list", + "loading": "Loading your projects...", + "createFailed": "Failed to create project", + "createError": "Error creating project", + "createNew": "Create New Project", + "saveFailed": "Failed to save project", + "id": "Project ID", + "name": "Project Name", + "description": "Project Description", + "questions": "Questions", + "datasets": "Datasets", + "evalDatasets": "Eval Datasets", + "tokens": "Tokens", + "lastUpdated": "Last Updated", + "viewDetails": "View Details", + "createFirst": "Please create a project first", + "noProjects": "No projects found", + "notExist": "The project does not exist.", + "createProject": "Create Project", + "deleteConfirm": "Are you sure you want to delete this project? This action cannot be undone.", + "deleteSuccess": "Project deleted successfully", + "deleteFailed": "Failed to delete project", + "backToHome": "Back to Home", + "deleteConfirmTitle": "Confirm Delete" + }, + "textSplit": { + "dragToUpload": "Drag files to upload", + "fileList": "File List", + "autoGenerateQuestions": "Auto Generate", + "autoGenerateQuestionsTip": "Create background batch processing tasks: automatically query text blocks pending question generation and extract questions.", + "exportChunks": "Export Chunks", + "allChunks": "All Text Chunks", + "generatedQuestions2": "With Questions", + "ungeneratedQuestions": "Without Questions", + "contentKeyword": "Text Chunk Content", + "contentKeywordPlaceholder": "Enter keywords to search chunk content", + "characterRange": "Character Range", + "questionStatus": "Question Status", + "noFilesUploaded": "No files uploaded yet", + "unknownFile": "Unknown File", + "fetchFilesFailed": "Failed to fetch files", + "editTag": "Edit Tag", + "deleteTag": "Delete Tag", + "addTag": "Add Tag", + "selectedCount": "Selected {{count}} text chunks", + "totalCount": "total {{count}} text chunks", + "batchGenerateQuestions": "Batch Generate", + "batchDeleteChunks": "Batch Delete", + "batchDeleteChunksConfirmTitle": "Confirm Batch Delete", + "batchDeleteChunksConfirmMessage": "Are you sure you want to delete the selected {{count}} text chunks? This action cannot be undone.", + "uploadedDocuments": "Uploaded {{count}} Documents", + "title": "Texts", + "uploadNewDocument": "Upload New Document", + "selectFile": "Select File", + "markdownOnly": "Currently only supports Markdown (.md) format files", + "supportedFormats": "Supported formats: .pdf .md, .txt, .docx", + "uploadAndProcess": "Upload and Process", + "selectedFiles": "Selected Files ({{count}})", + "oneFileMessage": "File upload not allowed, please delete the existing file first", + "mutilFileMessage": "After uploading a new file, the domain tree will be rebuilt", + "noChunks": " No text chunks found", + "chunkDetails": "Chunk Details: {{chunkId}}", + "fetchChunksFailed": "Failed to fetch text chunks", + "fetchChunksError": "Error fetching text chunks", + "fileResultReceived": "File result received", + "fileUploadSuccess": "File uploaded successfully", + "splitTextFailed": "Text splitting failed", + "splitTextError": "Error splitting text", + "deleteChunkFailed": "Failed to delete text chunk", + "deleteChunkError": "Error deleting text chunk", + "selectModelFirst": "Please select a model first, you can select from the top navigation bar", + "modelNotAvailable": "Selected model is not available, please select again", + "generateQuestionsFailed": "Failed to generate questions for chunk {{chunkId}}", + "questionsGenerated": "{{total}} questions generated", + "customSplitMode": "Custom Split Mode", + "customSplitInstructions": "Select text to add split points. The system will place split markers at your selected positions.", + "splitPointsList": "Added Split Points", + "saveSplitPoints": "Save Split Points", + "confirmCustomSplitTitle": "Confirm Split Replacement", + "confirmCustomSplitMessage": "Note: Custom split points will replace the previously automated split results for this document. Do you want to continue?", + "customSplitSuccess": "Custom split saved successfully", + "customSplitFailed": "Failed to save custom split", + "missingRequiredData": "Missing required data", + "chunksPreview": "Chunks Size Preview", + "chunk": "Chunk", + "characters": " chars", + "questionsGeneratedSuccess": "Successfully generated {{total}} questions for the text chunk", + "generateQuestionsForChunkFailed": "Failed to generate questions for chunk {{chunkId}}", + "generateQuestionsForChunkError": "Error generating questions for chunk {{chunkId}}", + "generateQuestionsError": "Error generating questions", + "partialSuccess": "Partially successful question generation ({{successCount}}/{{total}}), {{errorCount}} chunks failed", + "allSuccess": "Successfully generated {{totalQuestions}} questions for {{successCount}} text chunks", + "fileDeleted": "File {{fileName}} deleted, refreshing text chunk list", + "tabs": { + "smartSplit": "Smart Split", + "domainAnalysis": "Domain Analysis" + }, + "loading": "Loading...", + "fetchingDocuments": "Fetching document data", + "processing": "Processing...", + "progressStatus": "Selected {{total}} text chunks, {{completed}} completed", + "processingPleaseWait": "Processing, please wait!", + "oneFileLimit": "File upload not allowed, there is already an uploaded file", + "unsupportedFormat": "Unsupported file format: {{files}}", + "modelInfoParseError": "Failed to parse model information", + "uploadFailed": "Upload failed", + "uploadSuccess": "Successfully uploaded {{count}} files", + "deleteFailed": "Failed to delete file", + "deleteSuccess": "File {{fileName}} has been successfully deleted", + "generatedQuestions": "{{count}} Questions", + "generatedEvalQuestions": "{{count}} Test Questions", + "viewDetails": "View Details", + "generateQuestions": "Generate Questions", + "generateEvalQuestions": "Generate Test Set", + "evalQuestionsGeneratedSuccess": "Successfully generated {{total}} evaluation questions", + "generateEvalQuestionsFailed": "Failed to generate evaluation questions", + "dataCleaning": "Data Cleaning", + "batchDataCleaning": "Batch Data Cleaning", + "autoDataCleaning": "Auto Data Cleaning", + "autoDataCleaningTip": "Create background batch processing task: automatically clean all text chunks", + "autoEvalGeneration": "Auto Generate Eval Dataset", + "autoEvalGenerationTip": "Create background batch processing task: automatically generate evaluation datasets for all text chunks without eval questions", + "autoTasks": "Auto Tasks", + "dataCleaningSuccess": "Data cleaning completed, original length: {{originalLength}}, cleaned length: {{cleanedLength}}", + "dataCleaningFailed": "Data cleaning failed for text chunk {{chunkId}}", + "dataCleaningForChunkSuccess": "Data cleaning completed for text chunk {{chunkId}}", + "dataCleaningForChunkFailed": "Data cleaning failed for text chunk {{chunkId}}", + "dataCleaningForChunkError": "Data cleaning error for text chunk {{chunkId}}", + "dataCleaningPartialSuccess": "Partial data cleaning success ({{successCount}}/{{total}}), {{errorCount}} text chunks failed", + "dataCleaningAllSuccess": "Successfully completed data cleaning for {{successCount}} text chunks", + "charsCount": "Characters", + "pdfProcessStatus": "Total {{total}} files, {{completed}} have been converted", + "pdfPageProcessStatus": "Processing {{fileName}}, {{completed}} out of {{total}} pages converted", + "pdfProcessing": "Converting file...", + "pdfProcessingFailed": "File conversion failed!", + "pdfProcess": "File detected!", + "selectPdfProcessingStrategy": "Please select the file processing method:", + "pdfProcessingStrategyDefault": "Default", + "pdfProcessingStrategyDefaultHelper": "Use the built-in PDF parsing strategy", + "pdfProcessingStrategyMinerUHelper": "Use MinerU API for parsing. Please configure the MinerU API Token first", + "pdfProcessingStrategyVision": "Custom Vision Model", + "pdfProcessingStrategyVisionHelper": "Use a custom vision model for parsing", + "pdfProcessingToast": "File detected. The system will create a background task to parse the file", + "pdfProcessingWaring": "There is a file processing task in progress. It is recommended to wait for the task to complete before performing other operations, otherwise it may affect the quality of data generation!", + "pdfProcessingLoading": "Executing file conversion task, please wait for the task to complete before uploading new files...", + "basicPdfParsing": "Basic PDF Parsing", + "basicPdfParsingDesc": "Capable of identifying the key outlines of simple PDF files with high speed", + "mineruApiDesc": "Capable of identifying complex PDF files, including formulas and charts (Requires configuration of MinerU API Key)", + "mineruLocalDesc": "Capable of identifying complex PDF files, including formulas and charts (requires configuring MinerU Local URL)", + "mineruApiDescDisabled": "Please go to Project Settings - Task Configuration to set up MinerU Token", + "mineruLocalDisabled": "Please first set up MinerU Local URL in [Project Settings - Task Configuration]", + "mineruWebPlatform": "MinerU Online Platform Parsing", + "mineruWebPlatformDesc": "Capable of identifying complex PDF files, including formulas and charts (Requires redirecting to another website)", + "mineruSelected": "Selected to use MinerU for parsing PDFs", + "mineruLocalSelected": "Selected to use MinerU Local for parsing PDFs", + "customVisionModel": "Custom Vision Model Parsing", + "customVisionModelDesc": "Capable of identifying complex PDF files, including formulas and charts (Requires adding vision model configuration to the model configuration)", + "customVisionModelSelected": "Selected to use the visual large model {{name}} ({{provider}}) for parsing PDFs", + "defaultSelected": "Selected to use the default built-in strategy for parsing PDFs", + "download": "Download the document", + "deleteFile": "Delete document", + "batchDelete": "Batch Delete ({{count}})", + "batchDeleteTitle": "Batch Delete Files", + "batchDeleteConfirm": "Are you sure you want to delete the selected {{count}} files? This action cannot be undone.", + "batchDeleteSuccess": "Successfully deleted {{count}} files", + "batchDeleteFailed": "Batch delete failed", + "searchFiles": "Search files...", + "searchResults": "Found {{count}} files ({{total}} total)", + "noSearchResults": "No files found containing \"{{searchTerm}}\"", + "noResultsOnCurrentPage": "No search results on current page, please return to the first page", + "noDataOnCurrentPage": "No data on current page", + "viewChunk": "View Text Chunk", + "editChunk": "Edit Text Chunk {{chunkId}}", + "editChunkSuccess": "Text chunk edited successfully", + "editChunkFailed": "Failed to edit text chunk", + "editChunkError": "Error occurred when editing text chunk", + "deleteFileWarning": "Warning: Deleting this document will also delete the following related items", + "deleteFileWarningChunks": "All associated text chunks", + "deleteFileWarningQuestions": "All questions generated from these chunks", + "deleteFileWarningDatasets": "All datasets created from these questions", + "domainTree": { + "firstUploadTitle": "Domain Tree Generation", + "uploadTitle": "Document Upload - Domain Tree Processing", + "deleteTitle": "Document Deletion - Domain Tree Processing", + "reviseOption": "Revise Domain Tree", + "reviseDesc": "Modify the current domain tree based on added or deleted documents, only affecting changed parts", + "rebuildOption": "Rebuild Domain Tree", + "rebuildDesc": "Generate a completely new domain tree based on all document contents", + "keepOption": "Keep Unchanged", + "keepDesc": "Keep the current domain tree structure unchanged without any modifications" + } + }, + "domain": { + "title": "Domain Knowledge Tree", + "addRootTag": "Add Root Tag", + "addFirstTag": "Add First Tag", + "noTags": "No domain tags available", + "docStructure": "Document Structure", + "noToc": "No table of contents available. Please upload and process the document first.", + "editTag": "Edit Tag", + "deleteTag": "Delete Tag", + "addChildTag": "Add Child Tag", + "deleteTagConfirmTitle": "Delete Tag", + "deleteTagConfirmMessage": "Are you sure you want to delete tag \"{{tag}}\"?", + "deleteWarning": "This action will delete this tag and all its child tags, questions, and datasets. This cannot be undone!", + "dialog": { + "addTitle": "Add Tag", + "editTitle": "Edit Tag", + "addChildTitle": "Add Child Tag", + "inputRoot": "Please enter a new root tag name", + "inputEdit": "Please edit the tag name", + "inputChild": "Please add a child tag for \"{label}\"", + "labelName": "Tag Name", + "saving": "Saving...", + "save": "Save", + "deleteConfirm": "Are you sure to delete tag \"{label}\"?", + "deleteWarning": "This action will delete all child tags and cannot be undone.", + "emptyLabel": "Tag name cannot be empty" + }, + "tabs": { + "tree": "Domain Tree", + "structure": "Document Structure" + }, + "errors": { + "saveFailed": "Failed to save tags" + }, + "messages": { + "updateSuccess": "Tags updated successfully" + } + }, + "export": { + "alpacaSettings": "Alpaca Format Settings", + "questionFieldType": "Question Field Type", + "useInstruction": "Use instruction field", + "useInput": "Use input field", + "customInstruction": "Custom Instruction Content", + "instructionPlaceholder": "Enter fixed instruction content", + "instructionHelperText": "When using input field, you can specify fixed instruction content here", + "title": "Export", + "format": "Format", + "fileFormat": "File Format", + "systemPrompt": "System Prompt", + "systemPromptPlaceholder": "Please enter system prompt...", + "ReasoninglanguagePlaceholder": "Please enter Reasoning language : English or Chinese or others", + "onlyConfirmed": "Only export confirmed data", + "example": "Format Example", + "confirmExport": "Confirm Export", + "includeCOT": "Include Chain of Thought", + "cotDescription": "Includes the reasoning process before the final answer", + "customFormat": "Custom Format", + "customFormatSettings": "Custom Format Settings", + "questionFieldName": "Question Field Name", + "multilingualThinkingFormat": "Multilingual‑Thinking", + "Reasoninglanguage": "Reasoning language", + "sampleInstruction": "Human instruction (required)", + "sampleOutput": "Model response (required)", + "sampleSystem": "System prompt (optional)", + "sampleInstruction2": "Second instruction", + "sampleOutput2": "Second response", + "sampleSystemShort": "System prompt", + "fixedInstruction": "Fixed instruction content", + "sampleInput": "Human question (required)", + "sampleInput2": "Second question", + "sampleInputOptional": "Human input (optional)", + "sampleUserMessage": "Human instruction", + "sampleAssistantMessage": "Model response", + "sampleAnalysis": "Model's chain of thought content", + "sampleFinal": "Model response", + "sampleThinking": "Model's chain of thought content", + "fetchLabelStatsError": "Failed to fetch label statistics:" + }, + "import": { + "title": "Import", + "fileUpload": "File Upload", + "fileUploadDescription": "Upload local files to import datasets", + "mapFields": "Field Mapping", + "importing": "Importing", + "uploadFile": "Upload File", + "supportedFormats": "Supports JSON, JSONL, CSV format files", + "dragDropFile": "Drag and drop files here or click to select files", + "dropFileHere": "Release to upload file", + "maxFileSize": "Maximum file size: 50MB", + "processingFile": "Processing file...", + "uploadedFiles": "Uploaded Files", + "uploadError": "File upload failed, please check if the file format is correct", + "selectFromSource": "Select dataset from {{source}}", + "sourceDescription": "Enter dataset name or keywords to search", + "datasetName": "Dataset Name", + "hfPlaceholder": "e.g.: squad, glue, imdb", + "msPlaceholder": "e.g.: damo/nlp_bert_document-classification", + "search": "Search", + "searching": "Searching", + "searchResults": "Search Results", + "downloads": "Downloads", + "download": "Download", + "downloading": "Downloading", + "hfNote": "Note: Downloading large datasets may take a long time, it is recommended to select smaller datasets for testing.", + "msNote": "Note: ModelScope dataset download requires network connection, please ensure network connectivity.", + "fieldMapping": "Field Mapping", + "mappingDescription": "Please map the source data fields to target fields. The system has automatically identified possible mapping relationships, you can adjust as needed.", + "selectMapping": "Select Field Mapping", + "questionField": "Question Field", + "answerField": "Answer Field", + "cotField": "Chain of Thought Field", + "tagsField": "Tags Field", + "selectField": "Select Field", + "questionDesc": "User's question or input content (required)", + "answerDesc": "AI's answer or output content (required)", + "cotDesc": "Chain of thought or reasoning process (optional)", + "tagsDesc": "Tag array, multiple tags separated by commas (optional)", + "dataPreview": "Data Preview", + "previewNote": "Shows first 3 records, each field value displays up to 100 characters", + "confirmMapping": "Confirm Mapping", + "requiredFields": "Please select at least question and answer field mappings", + "mappingRequired": "Question and answer fields are required", + "duplicateMapping": "Cannot map multiple target fields to the same source field", + "noPreviewData": "No preview data available", + "preparingData": "Preparing data...", + "uploadingData": "Uploading data...", + "processing": "Processing... {{processed}}/{{total}}", + "completed": "Import completed", + "importStats": "Import Statistics", + "total": "Total: {{count}}", + "success": "Success: {{count}}", + "failed": "Failed: {{count}}", + "source": "Data Source", + "description": "Description", + "errors": "Error Messages", + "moreErrors": "{{count}} more errors not shown...", + "importSuccess": "Dataset import completed!", + "enterDatasetName": "Please enter dataset name", + "noDatasetFound": "No matching datasets found", + "complete": "Complete", + "addToEval": "Add to Eval Dataset", + "addToEvalSuccess": "Successfully added to eval dataset", + "addToEvalFailed": "Failed to add", + "generateEvalVariant": "Generate Eval Variant", + "selectModelFirst": "Please select a model first", + "generateVariantFailed": "Failed to generate variant", + "saveVariantSuccess": "Saved to eval dataset", + "saveVariantFailed": "Failed to save", + "evalVariantTitle": "Generate Evaluation Variant", + "evalVariantHint": "AI has generated a new test variant based on the original Q&A. You can edit and save it.", + "saveToEval": "Save to Eval Dataset", + "evalVariantConfigHint": "Please select the question type and quantity. AI will rewrite based on the current Q&A pair.", + "questionType": "Question Type", + "typeOpenEnded": "Open-ended", + "typeSingleChoice": "Single Choice", + "typeMultipleChoice": "Multiple Choice", + "typeTrueFalse": "True/False", + "typeShortAnswer": "Short Answer", + "generateCount": "Generate Count", + "evalVariantPreviewHint": "You can edit the generated questions and save them to the evaluation set after confirmation.", + "questionIndex": "Question {{index}}", + "options": "Options (JSON Array)", + "optionsHint": "e.g.: [\"Option A\", \"Option B\"]", + "answerArrayHint": "For multiple choice, please enter an array, e.g. [\"A\", \"C\"]", + "answerBoolHint": "For True/False, please enter ✅ or ❌", + "evalVariantPreviewTitle": "Confirm Generated Questions", + "generate": "Generate" + }, + "export_extended": { + "answerFieldName": "Answer Field Name", + "cotFieldName": "Cot Field Name", + "includeLabels": "Include Labels", + "includeChunk": "Include Text Chunk", + "questionOnly": "Export Questions Only", + "localTab": "Local Export", + "llamaFactoryTab": "Llama Factory", + "huggingFaceTab": "HuggingFace", + "configExists": "Configuration File Exists", + "configPath": "Configuration File Path", + "updateConfig": "Update LLaMA Factory Configuration", + "noConfig": "No configuration file exists, click the button below to generate", + "generateConfig": "Generate LLaMA Factory Configuration", + "huggingFaceComingSoon": "HuggingFace export feature coming soon", + "uploadToHuggingFace": "Upload to HuggingFace", + "datasetName": "Dataset Name", + "datasetNameHelp": "Format: username/dataset-name", + "privateDataset": "Private Dataset", + "datasetSettings": "Dataset Settings", + "exportOptions": "Export Options", + "uploadSuccess": "Dataset uploaded successfully to HuggingFace", + "viewOnHuggingFace": "View on HuggingFace", + "noTokenWarning": "Hugging Face Token not found. Please configure it in project settings.", + "goToSettings": "Go to Settings", + "tokenHelp": "You can get your token from HuggingFace settings page" + }, + "datasets": { + "loadingDataset": "Loading Dataset...", + "datasetNotFound": "Dataset Not Found", + "optimizeTitle": "AI Optimize", + "optimizeAdvice": "Optimize Advice", + "optimizePlaceholder": "Please enter your suggestions for improving the answer, and AI will optimize the answer and reasoning chain based on your suggestions", + "generatingDataset": "Generating Dataset", + "aiOptimizeAdvicePlaceholder": "Please enter your suggestions for improving the answer, and AI will optimize the answer and reasoning chain based on your suggestions", + "aiOptimizeAdvice": "Please enter your suggestions for improving the answer, and AI will optimize the answer and reasoning chain based on your suggestions", + "aiOptimize": "AI Optimize", + "partialSuccess": "Partially successful dataset generation ({{successCount}}/{{total}}), {{failCount}} questions failed", + "generating": "Generating Dataset", + "generateError": "Failed to generate dataset", + "management": "Datasets", + "question": "Question", + "filterAll": "All", + "filterConfirmed": "Confirmed", + "filterUnconfirmed": "Unconfirmed", + "createdAt": "Created At", + "model": "Model", + "domainTag": "Domain Tag", + "cot": "COT", + "answer": "Answer", + "chunkId": "Text Chunk", + "confirmed": "Confirmed", + "noTag": "No Tag", + "noData": "No Data", + "rowsPerPage": "Rows per page", + "pagination": "{{from}}-{{to}} of {{count}}", + "confirmDeleteMessage": "Are you sure you want to delete this dataset? This action cannot be undone.", + "questionLabel": "Question", + "fetchFailed": "Failed to fetch dataset", + "deleteFailed": "Failed to delete dataset", + "deleteSuccess": "Delete successful", + "exportSuccess": "Dataset exported successfully", + "exportFailed": "Export failed", + "exportProgress": "Export Progress", + "exportingData": "Exporting dataset", + "processedCount": "Processed {{processed}} / {{total}} items", + "exportInProgress": "Fetching data, please wait...", + "exportFinalizing": "Generating file, almost done...", + "loading": "Loading datasets...", + "stats": "Total {{total}} datasets, {{confirmed}} confirmed ({{percentage}}%)", + "selected": "Total selected: {{count}}", + "batchconfirmDeleteMessage": "Are you sure you want to delete {{count}} selected questions? This action cannot be undone.", + "batchDelete": "Batch Delete", + "batchDeleteProgress": "Completed: {{completed}}/{{total}}", + "batchDeleteCount": "Delete count: {{count}}", + "searchPlaceholder": "Search datasets...", + "fieldQuestion": "Question", + "fieldAnswer": "Answer", + "fieldCOT": "COT", + "fieldLabel": "Domain Label", + "moreFilters": "More Filters", + "filtersTitle": "Filter Options", + "filterConfirmationStatus": "Confirmation Status", + "filterCotStatus": "Chain of Thought Status", + "filterHasCot": "Has CoT", + "filterNoCot": "No CoT", + "filterScoreRange": "Rating Range", + "filterNoteKeyword": "Note Keyword", + "filterNoteKeywordPlaceholder": "Enter note keyword...", + "filterChunkName": "Chunk Name", + "filterChunkNamePlaceholder": "Enter chunk name...", + "filterCustomTag": "Custom Tag", + "resetFilters": "Reset", + "applyFilters": "Apply", + "viewDetails": "View Details", + "datasetDetail": "Dataset Details", + "metadata": "Metadata", + "confirmSave": "Confirm Save", + "unconfirm": "Unconfirm", + "unconfirming": "Unconfirming...", + "uncategorized": "Uncategorized", + "questionCount": "{{count}} Questions", + "source": "Source", + "generateDataset": "Generate Dataset", + "generateNotImplemented": "Dataset generation is not implemented", + "generateSuccess": "Successfully generated dataset for: {{question}}", + "generateFailed": "Failed to generate dataset: {{error}}", + "noTagsAndQuestions": "No tags and questions available", + "answerCount": "{{count}} Answers", + "answered": "Answered", + "enableShortcuts": "Page shortcut key", + "shortcutsHelp": "Press ← forward, press → backward, press y to confirm, press d to delete", + "filterDistill": "Distilled Dataset", + "filterDistillYes": "Distilled Dataset", + "filterDistillNo": "Non-distilled Dataset", + "evaluation": "Dataset Evaluation", + "rating": "Rating", + "ratingExcellent": "Excellent", + "ratingGood": "Good", + "ratingAverage": "Average", + "ratingPoor": "Poor", + "ratingVeryPoor": "Very Poor", + "ratingUnrated": "Unrated", + "customTags": "Custom Tags", + "addCustomTag": "Add custom tag...", + "note": "Note", + "addNote": "Add note...", + "noNote": "No notes", + "clickToAddNote": "Click to add note...", + "enterNote": "Enter note...", + "noteShortcuts": "Ctrl+Enter to save, Esc to cancel", + "aiEvaluation": "AI Quality Assessment", + "addToEval": "Add to Eval Dataset", + "addToEvalSuccess": "Successfully added to eval dataset", + "addToEvalFailed": "Failed to add", + "generateEvalVariant": "Generate Eval Variant", + "generateVariantFailed": "Failed to generate variant", + "saveVariantSuccess": "Saved to eval dataset", + "saveVariantFailed": "Failed to save", + "evalVariantTitle": "Generate Evaluation Variant", + "evalVariantPreviewTitle": "Confirm Generated Questions", + "saveToEval": "Save to Eval Dataset", + "evalVariantConfigHint": "Please select the question type and quantity. AI will rewrite based on the current Q&A pair.", + "questionType": "Question Type", + "typeOpenEnded": "Open-ended", + "typeSingleChoice": "Single Choice", + "typeMultipleChoice": "Multiple Choice", + "typeTrueFalse": "True/False", + "typeShortAnswer": "Short Answer", + "generateCount": "Generate Count", + "evalVariantPreviewHint": "You can edit the generated questions and save them to the evaluation set after confirmation.", + "questionIndex": "Question {{index}}", + "options": "Options (JSON Array)", + "optionsHint": "e.g.: [\"Option A\", \"Option B\"]", + "answerArrayHint": "For multiple choice, please enter an array, e.g. [\"A\", \"C\"]", + "answerBoolHint": "For True/False, please enter ✅ or ❌", + "generate": "Generate", + "updateSuccess": "Update successful", + "updateFailed": "Update failed", + "evaluate": "Evaluate", + "evaluating": "Evaluating...", + "batchEvaluate": "Batch Evaluate", + "selectModelFirst": "Please select a model first", + "evaluateSuccess": "Evaluation completed! Score: {{score}}/5", + "evaluateFailed": "Evaluation failed", + "evaluateError": "Evaluation failed: {{error}}", + "batchEvaluateStarted": "Batch evaluation task started, processing in background", + "batchEvaluateStartFailed": "Failed to start batch evaluation", + "batchEvaluateFailed": "Batch evaluation failed: {{error}}", + "scoreRange": "{{min}} - {{max}} points", + "singleTurn": "Single-turn Q&A Dataset", + "multiTurn": "Multi-turn Conversation Dataset", + "imageQA": "Image Q&A Dataset", + "conversationDetail": "Multi-turn Conversation Details", + "conversationContent": "Conversation Content", + "basicInfo": "Basic Information", + "firstQuestion": "First Question", + "conversationScenario": "Conversation Scenario", + "conversationRounds": "Conversation Rounds", + "modelUsed": "Model Used", + "qualityScore": "Quality Score", + "notes": "Notes", + "createTime": "Create Time", + "notSet": "Not Set", + "noTags": "No Tags", + "noNotes": "No Notes", + "notEvaluated": "Not Evaluated", + "round": "Round {{round}}", + "system": "System", + "user": "User", + "assistant": "Assistant", + "confirmDelete": "Confirm Delete", + "confirmDeleteConversation": "Are you sure you want to delete this multi-turn conversation dataset? This action cannot be undone.", + "conversationNotFound": "Conversation dataset not found", + "fetchDataFailed": "Failed to fetch data", + "saveFailed": "Save failed", + "saveSuccess": "Save successful", + "saving": "Saving...", + "inputTagsPlaceholder": "Enter tags, separated by spaces", + "addNotesPlaceholder": "Add notes", + "noConversations": "No multi-turn conversations", + "notRated": "Not Rated", + "minScore": "Min Score", + "maxScore": "Max Score", + "unconfirmed": "Unconfirmed" + }, + "rating": { + "veryPoor": "Very Poor", + "poor": "Poor", + "belowAverage": "Below Average", + "fair": "Fair", + "average": "Average", + "good": "Good", + "veryGood": "Very Good", + "excellent": "Excellent", + "outstanding": "Outstanding", + "perfect": "Perfect", + "unrated": "Unrated" + }, + "tags": { + "noTags": "No tags", + "addTag": "Add tag...", + "addCustomTag": "Add custom tag", + "maxTagsReached": "Maximum {{maxTags}} tags reached", + "availableTagsHint": "Select from existing tags or enter new ones" + }, + "update": { + "newVersion": "New Version", + "newVersionAvailable": "New Version Available", + "currentVersion": "Current Version", + "latestVersion": "Latest Version", + "downloadNow": "Download Now", + "downloading": "Downloading", + "installNow": "Install Now", + "updating": "Updating...", + "updateNow": "Update Now", + "viewRelease": "View Release Notes", + "checking": "Checking for updates...", + "noUpdates": "Already up to date", + "updateError": "Update Error", + "updateSuccess": "Update Successful", + "restartRequired": "Restart Required", + "restartNow": "Restart Now", + "restartLater": "Restart Later" + }, + "datasetSquare": { + "title": "Dataset Square", + "subtitle": "Discover and explore various public dataset resources to support your model training and research", + "searchPlaceholder": "Search dataset keywords...", + "searchVia": "Search via", + "categoryTitle": "Dataset Categories", + "categories": { + "all": "All", + "popular": "Popular", + "chinese": "Chinese Resources", + "english": "English Resources", + "research": "Research Data", + "multimodal": "Multimodal" + }, + "foundResources": "Found {{count}} dataset resources", + "currentFilter": "Current filter: {{category}}", + "noDatasets": "No datasets found matching your criteria", + "tryOtherCategories": "Please try other categories or return to view all datasets", + "dataset": "Dataset", + "viewDataset": "View Dataset" + }, + "playground": { + "title": "Model Testing", + "selectModelFirst": "Please select a model", + "sendFirstMessage": "Send your first message to start testing", + "inputMessage": "Enter message...", + "send": "Send", + "outputMode": "Output Mode", + "normalOutput": "Normal Output", + "streamingOutput": "Streaming Output", + "clearConversation": "Clear Conversation", + "selectModelMax3": "Please select up to 3 models to test", + "reasoningProcess": "Reasoning Chain" + }, + "chunks": { + "title": "Text Chunk", + "defaultTitle": "Default Title" + }, + "documentation": "Documentation", + "models": { + "configNotFound": "Model config not found", + "parseError": "Failed to parse model config", + "fetchFailed": "Failed to fetch model", + "saveFailed": "Failed to save model config", + "pleaseSelectModel": "Please select at least one model", + "title": "Model Settings", + "add": "Add Model", + "unselectedModel": "Unselected Model", + "unconfiguredAPIKey": "Unconfigured API Key", + "saveAllModels": "Save All Models", + "edit": "Edit", + "delete": "Delete", + "modelId": "Model ID", + "modelName": "Model Name", + "modelNamePlaceholder": "Enter model name (optional, defaults to Model ID)", + "modelIdPlaceholder": "Enter model ID (e.g., gpt-4o)", + "endpoint": "Endpoint", + "apiKey": "API Key", + "provider": "Provider", + "localModel": "Local Model", + "apiKeyConfigured": "API Key Configured", + "apiKeyNotConfigured": "API Key Not Configured", + "temperature": "Temperature", + "maxTokens": "Max Tokens", + "maxTokensInputTip": "Slider range: 1-{{max}}. You can also input any positive integer.", + "topP": "Top P", + "type": "Model Type", + "text": "Large Language Model", + "vision": "Vision Large Model", + "typeTips": "If you want to use a custom vision model to parse PDFs, please configure at least one vision large model", + "refresh": "Refresh Models", + "configuredModels": "Configured Models", + "unconfiguredModels": "Unconfigured Models", + "noConfiguredModels": "No configured models", + "noUnconfiguredModels": "No unconfigured models", + "checkEndpointHealth": "Check endpoint health", + "checkAllEndpointHealth": "Check all endpoints", + "endpointHealthy": "Endpoint is healthy", + "endpointCheckFailed": "Endpoint check failed", + "endpointMissing": "Endpoint is empty", + "endpointReachableModelMissing": "Endpoint reachable, but current model is not in the returned model list", + "healthCheckSummary": "Health check completed: {{okCount}} healthy, {{failCount}} failed", + "checking": "Checking...", + "healthy": "Healthy", + "reachable": "Reachable", + "unhealthy": "Unhealthy", + "notChecked": "Not checked" + }, + "stats": { + "ongoingProjects": "Ongoing Projects", + "questionCount": "Question Count", + "generatedDatasets": "Generated Datasets", + "supportedModels": "Supported Models" + }, + "migration": { + "title": "Project Migration", + "description": "Some projects need to be migrated to the database. Migration can improve performance and support more features.", + "projectsList": "Unmigrated Projects", + "migrate": "Start Migration", + "migrating": "Migrating...", + "success": "Successfully migrated {{count}} projects", + "failed": "Migration failed", + "checkFailed": "Failed to check unmigrated projects", + "checkError": "Error checking unmigrated projects", + "starting": "Starting migration task...", + "processing": "Processing migration task...", + "completed": "Migration completed", + "startFailed": "Failed to start migration task", + "statusFailed": "Failed to get migration status", + "taskNotFound": "Migration task not found", + "progressStatus": "Migrated {{completed}}/{{total}} projects", + "openDirectory": "Open Directory", + "deleteDirectory": "Delete Directory", + "confirmDelete": "Are you sure you want to delete this project directory? This action cannot be undone.", + "openDirectoryFailed": "Failed to open project directory", + "deleteDirectoryFailed": "Failed to delete project directory" + }, + "distill": { + "title": "Distill", + "generateRootTags": "Generate Root Tags", + "generateSubTags": "Generate Sub Tags", + "generateQuestions": "Generate Questions", + "generateRootTagsTitle": "Generate Root Domain Tags", + "generateSubTagsTitle": "Generate Sub Tags for {{parentTag}}", + "generateQuestionsTitle": "Generate Questions for {{tag}}", + "parentTag": "Parent Tag", + "parentTagPlaceholder": "Enter parent tag name (e.g., Sports, Technology)", + "parentTagHelp": "Enter a domain topic, and the system will generate related tags based on it", + "generateQuestionsError": "Failed to generate questions", + "tagCount": "Number of Tags", + "tagCountHelp": "Enter the number of tags to generate, maximum is 100", + "questionCount": "Number of Questions", + "questionCountHelp": "Enter the number of questions to generate, maximum is 100", + "generatedTags": "Generated Tags", + "generatedQuestions": "Generated Questions", + "tagPath": "Tag Path", + "noTags": "No Tags", + "noQuestions": "No Questions", + "clickGenerateButton": "Click the generate button above to create tags", + "selectModelFirst": "Please select a model first", + "selectModel": "Select Model", + "generateTagsError": "Failed to generate tags", + "generateTags": "Generate Tags", + "subTags": "sub-tags", + "questions": "questions", + "deleteTagConfirmTitle": "Confirm to delete tag?", + "editTagTitle": "Edit Tag", + "tagName": "Tag Name", + "labelRequired": "Tag name cannot be empty", + "tagUpdateSuccess": "Tag updated successfully", + "tagUpdateFailed": "Failed to update tag", + "unknownTag": "Unknown Tag", + "autoDistillButton": "Auto Distill Dataset", + "autoDistillTitle": "Automated Dataset Distillation Configuration", + "distillTopic": "Distillation Topic", + "tagLevels": "Tag Levels", + "tagLevelsHelper": "Set the number of levels, maximum is {{max}}", + "tagsPerLevel": "Tags Per Level", + "tagsPerLevelHelper": "Number of sub-tags to generate under each parent tag, maximum is {{max}}", + "questionsPerTag": "Questions Per Tag", + "questionsPerTagHelper": "Number of questions to generate for each leaf tag, maximum is {{max}}", + "estimationInfo": "Task Estimation Info", + "estimatedTags": "Estimated Tags", + "estimatedQuestions": "Estimated Questions", + "currentTags": "Current Tags", + "currentQuestions": "Current Questions", + "newTags": "New Tags", + "newQuestions": "New Questions", + "startAutoDistill": "Start Auto Distillation", + "autoDistillProgress": "Auto Distillation Progress", + "overallProgress": "Overall Progress", + "tagsProgress": "Tag Building Progress", + "questionsProgress": "Question Generation Progress", + "currentStage": "Current Stage", + "realTimeLogs": "Real-time Logs", + "waitingForLogs": "Waiting for logs...", + "autoDistillStarted": "{{time}} Auto distillation task started", + "autoDistillInsufficientError": "Current configuration will not produce new tags or questions, please adjust parameters", + "stageInitializing": "Initializing...", + "stageBuildingLevel1": "Building Level 1 Tags", + "stageBuildingLevel2": "Building Level 2 Tags", + "stageBuildingLevel3": "Building Level 3 Tags", + "stageBuildingLevel4": "Building Level 4 Tags", + "stageBuildingLevel5": "Building Level 5 Tags", + "stageBuildingQuestions": "Generating Questions", + "stageBuildingDatasets": "Building Datasets", + "stageCompleted": "Task Completed", + "datasetsProgress": "Datasets Progress", + "rootTopicHelperText": "By default, the project name is used as the top-level distillation theme. If you need to change it, please go to the project settings to modify the project name.", + "addChildTag": "Add Child Tag", + "datasetType": "Dataset Type", + "singleTurnDataset": "Single-turn Dataset", + "multiTurnDataset": "Multi-turn Dataset", + "bothDatasetTypes": "Generate Both Dataset Types", + "autoDistillTaskDetail": "Auto Distill Task: {{topic}}", + "backgroundTaskCreated": "Background distill task created. You can check the progress in the task management center.", + "backgroundTaskFailed": "Failed to create background task", + "taskExecutionError": "Task execution error: {{error}}" + }, + "tasks": { + "pending": "{{count}} tasks are processing", + "completed": "tasks are completed", + "title": "Task Management Center", + "loading": "Loading tasks...", + "empty": "No tasks found", + "confirmDelete": "Are you sure you want to delete this task?", + "confirmAbort": "Are you sure you want to abort this task? The task will be stopped.", + "deleteSuccess": "Task deleted", + "deleteFailed": "Failed to delete task", + "abortSuccess": "Task aborted", + "abortFailed": "Failed to abort task", + "status": { + "processing": "Processing", + "completed": "Completed", + "failed": "Failed", + "aborted": "Aborted", + "unknown": "Unknown" + }, + "types": { + "text-processing": "Text Processing", + "file-processing": "File Processing", + "data-cleaning": "Data Cleaning", + "question-generation": "Question Generation", + "answer-generation": "Answer Generation", + "eval-generation": "Evaluation Generation", + "multi-turn-generation": "Multi-turn Generation", + "image-question-generation": "Image Question Generation", + "data-distillation": "Data Distillation", + "pdf-processing": "PDF Processing" + }, + "filters": { + "status": "Task Status", + "type": "Task Type" + }, + "actions": { + "refresh": "Refresh task list", + "delete": "Delete task", + "abort": "Abort task" + }, + "table": { + "type": "Type", + "status": "Status", + "progress": "Progress", + "note": "Note", + "createTime": "Created", + "endTime": "Completed", + "duration": "Duration", + "model": "Model", + "detail": "Details", + "actions": "Actions" + }, + "duration": { + "seconds": "{{seconds}}s", + "minutes": "{{minutes}}m {{seconds}}s", + "hours": "{{hours}}h {{minutes}}m" + }, + "fetchFailed": "Failed to fetch task list", + "createSuccess": "Task created successfully", + "createFailed": "Failed to create task", + "multiTurnCreateSuccess": "Multi-turn conversation dataset task created successfully", + "notes": { + "selectedChunks": "{{count}} chunks selected", + "fileBatch": "File processing params: {{count}} files (strategy: {{strategy}})", + "jsonParams": "Task parameters configured", + "noChunksQuestion": "No chunks require question generation", + "noChunksCleaning": "No chunks require cleaning", + "processingFailed": "Processing failed: {{error}}", + "questionSummary": "Processed {{processed}}/{{total}}, succeeded {{succeeded}}, failed {{failed}}, questions generated {{generated}}", + "datasetSummary": "Processed {{processed}}/{{total}}, succeeded {{succeeded}}, failed {{failed}}, datasets generated {{generated}}", + "cleaningSummary": "Processed {{processed}}/{{total}}, succeeded {{succeeded}}, failed {{failed}}, original length {{original}}, cleaned length {{cleaned}}", + "genericSummary": "Processed {{processed}}/{{total}}, succeeded {{succeeded}}, failed {{failed}}" + } + }, + "gaPairs": { + "title": "Genre-Audience Pairs Management", + "loading": "Loading GA pairs...", + "addPair": "Add GA Pair", + "saveChanges": "Save Changes", + "saving": "Saving...", + "restoreBackup": "Restore Backup", + "noGaPairsTitle": "No Genre-Audience Pairs Found", + "noGaPairsDescription": "Generate AI-powered Genre-Audience pairs for this file", + "generateGaPairs": "Generate Genre-Audience Pairs", + "generating": "Generating...", + "generateMore": "Generate More Genre-Audience Pairs", + "activePairs": "Active Genre-Audience Pairs ({{active}}/{{total}})", + "pairNumber": "Genre-Audience Pair #{{number}}", + "active": "Active", + "deleteTooltip": "Delete GA Pair", + "genre": "Genre", + "genreDescription": "Genre Description", + "audience": "Audience", + "audienceDescription": "Audience Description", + "addDialogTitle": "Add New Genre-Audience Pair", + "genreTitle": "Genre Title", + "audienceTitle": "Audience Title", + "genreTitlePlaceholder": "Enter the genre title...", + "genreDescPlaceholder": "Describe the genre in detail...", + "audienceTitlePlaceholder": "Enter the audience title...", + "audienceDescPlaceholder": "Describe the target audience in detail...", + "cancel": "Cancel", + "addPairButton": "Add Genre-Audience Pair", + "requiredFields": "Genre Title and Audience Title are required", + "restoredFromBackup": "Restored from backup", + "allPairsDeleted": "All GA pairs deleted successfully", + "pairsSaved": "{{count}} GA pairs saved successfully", + "additionalPairsGenerated": "Successfully generated {{count}} additional Genre-Audience pairs. Total: {{total}}", + "validationError": "GA pair {{number}}: Genre and Audience titles are required", + "loadError": "Unable to load GA pairs: {{error}}", + "generateError": "Failed to generate GA pairs", + "saveError": "Failed to save GA pairs", + "noActiveModel": "Please configure an AI model in settings before generating GA pairs.", + "contentTooShort": "The file content is too short or not suitable for GA pair generation.", + "configError": "AI model configuration error. The required dependencies may not be installed.", + "serverError": "Server error ({{status}}). Please try again later.", + "emptyResponse": "Empty response from generation service", + "generationFailed": "Generation failed", + "saveOperationFailed": "Save operation failed", + "serviceNotAvailable": "GA Pairs generation service is not available. Please check your API configuration.", + "requestFailed": "Request failed ({{status}}). Please try again.", + "internalServerError": "Internal server error occurred.", + "batchGenerate": "Batch Generate GA Pairs", + "batchGenerateDescription": "Will batch generate GA pairs for {{count}} selected files. This operation may take some time.", + "appendMode": "Append Mode", + "appendModeDescription": "Generate additional GA pairs for files that already have GA pairs, rather than overwriting", + "selectAtLeastOneFile": "Please select at least one file first", + "noDefaultModel": "No default model set, please configure a model in project settings first", + "incompleteModelConfig": "Model configuration is incomplete, please check model settings", + "missingApiKey": "Model API key not configured, please add API key in model settings", + "loadingProjectModel": "Loading project model...", + "usingModel": "Using model", + "startGeneration": "Start Generation", + "batchGenCompleted": "Batch generation completed! Successfully generated GA pairs for {{success}}/{{total}} files.", + "generationError": "Error occurred during generation: {{error}}", + "fetchProjectInfoFailed": "Failed to fetch project info: {{status}}", + "fetchModelConfigFailed": "Failed to fetch model config: {{status}}", + "fetchProjectModelError": "Error fetching project model configuration", + "batchGenerationFailed": "Batch GA pair generation failed", + "batchGenerationSuccess": "Successfully generated GA pairs for {{count}} files", + "selectAllFiles": "Select All", + "deselectAllFiles": "Deselect All", + "batchGenerateTitle": "Batch Generate GA Pairs", + "generationMode": "Generation Mode", + "aiGenerateMode": "AI Generate", + "manualAddMode": "Manual Add", + "genreDesc": "Genre Description", + "audienceDesc": "Audience Description", + "manualGaPairRequired": "Please fill in Genre Title and Audience Title", + "batchAddManual": "Batch Add" + }, + "batchEdit": { + "title": "Batch Edit Text Chunks", + "batchEdit": "Batch Edit", + "batchEditTooltip": "Batch edit selected text chunks", + "position": "Add Position", + "atBeginning": "Add at Beginning", + "atEnd": "Add at End", + "contentToAdd": "Content to Add", + "contentPlaceholder": "Enter content to add to text chunks...", + "contentRequired": "Please enter content to add", + "contentHelp": "This content will be added to all selected text chunks", + "preview": "Preview", + "allChunksSelected": "All {{count}} text chunks selected", + "selectedChunks": "{{selected}} / {{total}} text chunks selected", + "processing": "Processing...", + "applyToChunks": "Apply to {{count}} chunks", + "editSuccess": "Successfully edited {{count}} text chunks", + "editFailed": "Batch edit failed", + "previewNote": "The above is a preview of the first selected text chunk. All selected text chunks will undergo the same modification" + }, + "errors": { + "projectIdRequired": "Project ID cannot be empty", + "getDatasetsFailed": "Failed to get datasets", + "getTagStatsFailed": "Failed to get tag statistics", + "deleteFileFailed": "Error deleting file", + "recordNotFound": "Current record does not exist", + "mineruTokenNotFound": "Token configuration not found, please check if MinerU token is configured in task settings", + "mineruLocalUrlNotFound": "MinerU local URL configuration not found, please check if MinerU local URL is configured in task settings" + }, + "sampleData": { + "questionContent": "Question content", + "answerContent": "Answer content", + "cotContent": "Chain of thought content", + "domainLabel": "Domain label", + "textChunk": "Text chunk" + }, + "exportDialog": { + "balancedExport": "Balanced Export", + "balancedExportTitle": "Balanced Export Settings", + "balancedExportDescription": "Configure the data volume for each category based on domain tags to achieve balanced dataset export", + "quickSettings": "Quick Settings", + "setAllTo50": "Set all to 50", + "setAllTo100": "Set all to 100", + "setAllTo200": "Set all to 200", + "customAmount": "Custom amount", + "tagName": "Tag Name", + "availableCount": "Available Count", + "exportCount": "Export Count", + "settings": "Settings", + "totalExportCount": "Total export count", + "tagCount": "Tag count", + "export": "Export" + }, + "imageDatasets": { + "title": "Image Q&A Dataset", + "subtitle": "Manage and optimize your image Q&A datasets", + "description": "Manage and optimize your image Q&A datasets.", + "searchPlaceholder": "Search questions or answers...", + "noAnswer": "No answer", + "labels": "Labels", + "typeLabel": "Label", + "typeCustom": "Custom", + "typeText": "Text", + "unscored": "Unscored", + "confirmed": "Confirmed", + "unconfirmed": "Unconfirmed", + "view": "View Details", + "evaluate": "Quality Assessment", + "delete": "Delete", + "deleteConfirm": "Are you sure you want to delete this dataset?", + "imageName": "Image Name", + "status": "Status", + "scoreRange": "Score Range", + "noData": "No image datasets", + "noDataTip": "Please generate Q&A datasets in Image Management first", + "fetchFailed": "Failed to fetch datasets", + "fetchDetailFailed": "Failed to fetch detail", + "deleteSuccess": "Deleted successfully", + "deleteFailed": "Failed to delete", + "updateSuccess": "Updated successfully", + "updateFailed": "Failed to update", + "regenerateSuccess": "AI recognition successful", + "regenerateFailed": "AI recognition failed", + "notFound": "Dataset not found", + "detail": "Detail", + "image": "Image", + "question": "Question", + "answer": "Answer", + "selectLabels": "Select Labels", + "noLabels": "No labels selected", + "jsonPlaceholder": "Enter JSON format data...", + "metadata": "Metadata", + "score": "Score", + "tags": "Tags", + "addTag": "Add tag...", + "note": "Note", + "notePlaceholder": "Add note...", + "modelInfo": "Model Info", + "createdAt": "Created At", + "updatedAt": "Updated At", + "exportTitle": "Export Image Dataset", + "exportFormat": "Export Format", + "rawFormat": "Raw Format", + "customFormat": "Custom Format", + "exportImagesOption": "Export Image Files", + "exportImagesDesc": "Package all images into a ZIP file for download", + "includeImagePath": "Include Image Path in Dataset", + "includeImagePathDesc": "Add image path in question or answer (format: /images/image_name)", + "systemPrompt": "System Prompt (Optional)", + "systemPromptPlaceholder": "Enter system prompt...", + "confirmedOnly": "Export Confirmed Only", + "exportTip": "Label format answers will be automatically parsed to text (comma separated)", + "exportSuccess": "Dataset exported successfully", + "exportFailed": "Export failed", + "noDataToExport": "No data to export", + "exportImagesSuccess": "Image ZIP package exported successfully", + "exportImagesFailed": "Failed to export images" + }, + "images": { + "resolution": "Resolution", + "uploadTime": "Upload Time", + "fileName": "File Name", + "title": "Image Management", + "importImages": "Import Images", + "searchPlaceholder": "Search image name...", + "hasQuestions": "Question Status", + "hasDatasets": "Dataset Status", + "withQuestions": "With Questions", + "withoutQuestions": "Without Questions", + "withDatasets": "With Datasets", + "withoutDatasets": "Without Datasets", + "noImages": "No images", + "noImagesDescription": "Start importing images to create your first dataset", + "preview": "Preview", + "questions": "Questions", + "datasets": "Datasets", + "datasetCount": "Dataset Count", + "generateQuestions": "Generate Questions", + "generateDataset": "Generate Dataset", + "deleteConfirm": "Are you sure you want to delete this image?", + "deleteSuccess": "Deleted successfully", + "deleteFailed": "Delete failed", + "batchDelete": "Batch Delete", + "selectImagesToDelete": "Please select images to delete", + "batchDeleteConfirm": "Are you sure you want to delete {{count}} selected images?", + "batchDeleteSuccess": "Successfully deleted {{count}} images", + "batchDeletePartialSuccess": "Successfully deleted {{success}}, failed {{fail}}", + "batchDeleteFailed": "Batch delete failed", + "importTip": "Select one or more directories containing images. All images will be imported into the project (duplicate names will be overwritten)", + "selectDirectory": "Select Directory", + "directoryPath": "Directory Path", + "enterDirectoryPath": "e.g., /Users/username/Pictures", + "selectedDirectories": "Selected Directories", + "selectAtLeastOne": "Please select at least one directory", + "importSuccess": "Successfully imported {{count}} images", + "importFailed": "Import failed", + "startImport": "Start Import", + "addDirectory": "Add Directory", + "importFromDirectory": "Import from Directory", + "importFromPdf": "Import from PDF", + "importFromZip": "Import from ZIP", + "pdfImportTip": "Select a PDF file, the system will automatically convert it to images and import", + "zipImportTip": "Select a ZIP archive file, the system will automatically extract and import images from it", + "clickToSelectPdf": "Click to select PDF file", + "clickToSelectZip": "Click to select ZIP file", + "supportedFormat": "Supported format: PDF", + "supportedZipFormat": "Supported format: ZIP", + "fileSize": "File size", + "selectedFile": "Selected file", + "invalidPdfFile": "Please select a valid PDF file", + "invalidZipFile": "Please select a valid ZIP file", + "selectPdfFile": "Please select a PDF file", + "selectZipFile": "Please select a ZIP file", + "pdfImportSuccess": "Successfully imported {{count}} images from PDF \"{{name}}\"", + "pdfImportFailed": "PDF import failed", + "zipImportSuccess": "Successfully imported {{count}} images from ZIP \"{{name}}\"", + "zipImportFailed": "ZIP import failed", + "convertAndImport": "Convert and Import", + "extractAndImport": "Extract and Import", + "electronRequired": "This feature requires the desktop application", + "selectDirectoryFailed": "Failed to select directory", + "imageName": "Image Name", + "questionCount": "Question Count", + "questionCountHelp": "Generate 1-10 questions", + "size": "Size", + "dimensions": "Dimensions", + "currentModel": "Current Model", + "selectModelFirst": "Please select a model first", + "visionModelRequired": "Please select a vision-capable model (e.g., GPT-4 Vision, Claude, etc.)", + "countRange": "Question count should be between 1-10", + "questionsGenerated": "Successfully generated {{count}} questions", + "generateFailed": "Generation failed", + "question": "Question", + "questionPlaceholder": "Enter your question...", + "questionRequired": "Please enter a question", + "datasetGenerated": "Dataset generated successfully", + "autoGenerateQuestions": "Auto Generate Questions", + "autoGenerateConfirm": "The system will automatically generate questions for all images without questions. This will create a background task, and you can view the progress in Task Management.", + "taskCreated": "Task created successfully, processing in background", + "taskCreateFailed": "Failed to create task", + "manualAnnotation": "Manual Annotation", + "annotationTitle": "Image Annotation", + "imageInfo": "Image Info", + "annotatedCount": "Annotated", + "selectQuestion": "Select or Create Question", + "selectQuestionPlaceholder": "Select question template...", + "universalQuestions": "Universal Questions", + "independentQuestions": "Independent Questions", + "answerTypeText": "Text", + "answerTypeLabel": "Label", + "answerTypeCustomFormat": "Custom Format", + "usedTimes": "Used {{count}} times", + "answer": "Answer", + "answerPlaceholder": "Enter answer...", + "selectLabels": "Select Labels", + "availableLabels": "Available Labels", + "noLabelsAvailable": "No labels available", + "addNewLabel": "Add new label...", + "selectedLabels": "Selected", + "customFormatAnswer": "Custom Format Answer", + "formatRequirement": "Format Requirement", + "customFormatPlaceholder": "Enter JSON in the required format...", + "note": "Note", + "notePlaceholder": "Note (optional)", + "saveAndContinue": "Save & Continue", + "noImageSelected": "No image selected", + "noTemplateSelected": "Please select a question", + "answerRequired": "Please enter an answer", + "invalidJsonFormat": "Invalid JSON format", + "annotationSuccess": "Annotation saved successfully", + "annotationFailed": "Failed to save annotation", + "allQuestionsAnnotated": "All questions for this image have been annotated", + "allImagesAnnotated": "All questions for all images have been annotated", + "noQuestionsAssociated": "No questions associated with this image", + "loadImageDetailFailed": "Failed to load image details", + "answeredQuestions": "Annotated Questions", + "useTemplate": "Use Template", + "formatJson": "Format", + "jsonFormatHelp": "Please enter valid JSON format data", + "imageLoadError": "Failed to load image", + "annotate": "Annotate", + "annotateImage": "Annotate Image", + "createQuestion": "Create Question", + "createTemplate": "Create Question Template", + "aiGenerate": "AI Recognize", + "aiGenerateSuccess": "AI generation successful", + "aiGenerateFailed": "AI generation failed", + "missingParameters": "Missing required parameters", + "selectNewQuestion": "Select New Question", + "fetchTemplatesFailed": "Failed to fetch question templates", + "createTemplateSuccess": "Question template created successfully", + "createTemplateFailed": "Failed to create question template", + "updateTemplateSuccess": "Question template updated successfully", + "updateTemplateFailed": "Failed to update question template", + "deleteTemplateSuccess": "Question template deleted successfully", + "deleteTemplateFailed": "Failed to delete question template", + "template": { + "management": "Manage Question Templates", + "create": "Create Template", + "edit": "Edit Template", + "question": "Question Content", + "description": "Description", + "noTemplates": "No question templates yet, click create button to add", + "deleteConfirm": "Are you sure you want to delete this question template?", + "used": "Used", + "addLabel": "Add Label", + "customFormat": "Custom Format", + "customFormatHelp": "Enter JSON format output constraint", + "customFormatInfo": "This format will be provided to the LLM as a prompt to constrain output format", + "type": { + "label": "Question Type", + "universal": "Universal Question", + "independent": "Independent Question" + }, + "answerType": { + "label": "Answer Type", + "text": "Text", + "tags": "Tags", + "customFormat": "Custom Format" + }, + "errors": { + "questionRequired": "Please enter question content", + "labelsRequired": "Label type questions require at least one label", + "customFormatRequired": "Please enter custom format", + "invalidJson": "Invalid JSON format" + } + } + }, + "monitoring": { + "title": "Resource Monitoring Dashboard", + "timeRange": { + "24h": "Last 24 hours", + "7d": "Last 7 days", + "30d": "Last 30 days" + }, + "filters": { + "allProjects": "All Projects", + "allProviders": "All Providers", + "allStatus": "All Status" + }, + "status": { + "success": "Success", + "failed": "Failed" + }, + "actions": { + "export": "Export Report" + }, + "stats": { + "totalTokens": "Total Token Usage", + "avgTokensPerCall": "Avg Tokens per Call", + "totalCalls": "Total Calls", + "avgLatency": "Avg Latency", + "inputOutput": "Input: {{input}} · Output: {{output}}", + "successCalls": "{{count}} Success", + "failedCalls": "{{count}} Failed", + "failureRate": "{{rate}}% Failure Rate", + "basedOnSuccessCalls": "Based on {{count}} successful calls", + "noSuccessCalls": "No successful calls" + }, + "charts": { + "tokenTrend": "Token Usage Trend", + "inputLegend": "Input", + "outputLegend": "Output", + "distributionTitle": "Token Usage Distribution (by Model)", + "distributionSubtitle": "Resource usage share by model", + "tokensTooltip": "{{value}}K Tokens" + }, + "table": { + "title": "Usage Details", + "searchPlaceholder": "Search project, model, or failure reason...", + "empty": "No data", + "rowsPerPage": "Rows per page:", + "columns": { + "projectName": "Project", + "provider": "Provider", + "model": "Model", + "status": "Status", + "failureReason": "Failure Reason", + "inputTokens": "Input Tokens", + "outputTokens": "Output Tokens", + "totalTokens": "Total", + "calls": "Calls", + "avgLatency": "Avg Latency" + } + }, + "errors": { + "fetchSummaryFailed": "Failed to fetch monitoring summary", + "fetchLogsFailed": "Failed to fetch monitoring logs" + } + }, + "eval": { + "title": "Eval", + "datasets": "Eval Datasets", + "tasks": "Eval Tasks", + "datasetsTitle": "Evaluation Datasets", + "datasetsDescription": "Manage and view all generated evaluation test questions", + "tasksTitle": "Evaluation Tasks", + "tasksComingSoon": "Coming Soon", + "tasksComingSoonHint": "Evaluation task feature is under development", + "totalQuestions": "Total Questions", + "questionType": "Type", + "question": "Question", + "answer": "Answer", + "options": "Options", + "correct": "Correct", + "wrong": "Wrong", + "sourceChunk": "Source Chunk", + "tags": "Tags", + "tagsPlaceholder": "Enter tags, separated by commas", + "note": "Note", + "detail": "Detail", + "notFound": "Question not found", + "noData": "No evaluation data", + "noDataHint": "Please generate evaluation test set from text split page first", + "searchPlaceholder": "Search question content...", + "cardView": "Card View", + "listView": "List View", + "deleteSelected": "Delete Selected ({{count}})", + "deleteConfirmTitle": "Confirm Delete", + "deleteConfirmMessage": "Are you sure you want to delete {{count}} question(s)? This action cannot be undone.", + "questionTypes": { + "true_false": "True/False", + "single_choice": "Single Choice", + "multiple_choice": "Multiple Choice", + "short_answer": "Short Answer", + "open_ended": "Open Ended" + } + }, + "evalDatasets": { + "import": { + "title": "Import Eval Datasets", + "questionType": "Question Type", + "selectTypeFirst": "Please select a question type first", + "selectFile": "Please select a file to import", + "invalidFileType": "Unsupported file format, please upload json, xls or xlsx files", + "formatPreview": "Data Format Preview", + "downloadTemplate": "Download Template", + "template": "Template", + "uploadFile": "Upload File", + "dropOrClick": "Click or drag file here", + "supportedFormats": "Supports JSON, XLS, XLSX formats", + "tags": "Tags (Optional)", + "tagsPlaceholder": "Add tags for imported data, separate multiple tags with commas", + "tagsHelp": "All imported data will be tagged with these labels", + "import": "Import", + "importing": "Importing...", + "failed": "Import failed", + "success": "Import successful", + "successMessage": "Successfully imported {{count}} evaluation datasets", + "showingErrors": "Showing first {{count}} errors", + "custom": "Custom Import", + "builtin": "Built-in Datasets", + "builtinTitle": "Select Built-in Dataset", + "searchPlaceholder": "Search datasets...", + "confirmImportTitle": "Confirm Import", + "confirmImportMessage": "Are you sure you want to import dataset \"{{name}}\"? This will add new evaluation data to the current project.", + "downloading": "Downloading..." + }, + "export": { + "title": "Export Eval Datasets", + "formatLabel": "Export Format", + "filterLabel": "Filter Criteria", + "previewLabel": "Data to export: ", + "records": " records", + "largeDataHint": "Large dataset, streaming export will be used, please wait", + "exporting": "Exporting...", + "exportBtn": "Export", + "jsonDesc": "Standard JSON array", + "jsonlDesc": "One record per line", + "csvDesc": "Table format", + "noTagsAvailable": "No tags available" + } + }, + "evalTasks": { + "title": "Model Evaluation Tasks", + "createTitle": "Create Evaluation Task", + "detailTitle": "Evaluation Task Details", + "createTask": "Create Task", + "noTasks": "No evaluation tasks", + "noTasksHint": "Create an evaluation task to test model performance on evaluation datasets", + "selectModels": "Select Test Models", + "selectModelsHint": "You can select multiple models for comparison", + "selectJudgeModel": "Select Judge Model", + "selectJudgeModelPlaceholder": "Please select...", + "selectJudgeModelHint": "Judge model is used to score subjective questions and cannot be the same as test models", + "judgeModel": "Judge Model", + "filterByType": "Filter by Question Type", + "filterByTypeHint": "Leave empty to use all questions", + "selectedQuestions": "Selected Questions", + "questions": "questions", + "hasSubjectiveHint": "Contains subjective questions (short answer/open-ended), a judge model is required", + "hasSubjective": "Has Subjective", + "startEval": "Start Evaluation", + "progress": "Progress", + "totalQuestions": "Questions", + "status": "Status", + "totalScore": "Total Score", + "correctCount": "Correct", + "accuracy": "Accuracy", + "statsByType": "Statistics by Type", + "resultDetails": "Result Details", + "question": "Question", + "questionType": "Type", + "result": "Result", + "score": "Score", + "correctAnswer": "Correct Answer", + "modelAnswer": "Model Answer", + "judgeResponse": "Judge Response", + "interrupt": "Interrupt", + "statusProcessing": "Processing", + "statusCompleted": "Completed", + "statusFailed": "Failed", + "statusInterrupted": "Interrupted", + "deleteConfirmTitle": "Confirm Delete", + "deleteConfirmMessage": "Are you sure you want to delete this evaluation task? All results will also be deleted.", + "interruptConfirmTitle": "Confirm Interrupt", + "interruptConfirmMessage": "Are you sure you want to interrupt this task? Completed results will be preserved.", + "errorNoModels": "Please select at least one test model", + "errorNoQuestions": "No evaluation questions available", + "errorNoJudgeModel": "Subjective questions exist, please select a judge model", + "errorJudgeSameAsTest": "Judge model cannot be the same as test models", + "errorCreateFailed": "Failed to create evaluation task", + "errorLoadFailed": "Failed to load evaluation tasks", + "errorDeleteFailed": "Failed to delete evaluation task", + "errorInterruptFailed": "Failed to interrupt evaluation task", + "statusSuccess": "Success", + "statusFormatError": "Format Error", + "statusApiError": "API Error", + "statusUnknown": "Unknown Status", + "duration": "Duration", + "answerStatus": "Answer Status", + "modelInfo": "Model Info", + "reportTitle": "Model Evaluation Report", + "taskIdLabel": "Task ID", + "pageInfo": "Page: {{page}} / {{totalPages}}", + "noMatchingResults": "No evaluation results match the current filters", + "reportFooter": "Easy Dataset Evaluation System · Generated by AI", + "finalSelection": "Final Selection: ", + "questionsSuffix": " questions", + "noModelsAvailable": "No models available, please configure models in settings first", + "filterTitle": "Question Filter", + "clearFilter": "Clear Filter", + "searchKeyword": "Search Keyword", + "searchPlaceholder": "Search question or answer content...", + "filterByTypeLabel": "Filter by Type", + "filterByTagLabel": "Filter by Tag", + "questionCountLabel": "Question Count: ", + "useAllQuestions": "Use all filtered results", + "randomSampleHint": "Randomly sample {{questionCount}} from {{filteredCount}} questions", + "durationFormat": "({{time}}s)", + "totalQuestionsLabel": "Total", + "correctLabel": "Correct", + "incorrectLabel": "Incorrect", + "judgeComment": "AI Judge Comment:", + "scoreUnit": " pts", + "shortAnswer": "Short Answer", + "openEnded": "Open-ended", + "scoreAnchorsTitle": "{{type}} Scoring Rules", + "customizable": "Customizable", + "scoreAnchorsHint": "Customize scoring criteria to guide LLM in evaluating model responses", + "restoreDefault": "Restore Default", + "scoreRange": "Score Range", + "scoreDescriptionPlaceholder": "Enter scoring criteria description for this range..." + }, + "blindTest": { + "title": "Human Blind Test", + "createTitle": "Create Blind Test", + "createTask": "Create Task", + "noTasks": "No blind test tasks", + "noTasksHint": "Create a blind test task to compare two models' answer quality", + "selectModels": "Select Models to Compare", + "modelA": "Model A", + "modelB": "Model B", + "modelComparison": "Model Comparison", + "selectQuestions": "Select Test Questions", + "questionType": "Question Type", + "questionTypeHint": "Blind test only supports short answer and open-ended questions", + "filterByTag": "Filter by Tag", + "questionCount": "Question Count", + "availableQuestions": "Available: {{count}} questions", + "useAllQuestions": "Use all filtered results", + "randomSample": "Randomly sample {{count}} questions", + "startBlindTest": "Start Blind Test", + "creating": "Creating...", + "noModelsAvailable": "No models available, please configure models in settings first", + "errorSelectModelA": "Please select Model A", + "errorSelectModelB": "Please select Model B", + "errorSameModel": "Two models cannot be the same", + "errorNoQuestions": "No questions match the criteria", + "statusProcessing": "In Progress", + "statusCompleted": "Completed", + "statusFailed": "Failed", + "statusInterrupted": "Interrupted", + "progress": "Progress", + "viewDetails": "View Details", + "continue": "Continue Test", + "interrupt": "Interrupt Task", + "deleteConfirmTitle": "Confirm Delete", + "deleteConfirmMessage": "Are you sure you want to delete this blind test task? This action cannot be undone.", + "interruptConfirmTitle": "Confirm Interrupt", + "interruptConfirmMessage": "Are you sure you want to interrupt this task? Completed results will be preserved.", + "inProgress": "Blind Test In Progress", + "generatingAnswers": "Generating answers...", + "question": "Question", + "answerA": "Answer A", + "answerB": "Answer B", + "duration": "Duration", + "whichBetter": "Which answer is better?", + "leftBetter": "Left is Better", + "rightBetter": "Right is Better", + "bothGood": "Both Good", + "bothBad": "Both Bad", + "loadQuestion": "Load Question", + "taskNotFound": "Task not found", + "resultTitle": "Blind Test Results", + "resultSummary": "Result Summary", + "wins": "Wins", + "times": "times", + "totalQuestions": "Total Questions", + "ties": "Ties", + "detailResults": "Detailed Results", + "left": "Left", + "right": "Right" + } +} diff --git a/easy-dataset-main/locales/pt-BR/translation.json b/easy-dataset-main/locales/pt-BR/translation.json new file mode 100644 index 0000000..86a9a89 --- /dev/null +++ b/easy-dataset-main/locales/pt-BR/translation.json @@ -0,0 +1,1854 @@ +{ + "language": { + "switchToEnglish": "Mudar para Inglês", + "switchToChinese": "Mudar para Chinês", + "switcherTitle": "Mudar Idioma / Change Language / Dil Değiştir", + "english": "Inglês", + "chineseSimplified": "Chinês Simplificado", + "turkish": "Turco", + "en": "EN", + "zh": "中" + }, + "theme": { + "switchToLight": "Mudar para Modo Claro", + "switchToDark": "Mudar para Modo Escuro" + }, + "settings": { + "promptConfig": "Configuração de Prompts", + "promptsDescription": "Configure vários prompts personalizados usados no projeto para intervenção manual na geração do conjunto de dados.", + "globalPrompt": "Prompt Global", + "questionPrompt": "Prompt de Geração de Perguntas", + "answerPrompt": "Prompt de Geração de Respostas", + "labelPrompt": "Prompt de Rotulação de Perguntas", + "domainTreePrompt": "Prompt de Construção da Árvore de Domínio", + "globalPromptPlaceholder": "Por favor, insira o prompt global (use com cautela, pode afetar a geração geral)", + "questionPromptPlaceholder": "Por favor, insira o prompt personalizado para geração de perguntas", + "answerPromptPlaceholder": "Por favor, insira o prompt personalizado para geração de respostas", + "labelPromptPlaceholder": "Por favor, insira o prompt personalizado para rotulação de perguntas (configuração temporariamente não suportada)", + "domainTreePromptPlaceholder": "Por favor, insira o prompt personalizado para construção da árvore de domínio", + "cleanPrompt": "Prompt de Limpeza de Dados", + "cleanPromptPlaceholder": "Por favor, insira o prompt personalizado para limpeza de dados", + "loadPromptsFailed": "Falha ao carregar configuração de prompts", + "savePromptsSuccess": "Configuração de prompts salva com sucesso", + "savePromptsFailed": "Falha ao salvar configuração de prompts", + "title": "Configurações do Projeto", + "basicInfo": "Informações Básicas", + "modelConfig": "Configuração do Modelo", + "taskConfig": "Configuração de Tarefas", + "tabsAriaLabel": "Abas de Configurações", + "idNotEditable": "ID do Projeto não pode ser editado", + "saveBasicInfo": "Salvar Informações Básicas", + "saveSuccess": "Salvo com sucesso", + "saveFailed": "Falha ao salvar", + "deleteSuccess": "Excluído com sucesso", + "deleteFailed": "Falha ao excluir", + "fetchTasksFailed": "Falha ao obter configuração de tarefas", + "saveTasksFailed": "Falha ao salvar configuração de tarefas", + "textSplitSettings": "Configurações de Divisão de Texto", + "minLength": "Comprimento Mínimo", + "maxLength": "Comprimento Máximo de Divisão", + "textSplitDescription": "Ajuste o intervalo de comprimento da divisão de texto, afeta a granularidade do resultado da divisão", + "splitType": "Estratégia de Divisão", + "splitTypeMarkdown": "Divisão por Estrutura de Documento (Markdown)", + "splitTypeMarkdownDesc": "Divide texto automaticamente com base nos títulos do documento, mantendo a integridade semântica, adequado para documentos Markdown com estrutura clara", + "splitTypeRecursive": "Divisão por Estrutura de Texto (Separadores Personalizados)", + "splitTypeRecursiveDesc": "Tenta recursivamente separadores de múltiplos níveis (configuráveis), usando primeiro separadores de alta prioridade, depois separadores secundários, adequado para documentos complexos", + "splitTypeText": "Divisão de Comprimento Fixo (Caracteres)", + "splitTypeTextDesc": "Divide texto pelo separador especificado (configurável), depois combina pelo comprimento especificado, adequado para arquivos de texto comuns", + "splitTypeToken": "Divisão de Comprimento Fixo (Token)", + "splitTypeTokenDesc": "Divide com base na contagem de Tokens (não caracteres)", + "splitTypeCode": "Divisão Inteligente de Código de Programa", + "splitTypeCodeDesc": "Realiza divisão inteligente com base na estrutura sintática de diferentes linguagens de programação, evitando divisões em locais com sintaxe incompleta", + "splitTypeCustom": "Divisão por Símbolos Personalizados", + "splitTypeCustomDesc": "Divide documentos com base em símbolos personalizados, os separadores serão descartados, os blocos de texto divididos não são afetados pelo tamanho do bloco", + "codeLanguage": "Linguagem de Código", + "codeLanguageHelper": "Selecione a linguagem de código para divisão, a divisão inteligente será realizada de acordo com as características da linguagem", + "chunkSize": "Tamanho do Bloco", + "chunkOverlap": "Comprimento de Sobreposição do Bloco", + "separator": "Separador", + "separatorHelper": "Separador usado para dividir texto, como \n\n representa linha em branco", + "customSeparator": "Separador Personalizado", + "customSeparatorHelper": "Separador personalizado usado para dividir texto, como --- ou ===", + "separators": "Lista de Separadores", + "separatorsInput": "Separadores (separados por vírgula)", + "separatorsHelper": "Lista de separadores separados por vírgula, ordenados por prioridade", + "questionGenSettings": "Configurações de Geração de Perguntas", + "questionGenLength": "Gerar uma pergunta a cada {{length}} caracteres", + "questionMaskRemovingProbability": "Remover {{probability}}% dos pontos de interrogação no final das perguntas", + "questionGenDescription": "Definir o comprimento máximo para geração de perguntas", + "concurrencyLimit": "Limite de Concorrência", + "concurrencyLimitHelper": "Limitar a quantidade de tarefas simultâneas para geração de perguntas e conjunto de dados", + "saveTaskConfig": "Salvar Configuração de Tarefas", + "pdfSettings": "Configuração de Conversão de Arquivos PDF", + "minerUToken": "Configuração de Token para Conversão de PDF (MinerU API)", + "minerUHelper": "O Token do MinerU tem validade de apenas 14 dias, por favor, substitua o Token a tempo", + "minerULocalUrl": "Configuração de URL para Conversão de PDF (MinerU Local)", + "vision": "Seleção de Modelo de Visão Personalizado", + "visionConcurrencyLimit": "Limite de Concorrência do Modelo de Visão", + "huggingfaceSettings": "Configurações do Hugging Face", + "huggingfaceToken": "Token do Hugging Face", + "multiTurnSettings": "Configurações de Conjunto de Dados de Diálogo de Múltiplas Rodadas", + "multiTurnSystemPrompt": "Prompt do Sistema", + "multiTurnSystemPromptHelper": "Definir a identidade e normas de comportamento do assistente de IA", + "multiTurnScenario": "Cenário de Diálogo", + "multiTurnScenarioHelper": "Descrever o cenário específico e objetivo do diálogo", + "multiTurnRounds": "Número de Rodadas de Diálogo: {{rounds}} rodadas", + "multiTurnRoleA": "Configuração do Personagem A (Usuário)", + "multiTurnRoleAHelper": "Definir a identidade e características do papel do usuário", + "multiTurnRoleB": "Configuração do Personagem B (Assistente)", + "multiTurnRoleBHelper": "Definir a identidade e características do papel do assistente", + "multiTurnDescription": "A configuração de diálogo de múltiplas rodadas é usada para gerar conjuntos de dados de diálogo coerentes de múltiplas rodadas, suporta personalização de personagens e cenários", + "evalQuestionSettings": "Configurações de Geração de Conjunto de Teste", + "evalQuestionSettingsDescription": "Configure a proporção de cada tipo de questão ao gerar conjunto de teste, proporção 0 significa não gerar esse tipo de questão", + "evalTrueFalseRatio": "Proporção de Questões de Verdadeiro/Falso", + "evalSingleChoiceRatio": "Proporção de Questões de Múltipla Escolha", + "evalMultipleChoiceRatio": "Proporção de Questões de Múltipla Escolha (Várias Respostas)", + "evalShortAnswerRatio": "Proporção de Respostas Curtas Fixas", + "evalOpenEndedRatio": "Proporção de Respostas Abertas", + "evalQuestionRatioHelper": "O sistema alocará automaticamente a quantidade de geração de cada tipo de questão de acordo com as proporções definidas, a soma de todas as proporções não precisa ser igual a um valor específico", + "prompts": { + "selectPromptFirst": "Por favor, selecione um prompt à esquerda", + "customized": "Personalizado", + "editPrompt": "Editar Prompt", + "restoreDefault": "Restaurar Padrão", + "promptType": "Tipo de Prompt", + "keyName": "Nome da Chave", + "contentPlaceholder": "Por favor, insira o conteúdo do prompt personalizado...", + "restoreDefaultContent": "Restaurar conteúdo padrão", + "noPromptsAvailable": "Nenhum prompt disponível", + "restoreSuccess": "Restaurado para o prompt padrão", + "restoreFailed": "Falha ao restaurar o prompt padrão", + "deleteError": "Erro ao excluir prompt:", + "saveSuccess": "Prompt salvo com sucesso", + "saveFailed": "Falha ao salvar prompt", + "saveError": "Erro ao salvar prompt:", + "createCustomPrompt": "Criar Prompt Personalizado", + "fetchContentError": "Falha ao obter o conteúdo mais recente do prompt:" + } + }, + "questions": { + "autoGenerateDataset": "Gerar Conjunto de Dados Automaticamente", + "autoGenerateDatasetTip": "Criar tarefa de processamento em lote em segundo plano: consultar automaticamente perguntas pendentes de geração de respostas e gerar conjunto de dados", + "generateSingleTurnDataset": "Gerar Conjunto de Dados de Diálogo de Rodada Única", + "generateSingleTurnDatasetDesc": "Gerar conjunto de dados de perguntas e respostas com base nas perguntas", + "generateMultiTurnDataset": "Gerar Conjunto de Dados de Diálogo de Múltiplas Rodadas", + "generateImageDataset": "Gerar Conjunto de Dados de Perguntas e Respostas de Imagens", + "generateMultiTurnDatasetDesc": "Gerar conjunto de dados de diálogo de múltiplas rodadas com base nas perguntas", + "multiTurnNotConfigured": "Por favor, configure primeiro os parâmetros relacionados a diálogo de múltiplas rodadas nas configurações do projeto", + "filterAll": "Todas as Perguntas", + "filterAnswered": "Respostas Geradas", + "filterUnanswered": "Respostas Não Geradas", + "filterChunkNamePlaceholder": "Filtrar por nome do bloco de texto...", + "sourceTypeAll": "Todas as Fontes de Dados", + "sourceTypeText": "Fonte de Dados de Texto", + "sourceTypeImage": "Fonte de Dados de Imagem", + "title": "Perguntas", + "confirmDeleteTitle": "Confirmar Exclusão de Pergunta", + "confirmDeleteContent": "Tem certeza de que deseja excluir a pergunta \"{{question}}\"? Esta ação não pode ser desfeita.", + "deleting": "Excluindo pergunta...", + "batchDeleteTitle": "Confirmar Exclusão em Lote de Perguntas", + "batchDeleting": "Excluindo {{count}} perguntas...", + "deleteSuccess": "Pergunta excluída com sucesso", + "deleteFailed": "Falha ao excluir pergunta", + "batchDeleteSuccess": "{{count}} perguntas excluídas com sucesso", + "batchDeletePartial": "Exclusão concluída, sucesso: {{success}}, falha: {{failed}}", + "batchDeleteFailed": "Falha na exclusão em lote de perguntas", + "noQuestionsSelected": "Por favor, selecione perguntas primeiro", + "batchGenerateStart": "Iniciando geração de conjunto de dados para {{count}} perguntas", + "invalidQuestionKey": "Chave de pergunta inválida", + "listView": "Lista de Perguntas", + "treeView": "Visualização em Árvore de Domínio", + "selectAll": "Selecionar Todos", + "selectedCount": "{{count}} perguntas selecionadas", + "totalCount": "Total de {{count}} perguntas", + "searchPlaceholder": "Pesquisar perguntas ou tags...", + "searchMatch": "Correspondência", + "searchNotMatch": "Não Corresponde", + "deleteSelected": "Excluir Selecionados", + "batchGenerate": "Construir Conjunto de Dados em Lote", + "generating": "Gerando conjunto de dados", + "generatingProgress": "Concluído: {{completed}}/{{total}}", + "generatedCount": "{{count}} conjuntos de dados gerados", + "pleaseWait": "Por favor, aguarde...", + "selectAllLimitReached": "{{count}} perguntas selecionadas (limite máximo atingido)", + "selectAllFailed": "Falha na operação de seleção total, por favor, tente novamente mais tarde", + "createSuccess": "Pergunta criada com sucesso", + "updateSuccess": "Pergunta atualizada com sucesso", + "operationSuccess": "Operação bem-sucedida", + "operationFailed": "Falha na operação", + "editQuestion": "Editar Pergunta", + "questionContent": "Conteúdo da Pergunta", + "sourceType": "Tipo de Fonte de Dados", + "sourceType.text": "Texto", + "sourceType.image": "Imagem", + "selectChunk": "Selecionar Bloco de Texto", + "searchChunk": "Pesquisar bloco de texto...", + "selectImage": "Selecionar Imagem", + "searchImage": "Pesquisar imagem...", + "selectTag": "Selecionar Tag", + "searchTag": "Pesquisar tag...", + "createQuestion": "Criar Pergunta", + "createNormalQuestion": "Criar Pergunta Normal", + "createQuestionTemplate": "Criar Modelo de Pergunta", + "questionPlaceholder": "Por favor, insira o conteúdo da pergunta", + "noChunkSelected": "Por favor, selecione um bloco de texto primeiro", + "fetchTemplatesFailed": "Falha ao obter modelos de pergunta", + "createTemplateSuccess": "Modelo de pergunta criado com sucesso", + "createTemplateFailed": "Falha ao criar modelo de pergunta", + "updateTemplateSuccess": "Modelo de pergunta atualizado com sucesso", + "updateTemplateFailed": "Falha ao atualizar modelo de pergunta", + "deleteTemplateSuccess": "Modelo de pergunta excluído com sucesso", + "deleteTemplateFailed": "Falha ao excluir modelo de pergunta", + "exportQuestions": "Exportar Conjunto de Perguntas", + "exportScope": "Escopo de Exportação", + "exportAll": "Exportar Todos ({{count}} perguntas)", + "exportSelected": "Exportar Selecionados ({{count}} perguntas)", + "exportFormat": "Formato de Exportação", + "txtFormat": "Texto Puro (apenas conteúdo da pergunta)", + "exportSuccess": "Conjunto de perguntas exportado com sucesso", + "exportFailed": "Falha ao exportar conjunto de perguntas", + "template": { + "management": "Modelo de Pergunta", + "create": "Criar Modelo de Pergunta", + "edit": "Editar Modelo de Pergunta", + "question": "Conteúdo da Pergunta", + "description": "Prompt", + "descriptionHelp": "Usado para adicionar ao prompt geral quando a IA gerar respostas relacionadas a este modelo de pergunta posteriormente, para intervir no resultado final da geração de respostas", + "noTemplates": "Nenhum modelo de pergunta disponível, clique no botão criar para adicionar", + "deleteConfirm": "Tem certeza de que deseja excluir este modelo de pergunta?", + "used": "Usado", + "addLabel": "Adicionar Tag", + "customFormat": "Formato Personalizado", + "customFormatHelp": "Insira restrições de saída no formato JSON", + "customFormatInfo": "Este formato será fornecido como prompt ao modelo grande, usado para restringir o formato de saída", + "sourceTypeInfo": "Tipo de Fonte de Dados", + "sourceType": { + "label": "Tipo de Fonte de Dados", + "image": "Imagem (usado para gerar QA de imagens)", + "text": "Texto (usado para gerar QA de blocos de texto)" + }, + "answerType": { + "label": "Formato de Saída da Resposta", + "text": "Texto Normal", + "tags": "Array de Tags", + "customFormat": "Formato Personalizado" + }, + "errors": { + "questionRequired": "Por favor, insira o conteúdo da pergunta", + "labelsRequired": "Perguntas do tipo tag precisam de pelo menos uma tag", + "customFormatRequired": "Por favor, insira o formato personalizado", + "invalidJson": "Formato JSON incorreto" + }, + "autoGenerate": "Gerar perguntas automaticamente após criar modelo", + "autoGenerateHelpText": "Criará automaticamente perguntas baseadas neste modelo para todos os blocos de texto no projeto", + "autoGenerateHelpImage": "Criará automaticamente perguntas baseadas neste modelo para todas as imagens no projeto", + "confirmAutoGenerate": "Confirmar geração automática de perguntas", + "confirmAutoGenerateTextMessage": "Você selecionou gerar perguntas automaticamente. O sistema criará perguntas baseadas neste modelo para todos os blocos de texto no projeto.", + "confirmAutoGenerateImageMessage": "Você selecionou gerar perguntas automaticamente. O sistema criará perguntas baseadas neste modelo para todas as imagens no projeto.", + "autoGenerateWarning": "Esta operação pode criar um grande número de perguntas, por favor, confirme antes de continuar.", + "autoGenerateSuccess": "Perguntas criadas com sucesso para {{count}} fontes de dados", + "autoGeneratePartialFail": "{{success}} perguntas criadas com sucesso, {{fail}} falhas", + "autoGenerateFailed": "Falha na geração automática de perguntas" + }, + "noTagSelected": "Por favor, selecione uma tag", + "deleteConfirm": "Tem certeza de que deseja excluir esta pergunta?", + "generateMultiTurn": "Gerar Diálogo de Múltiplas Rodadas", + "multiTurnGenerated": "Conjunto de dados de diálogo de múltiplas rodadas gerado com sucesso!" + }, + "common": { + "dataSource": "Fonte de Dados", + "menu": "Menu", + "openMenu": "Abrir Menu de Navegação", + "all": "Todos", + "jumpTo": "Ir para", + "unknownError": "Erro Desconhecido", + "create": "Criar", + "confirm": "Confirmar", + "edit": "Editar", + "delete": "Excluir", + "save": "Salvar", + "cancel": "Cancelar", + "complete": "Concluir", + "close": "Fechar", + "add": "Adicionar", + "remove": "Remover", + "loading": "Carregando...", + "yes": "Sim", + "no": "Não", + "confirmDelete": "Confirmar exclusão? Esta ação não pode ser desfeita!", + "saving": "Salvando...", + "deleting": "Excluindo...", + "actions": "Ações", + "confirmDeleteDataSet": "Confirmar exclusão do conjunto de dados? A operação não pode ser desfeita!", + "noData": "Nenhum", + "failed": "Falhou", + "success": "Sucesso", + "backToList": "Voltar para Lista", + "label": "Tag", + "confirmDeleteDescription": "Confirmar exclusão? Esta ação não pode ser recuperada.", + "more": "Mais", + "import": "Importar", + "export": "Exportar", + "fetchError": "Erro ao obter dados", + "confirmDeleteQuestion": "Confirmar exclusão desta pergunta? Esta ação não pode ser recuperada.", + "deleteSuccess": "Excluído com sucesso", + "visitGitHub": "Visitar Repositório GitHub", + "syncOldData": "Sincronizar Dados de Arquivos", + "copy": "Copiar", + "copied": "Copiado", + "enabled": "Ativado", + "disabled": "Desativado", + "generating": "Gerando...", + "processing": "Processando...", + "items": "itens", + "detailInfo": "Informações Detalhadas", + "reset": "Redefinir", + "apply": "Aplicar", + "mainNavigation": "Navegação Principal", + "goHome": "Voltar para Início", + "goToHomePage": "Voltar para Página Inicial", + "mobileNavigation": "Menu de Navegação Móvel", + "navigation": "Navegação", + "closeMenu": "Fechar Menu", + "documentation": "Documentação", + "viewOnGitHub": "Ver no GitHub", + "back": "Voltar", + "refresh": "Atualizar", + "expand": "Expandir Todos", + "collapse": "Recolher Conteúdo" + }, + "home": { + "title": "Easy Dataset", + "subtitle": "Uma poderosa ferramenta de criação de conjunto de dados para ajuste fino de grandes modelos de linguagem", + "createProject": "Criar Projeto", + "searchDataset": "Pesquisar Conjunto de Dados Público" + }, + "projects": { + "reuseConfig": "Reutilizar Configuração do Modelo", + "noReuse": "Não Reutilizar Configuração", + "selectProject": "Selecionar Projeto", + "fetchFailed": "Falha ao obter lista de projetos", + "fetchError": "Erro ao obter lista de projetos", + "loading": "Carregando seus projetos...", + "createFailed": "Falha ao criar projeto", + "createError": "Erro ao criar projeto", + "createNew": "Criar Novo Projeto", + "saveFailed": "Falha ao salvar projeto", + "id": "ID do Projeto", + "name": "Nome do Projeto", + "description": "Descrição do Projeto", + "questions": "Perguntas", + "datasets": "Conjuntos de Dados", + "evalDatasets": "Conjuntos de Avaliação", + "tokens": "Tokens", + "lastUpdated": "Última Atualização", + "viewDetails": "Ver Detalhes", + "createFirst": "Criar Primeiro Projeto", + "noProjects": "Nenhum Projeto", + "notExist": "Projeto não existe", + "createProject": "Criar Projeto", + "deleteConfirm": "Confirmar exclusão do projeto? Esta ação não pode ser recuperada.", + "deleteSuccess": "Projeto excluído com sucesso", + "deleteFailed": "Falha ao excluir projeto", + "backToHome": "Voltar para Início", + "deleteConfirmTitle": "Confirmar Exclusão do Projeto", + "title": "Gerenciamento de Projetos", + "openDirectory": "Abrir Diretório do Projeto" + }, + "textSplit": { + "dragToUpload": "Arraste arquivos para cá para fazer upload", + "fileList": "Lista de Arquivos", + "autoGenerateQuestions": "Extrair Perguntas Automaticamente", + "autoGenerateQuestionsTip": "Criar tarefa de processamento em lote em segundo plano: consultar automaticamente blocos de texto pendentes de geração de perguntas e extrair perguntas", + "exportChunks": "Exportar Blocos de Texto", + "allChunks": "Todos os Blocos de Texto", + "generatedQuestions2": "Perguntas Geradas", + "ungeneratedQuestions": "Perguntas Não Geradas", + "contentKeyword": "Conteúdo do Bloco de Texto", + "contentKeywordPlaceholder": "Insira palavras-chave para pesquisar conteúdo do bloco de texto", + "characterRange": "Intervalo de Caracteres", + "questionStatus": "Status da Pergunta", + "noFilesUploaded": "Nenhum arquivo enviado ainda", + "unknownFile": "Arquivo Desconhecido", + "fetchFilesFailed": "Erro ao obter lista de arquivos", + "editTag": "Editar Tag", + "deleteTag": "Excluir Tag", + "addTag": "Adicionar Tag", + "selectedCount": "{{count}} blocos de texto selecionados", + "totalCount": "Total de {{count}} blocos de texto", + "batchGenerateQuestions": "Gerar Perguntas em Lote", + "batchDeleteChunks": "Excluir em Lote", + "batchDeleteChunksConfirmTitle": "Confirmar Exclusão em Lote", + "batchDeleteChunksConfirmMessage": "Tem certeza de que deseja excluir os {{count}} blocos de texto selecionados? Esta ação não pode ser desfeita.", + "uploadedDocuments": "{{count}} documentos enviados", + "title": "Processamento de Arquivos", + "uploadNewDocument": "Enviar Novo Arquivo", + "selectFile": "Selecionar Arquivo (suporta múltiplos)", + "markdownOnly": "Atualmente suporta apenas arquivos no formato Markdown (.md) (recomenda-se enviar arquivos do mesmo domínio)", + "supportedFormats": "Formatos suportados: .pdf .md, .txt, .docx (recomenda-se enviar arquivos do mesmo domínio)", + "uploadAndProcess": "Enviar e Processar Arquivo", + "selectedFiles": "Arquivos Selecionados ({{count}})", + "oneFileMessage": "Um projeto limita o processamento a um arquivo, se precisar enviar um novo arquivo, por favor, exclua o arquivo existente primeiro", + "mutilFileMessage": "A árvore de domínio será reconstruída após enviar novo arquivo", + "noChunks": "Nenhum bloco de texto ainda, por favor, envie e processe o arquivo primeiro", + "chunkDetails": "Detalhes do Bloco de Texto: {{chunkId}}", + "fetchChunksFailed": "Falha ao obter blocos de texto", + "fetchChunksError": "Erro ao obter blocos de texto", + "fileResultReceived": "Resultado do arquivo recebido", + "fileUploadSuccess": "Arquivo enviado com sucesso", + "splitTextFailed": "Falha na divisão de texto", + "splitTextError": "Erro na divisão de texto", + "deleteChunkFailed": "Falha ao excluir bloco de texto", + "deleteChunkError": "Erro ao excluir bloco de texto", + "selectModelFirst": "Por favor, selecione um modelo primeiro, pode selecionar na barra de navegação superior", + "modelNotAvailable": "O modelo selecionado não está disponível, por favor, selecione novamente", + "generateQuestionsFailed": "Falha ao gerar perguntas para o bloco de texto {{chunkId}}", + "questionsGenerated": "{{total}} perguntas geradas", + "customSplitMode": "Modo de Divisão Personalizado", + "customSplitInstructions": "Selecione o conteúdo do texto para adicionar pontos de divisão. O sistema adicionará marcadores de divisão na posição selecionada.", + "splitPointsList": "Pontos de Divisão Adicionados", + "saveSplitPoints": "Salvar Pontos de Divisão", + "confirmCustomSplitTitle": "Confirmar Substituição da Divisão Original", + "confirmCustomSplitMessage": "Atenção: A divisão personalizada substituirá o resultado da divisão automática anterior deste arquivo. Tem certeza de que deseja continuar salvando?", + "customSplitSuccess": "Divisão personalizada salva com sucesso", + "customSplitFailed": "Falha ao salvar divisão personalizada", + "missingRequiredData": "Dados necessários ausentes", + "chunksPreview": "Pré-visualização do Tamanho dos Blocos", + "chunk": "Bloco de Texto", + "characters": "caracteres", + "questionsGeneratedSuccess": "{{total}} perguntas geradas com sucesso para o bloco de texto", + "generateQuestionsForChunkFailed": "Falha ao gerar perguntas para o bloco de texto {{chunkId}}", + "generateQuestionsForChunkError": "Erro ao gerar perguntas para o bloco de texto {{chunkId}}", + "generateQuestionsError": "Erro ao gerar perguntas", + "partialSuccess": "Geração de perguntas parcialmente bem-sucedida para blocos de texto ({{successCount}}/{{total}}), {{errorCount}} blocos de texto falharam", + "allSuccess": "{{totalQuestions}} perguntas geradas com sucesso para {{successCount}} blocos de texto", + "fileDeleted": "Arquivo {{fileName}} excluído, atualizando lista de blocos de texto", + "tabs": { + "smartSplit": "Divisão Inteligente", + "domainAnalysis": "Análise de Domínio" + }, + "loading": "Carregando...", + "fetchingDocuments": "Obtendo dados de arquivos", + "processing": "Processando...", + "progressStatus": "{{total}} blocos de texto selecionados, {{completed}} processados", + "processingPleaseWait": "Processando, por favor, aguarde!", + "oneFileLimit": "Arquivo já enviado, não é permitido selecionar novo arquivo", + "unsupportedFormat": "Formato de arquivo não suportado: {{files}}", + "modelInfoParseError": "Falha ao analisar informações do modelo", + "uploadFailed": "Falha no upload, por favor, atualize a página e tente novamente!", + "uploadSuccess": "{{count}} arquivo(s) enviado(s) com sucesso", + "deleteFailed": "Falha ao excluir arquivo", + "deleteSuccess": "Arquivo {{fileName}} excluído com sucesso", + "generatedQuestions": "{{count}} perguntas geradas", + "generatedEvalQuestions": "{{count}} questões de teste geradas", + "generateQuestions": "Gerar Perguntas", + "generateEvalQuestions": "Gerar Conjunto de Teste", + "evalQuestionsGeneratedSuccess": "{{total}} questões de avaliação geradas com sucesso", + "generateEvalQuestionsFailed": "Falha ao gerar questões de avaliação", + "dataCleaning": "Limpeza de Dados", + "batchDataCleaning": "Limpeza de Dados em Lote", + "autoDataCleaning": "Limpeza de Dados Automática", + "autoDataCleaningTip": "Criar tarefa de processamento em lote em segundo plano: limpar automaticamente todos os blocos de texto", + "autoEvalGeneration": "Geração Automática de Conjunto de Avaliação", + "autoEvalGenerationTip": "Criar tarefa de processamento em lote em segundo plano: gerar automaticamente conjunto de dados de avaliação para todos os blocos de texto sem questões de avaliação geradas", + "autoTasks": "Tarefas Automáticas", + "dataCleaningSuccess": "Limpeza de dados concluída, comprimento original: {{originalLength}}, comprimento após limpeza: {{cleanedLength}}", + "dataCleaningFailed": "Falha na limpeza de dados para o bloco de texto {{chunkId}}", + "dataCleaningForChunkSuccess": "Limpeza de dados concluída para o bloco de texto {{chunkId}}", + "dataCleaningForChunkFailed": "Falha na limpeza de dados para o bloco de texto {{chunkId}}", + "dataCleaningForChunkError": "Erro na limpeza de dados para o bloco de texto {{chunkId}}", + "dataCleaningPartialSuccess": "Limpeza de dados parcialmente bem-sucedida para blocos de texto ({{successCount}}/{{total}}), {{errorCount}} blocos de texto falharam", + "dataCleaningAllSuccess": "Limpeza de dados concluída com sucesso para {{successCount}} blocos de texto", + "charsCount": "caracteres", + "pdfProcess": "Arquivo PDF detectado, por favor, selecione o método de processamento do arquivo PDF!", + "pdfProcessStatus": "Total de {{total}} arquivo(s), {{completed}} processado(s)", + "pdfPageProcessStatus": "Processando {{fileName}} total de {{total}} páginas, {{completed}} páginas convertidas", + "pdfProcessing": "Convertendo arquivo...", + "pdfProcessingFailed": "Falha no processamento do arquivo!", + "selectPdfProcessingStrategy": "Por favor, selecione o método de processamento do arquivo PDF:", + "pdfProcessingStrategyDefault": "Padrão", + "pdfProcessingStrategyDefaultHelper": "Usar estratégia de análise de PDF integrada", + "pdfProcessingStrategyMinerUHelper": "Usar análise MinerU API, por favor, configure o Token da API MinerU primeiro", + "pdfProcessingStrategyVision": "Modelo de Visão Personalizado", + "pdfProcessingStrategyVisionHelper": "Usar modelo de visão personalizado para análise", + "pdfProcessingToast": "Arquivo enviado com sucesso, o sistema criará tarefa em segundo plano para analisar o arquivo!", + "pdfProcessingLoading": "Executando tarefa de processamento de arquivo, por favor, aguarde a conclusão da tarefa antes de enviar novo arquivo...", + "pdfProcessingWaring": "Executando tarefa de processamento de arquivo, recomenda-se aguardar a conclusão da tarefa antes de realizar outras operações, caso contrário, pode afetar a qualidade da geração de dados!", + "basicPdfParsing": "Análise Básica de PDF", + "basicPdfParsingDesc": "Pode reconhecer arquivos PDF simples, incluindo estrutura de diretório principal, velocidade mais rápida", + "mineruApiDesc": "Pode reconhecer arquivos PDF complexos, incluindo fórmulas, gráficos (necessita configurar MinerU API Key)", + "mineruLocalDesc": "Pode reconhecer arquivos PDF complexos, incluindo fórmulas, gráficos (necessita configurar MinerU Local URL)", + "mineruApiDescDisabled": "Por favor, vá para [Configuração do Projeto - Configuração de Tarefas] para definir o Token do MinerU primeiro", + "mineruLocalDisabled": "Por favor, vá para [Configuração do Projeto - Configuração de Tarefas] para definir o URL do MinerU Local primeiro", + "mineruWebPlatform": "Análise da Plataforma Online MinerU", + "mineruWebPlatformDesc": "Pode reconhecer arquivos PDF complexos, incluindo fórmulas, gráficos (necessita ir para outro site)", + "mineruSelected": "MinerU selecionado para análise de PDF", + "mineruLocalSelected": "MinerU Local selecionado para análise de PDF", + "customVisionModel": "Análise por Modelo de Visão Personalizado", + "customVisionModelDesc": "Pode reconhecer arquivos PDF complexos, incluindo fórmulas, gráficos (necessita adicionar configuração de modelo de visão na configuração do modelo)", + "customVisionModelSelected": "Modelo de visão grande {{name}} ({{provider}}) selecionado para análise de PDF", + "defaultSelected": "Estratégia padrão integrada selecionada para análise de PDF", + "download": "Baixar Arquivo", + "deleteFile": "Excluir Arquivo", + "batchDelete": "Excluir em Lote ({{count}})", + "batchDeleteTitle": "Exclusão em Lote de Arquivos", + "batchDeleteConfirm": "Tem certeza de que deseja excluir os {{count}} arquivos selecionados? Esta ação não pode ser desfeita.", + "batchDeleteSuccess": "{{count}} arquivo(s) excluído(s) com sucesso", + "batchDeleteFailed": "Falha na exclusão em lote", + "searchFiles": "Pesquisar nome do arquivo...", + "searchResults": "{{count}} arquivo(s) encontrado(s) (total de {{total}})", + "noSearchResults": "Nenhum arquivo contendo \"{{searchTerm}}\" encontrado", + "noResultsOnCurrentPage": "Nenhum resultado de pesquisa na página atual, por favor, volte para a primeira página para visualizar", + "noDataOnCurrentPage": "Nenhum dado na página atual", + "viewChunk": "Ver Bloco de Texto", + "editChunk": "Editar Bloco de Texto {{chunkId}}", + "editChunkSuccess": "Bloco de texto editado com sucesso", + "editChunkFailed": "Falha ao editar bloco de texto", + "editChunkError": "Erro ao editar bloco de texto", + "deleteFileWarning": "Aviso: A exclusão do arquivo também excluirá o seguinte conteúdo relacionado", + "deleteFileWarningChunks": "Todos os blocos de texto associados", + "deleteFileWarningQuestions": "Todas as perguntas geradas pelos blocos de texto", + "deleteFileWarningDatasets": "Todos os conjuntos de dados gerados pelas perguntas", + "domainTree": { + "firstUploadTitle": "Geração da Árvore de Domínio", + "uploadTitle": "Envio de Arquivo - Processamento da Árvore de Domínio", + "deleteTitle": "Exclusão de Arquivo - Processamento da Árvore de Domínio", + "reviseOption": "Revisar Árvore de Domínio", + "reviseDesc": "Corrigir a árvore de domínio atual com base nas informações de arquivos adicionados ou excluídos, afetando apenas as partes alteradas", + "rebuildOption": "Reconstruir Árvore de Domínio", + "rebuildDesc": "Regenerar a árvore de domínio completa com base nas informações de diretório de todos os arquivos", + "keepOption": "Manter Inalterado", + "keepDesc": "Manter a estrutura atual da árvore de domínio inalterada, sem fazer modificações" + } + }, + "domain": { + "title": "Árvore de Conhecimento de Domínio", + "addRootTag": "Adicionar Tag de Primeiro Nível", + "addFirstTag": "Adicionar Primeira Tag", + "noTags": "Nenhum dado de árvore de tags de domínio disponível", + "docStructure": "Estrutura de Diretório do Documento", + "noToc": "Nenhuma estrutura de diretório disponível, por favor, envie e processe o arquivo primeiro", + "editTag": "Editar Tag", + "deleteTag": "Excluir Tag", + "addChildTag": "Adicionar Tag Filha", + "deleteTagConfirmTitle": "Excluir Tag", + "deleteTagConfirmMessage": "Tem certeza de que deseja excluir a tag \"{{tag}}\"?", + "deleteWarning": "Esta ação excluirá esta tag e todas as suas tags filhas, perguntas e conjuntos de dados, e não poderá ser recuperada!", + "dialog": { + "addTitle": "Adicionar Tag", + "editTitle": "Editar Tag", + "addChildTitle": "Adicionar Tag Filha", + "inputRoot": "Por favor, insira o nome da nova tag de primeiro nível", + "inputEdit": "Por favor, edite o nome da tag", + "inputChild": "Por favor, adicione tag filha para \"{label}\"", + "labelName": "Nome da Tag", + "saving": "Salvando...", + "save": "Salvar", + "deleteConfirm": "Tem certeza de que deseja excluir a tag \"{label}\"?", + "deleteWarning": "Esta ação também excluirá todas as tags filhas e não poderá ser recuperada.", + "emptyLabel": "O nome da tag não pode estar vazio" + }, + "tabs": { + "tree": "Árvore de Domínio", + "structure": "Estrutura de Diretório" + }, + "errors": { + "saveFailed": "Falha ao salvar tag" + }, + "messages": { + "updateSuccess": "Tag atualizada com sucesso" + } + }, + "export": { + "alpacaSettings": "Configurações de Formato Alpaca", + "questionFieldType": "Tipo de Campo de Pergunta", + "useInstruction": "Usar campo instruction", + "useInput": "Usar campo input", + "customInstruction": "Conteúdo personalizado do campo instruction", + "instructionPlaceholder": "Por favor, insira o conteúdo fixo da instrução", + "instructionHelperText": "Quando usar o campo input, pode especificar aqui o conteúdo fixo do instruction", + "title": "Exportar", + "format": "Estilo do Conjunto de Dados", + "fileFormat": "Formato do Arquivo", + "systemPrompt": "Prompt do Sistema", + "systemPromptPlaceholder": "Por favor, insira o prompt do sistema...", + "ReasoninglanguagePlaceholder": "Por favor, insira a linguagem de Raciocínio: Inglês ou Chinês ou outras", + "Reasoninglanguage": "Linguagem de Raciocínio", + "onlyConfirmed": "Exportar apenas dados confirmados", + "example": "Exemplo de Formato", + "confirmExport": "Confirmar Exportação", + "includeCOT": "Incluir Cadeia de Pensamento", + "cotDescription": "Incluir o processo de raciocínio antes da resposta final", + "customFormat": "Formato Personalizado", + "customFormatSettings": "Configurações de Formato Personalizado", + "questionFieldName": "Nome do Campo de Pergunta", + "answerFieldName": "Nome do Campo de Resposta", + "cotFieldName": "Nome do Campo de Cadeia de Pensamento", + "includeLabels": "Incluir Tags", + "includeChunk": "Incluir Bloco de Texto", + "questionOnly": "Exportar apenas perguntas", + "localTab": "Exportar para Local", + "llamaFactoryTab": "Usar no LLaMA Factory", + "huggingFaceTab": "Enviar para Hugging Face", + "configExists": "Arquivo de configuração já existe", + "configPath": "Caminho do arquivo de configuração", + "updateConfig": "Atualizar Configuração do LLaMA Factory", + "noConfig": "Nenhum arquivo de configuração ainda, clique no botão abaixo para gerar", + "generateConfig": "Gerar Configuração do LLaMA Factory", + "huggingFaceComingSoon": "Funcionalidade de exportação para HuggingFace em breve", + "uploadToHuggingFace": "Enviar para HuggingFace", + "datasetName": "Nome do Conjunto de Dados", + "datasetNameHelp": "Formato: nome de usuário/nome do conjunto de dados", + "privateDataset": "Conjunto de Dados Privado", + "datasetSettings": "Configurações do Conjunto de Dados", + "exportOptions": "Opções de Exportação", + "uploadSuccess": "Conjunto de dados enviado com sucesso para HuggingFace", + "viewOnHuggingFace": "Ver no HuggingFace", + "noTokenWarning": "Token do HuggingFace não encontrado. Por favor, configure o token nas configurações do projeto.", + "goToSettings": "Ir para Configurações", + "tokenHelp": "Você pode obter o token na página de configurações do HuggingFace", + "multilingualThinkingFormat": "Pensamento Multilíngue", + "sampleInstruction": "Instrução Humana (obrigatório)", + "sampleOutput": "Resposta do Modelo (obrigatório)", + "sampleSystem": "Prompt do Sistema (opcional)", + "sampleInstruction2": "Segunda Instrução", + "sampleOutput2": "Segunda Resposta", + "sampleSystemShort": "Prompt do Sistema", + "fixedInstruction": "Conteúdo fixo da instrução", + "sampleInput": "Pergunta Humana (obrigatório)", + "sampleInput2": "Segunda Pergunta", + "sampleInputOptional": "Entrada Humana (opcional)", + "sampleUserMessage": "Instrução Humana", + "sampleAssistantMessage": "Resposta do Modelo", + "sampleAnalysis": "Conteúdo da cadeia de pensamento do modelo", + "sampleFinal": "Resposta do Modelo", + "sampleThinking": "Conteúdo da cadeia de pensamento do modelo", + "fetchLabelStatsError": "Falha ao obter estatísticas de tags:" + }, + "datasets": { + "loadingDataset": "Carregando detalhes do conjunto de dados...", + "datasetNotFound": "Conjunto de dados não encontrado", + "optimizeTitle": "Otimização de IA", + "optimizeAdvice": "Sugestão de Otimização", + "optimizePlaceholder": "Por favor, insira suas sugestões de melhoria para a resposta, a IA otimizará a resposta e a cadeia de pensamento de acordo com suas sugestões", + "generatingDataset": "Gerando conjunto de dados", + "aiOptimizeAdvicePlaceholder": "Por favor, insira suas sugestões de melhoria para a resposta, a IA otimizará a resposta e a cadeia de pensamento de acordo com suas sugestões", + "aiOptimizeAdvice": "Por favor, insira suas sugestões de melhoria para a resposta, a IA otimizará a resposta e a cadeia de pensamento de acordo com suas sugestões", + "aiOptimize": "Otimização Inteligente de IA", + "generating": "Gerando conjunto de dados", + "partialSuccess": "Geração de conjunto de dados parcialmente bem-sucedida para perguntas ({{successCount}}/{{total}}), {{failCount}} perguntas falharam", + "generateError": "Falha ao gerar conjunto de dados", + "management": "Conjunto de Dados", + "question": "Pergunta", + "filterAll": "Todos", + "filterConfirmed": "Confirmado", + "filterUnconfirmed": "Não Confirmado", + "createdAt": "Tempo de Criação", + "model": "Modelo Usado", + "domainTag": "Tag de Domínio", + "cot": "Cadeia de Pensamento", + "answer": "Resposta", + "chunkId": "Bloco de Texto", + "confirmed": "Confirmado", + "noTag": "Sem Tag", + "noData": "Nenhum dado", + "rowsPerPage": "Linhas por Página", + "pagination": "{{from}}-{{to}} de {{count}}", + "confirmDeleteMessage": "Tem certeza de que deseja excluir este conjunto de dados? Esta ação não pode ser desfeita.", + "questionLabel": "Pergunta", + "fetchFailed": "Falha ao obter conjunto de dados", + "deleteFailed": "Falha ao excluir conjunto de dados", + "deleteSuccess": "Excluído com sucesso", + "exportSuccess": "Conjunto de dados exportado com sucesso", + "exportFailed": "Falha na exportação", + "exportProgress": "Progresso da Exportação", + "exportingData": "Exportando conjunto de dados", + "processedCount": "Processado {{processed}} / {{total}} itens", + "exportInProgress": "Obtendo dados, por favor, aguarde...", + "exportFinalizing": "Gerando arquivo, quase concluído...", + "loading": "Carregando conjunto de dados...", + "stats": "Total de {{total}} conjuntos de dados, {{confirmed}} confirmados ({{percentage}}%)", + "selected": "{{ count }} conjuntos de dados selecionados no total", + "batchconfirmDeleteMessage": "Tem certeza de que deseja excluir os {{count}} conjuntos de dados selecionados? Esta ação não pode ser desfeita.", + "batchDelete": "Excluir em Lote", + "batchDeleteProgress": "Concluído: {{completed}}/{{total}}", + "batchDeleteCount": "Gerado: {{count}}", + "evaluation": "Anotação do Conjunto de Dados", + "rating": "Avaliação", + "ratingExcellent": "Excelente", + "ratingGood": "Bom", + "ratingAverage": "Médio", + "ratingPoor": "Ruim", + "ratingVeryPoor": "Muito Ruim", + "ratingUnrated": "Não Avaliado", + "customTags": "Tags Personalizadas", + "addCustomTag": "Adicionar tag personalizada...", + "note": "Observação", + "addNote": "Adicionar observação...", + "noNote": "Nenhuma observação", + "clickToAddNote": "Clique para adicionar observação...", + "enterNote": "Por favor, insira a observação...", + "noteShortcuts": "Ctrl+Enter para salvar, Esc para cancelar", + "aiEvaluation": "Avaliação de Qualidade de IA", + "addToEval": "Adicionar ao Conjunto de Dados de Avaliação", + "addToEvalSuccess": "Adicionado ao conjunto de dados de avaliação com sucesso", + "addToEvalFailed": "Falha ao adicionar", + "generateEvalVariant": "Gerar Variante do Conjunto de Avaliação", + "generateVariantFailed": "Falha ao gerar variante", + "saveVariantSuccess": "Salvo no conjunto de dados de avaliação", + "saveVariantFailed": "Falha ao salvar", + "evalVariantTitle": "Gerar Variante do Conjunto de Avaliação", + "evalVariantPreviewTitle": "Confirmar Questões Geradas", + "saveToEval": "Salvar no Conjunto de Avaliação", + "evalVariantConfigHint": "Por favor, selecione o tipo e quantidade de questões a serem geradas, a IA reescreverá com base no par de perguntas e respostas atual.", + "questionType": "Tipo de Questão", + "typeOpenEnded": "Pergunta e Resposta Aberta", + "typeSingleChoice": "Múltipla Escolha", + "typeMultipleChoice": "Múltipla Escolha (Várias Respostas)", + "typeTrueFalse": "Verdadeiro/Falso", + "generateCount": "Quantidade a Gerar", + "evalVariantPreviewHint": "Você pode editar as questões geradas, confirme e salve no conjunto de avaliação após verificação.", + "questionIndex": "Questão {{index}}", + "options": "Opções (Array JSON)", + "optionsHint": "Por exemplo: [\"Opção A\", \"Opção B\"]", + "answerArrayHint": "Para respostas de múltipla escolha, insira um array, como [\"A\", \"C\"]", + "answerBoolHint": "Para questões de verdadeiro/falso, insira ✅ ou ❌", + "generate": "Gerar", + "updateSuccess": "Atualização bem-sucedida", + "updateFailed": "Falha na atualização", + "searchPlaceholder": "Pesquisar conjunto de dados...", + "fieldQuestion": "Pergunta", + "fieldAnswer": "Resposta", + "fieldCOT": "Cadeia de Pensamento", + "fieldLabel": "Tag de Domínio", + "moreFilters": "Mais", + "filtersTitle": "Condições de Filtro", + "filterConfirmationStatus": "Status de Confirmação", + "filterCotStatus": "Status de Cadeia de Pensamento", + "filterHasCot": "Inclui Cadeia de Pensamento", + "filterNoCot": "Não Inclui Cadeia de Pensamento", + "filterScoreRange": "Intervalo de Pontuação", + "filterNoteKeyword": "Palavra-chave da Observação", + "filterNoteKeywordPlaceholder": "Por favor, insira a palavra-chave da observação...", + "filterChunkName": "Nome do Bloco de Texto", + "filterChunkNamePlaceholder": "Por favor, insira o nome do bloco de texto...", + "filterCustomTag": "Tag Personalizada", + "resetFilters": "Redefinir", + "applyFilters": "Aplicar Filtros", + "viewDetails": "Ver Detalhes", + "datasetDetail": "Detalhes do Conjunto de Dados", + "metadata": "Metadados", + "confirmSave": "Confirmar Retenção", + "unconfirm": "Cancelar Confirmação", + "unconfirming": "Cancelando confirmação...", + "uncategorized": "Não Categorizado", + "questionCount": "{{count}} perguntas", + "source": "Fonte", + "generateDataset": "Gerar Conjunto de Dados", + "generateNotImplemented": "Funcionalidade de geração de conjunto de dados não implementada", + "generateSuccess": "Conjunto de dados gerado com sucesso: {{question}}", + "generateFailed": "Falha ao gerar conjunto de dados: {{error}}", + "noTagsAndQuestions": "Nenhuma tag e pergunta disponível", + "answerCount": "{{count}} respostas", + "answered": "Resposta Gerada", + "enableShortcuts": "Atalhos de Paginação", + "shortcutsHelp": "Pressione ← para anterior, → para próximo, y para confirmar, d para excluir", + "filterDistill": "É Conjunto de Dados Destilado", + "filterDistillYes": "Conjunto de Dados Destilado", + "filterDistillNo": "Conjunto de Dados Não Destilado", + "evaluate": "Avaliação de Qualidade", + "evaluating": "Avaliando...", + "batchEvaluate": "Avaliação de Qualidade Automática", + "selectModelFirst": "Por favor, selecione um modelo primeiro", + "evaluateSuccess": "Avaliação concluída! Pontuação: {{score}}/5", + "evaluateFailed": "Falha na avaliação", + "evaluateError": "Falha na avaliação: {{error}}", + "batchEvaluateStarted": "Tarefa de avaliação em lote iniciada, será processada em segundo plano", + "batchEvaluateStartFailed": "Falha ao iniciar avaliação em lote", + "batchEvaluateFailed": "Falha na avaliação em lote: {{error}}", + "scoreRange": "{{min}} - {{max}} pontos", + "singleTurn": "Conjunto de Dados de Pergunta e Resposta de Rodada Única", + "multiTurn": "Conjunto de Dados de Diálogo de Múltiplas Rodadas", + "imageQA": "Conjunto de Dados de Pergunta e Resposta de Imagens", + "conversationDetail": "Detalhes do Diálogo de Múltiplas Rodadas", + "conversationContent": "Conteúdo do Diálogo", + "basicInfo": "Informações Básicas", + "firstQuestion": "Primeira Pergunta", + "conversationScenario": "Cenário do Diálogo", + "conversationRounds": "Número de Rodadas do Diálogo", + "modelUsed": "Modelo Usado", + "qualityScore": "Pontuação de Qualidade", + "notes": "Observações", + "createTime": "Tempo de Criação", + "notSet": "Não Definido", + "noTags": "Sem Tags", + "noNotes": "Sem Observações", + "notEvaluated": "Ainda Não Avaliado", + "round": "Rodada {{round}}", + "system": "Sistema", + "user": "Usuário", + "assistant": "Assistente", + "confirmDelete": "Confirmar Exclusão", + "confirmDeleteConversation": "Tem certeza de que deseja excluir este conjunto de dados de diálogo de múltiplas rodadas? Esta ação não pode ser desfeita.", + "conversationNotFound": "Conjunto de dados de diálogo não existe", + "fetchDataFailed": "Falha ao obter dados", + "saveFailed": "Falha ao salvar", + "saveSuccess": "Salvo com sucesso", + "saving": "Salvando...", + "inputTagsPlaceholder": "Insira tags, separadas por espaço", + "addNotesPlaceholder": "Adicionar informações de observação", + "noConversations": "Nenhum conjunto de dados de diálogo de múltiplas rodadas", + "notRated": "Não Avaliado", + "minScore": "Pontuação Mínima", + "maxScore": "Pontuação Máxima", + "unconfirmed": "Não Confirmado" + }, + "rating": { + "veryPoor": "Muito Ruim", + "poor": "Ruim", + "belowAverage": "Abaixo da Média", + "fair": "Regular", + "average": "Médio", + "good": "Bom", + "veryGood": "Muito Bom", + "excellent": "Excelente", + "outstanding": "Excepcional", + "perfect": "Perfeito", + "unrated": "Não Avaliado" + }, + "tags": { + "noTags": "Nenhuma tag", + "addTag": "Adicionar tag...", + "addCustomTag": "Adicionar tag personalizada", + "maxTagsReached": "Máximo de {{maxTags}} tags permitidas", + "availableTagsHint": "Pode selecionar de tags existentes ou inserir novas tags" + }, + "import": { + "title": "Importar", + "fileUpload": "Upload de Arquivo", + "fileUploadDescription": "Fazer upload de arquivo local para importar conjunto de dados", + "uploadFile": "Enviar Arquivo", + "supportedFormats": "Suporta arquivos nos formatos JSON, JSONL, CSV", + "dragDropFile": "Arraste o arquivo para cá ou clique para selecionar o arquivo", + "dropFileHere": "Solte para fazer upload do arquivo", + "maxFileSize": "Tamanho máximo do arquivo: 50MB", + "processingFile": "Processando arquivo...", + "uploadedFiles": "Arquivos Enviados", + "uploadError": "Falha no upload do arquivo, por favor, verifique se o formato do arquivo está correto", + "mapFields": "Mapeamento de Campos", + "importing": "Importando", + "fieldMapping": "Mapeamento de Campos", + "mappingDescription": "Por favor, mapeie os campos dos dados de origem para os campos de destino. O sistema já identificou automaticamente possíveis relações de mapeamento, você pode ajustar conforme necessário.", + "selectMapping": "Selecionar Mapeamento de Campos", + "questionField": "Campo de Pergunta", + "answerField": "Campo de Resposta", + "cotField": "Campo de Cadeia de Pensamento", + "tagsField": "Campo de Tags", + "selectField": "Selecionar Campo", + "questionDesc": "Pergunta do usuário ou conteúdo de entrada (obrigatório)", + "answerDesc": "Resposta da IA ou conteúdo de saída (obrigatório)", + "cotDesc": "Cadeia de pensamento ou processo de raciocínio (opcional)", + "tagsDesc": "Array de tags, múltiplas tags separadas por vírgula (opcional)", + "dataPreview": "Pré-visualização de Dados", + "previewNote": "Exibindo os primeiros 3 registros, cada valor de campo exibindo no máximo 100 caracteres", + "confirmMapping": "Confirmar Mapeamento", + "requiredFields": "Por favor, selecione pelo menos o mapeamento dos campos de pergunta e resposta", + "mappingRequired": "Campos de pergunta e resposta são obrigatórios", + "duplicateMapping": "Não é possível mapear múltiplos campos de destino para o mesmo campo de origem", + "noPreviewData": "Nenhum dado disponível para pré-visualização", + "preparingData": "Preparando dados...", + "uploadingData": "Enviando dados...", + "processing": "Processando... {{processed}}/{{total}}", + "completed": "Importação Concluída", + "importStats": "Estatísticas de Importação", + "total": "Total: {{count}}", + "success": "Sucesso: {{count}}", + "failed": "Falha: {{count}}", + "source": "Fonte de Dados", + "description": "Descrição", + "errors": "Mensagens de Erro", + "moreErrors": "Mais {{count}} erros não exibidos...", + "importSuccess": "Importação do conjunto de dados concluída!", + "enterDatasetName": "Por favor, insira o nome do conjunto de dados", + "noDatasetFound": "Nenhum conjunto de dados correspondente encontrado", + "complete": "Concluir", + "addToEval": "Adicionar ao Conjunto de Dados de Avaliação", + "addToEvalSuccess": "Adicionado ao conjunto de dados de avaliação com sucesso", + "addToEvalFailed": "Falha ao adicionar", + "generateEvalVariant": "Gerar Variante do Conjunto de Avaliação", + "selectModelFirst": "Por favor, selecione um modelo primeiro", + "generateVariantFailed": "Falha ao gerar variante", + "saveVariantSuccess": "Salvo no conjunto de dados de avaliação", + "saveVariantFailed": "Falha ao salvar", + "evalVariantTitle": "Gerar Variante do Conjunto de Avaliação", + "evalVariantHint": "A IA gerou novas variantes de teste com base no par de perguntas e respostas original, você pode editar manualmente antes de salvar.", + "saveToEval": "Salvar no Conjunto de Avaliação", + "evalVariantConfigHint": "Por favor, selecione o tipo e quantidade de questões a serem geradas, a IA reescreverá com base no par de perguntas e respostas atual.", + "questionType": "Tipo de Questão", + "typeOpenEnded": "Pergunta e Resposta Aberta", + "typeSingleChoice": "Múltipla Escolha", + "typeMultipleChoice": "Múltipla Escolha (Várias Respostas)", + "typeTrueFalse": "Verdadeiro/Falso", + "typeShortAnswer": "Resposta Curta", + "generateCount": "Quantidade a Gerar", + "evalVariantPreviewHint": "Você pode editar as questões geradas, confirme e salve no conjunto de avaliação após verificação.", + "questionIndex": "Questão {{index}}", + "options": "Opções (Array JSON)", + "optionsHint": "Por exemplo: [\"Opção A\", \"Opção B\"]", + "answerArrayHint": "Para respostas de múltipla escolha, insira um array, como [\"A\", \"C\"]", + "answerBoolHint": "Para questões de verdadeiro/falso, insira ✅ ou ❌", + "evalVariantPreviewTitle": "Confirmar Questões Geradas", + "generate": "Gerar" + }, + "update": { + "newVersion": "Nova Versão", + "newVersionAvailable": "Nova versão disponível", + "currentVersion": "Versão Atual", + "latestVersion": "Última Versão", + "downloadNow": "Baixar Agora", + "downloading": "Baixando:", + "installNow": "Instalar Agora", + "updating": "Atualizando...", + "updateNow": "Atualizar Agora", + "viewRelease": "Baixar Última Versão", + "checking": "Verificando atualizações...", + "noUpdates": "Já está na versão mais recente", + "updateError": "Erro na atualização", + "updateSuccess": "Atualização bem-sucedida", + "restartRequired": "Necessário reiniciar o aplicativo", + "restartNow": "Reiniciar Agora", + "restartLater": "Reiniciar Mais Tarde" + }, + "datasetSquare": { + "title": "Praça de Conjuntos de Dados", + "subtitle": "Descubra e explore vários recursos de conjuntos de dados públicos para auxiliar no treinamento e pesquisa do seu modelo", + "searchPlaceholder": "Pesquisar palavras-chave do conjunto de dados...", + "searchVia": "Via", + "categoryTitle": "Categorias de Conjuntos de Dados", + "categories": { + "all": "Todos", + "popular": "Recomendações Populares", + "chinese": "Recursos em Chinês", + "english": "Recursos em Inglês", + "research": "Dados de Pesquisa", + "multimodal": "Multimodal" + }, + "foundResources": "{{count}} recursos de conjuntos de dados encontrados", + "currentFilter": "Filtro atual: {{category}}", + "noDatasets": "Nenhum conjunto de dados correspondente encontrado", + "tryOtherCategories": "Por favor, tente outras categorias ou volte para ver todos os conjuntos de dados", + "dataset": "Conjunto de Dados", + "viewDataset": "Ver Conjunto de Dados" + }, + "playground": { + "title": "Teste de Modelo", + "selectModelFirst": "Por favor, selecione pelo menos um modelo", + "sendFirstMessage": "Envie a primeira mensagem para iniciar o teste", + "inputMessage": "Digite a mensagem...", + "send": "Enviar", + "outputMode": "Modo de Saída", + "normalOutput": "Saída Normal", + "streamingOutput": "Saída em Streaming", + "clearConversation": "Limpar Diálogo", + "selectModelMax3": "Selecionar Modelo (máximo 3)", + "reasoningProcess": "Processo de Raciocínio" + }, + "chunks": { + "title": "Blocos de Texto", + "defaultTitle": "Título Padrão" + }, + "documentation": "Documentação", + "models": { + "configNotFound": "Configuração do modelo não encontrada", + "parseError": "Falha ao analisar configuração do modelo", + "fetchFailed": "Falha ao obter modelo", + "saveFailed": "Falha ao salvar configuração do modelo", + "pleaseSelectModel": "Por favor, selecione pelo menos um modelo", + "title": "Gerenciamento de Modelos", + "add": "Adicionar Modelo", + "unselectedModel": "Modelo Não Selecionado", + "unconfiguredAPIKey": "API Key Não Configurada", + "saveAllModels": "Salvar Todas as Configurações de Modelos", + "edit": "Editar", + "delete": "Excluir", + "modelId": "ID do Modelo", + "modelName": "Nome do Modelo", + "modelNamePlaceholder": "Por favor, insira o nome do modelo (opcional, padrão é o ID do modelo)", + "modelIdPlaceholder": "Nome do modelo (pode inserir personalizado)", + "endpoint": "Endereço da Interface", + "apiKey": "Chave da API", + "provider": "Provedor (pode inserir personalizado)", + "localModel": "Modelo Local", + "apiKeyConfigured": "API Key já configurada", + "apiKeyNotConfigured": "API Key não configurada", + "temperature": "Temperatura do Modelo", + "maxTokens": "Número Máximo de Tokens Gerados", + "maxTokensInputTip": "Intervalo do controle deslizante: 1-{{max}}. Você também pode inserir qualquer número inteiro positivo.", + "topP": "Top P", + "type": "Tag do Modelo", + "text": "Modelo de Linguagem", + "vision": "Modelo de Visão", + "typeTips": "Se deseja usar modelo de visão personalizado para analisar PDF, certifique-se de configurar pelo menos um modelo grande de visão", + "refresh": "Atualizar Lista de Modelos", + "configuredModels": "Modelos Configurados", + "unconfiguredModels": "Modelos Não Configurados", + "noConfiguredModels": "Nenhum modelo configurado ainda", + "noUnconfiguredModels": "Nenhum modelo não configurado ainda", + "checkEndpointHealth": "Verificar Saúde do Endpoint", + "checkAllEndpointHealth": "Verificar Todos os Endpoints com um Clique", + "endpointHealthy": "Endpoint Saudável", + "endpointCheckFailed": "Falha na verificação do endpoint", + "endpointMissing": "Endpoint está vazio", + "endpointReachableModelMissing": "Endpoint acessível, mas o modelo atual não está na lista de retorno", + "healthCheckSummary": "Verificação de saúde concluída: {{okCount}} normal, {{failCount}} falha", + "checking": "Verificando...", + "healthy": "Saudável", + "reachable": "Acessível", + "unhealthy": "Anormal", + "notChecked": "Não Verificado" + }, + "stats": { + "ongoingProjects": "Projetos em Execução", + "questionCount": "Quantidade de Perguntas", + "generatedDatasets": "Conjuntos de Dados Gerados", + "supportedModels": "Modelos Suportados" + }, + "migration": { + "title": "【Importante】Migração de Dados Históricos", + "description": "Para melhorar o desempenho de recuperação de grandes conjuntos de dados, a partir da versão 1.3.1, o Easy Dataset mudou o método de armazenamento de arquivos para armazenamento em banco de dados local. Detectamos que você tem projetos históricos que ainda não foram migrados. Antes de concluir a migração, você não poderá acessar esses projetos. Por favor, conclua a migração o mais rápido possível!", + "projectsList": "Projetos Não Migrados", + "migrate": "Iniciar Migração", + "migrating": "Migrando...", + "success": "{{count}} projeto(s) migrado(s) com sucesso", + "failed": "Falha na migração", + "checkFailed": "Falha ao verificar projetos não migrados", + "checkError": "Erro ao verificar projetos não migrados", + "starting": "Iniciando tarefa de migração...", + "processing": "Processando tarefa de migração...", + "completed": "Migração concluída", + "startFailed": "Falha ao iniciar tarefa de migração", + "statusFailed": "Falha ao obter status da migração", + "taskNotFound": "Tarefa de migração não existe", + "progressStatus": "{{completed}}/{{total}} projeto(s) migrado(s)", + "openDirectory": "Abrir Diretório do Projeto", + "deleteDirectory": "Excluir Diretório do Projeto", + "confirmDelete": "Tem certeza de que deseja excluir este diretório de projeto? Esta ação não pode ser desfeita.", + "openDirectoryFailed": "Falha ao abrir diretório do projeto", + "deleteDirectoryFailed": "Falha ao excluir diretório do projeto" + }, + "distill": { + "title": "Destilação de Dados", + "generateRootTags": "Gerar Tags de Primeiro Nível", + "generateSubTags": "Gerar Tags Filhas", + "generateQuestions": "Gerar Perguntas", + "generateRootTagsTitle": "Gerar Tags de Domínio de Primeiro Nível", + "generateSubTagsTitle": "Gerar Tags Filhas para {{parentTag}}", + "generateQuestionsTitle": "Gerar Perguntas para {{tag}}", + "parentTag": "Tag Pai", + "parentTagPlaceholder": "Por favor, insira o nome da tag pai (ex: Esportes, Tecnologia, etc.)", + "parentTagHelp": "Insira um tema de domínio, o sistema gerará tags relacionadas com base nisso", + "tagCount": "Quantidade de Tags", + "tagCountHelp": "Insira a quantidade de tags a serem geradas, máximo de 100", + "questionCount": "Quantidade de Perguntas", + "questionCountHelp": "Insira a quantidade de perguntas a serem geradas, máximo de 100", + "generatedTags": "Tags Geradas", + "generatedQuestions": "Perguntas Geradas", + "generateTags": "Gerar Tags", + "tagPath": "Caminho da Tag", + "noTags": "Nenhuma tag ainda", + "noQuestions": "Nenhuma pergunta ainda", + "clickGenerateButton": "Clique no botão de gerar acima para começar a criar tags", + "selectModelFirst": "Por favor, selecione um modelo primeiro", + "selectModel": "Selecionar Modelo", + "generateTagsError": "Falha ao gerar tags", + "generateQuestionsError": "Falha ao gerar perguntas", + "deleteTagConfirmTitle": "Confirmar exclusão da tag? Isso excluirá todas as tags filhas, perguntas e conjuntos de dados associados a esta tag", + "editTagTitle": "Editar Tag", + "tagName": "Nome da Tag", + "labelRequired": "O nome da tag não pode estar vazio", + "tagUpdateSuccess": "Tag atualizada com sucesso", + "tagUpdateFailed": "Falha ao atualizar tag", + "unknownTag": "Tag desconhecida", + "autoDistillButton": "Destilação Automática Completa de Conjunto de Dados", + "autoDistillTitle": "Configuração de Destilação Automática Completa de Conjunto de Dados", + "distillTopic": "Tema da Destilação", + "tagLevels": "Níveis de Tags", + "tagLevelsHelper": "Definir a quantidade de níveis, máximo de {{max}} níveis", + "tagsPerLevel": "Quantidade de Tags por Nível", + "tagsPerLevelHelper": "Quantidade de tags filhas geradas sob cada tag pai, máximo de {{max}}", + "questionsPerTag": "Quantidade de Perguntas por Tag", + "questionsPerTagHelper": "Quantidade de perguntas geradas para cada tag folha, máximo de {{max}}", + "estimationInfo": "Informações Estimadas da Tarefa", + "estimatedTags": "Quantidade Estimada de Tags a Serem Geradas", + "estimatedQuestions": "Quantidade Estimada de Perguntas a Serem Geradas", + "currentTags": "Quantidade Atual de Tags", + "currentQuestions": "Quantidade Atual de Perguntas", + "newTags": "Quantidade Estimada de Novas Tags", + "newQuestions": "Quantidade Estimada de Novas Perguntas", + "startAutoDistill": "Iniciar Destilação Automática", + "autoDistillProgress": "Progresso da Destilação Automática", + "overallProgress": "Progresso Geral", + "tagsProgress": "Progresso da Construção de Tags", + "questionsProgress": "Progresso da Geração de Perguntas", + "currentStage": "Estágio Atual", + "realTimeLogs": "Logs em Tempo Real", + "waitingForLogs": "Aguardando saída de logs...", + "autoDistillStarted": "{{time}} Tarefa de destilação automática iniciada", + "autoDistillInsufficientError": "A configuração atual não produzirá novas tags ou perguntas, por favor, ajuste os parâmetros", + "stageInitializing": "Inicializando...", + "stageBuildingLevel1": "Construindo tags do primeiro nível", + "stageBuildingLevel2": "Construindo tags do segundo nível", + "stageBuildingLevel3": "Construindo tags do terceiro nível", + "stageBuildingLevel4": "Construindo tags do quarto nível", + "stageBuildingLevel5": "Construindo tags do quinto nível", + "stageBuildingQuestions": "Gerando perguntas", + "stageBuildingDatasets": "Gerando conjuntos de dados", + "stageCompleted": "Tarefa concluída", + "datasetsProgress": "Progresso da Geração de Conjuntos de Dados", + "rootTopicHelperText": "Por padrão, usa o nome do projeto como tema de destilação de primeiro nível. Se precisar alterar, vá para as configurações do projeto para alterar o nome do projeto.", + "addChildTag": "Gerar Tag Filha", + "datasetType": "Tipo de Conjunto de Dados", + "singleTurnDataset": "Conjunto de Dados de Diálogo de Rodada Única", + "multiTurnDataset": "Conjunto de Dados de Diálogo de Múltiplas Rodadas", + "bothDatasetTypes": "Gerar ambos os tipos de conjuntos de dados", + "autoDistillTaskDetail": "Tarefa de destilação automática: {{topic}}", + "backgroundTaskCreated": "Tarefa de destilação em segundo plano criada, pode verificar o progresso no gerenciamento de tarefas", + "backgroundTaskFailed": "Falha ao criar tarefa em segundo plano", + "taskExecutionError": "Erro na execução da tarefa: {{error}}" + }, + "tasks": { + "pending": "{{count}} tarefa(s) em processamento", + "completed": "Todas as tarefas concluídas", + "title": "Centro de Gerenciamento de Tarefas", + "loading": "Carregando lista de tarefas...", + "empty": "Nenhum registro de tarefa ainda", + "confirmDelete": "Confirmar exclusão desta tarefa?", + "confirmAbort": "Confirmar interrupção desta tarefa? A tarefa será interrompida.", + "deleteSuccess": "Tarefa excluída", + "deleteFailed": "Falha ao excluir tarefa", + "abortSuccess": "Tarefa interrompida", + "abortFailed": "Falha ao interromper tarefa", + "status": { + "processing": "Processando", + "completed": "Concluído", + "failed": "Falhou", + "aborted": "Interrompido", + "unknown": "Desconhecido" + }, + "types": { + "text-processing": "Processamento de Texto", + "file-processing": "Processamento de Arquivo", + "data-cleaning": "Limpeza de Dados", + "question-generation": "Geração de Perguntas", + "answer-generation": "Geração de Respostas", + "multi-turn-generation": "Geração de Diálogo de Múltiplas Rodadas", + "eval-generation": "Geração de Conjunto de Avaliação", + "image-question-generation": "Geração de Perguntas de Imagem", + "data-distillation": "Destilação de Dados", + "pdf-processing": "Análise de PDF" + }, + "filters": { + "status": "Status da Tarefa", + "type": "Tipo de Tarefa" + }, + "actions": { + "refresh": "Atualizar Lista de Tarefas", + "delete": "Excluir Tarefa", + "abort": "Interromper Tarefa" + }, + "table": { + "type": "Tipo de Tarefa", + "status": "Status", + "progress": "Progresso", + "note": "Observação", + "createTime": "Tempo de Criação", + "endTime": "Tempo de Conclusão", + "duration": "Tempo de Execução", + "model": "Modelo Usado", + "detail": "Detalhes da Tarefa", + "actions": "Ações" + }, + "duration": { + "seconds": "{{seconds}} segundos", + "minutes": "{{minutes}} minutos {{seconds}} segundos", + "hours": "{{hours}} horas {{minutes}} minutos" + }, + "fetchFailed": "Falha ao obter lista de tarefas", + "createSuccess": "Tarefa criada com sucesso", + "createFailed": "Falha ao criar tarefa", + "multiTurnCreateSuccess": "Tarefa de conjunto de dados de diálogo de múltiplas rodadas criada com sucesso", + "notes": { + "selectedChunks": "{{count}} blocos de texto selecionados", + "fileBatch": "Parâmetros de processamento de arquivo: {{count}} arquivo(s) (estratégia: {{strategy}})", + "jsonParams": "Parâmetros da tarefa configurados", + "noChunksQuestion": "Nenhum bloco de texto precisa gerar perguntas", + "noChunksCleaning": "Nenhum bloco de texto precisa ser limpo", + "processingFailed": "Falha na tarefa: {{error}}", + "questionSummary": "Processado {{processed}}/{{total}}, sucesso {{succeeded}}, falha {{failed}}, perguntas geradas {{generated}}", + "datasetSummary": "Processado {{processed}}/{{total}}, sucesso {{succeeded}}, falha {{failed}}, conjuntos de dados gerados {{generated}}", + "cleaningSummary": "Processado {{processed}}/{{total}}, sucesso {{succeeded}}, falha {{failed}}, comprimento original {{original}}, após limpeza {{cleaned}}", + "genericSummary": "Processado {{processed}}/{{total}}, sucesso {{succeeded}}, falha {{failed}}" + } + }, + "gaPairs": { + "title": "Gerenciamento de Pares Gênero-Público", + "loading": "Carregando pares gênero-público...", + "addPair": "Adicionar Par Gênero-Público", + "saveChanges": "Salvar Alterações", + "saving": "Salvando...", + "restoreBackup": "Restaurar Backup", + "noGaPairsTitle": "Nenhum Par Gênero-Público Encontrado", + "noGaPairsDescription": "Gerar pares gênero-público impulsionados por IA para este arquivo", + "generateGaPairs": "Gerar Pares Gênero-Público", + "generating": "Gerando...", + "generateMore": "Gerar Mais Pares Gênero-Público", + "activePairs": "Pares Gênero-Público Ativos ({{active}}/{{total}})", + "pairNumber": "Par Gênero-Público #{{number}}", + "active": "Ativo", + "deleteTooltip": "Excluir Par Gênero-Público", + "genre": "Gênero", + "genreDescription": "Descrição do Gênero", + "audience": "Público", + "audienceDescription": "Descrição do Público", + "addDialogTitle": "Adicionar Novo Par Gênero-Público", + "genreTitle": "Título do Gênero", + "audienceTitle": "Título do Público", + "genreTitlePlaceholder": "Por favor, insira o título do gênero...", + "genreDescPlaceholder": "Descreva detalhadamente este gênero...", + "audienceTitlePlaceholder": "Por favor, insira o título do público...", + "audienceDescPlaceholder": "Descreva detalhadamente o público-alvo...", + "cancel": "Cancelar", + "addPairButton": "Adicionar Par Gênero-Público", + "requiredFields": "Título do gênero e título do público são obrigatórios", + "restoredFromBackup": "Restaurado do backup", + "allPairsDeleted": "Todos os pares gênero-público excluídos com sucesso", + "pairsSaved": "{{count}} pares gênero-público salvos com sucesso", + "additionalPairsGenerated": "{{count}} pares gênero-público adicionais gerados com sucesso. Total: {{total}}", + "validationError": "Par Gênero-Público {{number}}: Título do gênero e título do público são obrigatórios", + "loadError": "Não foi possível carregar pares gênero-público: {{error}}", + "generateError": "Falha ao gerar pares gênero-público", + "saveError": "Falha ao salvar pares gênero-público", + "noActiveModel": "Por favor, configure o modelo de IA nas configurações antes de gerar pares gênero-público.", + "contentTooShort": "O conteúdo do arquivo é muito curto ou inadequado para gerar pares gênero-público.", + "configError": "Erro na configuração do modelo de IA. Podem faltar dependências necessárias.", + "serverError": "Erro do servidor ({{status}}). Por favor, tente novamente mais tarde.", + "emptyResponse": "O serviço de geração retornou resposta vazia", + "generationFailed": "Falha na geração", + "saveOperationFailed": "Falha na operação de salvamento", + "serviceNotAvailable": "Serviço de geração de pares gênero-público não disponível. Por favor, verifique sua configuração de API.", + "requestFailed": "Falha na solicitação ({{status}}). Por favor, tente novamente.", + "internalServerError": "Ocorreu um erro interno do servidor.", + "batchGenerate": "Gerar Pares Gênero-Público em Lote", + "batchGenerateDescription": "Gerará pares gênero-público em lote para {{count}} arquivo(s) selecionado(s), esta operação pode levar algum tempo.", + "appendMode": "Modo de Anexação", + "appendModeDescription": "Gerar mais pares gênero-público para arquivos que já possuem pares gênero-público, em vez de substituir", + "selectAtLeastOneFile": "Por favor, selecione pelo menos um arquivo", + "noDefaultModel": "Nenhum modelo padrão definido, por favor, configure o modelo nas configurações do projeto primeiro", + "incompleteModelConfig": "Configuração do modelo incompleta, por favor, verifique as configurações do modelo", + "missingApiKey": "API Key não configurada para o modelo, por favor, adicione a API Key nas configurações do modelo", + "loadingProjectModel": "Carregando modelo do projeto...", + "usingModel": "Usando modelo", + "startGeneration": "Iniciar Geração", + "batchGenCompleted": "Geração em lote concluída! Pares gênero-público gerados com sucesso para {{success}}/{{total}} arquivo(s).", + "generationError": "Erro durante o processo de geração: {{error}}", + "fetchProjectInfoFailed": "Falha ao obter informações do projeto: {{status}}", + "fetchModelConfigFailed": "Falha ao obter configuração do modelo: {{status}}", + "fetchProjectModelError": "Erro ao obter configuração do modelo do projeto", + "batchGenerationFailed": "Falha na geração em lote de pares gênero-público", + "batchGenerationSuccess": "Pares gênero-público gerados com sucesso para {{count}} arquivo(s)", + "selectAllFiles": "Selecionar Todos", + "deselectAllFiles": "Desmarcar Todos", + "batchGenerateTitle": "Gerar Pares Gênero-Público em Lote", + "generationMode": "Modo de Geração", + "aiGenerateMode": "Geração por IA", + "manualAddMode": "Adição Manual", + "genreDesc": "Descrição do Gênero", + "audienceDesc": "Descrição do Público", + "manualGaPairRequired": "Por favor, preencha o título do gênero e o título do público", + "batchAddManual": "Adicionar em Lote" + }, + "batchEdit": { + "title": "Edição em Lote de Blocos de Texto", + "batchEdit": "Edição em Lote", + "batchEditTooltip": "Editar em lote os blocos de texto selecionados", + "position": "Posição de Adição", + "atBeginning": "Adicionar no Início", + "atEnd": "Adicionar no Final", + "contentToAdd": "Conteúdo a Adicionar", + "contentPlaceholder": "Por favor, insira o conteúdo a ser adicionado ao bloco de texto...", + "contentRequired": "Por favor, insira o conteúdo a ser adicionado", + "contentHelp": "Este conteúdo será adicionado a todos os blocos de texto selecionados", + "preview": "Efeito de Pré-visualização", + "allChunksSelected": "Todos os {{count}} blocos de texto selecionados", + "selectedChunks": "{{selected}} / {{total}} blocos de texto selecionados", + "processing": "Processando...", + "applyToChunks": "Aplicar a {{count}} blocos de texto", + "editSuccess": "{{count}} blocos de texto editados com sucesso", + "editFailed": "Falha na edição em lote", + "previewNote": "Acima está o efeito de pré-visualização do primeiro bloco de texto selecionado, todos os blocos de texto selecionados serão modificados da mesma forma" + }, + "errors": { + "projectIdRequired": "ID do projeto não pode estar vazio", + "getDatasetsFailed": "Falha ao obter conjuntos de dados", + "getTagStatsFailed": "Falha ao obter estatísticas de tags", + "deleteFileFailed": "Erro ao excluir arquivo", + "recordNotFound": "Registro atual não existe", + "mineruTokenNotFound": "Configuração de token não encontrada, por favor, verifique se o token MinerU está configurado nas configurações de tarefa", + "mineruLocalUrlNotFound": "Configuração de URL local do MinerU não encontrada, por favor, verifique se a URL local do MinerU está configurada nas configurações de tarefa" + }, + "sampleData": { + "questionContent": "Conteúdo da Pergunta", + "answerContent": "Conteúdo da Resposta", + "cotContent": "Conteúdo do Processo de Cadeia de Pensamento", + "domainLabel": "Tag de Domínio", + "textChunk": "Bloco de Texto" + }, + "exportDialog": { + "balancedExport": "Exportação Balanceada", + "balancedExportTitle": "Configurações de Exportação Balanceada", + "balancedExportDescription": "Configure a quantidade de dados para cada categoria de acordo com as tags de domínio, realizando exportação balanceada do conjunto de dados", + "quickSettings": "Configurações Rápidas", + "setAllTo50": "Definir Todos para 50", + "setAllTo100": "Definir Todos para 100", + "setAllTo200": "Definir Todos para 200", + "customAmount": "Quantidade Personalizada", + "tagName": "Nome da Tag", + "availableCount": "Quantidade Disponível", + "exportCount": "Quantidade a Exportar", + "settings": "Configurações", + "totalExportCount": "Quantidade Total de Exportação", + "tagCount": "Quantidade de Tags", + "export": "Exportar" + }, + "imageDatasets": { + "title": "Conjunto de Dados de Pergunta e Resposta de Imagens", + "subtitle": "Gerencie e otimize seu conjunto de dados de pergunta e resposta de imagens", + "description": "Gerencie e otimize seu conjunto de dados de pergunta e resposta de imagens.", + "searchPlaceholder": "Pesquisar perguntas ou respostas...", + "noAnswer": "Nenhuma resposta ainda", + "labels": "Tags", + "typeLabel": "Tag", + "typeCustom": "JSON Personalizado", + "typeText": "Texto Normal", + "unscored": "Não pontuado", + "confirmed": "Confirmado", + "unconfirmed": "Não confirmado", + "view": "Ver Detalhes", + "evaluate": "Avaliação de Qualidade", + "delete": "Excluir", + "deleteConfirm": "Tem certeza de que deseja excluir este conjunto de dados?", + "imageName": "Nome da Imagem", + "status": "Status", + "scoreRange": "Intervalo de Pontuação", + "noData": "Nenhum conjunto de dados de imagem", + "noDataTip": "Por favor, gere primeiro o conjunto de dados de pergunta e resposta no gerenciamento de imagens", + "fetchFailed": "Falha ao obter conjunto de dados", + "fetchDetailFailed": "Falha ao obter detalhes", + "deleteSuccess": "Excluído com sucesso", + "deleteFailed": "Falha ao excluir", + "updateSuccess": "Atualização bem-sucedida", + "updateFailed": "Falha na atualização", + "regenerateSuccess": "Reconhecimento de IA bem-sucedido", + "regenerateFailed": "Falha no reconhecimento de IA", + "notFound": "Conjunto de dados não existe", + "detail": "Detalhes", + "image": "Imagem", + "question": "Pergunta", + "answer": "Resposta", + "selectLabels": "Selecionar Tags", + "noLabels": "Nenhuma tag selecionada", + "jsonPlaceholder": "Insira dados no formato JSON...", + "metadata": "Metadados", + "score": "Pontuação", + "tags": "Tags", + "addTag": "Adicionar tag...", + "note": "Observação", + "notePlaceholder": "Adicionar informações de observação...", + "modelInfo": "Informações do Modelo", + "createdAt": "Tempo de Criação", + "updatedAt": "Tempo de Atualização", + "exportTitle": "Exportar Conjunto de Dados de Imagens", + "exportFormat": "Formato de Exportação", + "rawFormat": "Formato Bruto", + "customFormat": "Formato Personalizado", + "exportImagesOption": "Exportar Arquivos de Imagem", + "exportImagesDesc": "Empacotar todas as imagens em um arquivo ZIP para download, após o download, pode descompactar manualmente na pasta Images no mesmo diretório do arquivo de conjunto de dados", + "includeImagePath": "Incluir caminho da imagem no conjunto de dados", + "includeImagePathDesc": "Adicionar caminho da imagem na pergunta ou resposta (formato: /images/nome_da_imagem)", + "systemPrompt": "Prompt do Sistema (opcional)", + "systemPromptPlaceholder": "Insira o prompt do sistema...", + "confirmedOnly": "Exportar apenas conjuntos de dados confirmados", + "exportTip": "Respostas em formato de tag serão automaticamente analisadas como texto (separadas por vírgula)", + "exportSuccess": "Conjunto de dados exportado com sucesso", + "exportFailed": "Falha na exportação", + "noDataToExport": "Nenhum dado para exportar", + "exportImagesSuccess": "Pacote de imagens exportado com sucesso", + "exportImagesFailed": "Falha na exportação de imagens" + }, + "images": { + "resolution": "Resolução", + "uploadTime": "Tempo de Envio", + "fileName": "Nome do Arquivo", + "title": "Gerenciamento de Imagens", + "importImages": "Importar Imagens", + "searchPlaceholder": "Pesquisar nome da imagem...", + "hasQuestions": "Status de Perguntas", + "hasDatasets": "Status de Conjuntos de Dados", + "withQuestions": "Perguntas Geradas", + "withoutQuestions": "Perguntas Não Geradas", + "withDatasets": "Conjuntos de Dados Gerados", + "withoutDatasets": "Conjuntos de Dados Não Gerados", + "noImages": "Nenhuma imagem", + "noImagesDescription": "Comece a importar imagens, crie seu primeiro conjunto de dados de imagens", + "preview": "Pré-visualização", + "questions": "Perguntas", + "datasets": "Conjuntos de Dados", + "datasetCount": "Quantidade de Conjuntos de Dados", + "generateQuestions": "Gerar Perguntas", + "generateDataset": "Gerar Conjunto de Dados", + "deleteConfirm": "Tem certeza de que deseja excluir esta imagem?", + "deleteSuccess": "Excluído com sucesso", + "deleteFailed": "Falha ao excluir", + "batchDelete": "Excluir em Lote", + "selectImagesToDelete": "Por favor, selecione imagens para excluir", + "batchDeleteConfirm": "Tem certeza de que deseja excluir as {{count}} imagens selecionadas?", + "batchDeleteSuccess": "{{count}} imagem(ns) excluída(s) com sucesso", + "batchDeletePartialSuccess": "{{success}} excluída(s) com sucesso, {{fail}} falha(s)", + "batchDeleteFailed": "Falha na exclusão em lote", + "importTip": "Selecione um ou mais diretórios contendo imagens, todas as imagens serão importadas para o projeto (imagens com o mesmo nome serão substituídas)", + "selectDirectory": "Selecionar Diretório", + "directoryPath": "Caminho do Diretório", + "enterDirectoryPath": "Por exemplo: /Users/username/Pictures", + "selectedDirectories": "Diretórios Selecionados", + "selectAtLeastOne": "Por favor, selecione pelo menos um diretório", + "importSuccess": "{{count}} imagem(ns) importada(s) com sucesso", + "importFailed": "Falha na importação", + "startImport": "Iniciar Importação", + "addDirectory": "Adicionar Diretório", + "importFromDirectory": "Importar do Diretório", + "importFromPdf": "Importar do PDF", + "importFromZip": "Importar do ZIP", + "pdfImportTip": "Selecione o arquivo PDF, o sistema converterá automaticamente para imagens e importará", + "zipImportTip": "Selecione o arquivo ZIP, o sistema descompactará automaticamente e importará as imagens", + "clickToSelectPdf": "Clique para selecionar arquivo PDF", + "clickToSelectZip": "Clique para selecionar arquivo ZIP", + "supportedFormat": "Formato suportado: PDF", + "supportedZipFormat": "Formato suportado: ZIP", + "fileSize": "Tamanho do Arquivo", + "selectedFile": "Arquivo Selecionado", + "invalidPdfFile": "Por favor, selecione um arquivo PDF válido", + "invalidZipFile": "Por favor, selecione um arquivo ZIP válido", + "selectPdfFile": "Por favor, selecione o arquivo PDF", + "selectZipFile": "Por favor, selecione o arquivo ZIP", + "pdfImportSuccess": "{{count}} imagem(ns) importada(s) com sucesso do PDF \"{{name}}\"", + "pdfImportFailed": "Falha na importação do PDF", + "zipImportSuccess": "{{count}} imagem(ns) importada(s) com sucesso do pacote \"{{name}}\"", + "zipImportFailed": "Falha na importação do pacote", + "convertAndImport": "Converter e Importar", + "extractAndImport": "Descompactar e Importar", + "electronRequired": "Esta funcionalidade precisa ser usada no aplicativo desktop", + "selectDirectoryFailed": "Falha ao selecionar diretório", + "imageName": "Nome da Imagem", + "questionCount": "Quantidade de Perguntas", + "questionCountHelp": "Gerar 1-10 perguntas", + "size": "Tamanho", + "dimensions": "Dimensões", + "currentModel": "Modelo Atual", + "selectModelFirst": "Por favor, selecione um modelo primeiro", + "visionModelRequired": "Por favor, selecione um modelo com suporte a visão (como GPT-4 Vision, Claude, etc.)", + "countRange": "A quantidade de perguntas deve estar entre 1-10", + "questionsGenerated": "{{count}} pergunta(s) gerada(s) com sucesso", + "generateFailed": "Falha na geração", + "question": "Pergunta", + "questionPlaceholder": "Por favor, insira a pergunta que deseja fazer...", + "questionRequired": "Por favor, insira a pergunta", + "datasetGenerated": "Conjunto de dados gerado com sucesso", + "autoGenerateQuestions": "Extrair Perguntas Automaticamente", + "autoGenerateConfirm": "O sistema gerará automaticamente perguntas para todas as imagens sem perguntas geradas. Esta operação criará uma tarefa em segundo plano, você pode verificar o progresso no gerenciamento de tarefas.", + "taskCreated": "Tarefa criada com sucesso, processando em segundo plano", + "taskCreateFailed": "Falha ao criar tarefa", + "manualAnnotation": "Anotação Manual", + "annotationTitle": "Anotação de Imagem", + "imageInfo": "Informações da Imagem", + "annotatedCount": "Anotado", + "selectQuestion": "Selecionar ou Criar Pergunta", + "selectQuestionPlaceholder": "Por favor, selecione o modelo de pergunta...", + "universalQuestions": "Perguntas Universais", + "independentQuestions": "Perguntas Independentes", + "answerTypeText": "Texto", + "answerTypeLabel": "Tag", + "answerTypeCustomFormat": "Formato Personalizado", + "usedTimes": "Usado {{count}} vez(es)", + "answer": "Resposta", + "answerPlaceholder": "Por favor, insira a resposta...", + "selectLabels": "Selecionar Tags", + "availableLabels": "Tags Disponíveis", + "noLabelsAvailable": "Nenhuma tag disponível ainda", + "addNewLabel": "Adicionar nova tag...", + "selectedLabels": "Selecionado", + "customFormatAnswer": "Resposta em Formato Personalizado", + "formatRequirement": "Requisito de Formato", + "customFormatPlaceholder": "Por favor, insira JSON no formato correto...", + "note": "Observação", + "notePlaceholder": "Informações de observação (opcional)", + "saveAndContinue": "Salvar e Continuar", + "noImageSelected": "Nenhuma imagem selecionada", + "noTemplateSelected": "Por favor, selecione a pergunta", + "answerRequired": "Por favor, insira a resposta", + "invalidJsonFormat": "Formato JSON incorreto", + "annotationSuccess": "Anotação salva com sucesso", + "annotationFailed": "Falha ao salvar anotação", + "allQuestionsAnnotated": "Todas as perguntas da imagem atual foram anotadas", + "allImagesAnnotated": "Todas as perguntas de todas as imagens foram anotadas", + "noQuestionsAssociated": "Nenhuma pergunta associada à imagem atual", + "loadImageDetailFailed": "Falha ao carregar detalhes da imagem", + "answeredQuestions": "Perguntas Respondidas", + "useTemplate": "Usar Modelo", + "formatJson": "Formatar", + "jsonFormatHelp": "Por favor, insira dados em formato JSON válido", + "imageLoadError": "Falha ao carregar imagem", + "annotate": "Anotar", + "annotateImage": "Anotar Imagem", + "createQuestion": "Criar Pergunta", + "createTemplate": "Criar Modelo de Pergunta", + "aiGenerate": "Reconhecimento de IA", + "aiGenerateSuccess": "Geração de IA bem-sucedida", + "aiGenerateFailed": "Falha na geração de IA", + "missingParameters": "Parâmetros necessários ausentes", + "selectNewQuestion": "Selecionar Nova Pergunta", + "fetchTemplatesFailed": "Falha ao obter modelos de pergunta", + "createTemplateSuccess": "Modelo de pergunta criado com sucesso", + "createTemplateFailed": "Falha ao criar modelo de pergunta", + "updateTemplateSuccess": "Modelo de pergunta atualizado com sucesso", + "updateTemplateFailed": "Falha ao atualizar modelo de pergunta", + "deleteTemplateSuccess": "Modelo de pergunta excluído com sucesso", + "deleteTemplateFailed": "Falha ao excluir modelo de pergunta", + "template": { + "management": "Gerenciar Modelos de Pergunta", + "create": "Criar Modelo", + "edit": "Editar Modelo", + "question": "Conteúdo da Pergunta", + "description": "Descrição", + "noTemplates": "Nenhum modelo de pergunta ainda, clique no botão criar para adicionar", + "deleteConfirm": "Tem certeza de que deseja excluir este modelo de pergunta?", + "used": "Usado", + "addLabel": "Adicionar Tag", + "customFormat": "Formato Personalizado", + "customFormatHelp": "Insira restrições de saída no formato JSON", + "customFormatInfo": "Este formato será fornecido como prompt ao modelo grande, usado para restringir o formato de saída", + "type": { + "label": "Tipo de Pergunta", + "universal": "Pergunta Universal", + "independent": "Pergunta Independente" + }, + "answerType": { + "label": "Tipo de Resposta", + "text": "Texto", + "tags": "Tags", + "customFormat": "Formato Personalizado" + }, + "errors": { + "questionRequired": "Por favor, insira o conteúdo da pergunta", + "labelsRequired": "Perguntas do tipo tag precisam de pelo menos uma tag", + "customFormatRequired": "Por favor, insira o formato personalizado", + "invalidJson": "Formato JSON incorreto" + } + } + }, + "monitoring": { + "title": "Painel de Monitoramento de Recursos", + "timeRange": { + "24h": "24 Horas", + "7d": "Últimos 7 Dias", + "30d": "Últimos 30 Dias" + }, + "filters": { + "allProjects": "Todos os Projetos", + "allProviders": "Todos os Provedores", + "allStatus": "Todos os Status" + }, + "status": { + "success": "Sucesso", + "failed": "Falha" + }, + "actions": { + "export": "Exportar Relatório" + }, + "stats": { + "totalTokens": "Consumo Total de Tokens", + "avgTokensPerCall": "Consumo Médio de Tokens/Chamada", + "totalCalls": "Total de Chamadas", + "avgLatency": "Tempo Médio de Resposta", + "inputOutput": "Entrada: {{input}} · Saída: {{output}}", + "successCalls": "{{count}} sucesso", + "failedCalls": "{{count}} falha", + "failureRate": "{{rate}}% taxa de falha", + "basedOnSuccessCalls": "Baseado em {{count}} solicitações bem-sucedidas", + "noSuccessCalls": "Nenhuma solicitação bem-sucedida ainda" + }, + "charts": { + "tokenTrend": "Tendência de Consumo de Tokens", + "inputLegend": "Entrada", + "outputLegend": "Saída", + "distributionTitle": "Distribuição de Consumo de Tokens (por Modelo)", + "distributionSubtitle": "Proporção de consumo de recursos de diferentes modelos", + "tokensTooltip": "{{value}}K Tokens" + }, + "table": { + "title": "Detalhes de Uso Detalhados", + "searchPlaceholder": "Pesquisar projeto, modelo ou motivo de falha...", + "empty": "Nenhum dado ainda", + "rowsPerPage": "Linhas por página:", + "columns": { + "projectName": "Nome do Projeto", + "provider": "Provedor do Modelo", + "model": "Nome do Modelo", + "status": "Status", + "failureReason": "Motivo da Falha", + "inputTokens": "TOKEN de Entrada", + "outputTokens": "TOKEN de Saída", + "totalTokens": "TOTAL", + "calls": "Número de Chamadas", + "avgLatency": "Tempo Médio" + } + }, + "errors": { + "fetchSummaryFailed": "Falha ao obter dados resumidos de monitoramento", + "fetchLogsFailed": "Falha ao obter logs de monitoramento" + } + }, + "eval": { + "title": "Avaliação", + "datasets": "Conjuntos de Dados de Avaliação", + "tasks": "Tarefas de Avaliação Automática", + "datasetsTitle": "Conjuntos de Dados de Avaliação", + "datasetsDescription": "Gerenciar e visualizar todas as questões de teste de avaliação geradas", + "tasksTitle": "Tarefas de Avaliação", + "tasksComingSoon": "Funcionalidade em desenvolvimento", + "tasksComingSoonHint": "A funcionalidade de tarefas de avaliação será lançada em breve, fique atento", + "totalQuestions": "Total de Questões", + "questionType": "Tipo de Questão", + "question": "Questão", + "answer": "Resposta", + "options": "Opções", + "correct": "Correto", + "wrong": "Errado", + "sourceChunk": "Bloco de Texto de Origem", + "tags": "Tags", + "tagsPlaceholder": "Insira tags, múltiplas tags separadas por vírgula", + "note": "Observação", + "detail": "Detalhes", + "notFound": "Questão não encontrada", + "noData": "Nenhum dado de avaliação ainda", + "noDataHint": "Por favor, gere primeiro o conjunto de teste de avaliação na página de divisão de texto", + "searchPlaceholder": "Pesquisar conteúdo da questão...", + "cardView": "Visualização em Cartões", + "listView": "Visualização em Lista", + "deleteSelected": "Excluir Selecionados ({{count}})", + "deleteConfirmTitle": "Confirmar Exclusão", + "deleteConfirmMessage": "Tem certeza de que deseja excluir {{count}} questão(ões)? Esta ação não pode ser desfeita.", + "questionTypes": { + "true_false": "Verdadeiro/Falso", + "single_choice": "Múltipla Escolha", + "multiple_choice": "Múltipla Escolha (Várias Respostas)", + "short_answer": "Resposta Curta", + "open_ended": "Questão Aberta" + } + }, + "evalDatasets": { + "import": { + "title": "Importar Conjunto de Dados de Avaliação", + "questionType": "Tipo de Questão", + "selectTypeFirst": "Por favor, selecione o tipo de questão primeiro", + "selectFile": "Por favor, selecione o arquivo a ser importado", + "invalidFileType": "Formato de arquivo não suportado, por favor, faça upload de arquivo json, xls ou xlsx", + "formatPreview": "Pré-visualização do Formato de Dados", + "downloadTemplate": "Baixar Modelo", + "template": "Modelo", + "uploadFile": "Enviar Arquivo", + "dropOrClick": "Clique ou arraste o arquivo para cá", + "supportedFormats": "Suporta formatos JSON, XLS, XLSX", + "tags": "Tags (opcional)", + "tagsPlaceholder": "Adicionar tags aos dados importados, múltiplas tags separadas por vírgula", + "tagsHelp": "Todos os dados importados serão marcados com estas tags", + "import": "Importar", + "importing": "Importando...", + "failed": "Falha na importação", + "success": "Importação bem-sucedida", + "successMessage": "{{count}} dados de avaliação importados com sucesso", + "showingErrors": "Exibindo primeiros {{count}} erros", + "custom": "Importar Conjunto de Dados Personalizado", + "builtin": "Importar Conjunto de Dados Integrado", + "builtinTitle": "Selecionar Conjunto de Dados Integrado", + "searchPlaceholder": "Pesquisar conjunto de dados...", + "confirmImportTitle": "Confirmar Importação", + "confirmImportMessage": "Tem certeza de que deseja importar o conjunto de dados \"{{name}}\"? Isso adicionará novos dados de avaliação ao projeto atual.", + "downloading": "Baixando..." + }, + "export": { + "title": "Exportar Conjunto de Dados de Avaliação", + "formatLabel": "Formato de Exportação", + "filterLabel": "Condições de Filtro", + "previewLabel": "Dados a serem exportados:", + "records": "registros", + "largeDataHint": "Grande volume de dados, será usada exportação em streaming, por favor, aguarde pacientemente", + "exporting": "Exportando...", + "exportBtn": "Exportar", + "jsonDesc": "Array JSON padrão", + "jsonlDesc": "Um registro por linha", + "csvDesc": "Formato de tabela", + "noTagsAvailable": "Nenhuma tag disponível" + } + }, + "evalTasks": { + "title": "Tarefas de Avaliação de Modelo", + "createTitle": "Criar Tarefa de Avaliação", + "detailTitle": "Detalhes da Tarefa de Avaliação", + "createTask": "Criar Tarefa", + "noTasks": "Nenhuma tarefa de avaliação ainda", + "noTasksHint": "Crie tarefas de avaliação para testar o desempenho do modelo no conjunto de dados de avaliação", + "selectModels": "Selecionar Modelos de Teste", + "selectModelsHint": "Pode selecionar múltiplos modelos para avaliação comparativa", + "selectJudgeModel": "Selecionar Modelo Professor", + "selectJudgeModelPlaceholder": "Por favor, selecione...", + "selectJudgeModelHint": "O modelo professor é usado para avaliar questões de resposta curta e questões abertas, não pode ser o mesmo que o modelo de teste", + "judgeModel": "Modelo Professor", + "filterByType": "Filtrar por Tipo de Questão", + "filterByTypeHint": "Se não selecionar, usará todas as questões", + "selectedQuestions": "Questões Selecionadas", + "questions": "questões", + "hasSubjectiveHint": "Inclui questões subjetivas (resposta curta/aberta), precisa selecionar modelo professor para pontuação", + "hasSubjective": "Inclui Questões Subjetivas", + "startEval": "Iniciar Avaliação", + "progress": "Progresso", + "totalQuestions": "Quantidade de Questões", + "status": "Status", + "totalScore": "Pontuação Total", + "correctCount": "Quantidade de Acertos", + "accuracy": "Taxa de Acerto", + "statsByType": "Estatísticas por Tipo de Questão", + "resultDetails": "Detalhes dos Resultados da Avaliação", + "question": "Questão", + "questionType": "Tipo de Questão", + "result": "Resultado", + "score": "Pontuação", + "correctAnswer": "Resposta Correta", + "modelAnswer": "Resposta do Modelo", + "judgeResponse": "Pontuação do Modelo Professor", + "interrupt": "Interromper Tarefa", + "statusProcessing": "Em Andamento", + "statusCompleted": "Concluído", + "statusFailed": "Falhou", + "statusInterrupted": "Interrompido", + "deleteConfirmTitle": "Confirmar Exclusão", + "deleteConfirmMessage": "Tem certeza de que deseja excluir esta tarefa de avaliação? Esta ação também excluirá todos os resultados de avaliação.", + "interruptConfirmTitle": "Confirmar Interrupção", + "interruptConfirmMessage": "Tem certeza de que deseja interromper esta tarefa de avaliação? Os resultados de avaliação concluídos serão retidos.", + "errorNoModels": "Por favor, selecione pelo menos um modelo de teste", + "errorNoQuestions": "Nenhuma questão de avaliação disponível", + "errorNoJudgeModel": "Existem questões subjetivas, por favor, selecione um modelo professor para pontuação", + "errorJudgeSameAsTest": "O modelo professor não pode ser o mesmo que o modelo de teste", + "errorCreateFailed": "Falha ao criar tarefa de avaliação", + "errorLoadFailed": "Falha ao carregar tarefa de avaliação", + "errorDeleteFailed": "Falha ao excluir tarefa de avaliação", + "errorInterruptFailed": "Falha ao interromper tarefa de avaliação", + "statusSuccess": "Sucesso", + "statusFormatError": "Erro de Formato", + "statusApiError": "Erro de API", + "statusUnknown": "Status Desconhecido", + "duration": "Duração", + "answerStatus": "Status da Resposta", + "modelInfo": "Informações do Modelo", + "reportTitle": "Relatório de Avaliação de Capacidade do Modelo", + "taskIdLabel": "ID da Tarefa", + "pageInfo": "Página {{page}} / {{totalPages}}", + "noMatchingResults": "Nenhum resultado de avaliação correspondente ainda", + "reportFooter": "Easy Dataset Evaluation System · Gerado por IA", + "finalSelection": "Seleção Final:", + "questionsSuffix": "questões", + "noModelsAvailable": "Nenhum modelo disponível ainda, por favor, configure o modelo nas configurações primeiro", + "filterTitle": "Filtro de Questões", + "clearFilter": "Limpar Filtro", + "searchKeyword": "Pesquisar Palavra-chave", + "searchPlaceholder": "Pesquisar conteúdo da questão ou resposta...", + "filterByTypeLabel": "Filtro por Tipo", + "filterByTagLabel": "Filtro por Tag", + "questionCountLabel": "Quantidade de Questões:", + "useAllQuestions": "Usar Todos os Resultados do Filtro", + "randomSampleHint": "Será extraída aleatoriamente {{questionCount}} questão(ões) de {{filteredCount}}", + "durationFormat": "(Duração {{time}}s)", + "totalQuestionsLabel": "Total de Questões", + "correctLabel": "Correto", + "incorrectLabel": "Incorreto", + "judgeComment": "Comentário do Professor de IA:", + "scoreUnit": "pontos", + "shortAnswer": "Resposta Curta", + "openEnded": "Questão Aberta", + "scoreAnchorsTitle": "Regras de Pontuação para {{type}}", + "customizable": "Personalizável", + "scoreAnchorsHint": "Personalizar critérios de pontuação, usados para orientar o LLM na avaliação da qualidade das respostas do modelo", + "restoreDefault": "Restaurar Padrão", + "scoreRange": "Intervalo de Pontuação", + "scoreDescriptionPlaceholder": "Por favor, insira a descrição do critério de pontuação para este intervalo..." + }, + "blindTest": { + "title": "Tarefas de Teste Cego Manual", + "createTitle": "Criar Tarefa de Teste Cego", + "createTask": "Criar Tarefa", + "noTasks": "Nenhuma tarefa de teste cego ainda", + "noTasksHint": "Crie tarefas de teste cego para comparar a qualidade das respostas de dois modelos", + "selectModels": "Selecionar Modelos para Comparação", + "modelA": "Modelo A", + "modelB": "Modelo B", + "modelComparison": "Comparação de Modelos", + "selectQuestions": "Selecionar Questões de Teste", + "questionType": "Tipo de Questão", + "questionTypeHint": "Tarefas de teste cego suportam apenas questões de resposta curta e questões abertas", + "filterByTag": "Filtrar por Tag", + "questionCount": "Quantidade de Questões", + "availableQuestions": "Questões Disponíveis: {{count}}", + "useAllQuestions": "Usar Todos os Resultados do Filtro", + "randomSample": "Será extraída aleatoriamente {{count}} questão(ões)", + "startBlindTest": "Iniciar Teste Cego", + "creating": "Criando...", + "noModelsAvailable": "Nenhum modelo disponível ainda, por favor, configure o modelo nas configurações primeiro", + "errorSelectModelA": "Por favor, selecione o Modelo A", + "errorSelectModelB": "Por favor, selecione o Modelo B", + "errorSameModel": "Os dois modelos não podem ser iguais", + "errorNoQuestions": "Nenhuma questão correspondente", + "statusProcessing": "Em Andamento", + "statusCompleted": "Concluído", + "statusFailed": "Falhou", + "statusInterrupted": "Interrompido", + "progress": "Progresso", + "viewDetails": "Ver Detalhes", + "continue": "Continuar Teste Cego", + "interrupt": "Interromper Tarefa", + "deleteConfirmTitle": "Confirmar Exclusão", + "deleteConfirmMessage": "Tem certeza de que deseja excluir esta tarefa de teste cego? Esta ação não pode ser desfeita.", + "interruptConfirmTitle": "Confirmar Interrupção", + "interruptConfirmMessage": "Tem certeza de que deseja interromper esta tarefa de teste cego? Os resultados de avaliação concluídos serão retidos.", + "inProgress": "Teste Cego em Andamento", + "generatingAnswers": "Gerando respostas...", + "question": "Questão", + "answerA": "Resposta A", + "answerB": "Resposta B", + "duration": "Duração", + "whichBetter": "Qual resposta é melhor?", + "leftBetter": "Esquerda é Melhor", + "rightBetter": "Direita é Melhor", + "bothGood": "Ambas são Boas", + "bothBad": "Ambas são Ruins", + "loadQuestion": "Carregar Questão", + "taskNotFound": "Tarefa não existe", + "resultTitle": "Resultados do Teste Cego", + "resultSummary": "Resumo dos Resultados de Avaliação", + "wins": "Vitórias", + "times": "vezes", + "totalQuestions": "Total de Questões", + "ties": "Empates", + "detailResults": "Resultados Detalhados", + "left": "Esquerda", + "right": "Direita" + } +} \ No newline at end of file diff --git a/easy-dataset-main/locales/tr/translation.json b/easy-dataset-main/locales/tr/translation.json new file mode 100644 index 0000000..1124a79 --- /dev/null +++ b/easy-dataset-main/locales/tr/translation.json @@ -0,0 +1,1506 @@ +{ + "language": { + "switchToEnglish": "İngilizce'ye Geç", + "switchToChinese": "Çince'ye Geç", + "switchToTurkish": "Türkçe'ye Geç", + "switcherTitle": "Dil Değiştir / Change Language / 切换语言", + "english": "İngilizce", + "chineseSimplified": "Basitleştirilmiş Çince", + "turkish": "Türkçe", + "portugues": "Portugês", + "en": "EN", + "zh": "中", + "tr": "TR" + }, + "theme": { + "switchToLight": "Açık Moda Geç", + "switchToDark": "Koyu Moda Geç" + }, + "settings": { + "promptConfig": "İstem Yapılandırması", + "promptsDescription": "Projede kullanılan istemlerinizi yapılandırın, global istemler ve senaryo özel istemler desteklenir.", + "globalPrompt": "Global İstem", + "questionPrompt": "Soru Üretme İstemi", + "answerPrompt": "Cevap Üretme İstemi", + "labelPrompt": "Soru Etiketleme İstemi", + "domainTreePrompt": "Alan Ağacı Oluşturma İstemi", + "globalPromptPlaceholder": "Tüm senaryolar için temel istem olacak global istemi girin", + "questionPromptPlaceholder": "Soru üretmek için istemi girin", + "answerPromptPlaceholder": "Cevap üretmek için istemi girin", + "labelPromptPlaceholder": "Soru etiketlemek için istemi girin (şu anda desteklenmiyor)", + "domainTreePromptPlaceholder": "Alan ağacı oluşturmak için istemi girin", + "cleanPrompt": "Veri Temizleme İstemi", + "cleanPromptPlaceholder": "Veri temizleme için özel istem girin", + "loadPromptsFailed": "İstem yapılandırmaları yüklenemedi", + "savePromptsSuccess": "İstem yapılandırmaları başarıyla kaydedildi", + "savePromptsFailed": "İstem yapılandırmaları kaydedilemedi", + "title": "Ayarlar", + "basicInfo": "Temel Bilgiler", + "modelConfig": "Model Yapılandırması", + "taskConfig": "Görev Yapılandırması", + "tabsAriaLabel": "Ayarlar Sekmeleri", + "idNotEditable": "Proje ID'si düzenlenemez", + "saveBasicInfo": "Temel Bilgileri Kaydet", + "saveSuccess": "Başarıyla Kaydedildi", + "saveFailed": "Kaydetme Başarısız", + "deleteSuccess": "Başarıyla Silindi", + "deleteFailed": "Silme Başarısız", + "fetchTasksFailed": "Görev ayarları getirilemedi", + "saveTasksFailed": "Görev ayarları kaydedilemedi", + "textSplitSettings": "Metin Bölme Ayarları", + "minLength": "Minimum Uzunluk", + "maxLength": "Maksimum Bölme Uzunluğu", + "textSplitDescription": "Metin bölme uzunluk aralığını ayarlayın", + "splitType": "Bölme Stratejisi", + "splitTypeMarkdown": "Belge Yapısı Bölme (Markdown)", + "splitTypeMarkdownDesc": "Metni belgedeki başlıklara göre otomatik olarak bölerek anlamsal bütünlüğü korur. Net yapıya sahip Markdown belgeleri için uygundur.", + "splitTypeRecursive": "Metin Yapısı Bölme (Özel Ayırıcı)", + "splitTypeRecursiveDesc": "Çok seviyeli ayırıcıları (yapılandırılabilir) özyinelemeli olarak dener. Önce yüksek öncelikli ayırıcıları, sonra ikincil ayırıcıları kullanır. Karmaşık belgeler için uygundur.", + "splitTypeText": "Sabit Uzunlukta Bölme (Karakter)", + "splitTypeTextDesc": "Metni belirtilen ayırıcıya (yapılandırılabilir) göre böler, ardından belirtilen uzunluğa göre birleştirir. Sıradan metin dosyaları için uygundur.", + "splitTypeToken": "Sabit Uzunlukta Bölme (Token)", + "splitTypeTokenDesc": "Token sayısına göre (karakter sayısı değil) bloklar.", + "splitTypeCode": "Program Kodu Akıllı Bölme", + "splitTypeCodeDesc": "Farklı programlama dillerinin sözdizimi yapısına göre akıllıca bloklar, sözdiziminin eksik olduğu yerlerde bölmekten kaçınır.", + "splitTypeCustom": "Özel Sembol Bölme", + "splitTypeCustomDesc": "Belgeleri özel sembollere göre böler. Ayırıcı atılır ve bölünen metin blokları blok boyutundan etkilenmez.", + "codeLanguage": "Programlama Dili", + "codeLanguageHelper": "Dil sözdizimi tabanlı daha akıllı kod bölme için programlama dilini seçin.", + "chunkSize": "Blok Boyutu", + "chunkOverlap": "Blok Örtüşmesi", + "separator": "Ayırıcı", + "separatorHelper": "Metni bölmek için kullanılan ayırıcı, örn. \\n\\n boş satırlar için", + "customSeparator": "Özel Ayırıcı", + "customSeparatorHelper": "Metni bölmek için kullanılan özel ayırıcı, örn. --- veya ===", + "separators": "Ayırıcılar Listesi", + "separatorsInput": "Ayırıcılar (virgülle ayrılmış)", + "separatorsHelper": "Öncelik sırasına göre virgülle ayrılmış ayırıcılar listesi", + "questionGenSettings": "Soru Üretme Ayarları", + "questionGenLength": "Soru Üretme Uzunluğu: {{length}}", + "questionMaskRemovingProbability": "Soru İşaretlerini Kaldırma Olasılığı: {{probability}}%", + "questionGenDescription": "Üretilen sorular için maksimum uzunluğu ayarlayın", + "huggingfaceSettings": "Hugging Face Ayarları", + "datasetUpload": "Veri Seti Yükleme Ayarları", + "huggingfaceToken": "Hugging Face Token", + "huggingfaceNotImplemented": "", + "concurrencyLimit": "Eşzamanlılık Sınırı", + "concurrencyLimitHelper": "Soru üretme ve veri seti üretme görevlerinin eşzamanlı sayısını sınırlayın.", + "saveTaskConfig": "Görev Yapılandırmasını Kaydet", + "pdfSettings": "PDF dosya dönüştürme yapılandırması", + "minerUToken": "MinerU Token yapılandırması", + "minerUHelper": "MinerU Token sadece 14 gün geçerlidir. Lütfen Token'ı zamanında değiştirin", + "minerULocalUrl": "PDF Dönüştürme (MinerU Local) URL Yapılandırması", + "vision": "Özel büyük ölçekli görüş modeli yapılandırması", + "visionConcurrencyLimit": "Özel büyük ölçekli görüş modelleri için eşzamanlılık sınırı", + "prompts": { + "selectPromptFirst": "Lütfen soldan bir istem seçin", + "customized": "Özelleştirilmiş", + "editPrompt": "İstemi Düzenle", + "restoreDefault": "Varsayılana Geri Dön", + "promptType": "İstem Türü", + "keyName": "Anahtar Adı", + "contentPlaceholder": "Lütfen özel istem içeriğini girin...", + "restoreDefaultContent": "Varsayılan İçeriği Geri Yükle", + "noPromptsAvailable": "Kullanılabilir istem yok", + "restoreSuccess": "Varsayılan isteme başarıyla geri dönüldü", + "restoreFailed": "Varsayılan isteme geri dönülemedi", + "deleteError": "İstem silinirken hata:", + "saveSuccess": "İstem başarıyla kaydedildi", + "saveFailed": "İstem kaydedilemedi", + "saveError": "İstem kaydedilirken hata:", + "createCustomPrompt": "Özel İstem Oluştur", + "fetchContentError": "En son istem içeriği getirilemedi:" + }, + "multiTurnSettings": "Çok Turlu Konuşma Ayarları", + "multiTurnSystemPrompt": "Sistem İstemi", + "multiTurnSystemPromptHelper": "Çok turlu konuşma üretimi için sistem istemi", + "multiTurnScenario": "Konuşma Senaryosu", + "multiTurnScenarioHelper": "Konuşma senaryosunu veya bağlamını açıklayın", + "multiTurnRounds": "Tur Sayısı", + "multiTurnRoleA": "Rol A", + "multiTurnRoleAHelper": "Konuşmadaki ilk katılımcının açıklaması", + "multiTurnRoleB": "Rol B", + "multiTurnRoleBHelper": "Konuşmadaki ikinci katılımcının açıklaması", + "multiTurnDescription": "Çok turlu konuşma üretim yapılandırması" + }, + "questions": { + "autoGenerateDataset": "Veri Setini Otomatik Oluştur", + "autoGenerateDatasetTip": "Arka plan toplu işleme görevi oluştur: bekleyen soru üretimi olan metin bloklarını otomatik olarak sorgula ve soruları çıkar.", + "filterAll": "Tüm Sorular", + "filterAnswered": "Cevaplı", + "filterUnanswered": "Cevapsız", + "filterChunkNamePlaceholder": "Blok adına göre filtrele...", + "sourceTypeAll": "Tüm Kaynaklar", + "sourceTypeText": "Metin Kaynağı", + "sourceTypeImage": "Görsel Kaynağı", + "title": "Sorular", + "confirmDeleteTitle": "Soruyu Silmeyi Onayla", + "confirmDeleteContent": "\"{{question}}\" sorusunu silmek istediğinizden emin misiniz? Bu işlem geri alınamaz.", + "deleting": "Soru siliniyor...", + "batchDeleteTitle": "Toplu Silmeyi Onayla", + "batchDeleting": "{{count}} soru siliniyor...", + "deleteSuccess": "Soru başarıyla silindi", + "deleteFailed": "Soru silinemedi", + "batchDeleteSuccess": "{{count}} soru başarıyla silindi", + "batchDeletePartial": "Silme tamamlandı, başarılı: {{success}}, başarısız: {{failed}}", + "batchDeleteFailed": "Sorular toplu olarak silinemedi", + "noQuestionsSelected": "Lütfen önce soruları seçin", + "batchGenerateStart": "{{count}} soru için veri setleri oluşturuluyor", + "invalidQuestionKey": "Geçersiz soru anahtarı", + "listView": "Soru Listesi", + "treeView": "Alan Ağacı", + "selectAll": "Tümünü Seç", + "selectedCount": "{{count}} soru seçildi", + "totalCount": "Toplam {{count}} soru", + "searchPlaceholder": "Soruları veya etiketleri ara...", + "searchModeInclude": "Dahil et", + "searchModeExclude": "Hariç tut", + "searchTargetAll": "Soru+Etiket", + "searchTargetQuestion": "Soru", + "searchTargetTag": "Etiket", + "deleteSelected": "Seçilenleri Sil", + "batchGenerate": "Toplu Veri Seti Oluştur", + "generating": "Veri Seti Oluşturuluyor", + "generatingProgress": "Tamamlanan: {{completed}}/{{total}}", + "generatedCount": "Oluşturulan: {{count}}", + "pleaseWait": "Lütfen bekleyin, işleniyor...", + "createSuccess": "Soru başarıyla oluşturuldu", + "updateSuccess": "Soru başarıyla güncellendi", + "operationSuccess": "İşlem başarılı", + "operationFailed": "İşlem başarısız", + "editQuestion": "Soruyu Düzenle", + "questionContent": "Soru İçeriği", + "sourceType": "Veri Kaynağı Türü", + "sourceType.text": "Metin", + "sourceType.image": "Görsel", + "selectChunk": "Metin Bloğu Seç", + "searchChunk": "Metin bloklarını ara...", + "selectImage": "Görsel Seç", + "searchImage": "Görselleri ara...", + "selectTag": "Etiket Seç", + "searchTag": "Etiketleri ara...", + "createQuestion": "Soru Oluştur", + "createNormalQuestion": "Normal Soru Oluştur", + "createQuestionTemplate": "Soru Şablonu Oluştur", + "questionPlaceholder": "Lütfen sorunuzu girin", + "noChunkSelected": "Lütfen önce bir metin bloğu seçin", + "noTagSelected": "Lütfen bir etiket seçin", + "fetchTemplatesFailed": "Soru şablonları getirilemedi", + "createTemplateSuccess": "Soru şablonu başarıyla oluşturuldu", + "createTemplateFailed": "Soru şablonu oluşturulamadı", + "updateTemplateSuccess": "Soru şablonu başarıyla güncellendi", + "updateTemplateFailed": "Soru şablonu güncellenemedi", + "deleteTemplateSuccess": "Soru şablonu başarıyla silindi", + "deleteTemplateFailed": "Soru şablonu silinemedi", + "template": { + "management": "Soru Şablonları", + "create": "Şablon Oluştur", + "edit": "Şablonu Düzenle", + "question": "Soru İçeriği", + "description": "İstem", + "descriptionHelp": "Bu soru şablonuyla ilgili AI cevapları üretilirken genel isteme dahil edilir, nihai cevap üretim sonuçlarını etkilemek için kullanılır", + "noTemplates": "Henüz soru şablonu yok, eklemek için oluştur butonuna tıklayın", + "deleteConfirm": "Bu soru şablonunu silmek istediğinizden emin misiniz?", + "used": "Kullanıldı", + "addLabel": "Etiket Ekle", + "customFormat": "Özel Format", + "customFormatHelp": "JSON format çıktı kısıtlaması girin", + "customFormatInfo": "Bu format, çıktı formatını kısıtlamak için LLM'e istem olarak sağlanacaktır", + "sourceTypeInfo": "Veri Kaynağı Türü", + "sourceType": { + "label": "Veri Kaynağı Türü", + "image": "Görsel", + "text": "Metin" + }, + "answerType": { + "label": "Cevap Türü", + "text": "Metin", + "tags": "Etiketler", + "customFormat": "Özel Format" + }, + "errors": { + "questionRequired": "Lütfen soru içeriğini girin", + "labelsRequired": "Etiket türü sorular en az bir etiket gerektirir", + "customFormatRequired": "Lütfen özel format girin", + "invalidJson": "Geçersiz JSON formatı" + }, + "autoGenerate": "Şablon oluşturduktan sonra otomatik soru oluştur", + "autoGenerateHelpText": "Projedeki tüm metin blokları için bu şablona dayalı sorular otomatik olarak oluşturulacaktır", + "autoGenerateHelpImage": "Projedeki tüm görseller için bu şablona dayalı sorular otomatik olarak oluşturulacaktır", + "confirmAutoGenerate": "Otomatik Soru Oluşturmayı Onayla", + "confirmAutoGenerateTextMessage": "Otomatik soru oluşturmayı seçtiniz. Sistem, projedeki tüm metin blokları için bu şablona dayalı sorular oluşturacaktır.", + "confirmAutoGenerateImageMessage": "Otomatik soru oluşturmayı seçtiniz. Sistem, projedeki tüm görseller için bu şablona dayalı sorular oluşturacaktır.", + "autoGenerateWarning": "Bu işlem çok sayıda soru oluşturabilir. Devam etmek için lütfen onaylayın.", + "autoGenerateSuccess": "{{count}} veri kaynağı için başarıyla soru oluşturuldu", + "autoGeneratePartialFail": "{{success}} soru başarıyla oluşturuldu, {{fail}} başarısız", + "autoGenerateFailed": "Otomatik soru oluşturma başarısız" + }, + "generateSingleTurnDataset": "Tek Turlu Veri Seti Oluştur", + "generateSingleTurnDatasetDesc": "Sorulara dayalı S&C veri seti oluştur", + "generateMultiTurnDataset": "Çok Turlu Veri Seti Oluştur", + "generateImageDataset": "Görsel S&C Veri Seti Oluştur", + "generateMultiTurnDatasetDesc": "Sorulara dayalı çok turlu konuşma veri seti oluştur", + "deleteConfirm": "Bu soruyu silmek istediğinizden emin misiniz? Bu işlem geri alınamaz." + }, + "monitoring": { + "title": "Resource Monitoring Dashboard", + "timeRange": { + "24h": "Last 24 hours", + "7d": "Last 7 days", + "30d": "Last 30 days" + }, + "filters": { + "allProjects": "All Projects", + "allProviders": "All Providers", + "allStatus": "All Status" + }, + "status": { + "success": "Success", + "failed": "Failed" + }, + "actions": { + "export": "Export Report" + }, + "stats": { + "totalTokens": "Total Token Usage", + "avgTokensPerCall": "Avg Tokens per Call", + "totalCalls": "Total Calls", + "avgLatency": "Avg Latency", + "inputOutput": "Input: {{input}} · Output: {{output}}", + "successCalls": "{{count}} Success", + "failedCalls": "{{count}} Failed", + "failureRate": "{{rate}}% Failure Rate", + "basedOnSuccessCalls": "Based on {{count}} successful calls", + "noSuccessCalls": "No successful calls" + }, + "charts": { + "tokenTrend": "Token Usage Trend", + "inputLegend": "Input", + "outputLegend": "Output", + "distributionTitle": "Token Usage Distribution (by Model)", + "distributionSubtitle": "Resource usage share by model", + "tokensTooltip": "{{value}}K Tokens" + }, + "table": { + "title": "Usage Details", + "searchPlaceholder": "Search project, model, or failure reason...", + "empty": "No data", + "rowsPerPage": "Rows per page:", + "columns": { + "projectName": "Project", + "provider": "Provider", + "model": "Model", + "status": "Status", + "failureReason": "Failure Reason", + "inputTokens": "Input Tokens", + "outputTokens": "Output Tokens", + "totalTokens": "Total", + "calls": "Calls", + "avgLatency": "Avg Latency" + } + }, + "errors": { + "fetchSummaryFailed": "Failed to fetch monitoring summary", + "fetchLogsFailed": "Failed to fetch monitoring logs" + } + }, + "common": { + "dataSource": "Veri Kaynağı", + "menu": "Menü", + "openMenu": "Navigasyon menüsünü aç", + "all": "Tümü", + "jumpTo": "Git", + "unknownError": "Bilinmeyen Hata", + "create": "Oluştur", + "edit": "Düzenle", + "delete": "Sil", + "save": "Kaydet", + "cancel": "İptal", + "confirm": "Onayla", + "complete": "Tamamla", + "close": "Kapat", + "add": "Ekle", + "remove": "Kaldır", + "loading": "Yükleniyor...", + "yes": "Evet", + "no": "Hayır", + "confirmDelete": "Silmeyi Onayla", + "saving": "Kaydediliyor...", + "deleting": "Siliniyor...", + "actions": "İşlemler", + "confirmDeleteDataSet": "Bu veri setini silmek istediğinizden emin misiniz? Bu işlem geri alınamaz.", + "noData": "Yok", + "failed": "Başarısız", + "success": "Başarılı", + "backToList": "Listeye Dön", + "label": "Etiket", + "confirmDeleteDescription": "Bu Dosyayı silmek istediğinizden emin misiniz? Bu işlem geri alınamaz.", + "more": "Daha Fazla", + "fetchError": "Veri getirme başarısız", + "confirmDeleteQuestion": "Bu soruyu silmek istediğinizden emin misiniz? Bu işlem geri alınamaz.", + "deleteSuccess": "Başarıyla silindi", + "visitGitHub": "GitHub Deposunu Ziyaret Et", + "syncOldData": "Eski Verileri Senkronize Et", + "copy": "Kopyala", + "enabled": "Etkin", + "disabled": "Devre Dışı", + "copied": "Kopyalandı", + "generating": "Oluşturuluyor...", + "items": "öğe", + "detailInfo": "Detaylı Bilgi" + }, + "home": { + "title": "Easy Dataset", + "subtitle": "Büyük Dil Modelleri için ince ayar veri setleri oluşturmak için güçlü bir araç", + "createProject": "Proje Oluştur", + "searchDataset": "Açık Veri Setlerinde Ara" + }, + "projects": { + "reuseConfig": "Model Yapılandırmasını Yeniden Kullan", + "noReuse": "Yapılandırma Yeniden Kullanma", + "selectProject": "Proje Seç", + "fetchFailed": "Proje listesi getirilemedi", + "fetchError": "Proje listesi getirilirken hata", + "loading": "Projeleriniz yükleniyor...", + "createFailed": "Proje oluşturulamadı", + "createError": "Proje oluşturulurken hata", + "createNew": "Yeni Proje Oluştur", + "saveFailed": "Proje kaydedilemedi", + "id": "Proje ID", + "name": "Proje Adı", + "description": "Proje Açıklaması", + "questions": "Sorular", + "datasets": "Veri Setleri", + "lastUpdated": "Son Güncelleme", + "viewDetails": "Detayları Görüntüle", + "createFirst": "Lütfen önce bir proje oluşturun", + "noProjects": "Proje bulunamadı", + "notExist": "Proje mevcut değil.", + "createProject": "Proje Oluştur", + "deleteConfirm": "Bu projeyi silmek istediğinizden emin misiniz? Bu işlem geri alınamaz.", + "deleteSuccess": "Proje başarıyla silindi", + "deleteFailed": "Proje silinemedi", + "backToHome": "Ana Sayfaya Dön", + "deleteConfirmTitle": "Silmeyi Onayla" + }, + "textSplit": { + "dragToUpload": "Yüklemek için dosyaları sürükleyin", + "fileList": "Dosya Listesi", + "autoGenerateQuestions": "Otomatik Oluştur", + "autoGenerateQuestionsTip": "Arka plan toplu işleme görevi oluştur: bekleyen soru üretimi olan metin bloklarını otomatik olarak sorgula ve soruları çıkar.", + "exportChunks": "Blokları Dışa Aktar", + "allChunks": "Tüm Metin Blokları", + "generatedQuestions2": "Sorulu", + "ungeneratedQuestions": "Sorusuz", + "noFilesUploaded": "Henüz dosya yüklenmedi", + "unknownFile": "Bilinmeyen Dosya", + "fetchFilesFailed": "Dosyalar getirilemedi", + "editTag": "Etiketi Düzenle", + "deleteTag": "Etiketi Sil", + "addTag": "Etiket Ekle", + "selectedCount": "{{count}} metin bloğu seçildi", + "totalCount": "toplam {{count}} metin bloğu", + "batchGenerateQuestions": "Toplu Oluştur", + "uploadedDocuments": "{{count}} Belge Yüklendi", + "title": "Metinler", + "uploadNewDocument": "Yeni Belge Yükle", + "selectFile": "Dosya Seç", + "markdownOnly": "Şu anda yalnızca Markdown (.md) format dosyaları destekleniyor", + "supportedFormats": "Desteklenen formatlar: .pdf .md, .txt, .docx", + "uploadAndProcess": "Yükle ve İşle", + "selectedFiles": "Seçilen Dosyalar ({{count}})", + "oneFileMessage": "Dosya yüklenemez, lütfen önce mevcut dosyayı silin", + "mutilFileMessage": "Yeni dosya yüklendikten sonra alan ağacı yeniden oluşturulacak", + "noChunks": "Metin bloğu bulunamadı", + "chunkDetails": "Blok Detayları: {{chunkId}}", + "fetchChunksFailed": "Metin blokları getirilemedi", + "fetchChunksError": "Metin blokları getirilirken hata", + "fileResultReceived": "Dosya sonucu alındı", + "fileUploadSuccess": "Dosya başarıyla yüklendi", + "splitTextFailed": "Metin bölme başarısız", + "splitTextError": "Metin bölme hatası", + "deleteChunkFailed": "Metin bloğu silinemedi", + "deleteChunkError": "Metin bloğu silinirken hata", + "selectModelFirst": "Lütfen önce bir model seçin, üst gezinme çubuğundan seçebilirsiniz", + "modelNotAvailable": "Seçilen model kullanılamıyor, lütfen tekrar seçin", + "generateQuestionsFailed": "{{chunkId}} bloğu için soru üretilemedi", + "questionsGenerated": "{{total}} soru üretildi", + "customSplitMode": "Özel Bölme Modu", + "customSplitInstructions": "Bölme noktaları eklemek için metin seçin. Sistem, seçtiğiniz konumlara bölme işaretleyicileri yerleştirecektir.", + "splitPointsList": "Eklenen Bölme Noktaları", + "saveSplitPoints": "Bölme Noktalarını Kaydet", + "confirmCustomSplitTitle": "Bölme Değişimini Onayla", + "confirmCustomSplitMessage": "Not: Özel bölme noktaları, bu belge için önceki otomatik bölme sonuçlarının yerini alacaktır. Devam etmek istiyor musunuz?", + "customSplitSuccess": "Özel bölme başarıyla kaydedildi", + "customSplitFailed": "Özel bölme kaydedilemedi", + "missingRequiredData": "Gerekli veri eksik", + "chunksPreview": "Blok Boyutu Önizlemesi", + "chunk": "Blok", + "characters": " karakter", + "questionsGeneratedSuccess": "Metin bloğu için başarıyla {{total}} soru üretildi", + "generateQuestionsForChunkFailed": "{{chunkId}} bloğu için soru üretilemedi", + "generateQuestionsForChunkError": "{{chunkId}} bloğu için soru üretilirken hata", + "generateQuestionsError": "Soru üretilirken hata", + "partialSuccess": "Kısmi başarılı soru üretimi ({{successCount}}/{{total}}), {{errorCount}} blok başarısız", + "allSuccess": "{{successCount}} metin bloğu için başarıyla {{totalQuestions}} soru üretildi", + "fileDeleted": "{{fileName}} dosyası silindi, metin bloğu listesi yenileniyor", + "tabs": { + "smartSplit": "Akıllı Bölme", + "domainAnalysis": "Alan Analizi" + }, + "loading": "Yükleniyor...", + "fetchingDocuments": "Belge verileri getiriliyor", + "processing": "İşleniyor...", + "progressStatus": "{{total}} metin bloğu seçildi, {{completed}} tamamlandı", + "processingPleaseWait": "İşleniyor, lütfen bekleyin!", + "oneFileLimit": "Dosya yüklenemez, zaten yüklenmiş bir dosya var", + "unsupportedFormat": "Desteklenmeyen dosya formatı: {{files}}", + "modelInfoParseError": "Model bilgisi ayrıştırılamadı", + "uploadFailed": "Yükleme başarısız", + "uploadSuccess": "{{count}} dosya başarıyla yüklendi", + "deleteFailed": "Dosya silinemedi", + "deleteSuccess": "{{fileName}} dosyası başarıyla silindi", + "generatedQuestions": "{{count}} Soru", + "viewDetails": "Detayları Görüntüle", + "generateQuestions": "Soru Üret", + "dataCleaning": "Veri Temizleme", + "batchDataCleaning": "Toplu Veri Temizleme", + "autoDataCleaning": "Otomatik Veri Temizleme", + "autoDataCleaningTip": "Arka plan toplu işleme görevi oluştur: tüm metin bloklarını otomatik olarak temizle", + "dataCleaningSuccess": "Veri temizleme tamamlandı, orijinal uzunluk: {{originalLength}}, temizlenmiş uzunluk: {{cleanedLength}}", + "dataCleaningFailed": "{{chunkId}} metin bloğu için veri temizleme başarısız", + "dataCleaningForChunkSuccess": "{{chunkId}} metin bloğu için veri temizleme tamamlandı", + "dataCleaningForChunkFailed": "{{chunkId}} metin bloğu için veri temizleme başarısız", + "dataCleaningForChunkError": "{{chunkId}} metin bloğu için veri temizleme hatası", + "dataCleaningPartialSuccess": "Kısmi veri temizleme başarısı ({{successCount}}/{{total}}), {{errorCount}} metin bloğu başarısız", + "dataCleaningAllSuccess": "{{successCount}} metin bloğu için başarıyla veri temizleme tamamlandı", + "charsCount": "Karakter", + "pdfProcessStatus": "Toplam {{total}} dosya, {{completed}} dönüştürüldü", + "pdfPageProcessStatus": "{{fileName}} işleniyor, {{total}} sayfadan {{completed}} sayfa dönüştürüldü", + "pdfProcessing": "Dosya dönüştürülüyor...", + "pdfProcessingFailed": "Dosya dönüştürme başarısız!", + "pdfProcess": "Dosya algılandı!", + "selectPdfProcessingStrategy": "Lütfen dosya işleme yöntemini seçin:", + "pdfProcessingStrategyDefault": "Varsayılan", + "pdfProcessingStrategyDefaultHelper": "Yerleşik PDF ayrıştırma stratejisini kullan", + "pdfProcessingStrategyMinerUHelper": "Ayrıştırma için MinerU API kullan. Lütfen önce MinerU API Token yapılandırın", + "pdfProcessingStrategyVision": "Özel Görsel Model", + "pdfProcessingStrategyVisionHelper": "Ayrıştırma için özel görsel model kullan", + "pdfProcessingToast": "Dosya algılandı. Sistem, dosyayı ayrıştırmak için bir arka plan görevi oluşturacak", + "pdfProcessingWaring": "Devam eden bir dosya işleme görevi var. Görevin tamamlanmasını beklemeden diğer işlemleri yaparsanız veri üretim kalitesi etkilenebilir!", + "pdfProcessingLoading": "Dosya dönüştürme görevi çalıştırılıyor, lütfen görev tamamlanana kadar yeni dosya yüklemeyin...", + "basicPdfParsing": "Temel PDF Ayrıştırma", + "basicPdfParsingDesc": "Basit PDF dosyalarının temel ana hatlarını tanıyabilir, yüksek hız", + "mineruApiDesc": "Formüller ve grafikler dahil karmaşık PDF dosyalarını tanıyabilir (MinerU API Key yapılandırması gerektirir)", + "mineruLocalDesc": "Formüller ve grafikler dahil karmaşık PDF dosyalarını tanıyabilir (MinerU Local URL yapılandırması gerektirir)", + "mineruApiDescDisabled": "Lütfen Proje Ayarları - Görev Yapılandırması'na gidip MinerU token ayarlayın", + "mineruLocalDisabled": "Lütfen önce [Proje Ayarları - Görev Yapılandırması]'nda MinerU Local URL ayarlayın", + "mineruWebPlatform": "MinerU Çevrimiçi Platform Ayrıştırma", + "mineruWebPlatformDesc": "Formüller ve grafikler dahil karmaşık PDF dosyalarını tanıyabilir (Başka bir web sitesine yönlendirme gerektirir)", + "mineruSelected": "PDF'leri ayrıştırmak için MinerU kullanımı seçildi", + "mineruLocalSelected": "PDF'leri ayrıştırmak için MinerU Local kullanımı seçildi", + "customVisionModel": "Özel Görsel Model Ayrıştırma", + "customVisionModelDesc": "Formüller ve grafikler dahil karmaşık PDF dosyalarını tanıyabilir (Model yapılandırmasına görsel model yapılandırması eklenmesi gerekir)", + "customVisionModelSelected": "PDF'leri ayrıştırmak için {{name}} ({{provider}}) görsel büyük modeli kullanımı seçildi", + "defaultSelected": "PDF'leri ayrıştırmak için varsayılan yerleşik strateji kullanımı seçildi", + "download": "Belgeyi indir", + "deleteFile": "Belgeyi sil", + "viewChunk": "Metin Bloğunu Görüntüle", + "editChunk": "Metin Bloğunu Düzenle {{chunkId}}", + "editChunkSuccess": "Metin bloğu başarıyla düzenlendi", + "editChunkFailed": "Metin bloğu düzenlenemedi", + "editChunkError": "Metin bloğu düzenlenirken hata oluştu", + "deleteFileWarning": "Uyarı: Bu belgeyi silmek aşağıdaki ilgili öğeleri de silecektir", + "deleteFileWarningChunks": "İlişkili tüm metin blokları", + "deleteFileWarningQuestions": "Bu bloklardan üretilen tüm sorular", + "deleteFileWarningDatasets": "Bu sorulardan oluşturulan tüm veri setleri", + "domainTree": { + "firstUploadTitle": "Alan Ağacı Oluşturma", + "uploadTitle": "Belge Yükleme - Alan Ağacı İşleme", + "deleteTitle": "Belge Silme - Alan Ağacı İşleme", + "reviseOption": "Alan Ağacını Revize Et", + "reviseDesc": "Eklenen veya silinen belgelere göre mevcut alan ağacını değiştir, sadece değişen kısımları etkiler", + "rebuildOption": "Alan Ağacını Yeniden Oluştur", + "rebuildDesc": "Tüm belge içeriklerine göre tamamen yeni bir alan ağacı oluştur", + "keepOption": "Değiştirme", + "keepDesc": "Mevcut alan ağacı yapısını değiştirmeden koru" + } + }, + "domain": { + "title": "Alan Bilgi Ağacı", + "addRootTag": "Kök Etiket Ekle", + "addFirstTag": "İlk Etiket Ekle", + "noTags": "Alan etiketleri mevcut değil", + "docStructure": "Belge Yapısı", + "noToc": "İçindekiler tablosu mevcut değil. Lütfen önce belgeyi yükleyin ve işleyin.", + "editTag": "Etiketi Düzenle", + "deleteTag": "Etiketi Sil", + "addChildTag": "Alt Etiket Ekle", + "deleteTagConfirmTitle": "Etiketi Sil", + "deleteTagConfirmMessage": "\"{{tag}}\" etiketini silmek istediğinizden emin misiniz?", + "deleteWarning": "Bu işlem bu etiketi ve tüm alt etiketlerini, sorularını ve veri setlerini silecektir. Bu işlem geri alınamaz!", + "dialog": { + "addTitle": "Etiket Ekle", + "editTitle": "Etiketi Düzenle", + "addChildTitle": "Alt Etiket Ekle", + "inputRoot": "Lütfen yeni bir kök etiket adı girin", + "inputEdit": "Lütfen etiket adını düzenleyin", + "inputChild": "Lütfen \"{label}\" için bir alt etiket ekleyin", + "labelName": "Etiket Adı", + "saving": "Kaydediliyor...", + "save": "Kaydet", + "deleteConfirm": "\"{label}\" etiketini silmek istediğinizden emin misiniz?", + "deleteWarning": "Bu işlem tüm alt etiketleri de silecektir ve geri alınamaz.", + "emptyLabel": "Etiket adı boş olamaz" + }, + "tabs": { + "tree": "Alan Ağacı", + "structure": "Belge Yapısı" + }, + "errors": { + "saveFailed": "Etiketler kaydedilemedi" + }, + "messages": { + "updateSuccess": "Etiketler başarıyla güncellendi" + } + }, + "export": { + "alpacaSettings": "Alpaca Format Ayarları", + "questionFieldType": "Soru Alanı Türü", + "useInstruction": "instruction alanını kullan", + "useInput": "input alanını kullan", + "customInstruction": "Özel Instruction İçeriği", + "instructionPlaceholder": "Sabit instruction içeriği girin", + "instructionHelperText": "Input alanı kullanılırken burada sabit instruction içeriği belirtebilirsiniz", + "title": "Dışa Aktar", + "format": "Format", + "fileFormat": "Dosya Formatı", + "systemPrompt": "Sistem İstemi", + "systemPromptPlaceholder": "Lütfen sistem istemi girin...", + "ReasoninglanguagePlaceholder": "Lütfen Akıl Yürütme dilini girin: İngilizce veya Çince veya diğerleri", + "onlyConfirmed": "Sadece onaylanmış verileri dışa aktar", + "example": "Format Örneği", + "confirmExport": "Dışa Aktarmayı Onayla", + "includeCOT": "Düşünce Zincirini Dahil Et", + "cotDescription": "Nihai cevaptan önceki akıl yürütme sürecini içerir", + "customFormat": "Özel Format", + "customFormatSettings": "Özel Format Ayarları", + "questionFieldName": "Soru Alanı Adı", + "multilingualThinkingFormat": "Multilingual‑Thinking", + "Reasoninglanguage": "Akıl Yürütme dili", + "sampleInstruction": "İnsan talimatı (gerekli)", + "sampleOutput": "Model yanıtı (gerekli)", + "sampleSystem": "Sistem istemi (isteğe bağlı)", + "sampleInstruction2": "İkinci talimat", + "sampleOutput2": "İkinci yanıt", + "sampleSystemShort": "Sistem istemi", + "fixedInstruction": "Sabit talimat içeriği", + "sampleInput": "İnsan sorusu (gerekli)", + "sampleInput2": "İkinci soru", + "sampleInputOptional": "İnsan girişi (isteğe bağlı)", + "sampleUserMessage": "İnsan talimatı", + "sampleAssistantMessage": "Model yanıtı", + "sampleAnalysis": "Modelin düşünce zinciri içeriği", + "sampleFinal": "Model yanıtı", + "sampleThinking": "Modelin düşünce zinciri içeriği", + "fetchLabelStatsError": "Etiket istatistikleri getirilemedi:" + }, + "import": { + "title": "İçe Aktar", + "fileUpload": "Dosya Yükleme", + "fileUploadDescription": "Veri setlerini içe aktarmak için yerel dosyaları yükleyin", + "mapFields": "Alan Eşleme", + "importing": "İçe Aktarılıyor", + "uploadFile": "Dosya Yükle", + "supportedFormats": "JSON, JSONL, CSV format dosyalarını destekler", + "dragDropFile": "Dosyaları buraya sürükleyin veya dosya seçmek için tıklayın", + "dropFileHere": "Dosyayı yüklemek için bırakın", + "maxFileSize": "Maksimum dosya boyutu: 50MB", + "processingFile": "Dosya işleniyor...", + "uploadedFiles": "Yüklenen Dosyalar", + "uploadError": "Dosya yükleme başarısız, lütfen dosya formatını kontrol edin", + "selectFromSource": "{{source}} kaynağından veri seti seç", + "sourceDescription": "Aramak için veri seti adı veya anahtar kelimeler girin", + "datasetName": "Veri Seti Adı", + "hfPlaceholder": "örn.: squad, glue, imdb", + "msPlaceholder": "örn.: damo/nlp_bert_document-classification", + "search": "Ara", + "searching": "Aranıyor", + "searchResults": "Arama Sonuçları", + "downloads": "İndirmeler", + "download": "İndir", + "downloading": "İndiriliyor", + "hfNote": "Not: Büyük veri setlerini indirmek uzun sürebilir, test için daha küçük veri setleri seçmeniz önerilir.", + "msNote": "Not: ModelScope veri seti indirmesi ağ bağlantısı gerektirir, lütfen ağ bağlantınızı kontrol edin.", + "fieldMapping": "Alan Eşleme", + "mappingDescription": "Lütfen kaynak veri alanlarını hedef alanlara eşleyin. Sistem olası eşleme ilişkilerini otomatik olarak belirledi, gerektiğinde ayarlayabilirsiniz.", + "selectMapping": "Alan Eşlemesini Seç", + "questionField": "Soru Alanı", + "answerField": "Cevap Alanı", + "cotField": "Düşünce Zinciri Alanı", + "tagsField": "Etiketler Alanı", + "selectField": "Alan Seç", + "questionDesc": "Kullanıcının sorusu veya giriş içeriği (gerekli)", + "answerDesc": "AI'nın cevabı veya çıktı içeriği (gerekli)", + "cotDesc": "Düşünce zinciri veya akıl yürütme süreci (isteğe bağlı)", + "tagsDesc": "Etiket dizisi, virgülle ayrılmış birden çok etiket (isteğe bağlı)", + "dataPreview": "Veri Önizlemesi", + "previewNote": "İlk 3 kaydı gösterir, her alan değeri en fazla 100 karakter gösterir", + "confirmMapping": "Eşlemeyi Onayla", + "requiredFields": "Lütfen en az soru ve cevap alanı eşlemelerini seçin", + "mappingRequired": "Soru ve cevap alanları gereklidir", + "duplicateMapping": "Birden çok hedef alanı aynı kaynak alanına eşleyemezsiniz", + "noPreviewData": "Önizleme verisi yok", + "preparingData": "Veri hazırlanıyor...", + "uploadingData": "Veri yükleniyor...", + "processing": "İşleniyor... {{processed}}/{{total}}", + "completed": "İçe aktarma tamamlandı", + "importStats": "İçe Aktarma İstatistikleri", + "total": "Toplam: {{count}}", + "success": "Başarılı: {{count}}", + "failed": "Başarısız: {{count}}", + "source": "Veri Kaynağı", + "description": "Açıklama", + "errors": "Hata Mesajları", + "moreErrors": "{{count}} hata daha gösterilmiyor...", + "importSuccess": "Veri seti içe aktarma tamamlandı!", + "enterDatasetName": "Lütfen veri seti adını girin", + "noDatasetFound": "Eşleşen veri seti bulunamadı", + "complete": "Tamamla" + }, + "export_extended": { + "answerFieldName": "Cevap Alanı Adı", + "cotFieldName": "Cot Alanı Adı", + "includeLabels": "Etiketleri Dahil Et", + "includeChunk": "Metin Bloğunu Dahil Et", + "questionOnly": "Sadece Soruları Dışa Aktar", + "localTab": "Yerel Dışa Aktarma", + "llamaFactoryTab": "Llama Factory", + "huggingFaceTab": "HuggingFace", + "configExists": "Yapılandırma Dosyası Mevcut", + "configPath": "Yapılandırma Dosyası Yolu", + "updateConfig": "LLaMA Factory Yapılandırmasını Güncelle", + "noConfig": "Yapılandırma dosyası mevcut değil, oluşturmak için aşağıdaki düğmeye tıklayın", + "generateConfig": "LLaMA Factory Yapılandırması Oluştur", + "huggingFaceComingSoon": "HuggingFace dışa aktarma özelliği yakında", + "uploadToHuggingFace": "HuggingFace'e Yükle", + "datasetName": "Veri Seti Adı", + "datasetNameHelp": "Format: kullanıcıadı/veri-seti-adı", + "privateDataset": "Özel Veri Seti", + "datasetSettings": "Veri Seti Ayarları", + "exportOptions": "Dışa Aktarma Seçenekleri", + "uploadSuccess": "Veri seti HuggingFace'e başarıyla yüklendi", + "viewOnHuggingFace": "HuggingFace'de Görüntüle", + "noTokenWarning": "Hugging Face Token bulunamadı. Lütfen proje ayarlarında yapılandırın.", + "goToSettings": "Ayarlara Git", + "tokenHelp": "Token'ınızı HuggingFace ayarlar sayfasından alabilirsiniz" + }, + "datasets": { + "loadingDataset": "Veri Seti Yükleniyor...", + "datasetNotFound": "Veri Seti Bulunamadı", + "optimizeTitle": "AI Optimize Et", + "optimizeAdvice": "Optimizasyon Önerisi", + "optimizePlaceholder": "Lütfen cevabı iyileştirmek için önerilerinizi girin, AI önerilerinize göre cevabı ve akıl yürütme zincirini optimize edecektir", + "generatingDataset": "Veri Seti Oluşturuluyor", + "aiOptimizeAdvicePlaceholder": "Lütfen cevabı iyileştirmek için önerilerinizi girin, AI önerilerinize göre cevabı ve akıl yürütme zincirini optimize edecektir", + "aiOptimizeAdvice": "Lütfen cevabı iyileştirmek için önerilerinizi girin, AI önerilerinize göre cevabı ve akıl yürütme zincirini optimize edecektir", + "aiOptimize": "AI Optimize Et", + "partialSuccess": "Kısmi başarılı veri seti üretimi ({{successCount}}/{{total}}), {{failCount}} soru başarısız", + "generating": "Veri Seti Oluşturuluyor", + "generateError": "Veri seti oluşturulamadı", + "management": "Veri Setleri", + "question": "Soru", + "filterAll": "Tümü", + "filterConfirmed": "Onaylandı", + "filterUnconfirmed": "Onaylanmadı", + "createdAt": "Oluşturma Tarihi", + "model": "Model", + "domainTag": "Alan Etiketi", + "cot": "COT", + "answer": "Cevap", + "chunkId": "Metin Bloğu", + "confirmed": "Onaylandı", + "noTag": "Etiket Yok", + "noData": "Veri Yok", + "rowsPerPage": "Sayfa başına satır", + "pagination": "{{from}}-{{to}} / {{count}}", + "confirmDeleteMessage": "Bu veri setini silmek istediğinizden emin misiniz? Bu işlem geri alınamaz.", + "questionLabel": "Soru", + "fetchFailed": "Veri seti getirilemedi", + "deleteFailed": "Veri seti silinemedi", + "deleteSuccess": "Başarıyla silindi", + "exportSuccess": "Veri seti başarıyla dışa aktarıldı", + "exportFailed": "Dışa aktarma başarısız", + "exportProgress": "Dışa Aktarma İlerlemesi", + "exportingData": "Veri seti dışa aktarılıyor", + "processedCount": "İşlenen {{processed}} / {{total}} öğe", + "exportInProgress": "Veri getiriliyor, lütfen bekleyin...", + "exportFinalizing": "Dosya oluşturuluyor, neredeyse bitti...", + "loading": "Veri setleri yükleniyor...", + "stats": "Toplam {{total}} veri seti, {{confirmed}} onaylandı ({{percentage}}%)", + "selected": "Toplam seçilen: {{count}}", + "batchconfirmDeleteMessage": "Seçilen {{count}} soruyu silmek istediğinizden emin misiniz? Bu işlem geri alınamaz.", + "batchDelete": "Toplu Sil", + "batchDeleteProgress": "Tamamlanan: {{completed}}/{{total}}", + "batchDeleteCount": "Silme sayısı: {{count}}", + "searchPlaceholder": "Veri setlerinde ara...", + "fieldQuestion": "Soru", + "fieldAnswer": "Cevap", + "fieldCOT": "COT", + "fieldLabel": "Alan Etiketi", + "moreFilters": "Daha Fazla Filtre", + "filtersTitle": "Filtre Seçenekleri", + "filterConfirmationStatus": "Onay Durumu", + "filterCotStatus": "Düşünce Zinciri Durumu", + "filterHasCot": "CoT Var", + "filterNoCot": "CoT Yok", + "filterScoreRange": "Puan Aralığı", + "filterNoteKeyword": "Not Anahtar Kelimesi", + "filterNoteKeywordPlaceholder": "Not anahtar kelimesi girin...", + "filterChunkName": "Blok Adı", + "filterChunkNamePlaceholder": "Blok adı girin...", + "filterCustomTag": "Özel Etiket", + "resetFilters": "Sıfırla", + "applyFilters": "Uygula", + "viewDetails": "Detayları Görüntüle", + "datasetDetail": "Veri Seti Detayları", + "metadata": "Meta Veri", + "confirmSave": "Kaydetmeyi Onayla", + "unconfirm": "Onayı Kaldır", + "unconfirming": "Onay kaldırılıyor...", + "uncategorized": "Kategorisiz", + "questionCount": "{{count}} Soru", + "source": "Kaynak", + "generateDataset": "Veri Seti Oluştur", + "generateNotImplemented": "Veri seti oluşturma uygulanmadı", + "generateSuccess": "Başarıyla veri seti oluşturuldu: {{question}}", + "generateFailed": "Veri seti oluşturulamadı: {{error}}", + "noTagsAndQuestions": "Etiket ve soru yok", + "answerCount": "{{count}} Cevap", + "answered": "Cevaplandı", + "enableShortcuts": "Sayfa kısayol tuşu", + "shortcutsHelp": "← ileri, → geri, y onaylamak, d silmek için basın", + "filterDistill": "Damıtılmış Veri Seti", + "filterDistillYes": "Damıtılmış Veri Seti", + "filterDistillNo": "Damıtılmamış Veri Seti", + "evaluation": "Veri Seti Değerlendirmesi", + "rating": "Puan", + "ratingExcellent": "Mükemmel", + "ratingGood": "İyi", + "ratingAverage": "Orta", + "ratingPoor": "Zayıf", + "ratingVeryPoor": "Çok Zayıf", + "ratingUnrated": "Puanlanmamış", + "customTags": "Özel Etiketler", + "addCustomTag": "Özel etiket ekle...", + "note": "Not", + "addNote": "Not ekle...", + "noNote": "Not yok", + "clickToAddNote": "Not eklemek için tıklayın...", + "enterNote": "Not girin...", + "noteShortcuts": "Kaydetmek için Ctrl+Enter, iptal için Esc", + "aiEvaluation": "AI Kalite Değerlendirmesi", + "addToEval": "Değerlendirme Veri Setine Ekle", + "addToEvalSuccess": "Değerlendirme veri setine başarıyla eklendi", + "addToEvalFailed": "Ekleme başarısız", + "generateEvalVariant": "Değerlendirme Varyantı Oluştur", + "generateVariantFailed": "Varyant oluşturulamadı", + "saveVariantSuccess": "Değerlendirme veri setine kaydedildi", + "saveVariantFailed": "Kaydetme başarısız", + "evalVariantTitle": "Değerlendirme Varyantı Oluştur", + "evalVariantPreviewTitle": "Oluşturulan Soruları Onayla", + "saveToEval": "Değerlendirme Veri Setine Kaydet", + "evalVariantConfigHint": "Lütfen soru türünü ve sayısını seçin. AI mevcut S&C çiftine göre yeniden yazacaktır.", + "questionType": "Soru Türü", + "typeOpenEnded": "Açık uçlu", + "typeSingleChoice": "Tek Seçimli", + "typeMultipleChoice": "Çok Seçimli", + "typeTrueFalse": "Doğru/Yanlış", + "typeShortAnswer": "Kısa Cevap", + "generateCount": "Oluşturma Sayısı", + "evalVariantPreviewHint": "Oluşturulan soruları düzenleyebilir ve doğruladıktan sonra değerlendirme setine kaydedebilirsiniz.", + "questionIndex": "Soru {{index}}", + "options": "Seçenekler (JSON Dizisi)", + "optionsHint": "Örn.: [\"Seçenek A\", \"Seçenek B\"]", + "answerArrayHint": "Çok seçimli için lütfen bir dizi girin, örn. [\"A\", \"C\"]", + "answerBoolHint": "Doğru/Yanlış için lütfen ✅ veya ❌ girin", + "generate": "Oluştur", + "updateSuccess": "Güncelleme başarılı", + "updateFailed": "Güncelleme başarısız", + "evaluate": "Değerlendir", + "evaluating": "Değerlendiriliyor...", + "batchEvaluate": "Toplu Değerlendir", + "selectModelFirst": "Lütfen önce bir model seçin", + "evaluateSuccess": "Değerlendirme tamamlandı! Puan: {{score}}/5", + "evaluateFailed": "Değerlendirme başarısız", + "evaluateError": "Değerlendirme başarısız: {{error}}", + "batchEvaluateStarted": "Toplu değerlendirme görevi başlatıldı, arka planda işleniyor", + "batchEvaluateStartFailed": "Toplu değerlendirme başlatılamadı", + "batchEvaluateFailed": "Toplu değerlendirme başarısız: {{error}}", + "scoreRange": "{{min}} - {{max}} puan", + "singleTurn": "Tek Turlu S&C Veri Seti", + "multiTurn": "Çok Turlu Konuşma Veri Seti", + "imageQA": "Görsel S&C Veri Seti", + "conversationDetail": "Çok Turlu Konuşma Detayları", + "conversationContent": "Konuşma İçeriği", + "basicInfo": "Temel Bilgiler", + "firstQuestion": "İlk Soru", + "conversationScenario": "Konuşma Senaryosu", + "conversationRounds": "Konuşma Turları", + "modelUsed": "Kullanılan Model", + "qualityScore": "Kalite Puanı", + "notes": "Notlar", + "createTime": "Oluşturma Zamanı", + "notSet": "Ayarlanmadı", + "noTags": "Etiket Yok", + "noNotes": "Not Yok", + "notEvaluated": "Değerlendirilmedi", + "round": "Tur {{round}}", + "system": "Sistem", + "user": "Kullanıcı", + "assistant": "Asistan", + "confirmDelete": "Silmeyi Onayla", + "confirmDeleteConversation": "Bu çok turlu konuşma veri setini silmek istediğinizden emin misiniz? Bu işlem geri alınamaz.", + "conversationNotFound": "Konuşma veri seti bulunamadı", + "fetchDataFailed": "Veri getirilemedi", + "saveFailed": "Kaydetme başarısız", + "saveSuccess": "Başarıyla kaydedildi", + "saving": "Kaydediliyor...", + "inputTagsPlaceholder": "Etiketleri girin, boşlukla ayırın", + "addNotesPlaceholder": "Not ekle", + "noConversations": "Çok turlu konuşma yok", + "notRated": "Puanlanmadı", + "minScore": "Min Puan", + "maxScore": "Max Puan", + "unconfirmed": "Onaylanmadı" + }, + "rating": { + "veryPoor": "Çok Zayıf", + "poor": "Zayıf", + "belowAverage": "Ortalamanın Altında", + "fair": "Vasat", + "average": "Orta", + "good": "İyi", + "veryGood": "Çok İyi", + "excellent": "Mükemmel", + "outstanding": "Üstün", + "perfect": "Kusursuz", + "unrated": "Puanlanmamış" + }, + "tags": { + "noTags": "Etiket yok", + "addTag": "Etiket ekle...", + "addCustomTag": "Özel etiket ekle", + "maxTagsReached": "Maksimum {{maxTags}} etikete ulaşıldı", + "availableTagsHint": "Mevcut etiketlerden seçin veya yeni etiket girin" + }, + "update": { + "newVersion": "Yeni Sürüm", + "newVersionAvailable": "Yeni Sürüm Mevcut", + "currentVersion": "Mevcut Sürüm", + "latestVersion": "En Son Sürüm", + "downloadNow": "Şimdi İndir", + "downloading": "İndiriliyor", + "installNow": "Şimdi Yükle", + "updating": "Güncelleniyor...", + "updateNow": "Şimdi Güncelle", + "viewRelease": "Sürüm Notlarını Görüntüle", + "checking": "Güncellemeler kontrol ediliyor...", + "noUpdates": "Zaten güncel", + "updateError": "Güncelleme Hatası", + "updateSuccess": "Güncelleme Başarılı", + "restartRequired": "Yeniden Başlatma Gerekli", + "restartNow": "Şimdi Yeniden Başlat", + "restartLater": "Daha Sonra Yeniden Başlat" + }, + "datasetSquare": { + "title": "Veri Seti Meydanı", + "subtitle": "Model eğitimi ve araştırmanızı desteklemek için çeşitli genel veri seti kaynaklarını keşfedin", + "searchPlaceholder": "Veri seti anahtar kelimeleri ara...", + "searchVia": "Arama yolu", + "categoryTitle": "Veri Seti Kategorileri", + "categories": { + "all": "Tümü", + "popular": "Popüler", + "chinese": "Çince Kaynaklar", + "english": "İngilizce Kaynaklar", + "research": "Araştırma Verileri", + "multimodal": "Çok Modlu" + }, + "foundResources": "{{count}} veri seti kaynağı bulundu", + "currentFilter": "Mevcut filtre: {{category}}", + "noDatasets": "Kriterlerinize uygun veri seti bulunamadı", + "tryOtherCategories": "Lütfen diğer kategorileri deneyin veya tüm veri setlerini görüntülemek için geri dönün", + "dataset": "Veri Seti", + "viewDataset": "Veri Setini Görüntüle" + }, + "playground": { + "title": "Model Testi", + "selectModelFirst": "Lütfen bir model seçin", + "sendFirstMessage": "Test başlatmak için ilk mesajınızı gönderin", + "inputMessage": "Mesaj girin...", + "send": "Gönder", + "outputMode": "Çıktı Modu", + "normalOutput": "Normal Çıktı", + "streamingOutput": "Akış Çıktısı", + "clearConversation": "Konuşmayı Temizle", + "selectModelMax3": "Test için en fazla 3 model seçin", + "reasoningProcess": "Akıl Yürütme Zinciri" + }, + "chunks": { + "title": "Metin Bloğu", + "defaultTitle": "Varsayılan Başlık" + }, + "documentation": "Dokümantasyon", + "models": { + "configNotFound": "Model yapılandırması bulunamadı", + "parseError": "Model yapılandırması ayrıştırılamadı", + "fetchFailed": "Model getirilemedi", + "saveFailed": "Model yapılandırması kaydedilemedi", + "pleaseSelectModel": "Lütfen en az bir model seçin", + "title": "Model Ayarları", + "add": "Model Ekle", + "unselectedModel": "Seçilmemiş Model", + "unconfiguredAPIKey": "Yapılandırılmamış API Anahtarı", + "saveAllModels": "Tüm Modelleri Kaydet", + "edit": "Düzenle", + "delete": "Sil", + "modelName": "Model Adı", + "endpoint": "Uç Nokta", + "apiKey": "API Anahtarı", + "provider": "Sağlayıcı", + "localModel": "Yerel Model", + "apiKeyConfigured": "API Anahtarı Yapılandırıldı", + "apiKeyNotConfigured": "API Anahtarı Yapılandırılmadı", + "temperature": "Sıcaklık", + "maxTokens": "Maks Token", + "maxTokensInputTip": "Kaydırıcı aralığı: 1-{{max}}. Ayrıca herhangi bir pozitif tam sayı girebilirsiniz.", + "topP": "Top P", + "type": "Model Türü", + "text": "Büyük Dil Modeli", + "vision": "Görsel Büyük Model", + "typeTips": "PDF'leri ayrıştırmak için özel görsel model kullanmak istiyorsanız, lütfen en az bir görsel büyük model yapılandırın", + "refresh": "Modelleri Yenile", + "configuredModels": "Yapılandırılmış Modeller", + "unconfiguredModels": "Yapılandırılmamış Modeller", + "noConfiguredModels": "Yapılandırılmış model yok", + "noUnconfiguredModels": "Yapılandırılmamış model yok", + "checkEndpointHealth": "Uç nokta sağlığını kontrol et", + "checkAllEndpointHealth": "Tüm uç noktaları kontrol et", + "endpointHealthy": "Uç nokta sağlıklı", + "endpointCheckFailed": "Uç nokta kontrolü başarısız", + "endpointMissing": "Uç nokta boş", + "endpointReachableModelMissing": "Uç nokta erişilebilir, ancak mevcut model dönen listede yok", + "healthCheckSummary": "Sağlık kontrolü tamamlandı: {{okCount}} sağlıklı, {{failCount}} başarısız", + "checking": "Kontrol ediliyor...", + "healthy": "Sağlıklı", + "reachable": "Erişilebilir", + "unhealthy": "Sağlıksız", + "notChecked": "Kontrol edilmedi" + }, + "stats": { + "ongoingProjects": "Devam Eden Projeler", + "questionCount": "Soru Sayısı", + "generatedDatasets": "Oluşturulan Veri Setleri", + "supportedModels": "Desteklenen Modeller" + }, + "migration": { + "title": "Proje Taşıma", + "description": "Bazı projelerin veritabanına taşınması gerekiyor. Taşıma, performansı artırabilir ve daha fazla özelliği destekleyebilir.", + "projectsList": "Taşınmamış Projeler", + "migrate": "Taşımayı Başlat", + "migrating": "Taşınıyor...", + "success": "{{count}} proje başarıyla taşındı", + "failed": "Taşıma başarısız", + "checkFailed": "Taşınmamış projeler kontrol edilemedi", + "checkError": "Taşınmamış projeler kontrol edilirken hata", + "starting": "Taşıma görevi başlatılıyor...", + "processing": "Taşıma görevi işleniyor...", + "completed": "Taşıma tamamlandı", + "startFailed": "Taşıma görevi başlatılamadı", + "statusFailed": "Taşıma durumu alınamadı", + "taskNotFound": "Taşıma görevi bulunamadı", + "progressStatus": "{{completed}}/{{total}} proje taşındı", + "openDirectory": "Dizini Aç", + "deleteDirectory": "Dizini Sil", + "confirmDelete": "Bu proje dizinini silmek istediğinizden emin misiniz? Bu işlem geri alınamaz.", + "openDirectoryFailed": "Proje dizini açılamadı", + "deleteDirectoryFailed": "Proje dizini silinemedi" + }, + "distill": { + "title": "Damıt", + "generateRootTags": "Kök Etiketler Oluştur", + "generateSubTags": "Alt Etiketler Oluştur", + "generateQuestions": "Soru Oluştur", + "generateRootTagsTitle": "Kök Alan Etiketleri Oluştur", + "generateSubTagsTitle": "{{parentTag}} için Alt Etiketler Oluştur", + "generateQuestionsTitle": "{{tag}} için Sorular Oluştur", + "parentTag": "Üst Etiket", + "parentTagPlaceholder": "Üst etiket adını girin (örn., Spor, Teknoloji)", + "parentTagHelp": "Bir alan konusu girin, sistem buna göre ilgili etiketler oluşturacaktır", + "generateQuestionsError": "Sorular oluşturulamadı", + "tagCount": "Etiket Sayısı", + "tagCountHelp": "Oluşturulacak etiket sayısını girin, maksimum 100", + "questionCount": "Soru Sayısı", + "questionCountHelp": "Oluşturulacak soru sayısını girin, maksimum 100", + "generatedTags": "Oluşturulan Etiketler", + "generatedQuestions": "Oluşturulan Sorular", + "tagPath": "Etiket Yolu", + "noTags": "Etiket Yok", + "noQuestions": "Soru Yok", + "clickGenerateButton": "Etiket oluşturmak için yukarıdaki oluştur butonuna tıklayın", + "selectModelFirst": "Lütfen önce bir model seçin", + "selectModel": "Model Seç", + "generateTagsError": "Etiketler oluşturulamadı", + "generateTags": "Etiket Oluştur", + "subTags": "alt-etiket", + "questions": "soru", + "deleteTagConfirmTitle": "Etiketi silmeyi onayla?", + "editTagTitle": "Etiketi Düzenle", + "tagName": "Etiket Adı", + "labelRequired": "Etiket adı boş olamaz", + "tagUpdateSuccess": "Etiket başarıyla güncellendi", + "tagUpdateFailed": "Etiket güncellenemedi", + "unknownTag": "Bilinmeyen Etiket", + "autoDistillButton": "Otomatik Damıtma Veri Seti", + "autoDistillTitle": "Otomatik Veri Seti Damıtma Yapılandırması", + "distillTopic": "Damıtma Konusu", + "tagLevels": "Etiket Seviyeleri", + "tagLevelsHelper": "Seviye sayısını ayarlayın, maksimum {{max}}", + "tagsPerLevel": "Seviye Başına Etiket", + "tagsPerLevelHelper": "Her üst etiket altında oluşturulacak alt etiket sayısı, maksimum {{max}}", + "questionsPerTag": "Etiket Başına Soru", + "questionsPerTagHelper": "Her yaprak etiket için oluşturulacak soru sayısı, maksimum {{max}}", + "estimationInfo": "Görev Tahmin Bilgisi", + "estimatedTags": "Tahmini Etiketler", + "estimatedQuestions": "Tahmini Sorular", + "currentTags": "Mevcut Etiketler", + "currentQuestions": "Mevcut Sorular", + "newTags": "Yeni Etiketler", + "newQuestions": "Yeni Sorular", + "startAutoDistill": "Otomatik Damıtmayı Başlat", + "autoDistillProgress": "Otomatik Damıtma İlerlemesi", + "overallProgress": "Genel İlerleme", + "tagsProgress": "Etiket Oluşturma İlerlemesi", + "questionsProgress": "Soru Üretme İlerlemesi", + "currentStage": "Mevcut Aşama", + "realTimeLogs": "Gerçek Zamanlı Günlükler", + "waitingForLogs": "Günlükler bekleniyor...", + "autoDistillStarted": "{{time}} Otomatik damıtma görevi başladı", + "autoDistillInsufficientError": "Mevcut yapılandırma yeni etiket veya soru üretmeyecek, lütfen parametreleri ayarlayın", + "stageInitializing": "Başlatılıyor...", + "stageBuildingLevel1": "Seviye 1 Etiketleri Oluşturuluyor", + "stageBuildingLevel2": "Seviye 2 Etiketleri Oluşturuluyor", + "stageBuildingLevel3": "Seviye 3 Etiketleri Oluşturuluyor", + "stageBuildingLevel4": "Seviye 4 Etiketleri Oluşturuluyor", + "stageBuildingLevel5": "Seviye 5 Etiketleri Oluşturuluyor", + "stageBuildingQuestions": "Sorular Oluşturuluyor", + "stageBuildingDatasets": "Veri Setleri Oluşturuluyor", + "stageCompleted": "Görev Tamamlandı", + "datasetsProgress": "Veri Setleri İlerlemesi", + "rootTopicHelperText": "Varsayılan olarak proje adı üst düzey damıtma konusu olarak kullanılır. Değiştirmek isterseniz, lütfen proje ayarlarına giderek proje adını değiştirin.", + "addChildTag": "Alt Etiket Ekle", + "datasetType": "Veri Seti Türü", + "singleTurnDataset": "Tek Turlu Veri Seti", + "multiTurnDataset": "Çok Turlu Veri Seti", + "bothDatasetTypes": "Her İki Veri Seti Türünü Oluştur", + "autoDistillTaskDetail": "Otomatik Damıtma Görevi: {{topic}}", + "backgroundTaskCreated": "Arka plan damıtma görevi oluşturuldu. İlerlemeyi görev yönetiminde kontrol edebilirsiniz.", + "backgroundTaskFailed": "Arka plan görevi oluşturulamadı", + "taskExecutionError": "Görev yürütme hatası: {{error}}" + }, + "tasks": { + "pending": "{{count}} görev işleniyor", + "completed": "görevler tamamlandı", + "title": "Görev Yönetim Merkezi", + "loading": "Görevler yükleniyor...", + "empty": "Görev bulunamadı", + "confirmDelete": "Bu görevi silmek istediğinizden emin misiniz?", + "confirmAbort": "Bu görevi iptal etmek istediğinizden emin misiniz? Görev durdurulacaktır.", + "deleteSuccess": "Görev silindi", + "deleteFailed": "Görev silinemedi", + "abortSuccess": "Görev iptal edildi", + "abortFailed": "Görev iptal edilemedi", + "status": { + "processing": "İşleniyor", + "completed": "Tamamlandı", + "failed": "Başarısız", + "aborted": "İptal Edildi", + "unknown": "Bilinmiyor" + }, + "types": { + "text-processing": "Metin İşleme", + "question-generation": "Soru Üretimi", + "answer-generation": "Cevap Üretimi", + "data-distillation": "Veri Damıtma", + "pdf-processing": "PDF İşleme" + }, + "filters": { + "status": "Görev Durumu", + "type": "Görev Türü" + }, + "actions": { + "refresh": "Görev listesini yenile", + "delete": "Görevi sil", + "abort": "Görevi iptal et" + }, + "table": { + "type": "Tür", + "status": "Durum", + "progress": "İlerleme", + "success": "Başarılı", + "failed": "Başarısız", + "createTime": "Oluşturuldu", + "endTime": "Tamamlandı", + "duration": "Süre", + "model": "Model", + "detail": "Detaylar", + "actions": "İşlemler" + }, + "duration": { + "seconds": "{{seconds}}s", + "minutes": "{{minutes}}d {{seconds}}s", + "hours": "{{hours}}s {{minutes}}d" + }, + "fetchFailed": "Görev listesi getirilemedi", + "createSuccess": "Görev başarıyla oluşturuldu", + "createFailed": "Görev oluşturulamadı", + "multiTurnCreateSuccess": "Çok turlu konuşma veri seti görevi başarıyla oluşturuldu" + }, + "gaPairs": { + "title": "Tür-Kitle Çiftleri Yönetimi", + "loading": "GA çiftleri yükleniyor...", + "addPair": "GA Çifti Ekle", + "saveChanges": "Değişiklikleri Kaydet", + "saving": "Kaydediliyor...", + "restoreBackup": "Yedeği Geri Yükle", + "noGaPairsTitle": "Tür-Kitle Çifti Bulunamadı", + "noGaPairsDescription": "Bu dosya için AI destekli Tür-Kitle çiftleri oluştur", + "generateGaPairs": "Tür-Kitle Çiftleri Oluştur", + "generating": "Oluşturuluyor...", + "generateMore": "Daha Fazla Tür-Kitle Çifti Oluştur", + "activePairs": "Aktif Tür-Kitle Çiftleri ({{active}}/{{total}})", + "pairNumber": "Tür-Kitle Çifti #{{number}}", + "active": "Aktif", + "deleteTooltip": "GA Çiftini Sil", + "genre": "Tür", + "genreDescription": "Tür Açıklaması", + "audience": "Kitle", + "audienceDescription": "Kitle Açıklaması", + "addDialogTitle": "Yeni Tür-Kitle Çifti Ekle", + "genreTitle": "Tür Başlığı", + "audienceTitle": "Kitle Başlığı", + "genreTitlePlaceholder": "Tür başlığını girin...", + "genreDescPlaceholder": "Türü detaylı olarak açıklayın...", + "audienceTitlePlaceholder": "Kitle başlığını girin...", + "audienceDescPlaceholder": "Hedef kitleyi detaylı olarak açıklayın...", + "cancel": "İptal", + "addPairButton": "Tür-Kitle Çifti Ekle", + "requiredFields": "Tür Başlığı ve Kitle Başlığı gereklidir", + "restoredFromBackup": "Yedekten geri yüklendi", + "allPairsDeleted": "Tüm GA çiftleri başarıyla silindi", + "pairsSaved": "{{count}} GA çifti başarıyla kaydedildi", + "additionalPairsGenerated": "{{count}} ek Tür-Kitle çifti başarıyla oluşturuldu. Toplam: {{total}}", + "validationError": "GA çifti {{number}}: Tür ve Kitle başlıkları gereklidir", + "loadError": "GA çiftleri yüklenemedi: {{error}}", + "generateError": "GA çiftleri oluşturulamadı", + "saveError": "GA çiftleri kaydedilemedi", + "noActiveModel": "GA çiftleri oluşturmadan önce ayarlarda bir AI modeli yapılandırın.", + "contentTooShort": "Dosya içeriği çok kısa veya GA çifti oluşturma için uygun değil.", + "configError": "AI model yapılandırma hatası. Gerekli bağımlılıklar yüklenmemiş olabilir.", + "serverError": "Sunucu hatası ({{status}}). Lütfen daha sonra tekrar deneyin.", + "emptyResponse": "Oluşturma servisinden boş yanıt", + "generationFailed": "Oluşturma başarısız", + "saveOperationFailed": "Kaydetme işlemi başarısız", + "serviceNotAvailable": "GA Çiftleri oluşturma servisi kullanılamıyor. Lütfen API yapılandırmanızı kontrol edin.", + "requestFailed": "İstek başarısız ({{status}}). Lütfen tekrar deneyin.", + "internalServerError": "İç sunucu hatası oluştu.", + "batchGenerate": "Toplu GA Çifti Oluştur", + "batchGenerateDescription": "Seçilen {{count}} dosya için toplu GA çiftleri oluşturulacaktır. Bu işlem biraz zaman alabilir.", + "appendMode": "Ekleme Modu", + "appendModeDescription": "Zaten GA çiftleri olan dosyalar için üzerine yazmak yerine ek GA çiftleri oluştur", + "selectAtLeastOneFile": "Lütfen önce en az bir dosya seçin", + "noDefaultModel": "Varsayılan model ayarlanmamış, lütfen önce proje ayarlarında bir model yapılandırın", + "incompleteModelConfig": "Model yapılandırması eksik, lütfen model ayarlarını kontrol edin", + "missingApiKey": "Model API anahtarı yapılandırılmamış, lütfen model ayarlarında API anahtarı ekleyin", + "loadingProjectModel": "Proje modeli yükleniyor...", + "usingModel": "Kullanılan model", + "startGeneration": "Oluşturmayı Başlat", + "batchGenCompleted": "Toplu oluşturma tamamlandı! {{success}}/{{total}} dosya için başarıyla GA çiftleri oluşturuldu.", + "generationError": "Oluşturma sırasında hata oluştu: {{error}}", + "fetchProjectInfoFailed": "Proje bilgisi getirilemedi: {{status}}", + "fetchModelConfigFailed": "Model yapılandırması getirilemedi: {{status}}", + "fetchProjectModelError": "Proje model yapılandırması getirilirken hata", + "batchGenerationFailed": "Toplu GA çifti oluşturma başarısız", + "batchGenerationSuccess": "{{count}} dosya için başarıyla GA çiftleri oluşturuldu", + "selectAllFiles": "Tümünü Seç", + "deselectAllFiles": "Tümünü Kaldır" + }, + "batchEdit": { + "title": "Toplu Metin Bloğu Düzenleme", + "batchEdit": "Toplu Düzenle", + "batchEditTooltip": "Seçilen metin bloklarını toplu düzenle", + "position": "Ekleme Konumu", + "atBeginning": "Başa Ekle", + "atEnd": "Sona Ekle", + "contentToAdd": "Eklenecek İçerik", + "contentPlaceholder": "Metin bloklarına eklenecek içeriği girin...", + "contentRequired": "Lütfen eklenecek içeriği girin", + "contentHelp": "Bu içerik tüm seçilen metin bloklarına eklenecektir", + "preview": "Önizleme", + "allChunksSelected": "Tüm {{count}} metin bloğu seçildi", + "selectedChunks": "{{selected}} / {{total}} metin bloğu seçildi", + "processing": "İşleniyor...", + "applyToChunks": "{{count}} bloğa uygula", + "editSuccess": "{{count}} metin bloğu başarıyla düzenlendi", + "editFailed": "Toplu düzenleme başarısız", + "previewNote": "Yukarıdaki, seçilen ilk metin bloğunun önizlemesidir. Tüm seçilen metin blokları aynı değişikliği alacaktır" + }, + "errors": { + "projectIdRequired": "Proje ID'si boş olamaz", + "getDatasetsFailed": "Veri setleri alınamadı", + "getTagStatsFailed": "Etiket istatistikleri alınamadı", + "deleteFileFailed": "Dosya silinirken hata", + "recordNotFound": "Mevcut kayıt mevcut değil", + "mineruTokenNotFound": "Token yapılandırması bulunamadı, lütfen görev ayarlarında MinerU token yapılandırıldığını kontrol edin", + "mineruLocalUrlNotFound": "MinerU yerel URL yapılandırması bulunamadı, lütfen görev ayarlarında MinerU yerel URL yapılandırıldığını kontrol edin" + }, + "sampleData": { + "questionContent": "Soru içeriği", + "answerContent": "Cevap içeriği", + "cotContent": "Düşünce zinciri içeriği", + "domainLabel": "Alan etiketi", + "textChunk": "Metin bloğu" + }, + "exportDialog": { + "balancedExport": "Dengeli Dışa Aktarma", + "balancedExportTitle": "Dengeli Dışa Aktarma Ayarları", + "balancedExportDescription": "Dengeli veri seti dışa aktarımı elde etmek için alan etiketlerine göre her kategori için veri miktarını yapılandırın", + "quickSettings": "Hızlı Ayarlar", + "setAllTo50": "Tümünü 50'ye ayarla", + "setAllTo100": "Tümünü 100'e ayarla", + "setAllTo200": "Tümünü 200'e ayarla", + "customAmount": "Özel miktar", + "tagName": "Etiket Adı", + "availableCount": "Mevcut Sayı", + "exportCount": "Dışa Aktarma Sayısı", + "settings": "Ayarlar", + "totalExportCount": "Toplam dışa aktarma sayısı", + "tagCount": "Etiket sayısı", + "export": "Dışa Aktar" + }, + "imageDatasets": { + "title": "Görsel S&C Veri Seti", + "subtitle": "Görsel S&C veri setlerinizi yönetin ve optimize edin", + "description": "Görsel S&C veri setlerinizi yönetin ve optimize edin.", + "searchPlaceholder": "Soruları veya cevapları ara...", + "noAnswer": "Cevap yok", + "labels": "Etiketler", + "typeLabel": "Etiket", + "typeCustom": "Özel", + "typeText": "Metin", + "unscored": "Puanlanmamış", + "confirmed": "Onaylandı", + "unconfirmed": "Onaylanmadı", + "view": "Detayları Görüntüle", + "evaluate": "Kalite Değerlendirmesi", + "delete": "Sil", + "deleteConfirm": "Bu veri setini silmek istediğinizden emin misiniz?", + "imageName": "Görsel Adı", + "status": "Durum", + "scoreRange": "Puan Aralığı", + "noData": "Görsel veri seti yok", + "noDataTip": "Lütfen önce Görsel Yönetimi'nde S&C veri setleri oluşturun", + "fetchFailed": "Veri setleri getirilemedi", + "fetchDetailFailed": "Detaylar getirilemedi", + "deleteSuccess": "Başarıyla silindi", + "deleteFailed": "Silme başarısız", + "updateSuccess": "Başarıyla güncellendi", + "updateFailed": "Güncelleme başarısız", + "regenerateSuccess": "AI tanıma başarılı", + "regenerateFailed": "AI tanıma başarısız", + "notFound": "Veri seti bulunamadı", + "detail": "Detay", + "image": "Görsel", + "question": "Soru", + "answer": "Cevap", + "selectLabels": "Etiketleri Seç", + "noLabels": "Etiket seçilmedi", + "jsonPlaceholder": "JSON format verisi girin...", + "metadata": "Meta Veri", + "score": "Puan", + "tags": "Etiketler", + "addTag": "Etiket ekle...", + "note": "Not", + "notePlaceholder": "Not ekle...", + "modelInfo": "Model Bilgisi", + "createdAt": "Oluşturulma Tarihi", + "updatedAt": "Güncellenme Tarihi", + "exportTitle": "Görsel Veri Setini Dışa Aktar", + "exportFormat": "Dışa Aktarma Formatı", + "rawFormat": "Ham Format", + "customFormat": "Özel Format", + "exportImagesOption": "Görsel Dosyalarını Dışa Aktar", + "exportImagesDesc": "Tüm görselleri indirmek için ZIP dosyasına paketle", + "includeImagePath": "Veri Setine Görsel Yolunu Dahil Et", + "includeImagePathDesc": "Soru veya cevapta görsel yolu ekle (format: /images/görsel_adı)", + "systemPrompt": "Sistem İstemi (İsteğe Bağlı)", + "systemPromptPlaceholder": "Sistem istemi girin...", + "confirmedOnly": "Sadece Onaylananları Dışa Aktar", + "exportTip": "Etiket formatı cevapları otomatik olarak metne (virgülle ayrılmış) ayrıştırılacaktır", + "exportSuccess": "Veri seti başarıyla dışa aktarıldı", + "exportFailed": "Dışa aktarma başarısız", + "noDataToExport": "Dışa aktarılacak veri yok", + "exportImagesSuccess": "Görsel ZIP paketi başarıyla dışa aktarıldı", + "exportImagesFailed": "Görseller dışa aktarılamadı" + }, + "images": { + "resolution": "Çözünürlük", + "uploadTime": "Yükleme Zamanı", + "fileName": "Dosya Adı", + "title": "Görsel Yönetimi", + "importImages": "Görselleri İçe Aktar", + "searchPlaceholder": "Görsel adı ara...", + "hasQuestions": "Soru Durumu", + "hasDatasets": "Veri Seti Durumu", + "withQuestions": "Sorulu", + "withoutQuestions": "Sorusuz", + "withDatasets": "Veri Setli", + "withoutDatasets": "Veri Setsiz", + "noImages": "Görsel yok", + "questions": "Sorular", + "datasets": "Veri Setleri", + "generateQuestions": "Soru Oluştur", + "generateDataset": "Veri Seti Oluştur", + "deleteConfirm": "Bu görseli silmek istediğinizden emin misiniz?", + "deleteSuccess": "Başarıyla silindi", + "deleteFailed": "Silme başarısız", + "importTip": "Görsel içeren bir veya daha fazla dizin seçin. Tüm görseller projeye içe aktarılacaktır (yinelenen adlar üzerine yazılacaktır)", + "selectDirectory": "Dizin Seç", + "directoryPath": "Dizin Yolu", + "enterDirectoryPath": "örn., /Users/kullanıcıadı/Resimler", + "selectedDirectories": "Seçilen Dizinler", + "selectAtLeastOne": "Lütfen en az bir dizin seçin", + "importSuccess": "{{count}} görsel başarıyla içe aktarıldı", + "importFailed": "İçe aktarma başarısız", + "startImport": "İçe Aktarmayı Başlat", + "addDirectory": "Dizin Ekle", + "importFromDirectory": "Dizinden İçe Aktar", + "importFromPdf": "PDF'den İçe Aktar", + "pdfImportTip": "PDF dosyası seçin, sistem otomatik olarak görsellere dönüştürüp içe aktaracaktır", + "clickToSelectPdf": "PDF dosyası seçmek için tıklayın", + "supportedFormat": "Desteklenen format: PDF", + "fileSize": "Dosya boyutu", + "selectedFile": "Seçilen dosya", + "invalidPdfFile": "Lütfen geçerli bir PDF dosyası seçin", + "selectPdfFile": "Lütfen bir PDF dosyası seçin", + "pdfImportSuccess": "\"{{name}}\" PDF'sinden {{count}} görsel başarıyla içe aktarıldı", + "pdfImportFailed": "PDF içe aktarma başarısız", + "convertAndImport": "Dönüştür ve İçe Aktar", + "electronRequired": "Bu özellik masaüstü uygulamasını gerektirir", + "selectDirectoryFailed": "Dizin seçilemedi", + "imageName": "Görsel Adı", + "questionCount": "Soru Sayısı", + "questionCountHelp": "1-10 soru oluştur", + "currentModel": "Mevcut Model", + "selectModelFirst": "Lütfen önce bir model seçin", + "visionModelRequired": "Lütfen görsel yetenekli bir model seçin (örn., GPT-4 Vision, Claude, vb.)", + "countRange": "Soru sayısı 1-10 arasında olmalıdır", + "questionsGenerated": "{{count}} soru başarıyla oluşturuldu", + "generateFailed": "Oluşturma başarısız", + "question": "Soru", + "questionPlaceholder": "Sorunuzu girin...", + "questionRequired": "Lütfen bir soru girin", + "datasetGenerated": "Veri seti başarıyla oluşturuldu", + "autoGenerateQuestions": "Otomatik Soru Oluştur", + "autoGenerateConfirm": "Sistem, sorusu olmayan tüm görseller için otomatik olarak sorular oluşturacaktır. Bu işlem bir arka plan görevi oluşturacaktır, ilerlemeyi Görev Yönetimi'nde görüntüleyebilirsiniz.", + "taskCreated": "Görev başarıyla oluşturuldu, arka planda işleniyor", + "taskCreateFailed": "Görev oluşturulamadı", + "manualAnnotation": "Manuel Etiketleme", + "annotationTitle": "Görsel Etiketleme", + "imageInfo": "Görsel Bilgisi", + "annotatedCount": "Etiketlendi", + "selectQuestion": "Soru Seç veya Oluştur", + "selectQuestionPlaceholder": "Soru şablonu seçin...", + "universalQuestions": "Evrensel Sorular", + "independentQuestions": "Bağımsız Sorular", + "answerTypeText": "Metin", + "answerTypeLabel": "Etiket", + "answerTypeCustomFormat": "Özel Format", + "usedTimes": "{{count}} kez kullanıldı", + "answer": "Cevap", + "answerPlaceholder": "Cevap girin...", + "selectLabels": "Etiketleri Seç", + "availableLabels": "Mevcut Etiketler", + "noLabelsAvailable": "Mevcut etiket yok", + "addNewLabel": "Yeni etiket ekle...", + "selectedLabels": "Seçildi", + "customFormatAnswer": "Özel Format Cevabı", + "formatRequirement": "Format Gereksinimi", + "customFormatPlaceholder": "Gerekli formatta JSON girin...", + "note": "Not", + "notePlaceholder": "Not (isteğe bağlı)", + "saveAndContinue": "Kaydet ve Devam Et", + "noImageSelected": "Görsel seçilmedi", + "noTemplateSelected": "Lütfen bir soru seçin", + "answerRequired": "Lütfen bir cevap girin", + "invalidJsonFormat": "Geçersiz JSON formatı", + "annotationSuccess": "Etiketleme başarıyla kaydedildi", + "annotationFailed": "Etiketleme kaydedilemedi", + "allQuestionsAnnotated": "Bu görsel için tüm sorular etiketlendi", + "allImagesAnnotated": "Tüm görseller için tüm sorular etiketlendi", + "noQuestionsAssociated": "Bu görsel ile ilişkilendirilmiş soru yok", + "loadImageDetailFailed": "Görsel detayları yüklenemedi", + "answeredQuestions": "Etiketlenmiş Sorular", + "useTemplate": "Şablon Kullan", + "formatJson": "Formatla", + "jsonFormatHelp": "Lütfen geçerli JSON format verisi girin", + "imageLoadError": "Görsel yüklenemedi", + "annotateImage": "Görseli Etiketle", + "createQuestion": "Soru Oluştur", + "createTemplate": "Soru Şablonu Oluştur", + "aiGenerate": "AI Tanı", + "aiGenerateSuccess": "AI oluşturma başarılı", + "aiGenerateFailed": "AI oluşturma başarısız", + "missingParameters": "Gerekli parametreler eksik", + "selectNewQuestion": "Yeni Soru Seç", + "fetchTemplatesFailed": "Soru şablonları getirilemedi", + "createTemplateSuccess": "Soru şablonu başarıyla oluşturuldu", + "createTemplateFailed": "Soru şablonu oluşturulamadı", + "updateTemplateSuccess": "Soru şablonu başarıyla güncellendi", + "updateTemplateFailed": "Soru şablonu güncellenemedi", + "deleteTemplateSuccess": "Soru şablonu başarıyla silindi", + "deleteTemplateFailed": "Soru şablonu silinemedi", + "template": { + "management": "Soru Şablonlarını Yönet", + "create": "Şablon Oluştur", + "edit": "Şablonu Düzenle", + "question": "Soru İçeriği", + "description": "Açıklama", + "noTemplates": "Henüz soru şablonu yok, eklemek için oluştur butonuna tıklayın", + "deleteConfirm": "Bu soru şablonunu silmek istediğinizden emin misiniz?", + "used": "Kullanıldı", + "addLabel": "Etiket Ekle", + "customFormat": "Özel Format", + "customFormatHelp": "JSON format çıktı kısıtlaması girin", + "customFormatInfo": "Bu format, çıktı formatını kısıtlamak için LLM'e istem olarak sağlanacaktır", + "type": { + "label": "Soru Türü", + "universal": "Evrensel Soru", + "independent": "Bağımsız Soru" + }, + "answerType": { + "label": "Cevap Türü", + "text": "Metin", + "tags": "Etiketler", + "customFormat": "Özel Format" + }, + "errors": { + "questionRequired": "Lütfen soru içeriğini girin", + "labelsRequired": "Etiket türü sorular en az bir etiket gerektirir", + "customFormatRequired": "Lütfen özel format girin", + "invalidJson": "Geçersiz JSON formatı" + } + } + } +} diff --git a/easy-dataset-main/locales/zh-CN/translation.json b/easy-dataset-main/locales/zh-CN/translation.json new file mode 100644 index 0000000..74101f6 --- /dev/null +++ b/easy-dataset-main/locales/zh-CN/translation.json @@ -0,0 +1,1855 @@ +{ + "language": { + "switchToEnglish": "切换到英文", + "switchToChinese": "切换到中文", + "switcherTitle": "切换语言 / Change Language / Dil Değiştir", + "english": "English", + "chineseSimplified": "简体中文", + "turkish": "Türkçe", + "en": "EN", + "zh": "中" + }, + "theme": { + "switchToLight": "切换到亮色模式", + "switchToDark": "切换到暗色模式" + }, + "settings": { + "promptConfig": "提示词配置", + "promptsDescription": "配置项目中使用的各类自定义提示词,可用于人工干预数据集的生成效果。", + "globalPrompt": "全局提示词", + "questionPrompt": "生成问题提示词", + "answerPrompt": "生成答案提示词", + "labelPrompt": "问题打标提示词", + "domainTreePrompt": "构建领域树提示词", + "globalPromptPlaceholder": "请输入全局提示词(慎用,可能影响整体生成效果)", + "questionPromptPlaceholder": "请输入自定义生成问题的提示词", + "answerPromptPlaceholder": "请输入自定义生成答案的提示词", + "labelPromptPlaceholder": "请输入自定义问题打标的提示词(暂不支持配置)", + "domainTreePromptPlaceholder": "请输入自定义构建领域树的提示词", + "cleanPrompt": "数据清洗提示词", + "cleanPromptPlaceholder": "请输入自定义数据清洗的提示词", + "loadPromptsFailed": "加载提示词配置失败", + "savePromptsSuccess": "保存提示词配置成功", + "savePromptsFailed": "保存提示词配置失败", + "title": "项目设置", + "basicInfo": "基本信息", + "modelConfig": "模型配置", + "taskConfig": "任务配置", + "tabsAriaLabel": "设置选项卡", + "idNotEditable": "项目 ID 不可编辑", + "saveBasicInfo": "保存基本信息", + "saveSuccess": "保存成功", + "saveFailed": "保存失败", + "deleteSuccess": "删除成功", + "deleteFailed": "删除失败", + "fetchTasksFailed": "获取任务配置失败", + "saveTasksFailed": "保存任务配置失败", + "textSplitSettings": "文本分块设置", + "minLength": "最小长度", + "maxLength": "最大分割长度", + "textSplitDescription": "调整文本分割的长度范围,影响分割结果的粒度", + "splitType": "分块策略", + "splitTypeMarkdown": "文档结构分块(Markdown)", + "splitTypeMarkdownDesc": "根据文档中的标题自动分割文本,保持语义完整性,适合结构化清晰的 Markdown 文档", + "splitTypeRecursive": "文本结构分块(自定义分隔符)", + "splitTypeRecursiveDesc": "递归地尝试多级分隔符(可配置),先用优先级高的分隔符,再用次级分隔符,适合复杂文档", + "splitTypeText": "固定长度分块(字符)", + "splitTypeTextDesc": "按指定分隔符(可配置)切分文本,然后按指定长度组合,适合普通文本文件", + "splitTypeToken": "固定长度分块(Token)", + "splitTypeTokenDesc": "基于 Token 数量(而非字符数)分块", + "splitTypeCode": "程序代码智能分块", + "splitTypeCodeDesc": "根据不同编程语言的语法结构进行智能分块,避免在语法不完整处分割", + "splitTypeCustom": "自定义符号分块", + "splitTypeCustomDesc": "根据自定义符号进行文档分割,分隔符将被舍弃,分割的文本块不受块大小影响", + "codeLanguage": "代码语言", + "codeLanguageHelper": "选择要分块的代码语言,会根据语言特性进行智能分块", + "chunkSize": "块大小", + "chunkOverlap": "块重叠长度", + "separator": "分隔符", + "separatorHelper": "用于分割文本的分隔符,如 \n\n 表示空行", + "customSeparator": "自定义分隔符", + "customSeparatorHelper": "用于分割文本的自定义分符,如 --- 或 ===", + "separators": "分隔符列表", + "separatorsInput": "分隔符(逗号分隔)", + "separatorsHelper": "用逗号分隔的分隔符列表,按优先级排序", + "questionGenSettings": "问题生成设置", + "questionGenLength": "{{length}} 个字符生成一个问题", + "questionMaskRemovingProbability": "将 {{probability}}% 问题结尾的问号去除", + "questionGenDescription": "设置生成问题的最大长度", + "concurrencyLimit": "并发限制数量", + "concurrencyLimitHelper": "限制同时生成问题、生成数据集的任务数量", + "saveTaskConfig": "保存任务配置", + "pdfSettings": "PDF文件转换配置", + "minerUToken": "PDF 转换(MinerU API)Token 配置", + "minerUHelper": "MinerU Token 只有14天有效期,请及时更换Token", + "minerULocalUrl": "PDF 转换(MinerU Local)URL 配置", + "vision": "自定义视觉模型选择", + "visionConcurrencyLimit": "视觉模型并发限制", + "huggingfaceSettings": "Hugging Face 设置", + "huggingfaceToken": "Hugging Face Token", + "multiTurnSettings": "多轮对话数据集设置", + "multiTurnSystemPrompt": "系统提示词", + "multiTurnSystemPromptHelper": "设定AI助手的身份和行为规范", + "multiTurnScenario": "对话场景", + "multiTurnScenarioHelper": "描述对话的具体场景和目标", + "multiTurnRounds": "对话轮数:{{rounds}} 轮", + "multiTurnRoleA": "角色A设定(用户)", + "multiTurnRoleAHelper": "定义用户角色的身份和特征", + "multiTurnRoleB": "角色B设定(助手)", + "multiTurnRoleBHelper": "定义助手角色的身份和特征", + "multiTurnDescription": "多轮对话配置用于生成连贯的多轮对话数据集,支持自定义角色和场景", + "evalQuestionSettings": "测试集生成设置", + "evalQuestionSettingsDescription": "配置生成测试集时各题型的比例,比例为0表示不生成该类型题目", + "evalTrueFalseRatio": "判断题比例", + "evalSingleChoiceRatio": "单选题比例", + "evalMultipleChoiceRatio": "多选题比例", + "evalShortAnswerRatio": "固定短答案比例", + "evalOpenEndedRatio": "开放式回答比例", + "evalQuestionRatioHelper": "系统会根据设置的比例自动分配各题型的生成数量,所有比例之和不需要等于特定值", + "prompts": { + "selectPromptFirst": "请在左侧选择一个提示词", + "customized": "已自定义", + "editPrompt": "编辑提示词", + "restoreDefault": "恢复默认", + "promptType": "提示词类型", + "keyName": "键名", + "contentPlaceholder": "请输入自定义提示词内容...", + "restoreDefaultContent": "恢复默认内容", + "noPromptsAvailable": "没有可用的提示词", + "restoreSuccess": "已恢复为默认提示词", + "restoreFailed": "恢复默认提示词失败", + "deleteError": "删除提示词出错:", + "saveSuccess": "提示词保存成功", + "saveFailed": "提示词保存失败", + "saveError": "保存提示词出错:", + "createCustomPrompt": "创建自定义提示词", + "fetchContentError": "获取最新提示词内容失败:" + } + }, + "questions": { + "autoGenerateDataset": "自动生成数据集", + "autoGenerateDatasetTip": "创建后台批量处理任务:自动查询待生成答案的问题并生成数据集", + "generateSingleTurnDataset": "生成单轮对话数据集", + "generateSingleTurnDatasetDesc": "基于问题生成问答数据集", + "generateMultiTurnDataset": "生成多轮对话数据集", + "generateImageDataset": "生成图像问答数据集", + "generateMultiTurnDatasetDesc": "基于问题生成多轮对话数据集", + "multiTurnNotConfigured": "请先在项目设置中配置多轮对话相关参数", + "filterAll": "全部问题", + "filterAnswered": "已生成答案", + "filterUnanswered": "未生成答案", + "filterChunkNamePlaceholder": "按文本块名称筛选...", + "sourceTypeAll": "全部数据源", + "sourceTypeText": "文本数据源", + "sourceTypeImage": "图片数据源", + "title": "问题", + "confirmDeleteTitle": "确认删除问题", + "confirmDeleteContent": "您确定要删除问题\"{{question}}\"吗?此操作不可恢复。", + "deleting": "正在删除问题...", + "batchDeleteTitle": "确认批量删除问题", + "batchDeleting": "正在删除 {{count}} 个问题...", + "deleteSuccess": "问题删除成功", + "deleteFailed": "删除问题失败", + "batchDeleteSuccess": "成功删除 {{count}} 个问题", + "batchDeletePartial": "删除完成,成功: {{success}}, 失败: {{failed}}", + "batchDeleteFailed": "批量删除问题失败", + "noQuestionsSelected": "请先选择问题", + "batchGenerateStart": "开始生成 {{count}} 个问题的数据集", + "invalidQuestionKey": "无效的问题键", + "listView": "问题列表", + "treeView": "领域树视图", + "selectAll": "全选", + "selectedCount": "已选择 {{count}} 个问题", + "totalCount": "共 {{count}} 个问题", + "searchPlaceholder": "搜索问题或标签...", + "searchMatch": "匹配", + "searchNotMatch": "不匹配", + "deleteSelected": "删除所选", + "batchGenerate": "批量构造数据集", + "generating": "正在生成数据集", + "generatingProgress": "已完成: {{completed}}/{{total}}", + "generatedCount": "已生成 {{count}} 个数据集", + "pleaseWait": "请稍候...", + "selectAllLimitReached": "已选中 {{count}} 条问题(已达最大限制)", + "selectAllFailed": "全选操作失败,请稍后重试", + "createSuccess": "问题创建成功", + "updateSuccess": "问题更新成功", + "operationSuccess": "操作成功", + "operationFailed": "操作失败", + "editQuestion": "编辑问题", + "questionContent": "问题内容", + "sourceType": "数据源类型", + "sourceType.text": "文本", + "sourceType.image": "图片", + "selectChunk": "选择文本块", + "searchChunk": "搜索文本块...", + "selectImage": "选择图片", + "searchImage": "搜索图片...", + "selectTag": "选择标签", + "searchTag": "搜索标签...", + "createQuestion": "创建问题", + "createNormalQuestion": "创建普通问题", + "createQuestionTemplate": "创建问题模板", + "questionPlaceholder": "请输入问题内容", + "noChunkSelected": "请先选择文本块", + "fetchTemplatesFailed": "获取问题模板失败", + "createTemplateSuccess": "问题模板创建成功", + "createTemplateFailed": "创建问题模板失败", + "updateTemplateSuccess": "问题模板更新成功", + "updateTemplateFailed": "更新问题模板失败", + "deleteTemplateSuccess": "问题模板删除成功", + "deleteTemplateFailed": "删除问题模板失败", + "exportQuestions": "导出问题集", + "exportScope": "导出范围", + "exportAll": "导出全部({{count}} 个问题)", + "exportSelected": "导出已选({{count}} 个问题)", + "exportFormat": "导出格式", + "txtFormat": "纯文本(仅问题内容)", + "exportSuccess": "问题集导出成功", + "exportFailed": "问题集导出失败", + "template": { + "management": "问题模板", + "create": "创建问题模板", + "edit": "编辑问题模板", + "question": "问题内容", + "description": "提示词", + "descriptionHelp": "用于在后续 AI 生成这个问题模版相关的答案时,加入到整体提示词中,以用于干预最终答案生成的结果", + "noTemplates": "暂无问题模板,点击创建按钮添加", + "deleteConfirm": "确定要删除这个问题模板吗?", + "used": "已使用", + "addLabel": "添加标签", + "customFormat": "自定义格式", + "customFormatHelp": "输入 JSON 格式的输出约束", + "customFormatInfo": "此格式将作为提示词提供给大模型,用于约束输出格式", + "sourceTypeInfo": "数据源类型", + "sourceType": { + "label": "数据源类型", + "image": "图像(用于对图像生成QA)", + "text": "文本(用于对文本块生成QA)" + }, + "answerType": { + "label": "答案输出格式", + "text": "普通文本", + "tags": "标签数组", + "customFormat": "自定义格式" + }, + "errors": { + "questionRequired": "请输入问题内容", + "labelsRequired": "标签类型问题至少需要一个标签", + "customFormatRequired": "请输入自定义格式", + "invalidJson": "JSON 格式不正确" + }, + "autoGenerate": "创建模板后自动生成问题", + "autoGenerateHelpText": "将为项目中的所有文本块自动创建基于此模板的问题", + "autoGenerateHelpImage": "将为项目中的所有图片自动创建基于此模板的问题", + "confirmAutoGenerate": "确认自动生成问题", + "confirmAutoGenerateTextMessage": "您选择了自动生成问题。系统将为项目中的所有文本块创建基于此模板的问题。", + "confirmAutoGenerateImageMessage": "您选择了自动生成问题。系统将为项目中的所有图片创建基于此模板的问题。", + "autoGenerateWarning": "此操作可能会创建大量问题,请确认后继续。", + "autoGenerateSuccess": "成功为 {{count}} 个数据源创建了问题", + "autoGeneratePartialFail": "成功创建 {{success}} 个问题,{{fail}} 个失败", + "autoGenerateFailed": "自动生成问题失败" + }, + "noTagSelected": "请选择标签", + "deleteConfirm": "确认要删除这个问题吗?", + "generateMultiTurn": "生成多轮对话", + "multiTurnGenerated": "多轮对话数据集生成成功!" + }, + "common": { + "dataSource": "数据源", + "menu": "菜单", + "openMenu": "打开导航菜单", + "all": "全部", + "jumpTo": "跳转至", + "unknownError": "未知错误", + "create": "创建", + "confirm": "确认", + "edit": "编辑", + "delete": "删除", + "save": "保存", + "cancel": "取消", + "complete": "完成", + "close": "关闭", + "add": "添加", + "remove": "删除", + "loading": "加载中...", + "yes": "是", + "no": "否", + "confirmDelete": "确认删除吗?此操作不可撤销!", + "saving": "保存中...", + "deleting": "删除中...", + "actions": "操作", + "confirmDeleteDataSet": "确认删除数据集吗?操作不可撤销!", + "noData": "无", + "failed": "失败", + "success": "成功", + "backToList": "返回列表", + "label": "标签", + "confirmDeleteDescription": "确认删除吗?此操作不可恢复。", + "more": "更多", + "import": "导入", + "export": "导出", + "fetchError": "获取数据出错", + "confirmDeleteQuestion": "确认删除此问题吗?此操作不可恢复。", + "deleteSuccess": "删除成功", + "visitGitHub": "访问GitHub仓库", + "syncOldData": "同步文件数据", + "copy": "复制", + "copied": "已复制", + "enabled": "已启用", + "disabled": "已禁用", + "generating": "正在生成...", + "processing": "处理中...", + "items": "项", + "detailInfo": "详细信息", + "reset": "重置", + "apply": "应用", + "mainNavigation": "主导航", + "goHome": "回到首页", + "goToHomePage": "回到首页", + "mobileNavigation": "移动端导航菜单", + "navigation": "导航", + "closeMenu": "关闭菜单", + "documentation": "文档", + "viewOnGitHub": "在 GitHub 上查看", + "back": "返回", + "refresh": "刷新", + "expand": "展开全部", + "collapse": "收起内容" + }, + "home": { + "title": "Easy Dataset", + "subtitle": "一个强大的大型语言模型微调数据集创建工具", + "createProject": "创建项目", + "searchDataset": "搜索公开数据集" + }, + "projects": { + "reuseConfig": "复用模型配置", + "noReuse": "不复用配置", + "selectProject": "选择项目", + "fetchFailed": "获取项目列表失败", + "fetchError": "获取项目列表出错", + "loading": "正在加载您的项目...", + "createFailed": "创建项目失败", + "createError": "创建项目出错", + "createNew": "创建新项目", + "saveFailed": "保存项目失败", + "id": "项目ID", + "name": "项目名称", + "description": "项目描述", + "questions": "问题", + "datasets": "数据集", + "evalDatasets": "评估集", + "tokens": "Tokens", + "lastUpdated": "最后更新", + "viewDetails": "查看详情", + "createFirst": "创建第一个项目", + "noProjects": "暂无项目", + "notExist": "项目不存在", + "createProject": "创建项目", + "deleteConfirm": "确认删除项目吗?此操作不可恢复。", + "deleteSuccess": "项目删除成功", + "deleteFailed": "删除项目失败", + "backToHome": "返回首页", + "deleteConfirmTitle": "确认删除项目", + "title": "项目管理", + "openDirectory": "打开项目目录" + }, + "textSplit": { + "dragToUpload": "拖拽文件到此处上传", + "fileList": "文件列表", + "autoGenerateQuestions": "自动提取问题", + "autoGenerateQuestionsTip": "创建后台批量处理任务:自动查询待生成问题的文本块并提取问题", + "exportChunks": "导出文本块", + "allChunks": "全部文本块", + "generatedQuestions2": "已生成问题", + "ungeneratedQuestions": "未生成问题", + "contentKeyword": "文本块内容", + "contentKeywordPlaceholder": "输入关键词搜索文本块内容", + "characterRange": "字数范围", + "questionStatus": "问题状态", + "noFilesUploaded": "暂未上传文件", + "unknownFile": "未知文件", + "fetchFilesFailed": "获取文件列表出错", + "editTag": "编辑标签", + "deleteTag": "删除标签", + "addTag": "添加标签", + "selectedCount": "已选择 {{count}} 个文本块", + "totalCount": "共 {{count}} 个文本块", + "batchGenerateQuestions": "批量生成问题", + "batchDeleteChunks": "批量删除", + "batchDeleteChunksConfirmTitle": "确认批量删除", + "batchDeleteChunksConfirmMessage": "您确定要删除选中的 {{count}} 个文本块吗?此操作不可恢复。", + "uploadedDocuments": "已上传 {{count}}个文档", + "title": "文件处理", + "uploadNewDocument": "上传新文件", + "selectFile": "选择文件(支持多个)", + "markdownOnly": "目前仅支持上传 Markdown (.md) 格式文件(建议上传同一领域的文件)", + "supportedFormats": "支持的格式: .pdf .md, .txt, .docx(建议上传同一领域的文件)", + "uploadAndProcess": "上传并处理文件", + "selectedFiles": "已选择文件({{count}})", + "oneFileMessage": "一个项目限制处理一个文件,如需上传新文件请先删除现有文件", + "mutilFileMessage": "上传新文件后会重新构建领域树", + "noChunks": "暂无文本块,请先上传并处理文件", + "chunkDetails": "文本块详情: {{chunkId}}", + "fetchChunksFailed": "获取文本块失败", + "fetchChunksError": "获取文本块出错", + "fileResultReceived": "获取到文件结果", + "fileUploadSuccess": "文件上传成功", + "splitTextFailed": "文本分割失败", + "splitTextError": "文本分割出错", + "deleteChunkFailed": "删除文本块失败", + "deleteChunkError": "删除文本块出错", + "selectModelFirst": "请先选择一个模型,可以在顶部导航栏选择", + "modelNotAvailable": "选择的模型不可用,请重新选择", + "generateQuestionsFailed": "为文本块 {{chunkId}} 生成问题失败", + "questionsGenerated": "已生成 {{total}} 个问题", + "customSplitMode": "自定义分块模式", + "customSplitInstructions": "选择文本内容以添加分块点。系统会在选中位置添加分割标记。", + "splitPointsList": "已添加的分块点", + "saveSplitPoints": "保存分块点", + "confirmCustomSplitTitle": "确认替换原有分块", + "confirmCustomSplitMessage": "注意:自定义分块将替换该文件之前自动分块的结果。确定要继续保存吗?", + "customSplitSuccess": "自定义分块保存成功", + "customSplitFailed": "自定义分块保存失败", + "missingRequiredData": "缺少必要的数据", + "chunksPreview": "分块字数预览", + "chunk": "文本块", + "characters": "字", + "questionsGeneratedSuccess": "成功为文本块生成了 {{total}} 个问题", + "generateQuestionsForChunkFailed": "为文本块 {{chunkId}} 生成问题失败", + "generateQuestionsForChunkError": "为文本块 {{chunkId}} 生成问题出错", + "generateQuestionsError": "生成问题出错", + "partialSuccess": "部分文本块生成问题成功 ({{successCount}}/{{total}}),{{errorCount}} 个文本块失败", + "allSuccess": "成功为 {{successCount}} 个文本块生成了 {{totalQuestions}} 个问题", + "fileDeleted": "文件 {{fileName}} 已删除,刷新文本块列表", + "tabs": { + "smartSplit": "智能分割", + "domainAnalysis": "领域分析" + }, + "loading": "加载中...", + "fetchingDocuments": "正在获取文件数据", + "processing": "处理中...", + "progressStatus": "已选择 {{total}} 个文本块,已处理完成 {{completed}} 个", + "processingPleaseWait": "正在努力处理中,请稍候!", + "oneFileLimit": "已有上传文件,不允许选择新文件", + "unsupportedFormat": "不支持的文件格式: {{files}}", + "modelInfoParseError": "解析模型信息失败", + "uploadFailed": "上传失败,请刷新页面后重试!", + "uploadSuccess": "成功上传 {{count}} 个文件", + "deleteFailed": "删除文件失败", + "deleteSuccess": "文件 {{fileName}} 已成功删除", + "generatedQuestions": "已生成 {{count}} 个问题", + "generatedEvalQuestions": "已生成 {{count}} 道测试题", + "generateQuestions": "生成问题", + "generateEvalQuestions": "生成测试集", + "evalQuestionsGeneratedSuccess": "成功生成 {{total}} 道测评题目", + "generateEvalQuestionsFailed": "生成测评题目失败", + "dataCleaning": "数据清洗", + "batchDataCleaning": "批量数据清洗", + "autoDataCleaning": "自动数据清洗", + "autoDataCleaningTip": "创建后台批量处理任务:自动对所有文本块进行数据清洗", + "autoEvalGeneration": "自动生成评估集", + "autoEvalGenerationTip": "创建后台批量处理任务:自动为所有未生成评估题目的文本块生成评估数据集", + "autoTasks": "自动任务", + "dataCleaningSuccess": "数据清洗完成,原长度: {{originalLength}},清洗后长度: {{cleanedLength}}", + "dataCleaningFailed": "为文本块 {{chunkId}} 数据清洗失败", + "dataCleaningForChunkSuccess": "文本块 {{chunkId}} 数据清洗完成", + "dataCleaningForChunkFailed": "为文本块 {{chunkId}} 数据清洗失败", + "dataCleaningForChunkError": "为文本块 {{chunkId}} 数据清洗出错", + "dataCleaningPartialSuccess": "部分文本块数据清洗成功 ({{successCount}}/{{total}}),{{errorCount}} 个文本块失败", + "dataCleaningAllSuccess": "成功为 {{successCount}} 个文本块完成数据清洗", + "charsCount": "字符", + "pdfProcess": "检测到PDF文件,请选择 PDF 文件处理方式!", + "pdfProcessStatus": "共 {{total}} 个文件,{{completed}} 个已处理完成", + "pdfPageProcessStatus": "正在处理 {{fileName}} 共{{total}}页,{{completed}}页已完成转换", + "pdfProcessing": "正在转换文件...", + "pdfProcessingFailed": "文件处理失败!", + "selectPdfProcessingStrategy": "请选择PDF文件处理方式:", + "pdfProcessingStrategyDefault": "默认", + "pdfProcessingStrategyDefaultHelper": "使用内置PDF解析策略", + "pdfProcessingStrategyMinerUHelper": "使用MinerU API解析,请先配置MinerU API Token", + "pdfProcessingStrategyVision": "自定义视觉模型", + "pdfProcessingStrategyVisionHelper": "使用自定义视觉模型解析", + "pdfProcessingToast": "上传文件成功,系统将创建后台任务解析文件!", + "pdfProcessingLoading": "正在执行文件处理任务,请等待任务完成后再上传新文件...", + "pdfProcessingWaring": "正在执行文件处理任务,建议当任务完成后再进行其他操作,否则可能会影响数据生成质量!", + "basicPdfParsing": "基础 PDF 解析", + "basicPdfParsingDesc": "可识别简单的 PDF 文件,包括关键目录结构,速度更快", + "mineruApiDesc": "可识别复杂 PDF 文件,包括公式、图表(需要配置 MinerU API Key)", + "mineruLocalDesc": "可识别复杂 PDF 文件,包括公式、图表(需要配置 MinerU Local URL)", + "mineruApiDescDisabled": "请先到【项目配置 - 任务配置】设置 MinerU Token", + "mineruLocalDisabled": "请先到【项目配置 - 任务配置】设置 MinerU Local URL", + "mineruWebPlatform": "MinerU 在线平台解析", + "mineruWebPlatformDesc": "可识别复杂 PDF 文件,包括公式、图表(需跳转到其他网站)", + "mineruSelected": "已选择使用 MinerU 解析PDF", + "mineruLocalSelected": "已选择使用 MinerU Local 解析PDF", + "customVisionModel": "自定义视觉模型解析", + "customVisionModelDesc": "可识别复杂 PDF 文件,包括公式、图表(需在模型配置增加视觉模型配置)", + "customVisionModelSelected": "已选择使用视觉大模型 {{name}}({{provider}}) 解析PDF)", + "defaultSelected": "已选择使用默认内置策略解析PDF", + "download": "下载文件", + "deleteFile": "删除文件", + "batchDelete": "批量删除 ({{count}})", + "batchDeleteTitle": "批量删除文件", + "batchDeleteConfirm": "确定要删除选中的 {{count}} 个文件吗?此操作不可恢复。", + "batchDeleteSuccess": "成功删除 {{count}} 个文件", + "batchDeleteFailed": "批量删除失败", + "searchFiles": "搜索文件名...", + "searchResults": "找到 {{count}} 个文件(共 {{total}} 个)", + "noSearchResults": "未找到包含 \"{{searchTerm}}\" 的文件", + "noResultsOnCurrentPage": "当前页面没有搜索结果,请返回第一页查看", + "noDataOnCurrentPage": "当前页面没有数据", + "viewChunk": "查看文本块", + "editChunk": "编辑文本块 {{chunkId}}", + "editChunkSuccess": "文本块编辑成功", + "editChunkFailed": "文本块编辑失败", + "editChunkError": "编辑文本块时出错", + "deleteFileWarning": "警告:删除文件将同时删除以下相关内容", + "deleteFileWarningChunks": "所有关联的文本块", + "deleteFileWarningQuestions": "所有文本块生成的问题", + "deleteFileWarningDatasets": "所有问题生成的数据集", + "domainTree": { + "firstUploadTitle": "领域树生成", + "uploadTitle": "文件上传 - 领域树处理", + "deleteTitle": "文件删除 - 领域树处理", + "reviseOption": "修订领域树", + "reviseDesc": "针对新增或删除的文件信息,对当前的领域树进行修正,只影响变更的部分", + "rebuildOption": "重建领域树", + "rebuildDesc": "基于所有文件的目录信息重新生成完整的领域树", + "keepOption": "保持不变", + "keepDesc": "保持当前领域树结构不变,不做任何修改" + } + }, + "domain": { + "title": "领域知识树", + "addRootTag": "添加一级标签", + "addFirstTag": "添加第一个标签", + "noTags": "暂无领域标签树数据", + "docStructure": "文档目录结构", + "noToc": "暂无目录结构,请先上传并处理文件", + "editTag": "编辑标签", + "deleteTag": "删除标签", + "addChildTag": "添加子标签", + "deleteTagConfirmTitle": "删除标签", + "deleteTagConfirmMessage": "您确定要删除标签 \"{{tag}}\" 吗?", + "deleteWarning": "此操作将删除该标签及其所有子标签、问题和数据集,且无法恢复!", + "dialog": { + "addTitle": "添加标签", + "editTitle": "编辑标签", + "addChildTitle": "添加子标签", + "inputRoot": "请输入新的一级标签名称", + "inputEdit": "请编辑标签名称", + "inputChild": "请为\"{label}\"添加子标签", + "labelName": "标签名称", + "saving": "保存中...", + "save": "保存", + "deleteConfirm": "确定要删除标签\"{label}\"吗?", + "deleteWarning": "此操作将同时删除所有子标签,且无法恢复。", + "emptyLabel": "标签名称不能为空" + }, + "tabs": { + "tree": "领域树", + "structure": "目录结构" + }, + "errors": { + "saveFailed": "保存标签失败" + }, + "messages": { + "updateSuccess": "标签更新成功" + } + }, + "export": { + "alpacaSettings": "Alpaca 格式设置", + "questionFieldType": "问题字段类型", + "useInstruction": "使用 instruction 字段", + "useInput": "使用 input 字段", + "customInstruction": "自定义 instruction 字段内容", + "instructionPlaceholder": "请输入固定的指令内容", + "instructionHelperText": "当使用 input 字段时,可以在这里指定固定的 instruction 内容", + "title": "导出", + "format": "数据集风格", + "fileFormat": "文件格式", + "systemPrompt": "系统提示词", + "systemPromptPlaceholder": "请输入系统提示词...", + "ReasoninglanguagePlaceholder": "请输入Reasoning language : English or Chinese or others", + "Reasoninglanguage": "推理语言", + "onlyConfirmed": "仅导出已确认数据", + "example": "格式示例", + "confirmExport": "确认导出", + "includeCOT": "包含思维链", + "cotDescription": "包含最终答案前的推理过程", + "customFormat": "自定义格式", + "customFormatSettings": "自定义格式设置", + "questionFieldName": "问题字段名", + "answerFieldName": "答案字段名", + "cotFieldName": "思维链字段名", + "includeLabels": "包含标签", + "includeChunk": "包含文本块", + "questionOnly": "仅导出问题", + "localTab": "导出到本地", + "llamaFactoryTab": "在 LLaMA Factory 中使用", + "huggingFaceTab": "上传至 Hugging Face", + "configExists": "已存在配置文件", + "configPath": "配置文件路径", + "updateConfig": "更新 LLaMA Factory 配置", + "noConfig": "暂无配置文件,点击下方按钮生成", + "generateConfig": "生成 LLaMA Factory 配置", + "huggingFaceComingSoon": "HuggingFace 导出功能即将推出", + "uploadToHuggingFace": "上传至 HuggingFace", + "datasetName": "数据集名称", + "datasetNameHelp": "格式:用户名/数据集名称", + "privateDataset": "私有数据集", + "datasetSettings": "数据集设置", + "exportOptions": "导出选项", + "uploadSuccess": "数据集已成功上传至 HuggingFace", + "viewOnHuggingFace": "在 HuggingFace查看", + "noTokenWarning": "未找到 HuggingFace 令牌。请在项目设置中配置令牌。", + "goToSettings": "前往设置", + "tokenHelp": "您可以从HuggingFace设置页面获取令牌", + "multilingualThinkingFormat": "Multilingual‑Thinking", + "sampleInstruction": "人类指令(必填)", + "sampleOutput": "模型回答(必填)", + "sampleSystem": "系统提示词(选填)", + "sampleInstruction2": "第二个指令", + "sampleOutput2": "第二个回答", + "sampleSystemShort": "系统提示词", + "fixedInstruction": "固定的指令内容", + "sampleInput": "人类问题(必填)", + "sampleInput2": "第二个问题", + "sampleInputOptional": "人类输入(选填)", + "sampleUserMessage": "人类指令", + "sampleAssistantMessage": "模型回答", + "sampleAnalysis": "模型的思维链内容", + "sampleFinal": "模型回答", + "sampleThinking": "模型的思维链内容", + "fetchLabelStatsError": "获取标签统计失败:" + }, + "datasets": { + "loadingDataset": "正在加载数据集详情...", + "datasetNotFound": "未找到数据集", + "optimizeTitle": "AI 优化", + "optimizeAdvice": "优化建议", + "optimizePlaceholder": "请输入您对答案的改进建议,AI将根据您的建议优化答案和思维链", + "generatingDataset": "正在生成数据集", + "aiOptimizeAdvicePlaceholder": "请输入您对答案的改进建议,AI将根据您的建议优化答案和思维链", + "aiOptimizeAdvice": "请输入您对答案的改进建议,AI将根据您的建议优化答案和思维链", + "aiOptimize": "AI 智能优化", + "generating": "正在生成数据集", + "partialSuccess": "部分问题生成数据集成功 ({{successCount}}/{{total}}),{{failCount}} 个问题失败", + "generateError": "生成数据集失败", + "management": "数据集", + "question": "问题", + "filterAll": "全部", + "filterConfirmed": "已确认", + "filterUnconfirmed": "未确认", + "createdAt": "创建时间", + "model": "使用模型", + "domainTag": "领域标签", + "cot": "思维链", + "answer": "回答", + "chunkId": "文本块", + "confirmed": "已确认", + "noTag": "无标签", + "noData": "暂无数据", + "rowsPerPage": "每页行数", + "pagination": "{{from}}-{{to}} 共 {{count}}", + "confirmDeleteMessage": "确定要删除这个数据集吗?这个操作不可撤销。", + "questionLabel": "问题", + "fetchFailed": "获取数据集失败", + "deleteFailed": "删除数据集失败", + "deleteSuccess": "删除成功", + "exportSuccess": "数据集导出成功", + "exportFailed": "导出失败", + "exportProgress": "导出进度", + "exportingData": "正在导出数据集", + "processedCount": "已处理 {{processed}} / {{total}} 条", + "exportInProgress": "正在获取数据,请稍候...", + "exportFinalizing": "正在生成文件,即将完成...", + "loading": "加载数据集...", + "stats": "共 {{total}} 个数据集,已确认 {{confirmed}} 个({{percentage}}%)", + "selected": "共选中{{ count }}个数据集", + "batchconfirmDeleteMessage": "您确定要删除选中的 {{count}} 个数据集吗?此操作不可恢复。", + "batchDelete": "批量删除", + "batchDeleteProgress": "已完成: {{completed}}/{{total}}", + "batchDeleteCount": "已生成: {{count}}", + "evaluation": "数据集标注", + "rating": "评分", + "ratingExcellent": "优秀", + "ratingGood": "良好", + "ratingAverage": "一般", + "ratingPoor": "较差", + "ratingVeryPoor": "很差", + "ratingUnrated": "未评分", + "customTags": "自定义标签", + "addCustomTag": "添加自定义标签...", + "note": "备注", + "addNote": "添加备注...", + "noNote": "暂无备注", + "clickToAddNote": "点击添加备注...", + "enterNote": "请输入备注...", + "noteShortcuts": "Ctrl+Enter 保存,Esc 取消", + "aiEvaluation": "AI 质量评估", + "addToEval": "添加到评估数据集", + "addToEvalSuccess": "成功添加到评估数据集", + "addToEvalFailed": "添加失败", + "generateEvalVariant": "生成评估集变体", + "generateVariantFailed": "生成变体失败", + "saveVariantSuccess": "已保存到评估数据集", + "saveVariantFailed": "保存失败", + "evalVariantTitle": "生成评估集变体", + "evalVariantPreviewTitle": "确认生成的题目", + "saveToEval": "保存到评估集", + "evalVariantConfigHint": "请选择生成的题目类型和数量,AI 将基于当前问答对进行改写。", + "questionType": "题目类型", + "typeOpenEnded": "开放式问答", + "typeSingleChoice": "单选题", + "typeMultipleChoice": "多选题", + "typeTrueFalse": "判断题", + "typeShortAnswer": "简答题", + "generateCount": "生成数量", + "evalVariantPreviewHint": "您可以编辑生成的题目,确认无误后保存到评估集。", + "questionIndex": "题目 {{index}}", + "options": "选项 (JSON数组)", + "optionsHint": "例如: [\"选项A\", \"选项B\"]", + "answerArrayHint": "多选题答案请输入数组,如 [\"A\", \"C\"]", + "answerBoolHint": "判断题答案请输入 ✅ 或 ❌", + "generate": "生成", + "updateSuccess": "更新成功", + "updateFailed": "更新失败", + "searchPlaceholder": "搜索数据集...", + "fieldQuestion": "问题", + "fieldAnswer": "回答", + "fieldCOT": "思维链", + "fieldLabel": "领域标签", + "moreFilters": "更多", + "filtersTitle": "筛选条件", + "filterConfirmationStatus": "确认状态", + "filterCotStatus": "思维链状态", + "filterHasCot": "包含思维链", + "filterNoCot": "不包含思维链", + "filterScoreRange": "评分范围", + "filterNoteKeyword": "备注关键字", + "filterNoteKeywordPlaceholder": "请输入备注关键字...", + "filterChunkName": "文本块名称", + "filterChunkNamePlaceholder": "请输入文本块名称...", + "filterCustomTag": "自定义标签", + "resetFilters": "重置", + "applyFilters": "应用筛选", + "viewDetails": "查看详情", + "datasetDetail": "数据集详情", + "metadata": "元数据", + "confirmSave": "确认保留", + "unconfirm": "取消确认", + "unconfirming": "取消确认中...", + "uncategorized": "未分类", + "questionCount": "{{count}} 个问题", + "source": "来源", + "generateDataset": "生成数据集", + "generateNotImplemented": "生成数据集功能未实现", + "generateSuccess": "成功生成数据集:{{question}}", + "generateFailed": "生成数据集失败:{{error}}", + "noTagsAndQuestions": "暂无标签和问题", + "answerCount": "{{count}} 个答案", + "answered": "已生成答案", + "enableShortcuts": "翻页快捷键", + "shortcutsHelp": "按 ← 向前,按 → 向后,按 y 确认,按 d 删除", + "filterDistill": "是否蒸馏数据集", + "filterDistillYes": "蒸馏数据集", + "filterDistillNo": "非蒸馏数据集", + "evaluate": "质量评估", + "evaluating": "评估中...", + "batchEvaluate": "自动质量评估", + "selectModelFirst": "请先选择模型", + "evaluateSuccess": "评估完成!评分:{{score}}/5", + "evaluateFailed": "评估失败", + "evaluateError": "评估失败: {{error}}", + "batchEvaluateStarted": "批量评估任务已启动,将在后台进行处理", + "batchEvaluateStartFailed": "启动批量评估失败", + "batchEvaluateFailed": "批量评估失败: {{error}}", + "scoreRange": "{{min}} - {{max}} 分", + "singleTurn": "单轮问答数据集", + "multiTurn": "多轮对话数据集", + "imageQA": "图片问答数据集", + "conversationDetail": "多轮对话详情", + "conversationContent": "对话内容", + "basicInfo": "基本信息", + "firstQuestion": "首轮问题", + "conversationScenario": "对话场景", + "conversationRounds": "对话轮数", + "modelUsed": "使用模型", + "qualityScore": "质量评分", + "notes": "备注", + "createTime": "创建时间", + "notSet": "未设置", + "noTags": "无标签", + "noNotes": "无备注", + "notEvaluated": "暂未评估", + "round": "第 {{round}} 轮", + "system": "系统", + "user": "用户", + "assistant": "助手", + "confirmDelete": "确认删除", + "confirmDeleteConversation": "确定要删除这个多轮对话数据集吗?此操作不可恢复。", + "conversationNotFound": "对话数据集不存在", + "fetchDataFailed": "获取数据失败", + "saveFailed": "保存失败", + "saveSuccess": "保存成功", + "saving": "保存中...", + "inputTagsPlaceholder": "输入标签,用空格分隔", + "addNotesPlaceholder": "添加备注信息", + "noConversations": "暂无多轮对话数据集", + "notRated": "未评分", + "minScore": "最低评分", + "maxScore": "最高评分", + "unconfirmed": "未确认" + }, + "rating": { + "veryPoor": "很差", + "poor": "差", + "belowAverage": "偏差", + "fair": "一般", + "average": "中等", + "good": "良好", + "veryGood": "很好", + "excellent": "优秀", + "outstanding": "杰出", + "perfect": "完美", + "unrated": "未评分" + }, + "tags": { + "noTags": "暂无标签", + "addTag": "添加标签...", + "addCustomTag": "添加自定义标签", + "maxTagsReached": "最多可添加 {{maxTags}} 个标签", + "availableTagsHint": "可从已有标签中选择,或输入新标签" + }, + "import": { + "title": "导入", + "fileUpload": "文件上传", + "fileUploadDescription": "上传本地文件导入数据集", + "uploadFile": "上传文件", + "supportedFormats": "支持 JSON、JSONL、CSV 格式文件", + "dragDropFile": "拖拽文件到此处或点击选择文件", + "dropFileHere": "松开以上传文件", + "maxFileSize": "最大文件大小: 50MB", + "processingFile": "正在处理文件...", + "uploadedFiles": "已上传文件", + "uploadError": "文件上传失败,请检查文件格式是否正确", + "mapFields": "字段映射", + "importing": "导入中", + "fieldMapping": "字段映射", + "mappingDescription": "请将源数据的字段映射到目标字段。系统已自动识别可能的映射关系,您可以根据需要调整。", + "selectMapping": "选择字段映射", + "questionField": "问题字段", + "answerField": "答案字段", + "cotField": "思维链字段", + "tagsField": "标签字段", + "selectField": "选择字段", + "questionDesc": "用户的问题或输入内容(必选)", + "answerDesc": "AI的回答或输出内容(必选)", + "cotDesc": "思维链或推理过程(可选)", + "tagsDesc": "标签数组,多个标签用逗号分隔(可选)", + "dataPreview": "数据预览", + "previewNote": "显示前3条记录,每个字段值最多显示100个字符", + "confirmMapping": "确认映射", + "requiredFields": "请至少选择问题和答案字段的映射", + "mappingRequired": "问题和答案字段为必选项", + "duplicateMapping": "不能将多个目标字段映射到同一个源字段", + "noPreviewData": "没有可预览的数据", + "preparingData": "准备数据...", + "uploadingData": "上传数据...", + "processing": "处理中... {{processed}}/{{total}}", + "completed": "导入完成", + "importStats": "导入统计", + "total": "总计: {{count}}", + "success": "成功: {{count}}", + "failed": "失败: {{count}}", + "source": "数据源", + "description": "描述", + "errors": "错误信息", + "moreErrors": "还有 {{count}} 个错误未显示...", + "importSuccess": "数据集导入完成!", + "enterDatasetName": "请输入数据集名称", + "noDatasetFound": "未找到匹配的数据集", + "complete": "完成", + "addToEval": "添加到评估数据集", + "addToEvalSuccess": "成功添加到评估数据集", + "addToEvalFailed": "添加失败", + "generateEvalVariant": "生成评估集变体", + "selectModelFirst": "请先选择模型", + "generateVariantFailed": "生成变体失败", + "saveVariantSuccess": "已保存到评估数据集", + "saveVariantFailed": "保存失败", + "evalVariantTitle": "生成评估集变体", + "evalVariantHint": "AI 已根据原问答对生成了新的测试变体,您可以手动编辑后保存。", + "saveToEval": "保存到评估集", + "evalVariantConfigHint": "请选择生成的题目类型和数量,AI 将基于当前问答对进行改写。", + "questionType": "题目类型", + "typeOpenEnded": "开放式问答", + "typeSingleChoice": "单选题", + "typeMultipleChoice": "多选题", + "typeTrueFalse": "判断题", + "typeShortAnswer": "简答题", + "generateCount": "生成数量", + "evalVariantPreviewHint": "您可以编辑生成的题目,确认无误后保存到评估集。", + "questionIndex": "题目 {{index}}", + "options": "选项 (JSON数组)", + "optionsHint": "例如: [\"选项A\", \"选项B\"]", + "answerArrayHint": "多选题答案请输入数组,如 [\"A\", \"C\"]", + "answerBoolHint": "判断题答案请输入 ✅ 或 ❌", + "evalVariantPreviewTitle": "确认生成的题目", + "generate": "生成" + }, + "update": { + "newVersion": "新版本", + "newVersionAvailable": "发现新版本", + "currentVersion": "当前版本", + "latestVersion": "最新版本", + "downloadNow": "立即下载", + "downloading": "正在下载:", + "installNow": "立即安装", + "updating": "更新中...", + "updateNow": "立即更新", + "viewRelease": "下载最新版本", + "checking": "正在检查更新...", + "noUpdates": "已是最新版本", + "updateError": "更新出错", + "updateSuccess": "更新成功", + "restartRequired": "需要重启应用", + "restartNow": "立即重启", + "restartLater": "稍后重启" + }, + "datasetSquare": { + "title": "数据集广场", + "subtitle": "发现和探索各种公开数据集资源,助力您的模型训练和研究", + "searchPlaceholder": "搜索数据集关键词...", + "searchVia": "通过", + "categoryTitle": "数据集分类", + "categories": { + "all": "全部", + "popular": "热门推荐", + "chinese": "中文资源", + "english": "英文资源", + "research": "研究数据", + "multimodal": "多模态" + }, + "foundResources": "找到 {{count}} 个数据集资源", + "currentFilter": "当前筛选: {{category}}", + "noDatasets": "没有找到符合条件的数据集", + "tryOtherCategories": "请尝试其他分类或返回全部数据集查看", + "dataset": "数据集", + "viewDataset": "查看数据集" + }, + "playground": { + "title": "模型测试", + "selectModelFirst": "请选择至少一个模型", + "sendFirstMessage": "发送第一条消息开始测试", + "inputMessage": "输入消息...", + "send": "发送", + "outputMode": "输出方式", + "normalOutput": "普通输出", + "streamingOutput": "流式输出", + "clearConversation": "清空对话", + "selectModelMax3": "选择模型(最多3个)", + "reasoningProcess": "推理过程" + }, + "chunks": { + "title": "文本块", + "defaultTitle": "默认标题" + }, + "documentation": "文档", + "models": { + "configNotFound": "未找到模型配置", + "parseError": "解析模型配置失败", + "fetchFailed": "获取模型失败", + "saveFailed": "保存模型配置失败", + "pleaseSelectModel": "请至少选择一个模型", + "title": "模型管理", + "add": "添加模型", + "unselectedModel": "未选择模型", + "unconfiguredAPIKey": "未配置 API Key", + "saveAllModels": "保存所有模型配置", + "edit": "编辑", + "delete": "删除", + "modelId": "模型 ID", + "modelName": "模型名称", + "modelNamePlaceholder": "请输入模型名称(可选,默认为模型 ID)", + "modelIdPlaceholder": "模型名称(可自定义输入)", + "endpoint": "接口地址", + "apiKey": "API密钥", + "provider": "提供商(可自定义输入)", + "localModel": "本地模型", + "apiKeyConfigured": "API Key 已经配置", + "apiKeyNotConfigured": "API Key 未配置", + "temperature": "模型温度", + "maxTokens": "最大生成 Token 数", + "maxTokensInputTip": "滑块范围:1-{{max}}。你也可以输入任意正整数。", + "topP": "Top P", + "type": "模型标签", + "text": "语言模型", + "vision": "视觉模型", + "typeTips": "如果希望使用自定义视觉模型解析PDF请务必配置至少一个视觉大模型", + "refresh": "刷新模型列表", + "configuredModels": "已配置模型", + "unconfiguredModels": "未配置模型", + "noConfiguredModels": "暂无已配置模型", + "noUnconfiguredModels": "暂无未配置模型", + "checkEndpointHealth": "检查端点健康度", + "checkAllEndpointHealth": "一键检查全部端点", + "endpointHealthy": "端点健康", + "endpointCheckFailed": "端点检查失败", + "endpointMissing": "端点为空", + "endpointReachableModelMissing": "端点可访问,但当前模型不在返回列表中", + "healthCheckSummary": "健康检查完成:正常 {{okCount}} 个,失败 {{failCount}} 个", + "checking": "检查中...", + "healthy": "健康", + "reachable": "可达", + "unhealthy": "异常", + "notChecked": "未检查" + }, + "stats": { + "ongoingProjects": "正在运行的项目", + "questionCount": "问题数量", + "generatedDatasets": "已生成的数据集", + "supportedModels": "已支持的模型" + }, + "migration": { + "title": "【重要】历史数据迁移", + "description": "为了提升大量数据集的检索性能,自 1.3.1 版本起,Easy Dataset 将文件存储方式变更为本地数据库存储,检测到您有历史项目尚未进行迁移,在完成迁移前,您将无法访问这些项目,请尽快完成迁移!", + "projectsList": "未迁移的项目", + "migrate": "开始迁移", + "migrating": "迁移中...", + "success": "成功迁移 {{count}} 个项目", + "failed": "迁移失败", + "checkFailed": "检查未迁移项目失败", + "checkError": "检查未迁移项目出错", + "starting": "正在启动迁移任务...", + "processing": "正在处理迁移任务...", + "completed": "迁移已完成", + "startFailed": "启动迁移任务失败", + "statusFailed": "获取迁移状态失败", + "taskNotFound": "迁移任务不存在", + "progressStatus": "已迁移 {{completed}}/{{total}} 个项目", + "openDirectory": "打开项目目录", + "deleteDirectory": "删除项目目录", + "confirmDelete": "确定要删除此项目目录吗?此操作不可恢复。", + "openDirectoryFailed": "打开项目目录失败", + "deleteDirectoryFailed": "删除项目目录失败" + }, + "distill": { + "title": "数据蒸馏", + "generateRootTags": "生成顶级标签", + "generateSubTags": "生成子标签", + "generateQuestions": "生成问题", + "generateRootTagsTitle": "生成顶级领域标签", + "generateSubTagsTitle": "为 {{parentTag}} 生成子标签", + "generateQuestionsTitle": "为 {{tag}} 生成问题", + "parentTag": "父标签", + "parentTagPlaceholder": "请输入父标签名称(如:体育、科技等)", + "parentTagHelp": "输入一个领域主题,系统将基于此生成相关标签", + "tagCount": "标签数量", + "tagCountHelp": "输入要生成的标签数量,最大为100个", + "questionCount": "问题数量", + "questionCountHelp": "输入要生成的问题数量,最大为100个", + "generatedTags": "已生成的标签", + "generatedQuestions": "已生成的问题", + "generateTags": "生成标签", + "tagPath": "标签路径", + "noTags": "暂无标签", + "noQuestions": "暂无问题", + "clickGenerateButton": "点击上方的生成按钮开始创建标签", + "selectModelFirst": "请先选择一个模型", + "selectModel": "选择模型", + "generateTagsError": "生成标签失败", + "generateQuestionsError": "生成问题失败", + "deleteTagConfirmTitle": "确认要删除标签吗?将关联删除所有该标签下的子标签、问题、数据集", + "editTagTitle": "编辑标签", + "tagName": "标签名称", + "labelRequired": "标签名称不能为空", + "tagUpdateSuccess": "标签更新成功", + "tagUpdateFailed": "标签更新失败", + "unknownTag": "未知标签", + "autoDistillButton": "全自动蒸馏数据集", + "autoDistillTitle": "全自动蒸馏数据集配置", + "distillTopic": "蒸馏主题", + "tagLevels": "标签层级", + "tagLevelsHelper": "设置层级数量,最大为{{max}}级", + "tagsPerLevel": "每层标签数量", + "tagsPerLevelHelper": "每个父标签下生成的子标签数量,最大为{{max}}个", + "questionsPerTag": "每个标签问题数量", + "questionsPerTagHelper": "每个叶子标签生成的问题数量,最大为{{max}}个", + "estimationInfo": "任务预估信息", + "estimatedTags": "预计生成标签数量", + "estimatedQuestions": "预计生成问题数量", + "currentTags": "当前标签数量", + "currentQuestions": "当前问题数量", + "newTags": "预计新增标签数量", + "newQuestions": "预计新增问题数量", + "startAutoDistill": "开始自动蒸馏", + "autoDistillProgress": "自动蒸馏进度", + "overallProgress": "整体进度", + "tagsProgress": "标签构建进度", + "questionsProgress": "问题生成进度", + "currentStage": "当前阶段", + "realTimeLogs": "实时日志", + "waitingForLogs": "等待日志输出...", + "autoDistillStarted": "{{time}} 自动蒸馏任务开始", + "autoDistillInsufficientError": "当前配置不会产生新的标签或问题,请调整参数", + "stageInitializing": "初始化中...", + "stageBuildingLevel1": "正在构建第一层标签", + "stageBuildingLevel2": "正在构建第二层标签", + "stageBuildingLevel3": "正在构建第三层标签", + "stageBuildingLevel4": "正在构建第四层标签", + "stageBuildingLevel5": "正在构建第五层标签", + "stageBuildingQuestions": "正在生成问题", + "stageBuildingDatasets": "正在生成数据集", + "stageCompleted": "任务已完成", + "datasetsProgress": "数据集生成进度", + "rootTopicHelperText": "默认以项目名称作为顶级蒸馏主题,如需更改,请到项目设置中更改项目名称。", + "addChildTag": "生成子标签", + "datasetType": "数据集类型", + "singleTurnDataset": "单轮对话数据集", + "multiTurnDataset": "多轮对话数据集", + "bothDatasetTypes": "两种数据集都生成", + "autoDistillTaskDetail": "自动蒸馏任务: {{topic}}", + "backgroundTaskCreated": "后台蒸馏任务已创建,可在任务管理中查看进度", + "backgroundTaskFailed": "创建后台任务失败", + "taskExecutionError": "任务执行出错: {{error}}" + }, + "tasks": { + "pending": "有 {{count}} 个任务处理中", + "completed": "全部任务已完成", + "title": "任务管理中心", + "loading": "加载任务列表...", + "empty": "暂无任务记录", + "confirmDelete": "确认删除该任务?", + "confirmAbort": "确认中断该任务?任务将停止执行。", + "deleteSuccess": "任务已删除", + "deleteFailed": "删除任务失败", + "abortSuccess": "任务已中断", + "abortFailed": "中断任务失败", + "status": { + "processing": "处理中", + "completed": "已完成", + "failed": "失败", + "aborted": "已中断", + "unknown": "未知" + }, + "types": { + "text-processing": "文本处理", + "file-processing": "文件处理", + "data-cleaning": "数据清洗", + "question-generation": "问题生成", + "answer-generation": "答案生成", + "multi-turn-generation": "多轮对话生成", + "eval-generation": "评估集生成", + "image-question-generation": "图片问题生成", + "data-distillation": "数据蒸馈", + "pdf-processing": "PDF解析" + }, + "filters": { + "status": "任务状态", + "type": "任务类型" + }, + "actions": { + "refresh": "刷新任务列表", + "delete": "删除任务", + "abort": "中断任务" + }, + "table": { + "type": "任务类型", + "status": "状态", + "progress": "进度", + "note": "备注", + "createTime": "创建时间", + "endTime": "完成时间", + "duration": "运行时间", + "model": "使用模型", + "detail": "任务详情", + "actions": "操作" + }, + "duration": { + "seconds": "{{seconds}}秒", + "minutes": "{{minutes}}分 {{seconds}}秒", + "hours": "{{hours}}小时 {{minutes}}分" + }, + "fetchFailed": "获取任务列表失败", + "createSuccess": "任务创建成功", + "createFailed": "任务创建失败", + "multiTurnCreateSuccess": "多轮对话数据集任务创建成功", + "notes": { + "selectedChunks": "已选择 {{count}} 个文本块", + "fileBatch": "文件处理参数:{{count}} 个文件(策略:{{strategy}})", + "jsonParams": "任务参数已配置", + "noChunksQuestion": "没有需要生成问题的文本块", + "noChunksCleaning": "没有需要清洗的文本块", + "processingFailed": "任务失败:{{error}}", + "questionSummary": "已处理 {{processed}}/{{total}},成功 {{succeeded}},失败 {{failed}},生成问题 {{generated}}", + "datasetSummary": "已处理 {{processed}}/{{total}},成功 {{succeeded}},失败 {{failed}},生成数据集 {{generated}}", + "cleaningSummary": "已处理 {{processed}}/{{total}},成功 {{succeeded}},失败 {{failed}},原文长度 {{original}},清洗后 {{cleaned}}", + "genericSummary": "已处理 {{processed}}/{{total}},成功 {{succeeded}},失败 {{failed}}" + } + }, + "gaPairs": { + "title": "文体-受众对管理", + "loading": "正在加载文体-受众对...", + "addPair": "添加文体-受众对", + "saveChanges": "保存更改", + "saving": "保存中...", + "restoreBackup": "恢复备份", + "noGaPairsTitle": "未找到文体-受众对", + "noGaPairsDescription": "为此文件生成AI驱动的文体-受众对", + "generateGaPairs": "生成文体-受众对", + "generating": "生成中...", + "generateMore": "生成更多文体-受众对", + "activePairs": "活跃的文体-受众对 ({{active}}/{{total}})", + "pairNumber": "文体-受众对 #{{number}}", + "active": "活跃", + "deleteTooltip": "删除文体-受众对", + "genre": "文体", + "genreDescription": "文体描述", + "audience": "受众", + "audienceDescription": "受众描述", + "addDialogTitle": "添加新的文体-受众对", + "genreTitle": "文体标题", + "audienceTitle": "受众标题", + "genreTitlePlaceholder": "请输入文体标题...", + "genreDescPlaceholder": "详细描述该文体...", + "audienceTitlePlaceholder": "请输入受众标题...", + "audienceDescPlaceholder": "详细描述目标受众...", + "cancel": "取消", + "addPairButton": "添加文体-受众对", + "requiredFields": "文体标题和受众标题为必填项", + "restoredFromBackup": "已从备份恢复", + "allPairsDeleted": "已成功删除所有文体-受众对", + "pairsSaved": "已成功保存 {{count}} 个文体-受众对", + "additionalPairsGenerated": "成功生成了 {{count}} 个额外的文体-受众对。总计:{{total}}", + "validationError": "文体-受众对 {{number}}:文体和受众标题为必填项", + "loadError": "无法加载文体-受众对:{{error}}", + "generateError": "生成文体-受众对失败", + "saveError": "保存文体-受众对失败", + "noActiveModel": "请在设置中配置AI模型后再生成文体-受众对。", + "contentTooShort": "文件内容过短或不适合生成文体-受众对。", + "configError": "AI模型配置错误。可能缺少必要的依赖项。", + "serverError": "服务器错误 ({{status}})。请稍后重试。", + "emptyResponse": "生成服务返回空响应", + "generationFailed": "生成失败", + "saveOperationFailed": "保存操作失败", + "serviceNotAvailable": "文体-受众对生成服务不可用。请检查您的API配置。", + "requestFailed": "请求失败 ({{status}})。请重试。", + "internalServerError": "发生内部服务器错误。", + "batchGenerate": "批量生成文体-受众对", + "batchGenerateDescription": "将为选中的 {{count}} 个文件批量生成文体-受众对,该操作可能需要一些时间。", + "appendMode": "追加模式", + "appendModeDescription": "为已有文体-受众对的文件生成更多文体-受众对,而不是覆盖", + "selectAtLeastOneFile": "请先选择至少一个文件", + "noDefaultModel": "未设置默认模型,请先在项目设置中配置模型", + "incompleteModelConfig": "模型配置不完整,请检查模型设置", + "missingApiKey": "模型未配置API密钥,请在模型设置中添加API密钥", + "loadingProjectModel": "加载项目模型中...", + "usingModel": "使用模型", + "startGeneration": "开始生成", + "batchGenCompleted": "批量生成完成!成功为 {{success}}/{{total}} 个文件生成了文体-受众对。", + "generationError": "生成过程发生错误: {{error}}", + "fetchProjectInfoFailed": "获取项目信息失败: {{status}}", + "fetchModelConfigFailed": "获取模型配置失败: {{status}}", + "fetchProjectModelError": "获取项目模型配置时出错", + "batchGenerationFailed": "批量生成文体-受众对失败", + "batchGenerationSuccess": "成功为 {{count}} 个文件生成文体-受众对", + "selectAllFiles": "全选", + "deselectAllFiles": "取消全选", + "batchGenerateTitle": "批量生成文体-受众对", + "generationMode": "生成方式", + "aiGenerateMode": "AI 生成", + "manualAddMode": "手动添加", + "genreDesc": "文体描述", + "audienceDesc": "受众描述", + "manualGaPairRequired": "请填写文体标题和受众标题", + "batchAddManual": "批量添加" + }, + "batchEdit": { + "title": "批量编辑文本块", + "batchEdit": "批量编辑", + "batchEditTooltip": "批量编辑选中的文本块", + "position": "添加位置", + "atBeginning": "在开头添加", + "atEnd": "在结尾添加", + "contentToAdd": "要添加的内容", + "contentPlaceholder": "请输入要添加到文本块的内容...", + "contentRequired": "请输入要添加的内容", + "contentHelp": "此内容将被添加到所有选中的文本块中", + "preview": "预览效果", + "allChunksSelected": "已选择全部 {{count}} 个文本块", + "selectedChunks": "已选择 {{selected}} / {{total}} 个文本块", + "processing": "处理中...", + "applyToChunks": "应用到 {{count}} 个文本块", + "editSuccess": "成功编辑了 {{count}} 个文本块", + "editFailed": "批量编辑失败", + "previewNote": "以上是第一个选中文本块的预览效果,所有选中的文本块都将进行相同的修改" + }, + "errors": { + "projectIdRequired": "项目ID不能为空", + "getDatasetsFailed": "获取数据集失败", + "getTagStatsFailed": "获取标签统计失败", + "deleteFileFailed": "删除文件出错", + "recordNotFound": "当前记录不存在", + "mineruTokenNotFound": "未找到token配置,请检查任务设置中是否配置了MinerU token", + "mineruLocalUrlNotFound": "未找到MinerU本地URL配置,请检查任务设置中是否配置了MinerU本地URL" + }, + "sampleData": { + "questionContent": "问题内容", + "answerContent": "答案内容", + "cotContent": "思维链过程内容", + "domainLabel": "领域标签", + "textChunk": "文本块" + }, + "exportDialog": { + "balancedExport": "平衡导出", + "balancedExportTitle": "平衡导出设置", + "balancedExportDescription": "根据领域标签配置每个类别的数据量,实现数据集的平衡导出", + "quickSettings": "快速设置", + "setAllTo50": "全部设为50", + "setAllTo100": "全部设为100", + "setAllTo200": "全部设为200", + "customAmount": "自定义数量", + "tagName": "标签名称", + "availableCount": "可用数量", + "exportCount": "导出数量", + "settings": "设置", + "totalExportCount": "总导出数量", + "tagCount": "标签数量", + "export": "导出" + }, + "imageDatasets": { + "title": "图片问答数据集", + "subtitle": "管理和优化您的图片问答数据集", + "description": "管理和优化您的图片问答数据集。", + "searchPlaceholder": "搜索问题或答案...", + "noAnswer": "暂无答案", + "labels": "标签", + "typeLabel": "标签", + "typeCustom": "自定义JSON", + "typeText": "普通文本", + "unscored": "未评分", + "confirmed": "已确认", + "unconfirmed": "未确认", + "view": "查看详情", + "evaluate": "质量评估", + "delete": "删除", + "deleteConfirm": "确定要删除这个数据集吗?", + "imageName": "图片名称", + "status": "状态", + "scoreRange": "评分范围", + "noData": "暂无图片数据集", + "noDataTip": "请先在图片管理中生成问答数据集", + "fetchFailed": "获取数据集失败", + "fetchDetailFailed": "获取详情失败", + "deleteSuccess": "删除成功", + "deleteFailed": "删除失败", + "updateSuccess": "更新成功", + "updateFailed": "更新失败", + "regenerateSuccess": "AI 识别成功", + "regenerateFailed": "AI 识别失败", + "notFound": "数据集不存在", + "detail": "详情", + "image": "图片", + "question": "问题", + "answer": "答案", + "selectLabels": "选择标签", + "noLabels": "未选择标签", + "jsonPlaceholder": "输入 JSON 格式数据...", + "metadata": "元数据", + "score": "评分", + "tags": "标签", + "addTag": "添加标签...", + "note": "备注", + "notePlaceholder": "添加备注信息...", + "modelInfo": "模型信息", + "createdAt": "创建时间", + "updatedAt": "更新时间", + "exportTitle": "导出图片数据集", + "exportFormat": "导出格式", + "rawFormat": "原始格式", + "customFormat": "自定义格式", + "exportImagesOption": "导出图片文件", + "exportImagesDesc": "将所有图片打包成 ZIP 压缩包一起下载,下载完成后可手动解压到和数据集文件同一目录下的 Images 文件夹中", + "includeImagePath": "在数据集中包含图片路径", + "includeImagePathDesc": "在问题或答案中添加图片路径(格式:/images/图片名称)", + "systemPrompt": "系统提示词(可选)", + "systemPromptPlaceholder": "输入系统提示词...", + "confirmedOnly": "仅导出已确认的数据集", + "exportTip": "标签格式的答案将自动解析为文本(逗号分隔)", + "exportSuccess": "数据集导出成功", + "exportFailed": "导出失败", + "noDataToExport": "没有可导出的数据", + "exportImagesSuccess": "图片压缩包导出成功", + "exportImagesFailed": "图片导出失败" + }, + "images": { + "resolution": "分辨率", + "uploadTime": "上传时间", + "fileName": "文件名", + "title": "图片管理", + "importImages": "导入图片", + "searchPlaceholder": "搜索图片名称...", + "hasQuestions": "问题状态", + "hasDatasets": "数据集状态", + "withQuestions": "已生成问题", + "withoutQuestions": "未生成问题", + "withDatasets": "已生成数据集", + "withoutDatasets": "未生成数据集", + "noImages": "暂无图片", + "noImagesDescription": "开始导入图片,创建您的第一个图片数据集", + "preview": "预览", + "questions": "问题", + "datasets": "数据集", + "datasetCount": "数据集数", + "generateQuestions": "生成问题", + "generateDataset": "生成数据集", + "deleteConfirm": "确定要删除这张图片吗?", + "deleteSuccess": "删除成功", + "deleteFailed": "删除失败", + "batchDelete": "批量删除", + "selectImagesToDelete": "请选择要删除的图片", + "batchDeleteConfirm": "确定要删除选中的 {{count}} 张图片吗?", + "batchDeleteSuccess": "成功删除 {{count}} 张图片", + "batchDeletePartialSuccess": "成功删除 {{success}} 张,失败 {{fail}} 张", + "batchDeleteFailed": "批量删除失败", + "importTip": "选择一个或多个包含图片的目录,所有图片将被导入到项目中(同名图片将被覆盖)", + "selectDirectory": "选择目录", + "directoryPath": "目录路径", + "enterDirectoryPath": "例如:/Users/username/Pictures", + "selectedDirectories": "已选择的目录", + "selectAtLeastOne": "请至少选择一个目录", + "importSuccess": "成功导入 {{count}} 张图片", + "importFailed": "导入失败", + "startImport": "开始导入", + "addDirectory": "添加目录", + "importFromDirectory": "从目录导入", + "importFromPdf": "从 PDF 导入", + "importFromZip": "从压缩包导入", + "pdfImportTip": "选择 PDF 文件,系统会自动将其转换为图片并导入", + "zipImportTip": "选择 ZIP 压缩包文件,系统会自动解压并导入其中的图片", + "clickToSelectPdf": "点击选择 PDF 文件", + "clickToSelectZip": "点击选择 ZIP 文件", + "supportedFormat": "支持格式:PDF", + "supportedZipFormat": "支持格式:ZIP", + "fileSize": "文件大小", + "selectedFile": "已选择文件", + "invalidPdfFile": "请选择有效的 PDF 文件", + "invalidZipFile": "请选择有效的 ZIP 文件", + "selectPdfFile": "请选择 PDF 文件", + "selectZipFile": "请选择 ZIP 文件", + "pdfImportSuccess": "成功从 PDF \"{{name}}\" 导入 {{count}} 张图片", + "pdfImportFailed": "PDF 导入失败", + "zipImportSuccess": "成功从压缩包 \"{{name}}\" 导入 {{count}} 张图片", + "zipImportFailed": "压缩包导入失败", + "convertAndImport": "转换并导入", + "extractAndImport": "解压并导入", + "electronRequired": "此功能需要在桌面应用中使用", + "selectDirectoryFailed": "选择目录失败", + "imageName": "图片名称", + "questionCount": "问题数量", + "questionCountHelp": "生成1-10个问题", + "size": "大小", + "dimensions": "尺寸", + "currentModel": "当前模型", + "selectModelFirst": "请先选择一个模型", + "visionModelRequired": "请选择支持视觉的模型(如 GPT-4 Vision、Claude 等)", + "countRange": "问题数量应在1-10之间", + "questionsGenerated": "成功生成 {{count}} 个问题", + "generateFailed": "生成失败", + "question": "问题", + "questionPlaceholder": "请输入您想问的问题...", + "questionRequired": "请输入问题", + "datasetGenerated": "数据集生成成功", + "autoGenerateQuestions": "自动提取问题", + "autoGenerateConfirm": "系统将为所有未生成问题的图片自动生成问题。此操作将创建一个后台任务,您可以在任务管理中查看进度。", + "taskCreated": "任务创建成功,正在后台处理", + "taskCreateFailed": "任务创建失败", + "manualAnnotation": "手动标注", + "annotationTitle": "图片标注", + "imageInfo": "图片信息", + "annotatedCount": "已标注", + "selectQuestion": "选择或创建问题", + "selectQuestionPlaceholder": "请选择问题模板...", + "universalQuestions": "通用问题", + "independentQuestions": "独立问题", + "answerTypeText": "文字", + "answerTypeLabel": "标签", + "answerTypeCustomFormat": "自定义格式", + "usedTimes": "使用 {{count}} 次", + "answer": "答案", + "answerPlaceholder": "请输入答案...", + "selectLabels": "选择标签", + "availableLabels": "可选标签", + "noLabelsAvailable": "暂无可选标签", + "addNewLabel": "添加新标签...", + "selectedLabels": "已选择", + "customFormatAnswer": "自定义格式答案", + "formatRequirement": "格式要求", + "customFormatPlaceholder": "请输入符合格式的 JSON...", + "note": "备注", + "notePlaceholder": "备注信息(可选)", + "saveAndContinue": "保存并继续", + "noImageSelected": "未选择图片", + "noTemplateSelected": "请选择问题", + "answerRequired": "请输入答案", + "invalidJsonFormat": "JSON 格式不正确", + "annotationSuccess": "标注保存成功", + "annotationFailed": "保存标注失败", + "allQuestionsAnnotated": "当前图片所有问题已标注完成", + "allImagesAnnotated": "所有图片的问题都已标注完成", + "noQuestionsAssociated": "当前图片未关联任何问题", + "loadImageDetailFailed": "加载图片详情失败", + "answeredQuestions": "已标注问题", + "useTemplate": "使用模板", + "formatJson": "格式化", + "jsonFormatHelp": "请输入有效的JSON格式数据", + "imageLoadError": "图片加载失败", + "annotate": "标注", + "annotateImage": "标注图片", + "createQuestion": "创建问题", + "createTemplate": "创建问题模板", + "aiGenerate": "AI 识别", + "aiGenerateSuccess": "AI 生成成功", + "aiGenerateFailed": "AI 生成失败", + "missingParameters": "缺少必要参数", + "selectNewQuestion": "选择新问题", + "fetchTemplatesFailed": "获取问题模板失败", + "createTemplateSuccess": "问题模板创建成功", + "createTemplateFailed": "创建问题模板失败", + "updateTemplateSuccess": "问题模板更新成功", + "updateTemplateFailed": "更新问题模板失败", + "deleteTemplateSuccess": "问题模板删除成功", + "deleteTemplateFailed": "删除问题模板失败", + "template": { + "management": "管理问题模板", + "create": "创建模板", + "edit": "编辑模板", + "question": "问题内容", + "description": "描述", + "noTemplates": "暂无问题模板,点击创建按钮添加", + "deleteConfirm": "确定要删除这个问题模板吗?", + "used": "已使用", + "addLabel": "添加标签", + "customFormat": "自定义格式", + "customFormatHelp": "输入 JSON 格式的输出约束", + "customFormatInfo": "此格式将作为提示词提供给大模型,用于约束输出格式", + "type": { + "label": "问题类型", + "universal": "通用问题", + "independent": "独立问题" + }, + "answerType": { + "label": "答案类型", + "text": "文字", + "tags": "标签", + "customFormat": "自定义格式" + }, + "errors": { + "questionRequired": "请输入问题内容", + "labelsRequired": "标签类型问题至少需要一个标签", + "customFormatRequired": "请输入自定义格式", + "invalidJson": "JSON 格式不正确" + } + } + }, + "monitoring": { + "title": "资源监控看板", + "timeRange": { + "24h": "24小时", + "7d": "近7天", + "30d": "近30天" + }, + "filters": { + "allProjects": "所有项目", + "allProviders": "所有提供商", + "allStatus": "全部状态" + }, + "status": { + "success": "成功", + "failed": "失败" + }, + "actions": { + "export": "导出报表" + }, + "stats": { + "totalTokens": "总 Token 消耗", + "avgTokensPerCall": "平均 Token 消耗/次", + "totalCalls": "总调用次数", + "avgLatency": "平均响应耗时", + "inputOutput": "输入: {{input}} · 输出: {{output}}", + "successCalls": "{{count}} 成功", + "failedCalls": "{{count}} 失败", + "failureRate": "{{rate}}% 失败率", + "basedOnSuccessCalls": "基于 {{count}} 次成功请求", + "noSuccessCalls": "暂无成功请求" + }, + "charts": { + "tokenTrend": "Token 消耗趋势", + "inputLegend": "输入", + "outputLegend": "输出", + "distributionTitle": "Token 消耗分布 (按模型)", + "distributionSubtitle": "不同模型的资源消耗占比", + "tokensTooltip": "{{value}}K Tokens" + }, + "table": { + "title": "详细使用明细", + "searchPlaceholder": "搜索项目、模型或失败原因...", + "empty": "暂无数据", + "rowsPerPage": "每页行数:", + "columns": { + "projectName": "项目名称", + "provider": "模型提供商", + "model": "模型名称", + "status": "状态", + "failureReason": "失败原因", + "inputTokens": "输入 TOKEN", + "outputTokens": "输出 TOKEN", + "totalTokens": "TOTAL", + "calls": "调用次数", + "avgLatency": "平均耗时" + } + }, + "errors": { + "fetchSummaryFailed": "获取监控汇总数据失败", + "fetchLogsFailed": "获取监控日志失败" + } + }, + "eval": { + "title": "评估", + "datasets": "评估数据集", + "tasks": "自动评估任务", + "datasetsTitle": "评估数据集", + "datasetsDescription": "管理和查看所有生成的评估测试题目", + "tasksTitle": "评估任务", + "tasksComingSoon": "功能开发中", + "tasksComingSoonHint": "评估任务功能即将上线,敬请期待", + "totalQuestions": "题目总数", + "questionType": "题型", + "question": "问题", + "answer": "答案", + "options": "选项", + "correct": "正确", + "wrong": "错误", + "sourceChunk": "来源文本块", + "tags": "标签", + "tagsPlaceholder": "输入标签,多个标签用逗号分隔", + "note": "备注", + "detail": "详情", + "notFound": "未找到该题目", + "noData": "暂无评估数据", + "noDataHint": "请先在文本分割页面生成评估测试集", + "searchPlaceholder": "搜索题目内容...", + "cardView": "卡片视图", + "listView": "列表视图", + "deleteSelected": "删除选中 ({{count}})", + "deleteConfirmTitle": "确认删除", + "deleteConfirmMessage": "确定要删除 {{count}} 个题目吗?此操作不可撤销。", + "questionTypes": { + "true_false": "判断题", + "single_choice": "单选题", + "multiple_choice": "多选题", + "short_answer": "简答题", + "open_ended": "开放题" + } + }, + "evalDatasets": { + "import": { + "title": "导入评估数据集", + "questionType": "题型", + "selectTypeFirst": "请先选择题型", + "selectFile": "请选择要导入的文件", + "invalidFileType": "不支持的文件格式,请上传 json、xls 或 xlsx 文件", + "formatPreview": "数据格式预览", + "downloadTemplate": "下载模板", + "template": "模板", + "uploadFile": "上传文件", + "dropOrClick": "点击或拖拽文件到此处", + "supportedFormats": "支持 JSON、XLS、XLSX 格式", + "tags": "标签(可选)", + "tagsPlaceholder": "为导入的数据添加标签,多个标签用逗号分隔", + "tagsHelp": "导入的所有数据将打上这些标签", + "import": "导入", + "importing": "导入中...", + "failed": "导入失败", + "success": "导入成功", + "successMessage": "成功导入 {{count}} 条评估数据", + "showingErrors": "显示前 {{count}} 条错误", + "custom": "导入自定义数据集", + "builtin": "导入内置数据集", + "builtinTitle": "选择内置数据集", + "searchPlaceholder": "搜索数据集...", + "confirmImportTitle": "确认导入", + "confirmImportMessage": "确定要导入数据集 \"{{name}}\" 吗?这将添加新的评估数据到当前项目。", + "downloading": "下载中..." + }, + "export": { + "title": "导出评估数据集", + "formatLabel": "导出格式", + "filterLabel": "筛选条件", + "previewLabel": "将导出数据:", + "records": "条记录", + "largeDataHint": "数据量较大,将采用流式导出,请耐心等待", + "exporting": "导出中...", + "exportBtn": "导出", + "jsonDesc": "标准JSON数组", + "jsonlDesc": "每行一条记录", + "csvDesc": "表格格式", + "noTagsAvailable": "暂无可用标签" + } + }, + "evalTasks": { + "title": "模型评估任务", + "createTitle": "创建评估任务", + "detailTitle": "评估任务详情", + "createTask": "创建任务", + "noTasks": "暂无评估任务", + "noTasksHint": "创建评估任务来测试模型在评估数据集上的表现", + "selectModels": "选择测试模型", + "selectModelsHint": "可选择多个模型进行对比评估", + "selectJudgeModel": "选择教师模型", + "selectJudgeModelPlaceholder": "请选择...", + "selectJudgeModelHint": "教师模型用于评估简答题和开放题,不能与测试模型相同", + "judgeModel": "教师模型", + "filterByType": "按题型筛选", + "filterByTypeHint": "不选择则使用所有题目", + "selectedQuestions": "已选题目", + "questions": "道", + "hasSubjectiveHint": "包含主观题(简答题/开放题),需要选择教师模型进行评分", + "hasSubjective": "含主观题", + "startEval": "开始评估", + "progress": "进度", + "totalQuestions": "题目数量", + "status": "状态", + "totalScore": "总分", + "correctCount": "正确数", + "accuracy": "准确率", + "statsByType": "按题型统计", + "resultDetails": "评估结果详情", + "question": "题目", + "questionType": "题型", + "result": "结果", + "score": "得分", + "correctAnswer": "正确答案", + "modelAnswer": "模型回答", + "judgeResponse": "教师模型评分", + "interrupt": "中断任务", + "statusProcessing": "进行中", + "statusCompleted": "已完成", + "statusFailed": "失败", + "statusInterrupted": "已中断", + "deleteConfirmTitle": "确认删除", + "deleteConfirmMessage": "确定要删除这个评估任务吗?此操作将同时删除所有评估结果。", + "interruptConfirmTitle": "确认中断", + "interruptConfirmMessage": "确定要中断这个评估任务吗?已完成的评估结果将保留。", + "errorNoModels": "请至少选择一个测试模型", + "errorNoQuestions": "没有可用的评估题目", + "errorNoJudgeModel": "存在主观题,请选择一个教师模型用于评分", + "errorJudgeSameAsTest": "教师模型不能与测试模型相同", + "errorCreateFailed": "创建评估任务失败", + "errorLoadFailed": "加载评估任务失败", + "errorDeleteFailed": "删除评估任务失败", + "errorInterruptFailed": "中断评估任务失败", + "statusSuccess": "成功", + "statusFormatError": "格式错误", + "statusApiError": "API错误", + "statusUnknown": "未知状态", + "duration": "耗时", + "answerStatus": "答题状态", + "modelInfo": "模型信息", + "reportTitle": "模型能力评估报告", + "taskIdLabel": "任务 ID", + "pageInfo": "第 {{page}} / {{totalPages}} 页", + "noMatchingResults": "暂无符合条件的评估结果", + "reportFooter": "Easy Dataset Evaluation System · Generated by AI", + "finalSelection": "最终选择:", + "questionsSuffix": "道题目", + "noModelsAvailable": "暂无可用模型,请先在设置中配置模型", + "filterTitle": "题目筛选", + "clearFilter": "清空筛选", + "searchKeyword": "搜索关键字", + "searchPlaceholder": "搜索题目或答案内容...", + "filterByTypeLabel": "题型筛选", + "filterByTagLabel": "标签筛选", + "questionCountLabel": "题目数量:", + "useAllQuestions": "使用全部筛选结果", + "randomSampleHint": "将从 {{filteredCount}} 道题中随机抽取 {{questionCount}} 道", + "durationFormat": "(耗时 {{time}}s)", + "totalQuestionsLabel": "总题数", + "correctLabel": "正确", + "incorrectLabel": "错误", + "judgeComment": "AI 教师点评:", + "scoreUnit": "分", + "shortAnswer": "简答题", + "openEnded": "开放题", + "scoreAnchorsTitle": "{{type}}评分规则", + "customizable": "可自定义", + "scoreAnchorsHint": "自定义评分标准,用于指导LLM评估模型的回答质量", + "restoreDefault": "恢复默认", + "scoreRange": "分数区间", + "scoreDescriptionPlaceholder": "请输入该分数区间的评分标准描述..." + }, + "blindTest": { + "title": "人工盲测任务", + "createTitle": "创建盲测任务", + "createTask": "创建任务", + "noTasks": "暂无盲测任务", + "noTasksHint": "创建盲测任务来对比两个模型的回答质量", + "selectModels": "选择对比模型", + "modelA": "模型 A", + "modelB": "模型 B", + "modelComparison": "模型对比", + "selectQuestions": "选择测试题目", + "questionType": "题型", + "questionTypeHint": "盲测任务仅支持简答题和开放题", + "filterByTag": "按标签筛选", + "questionCount": "题目数量", + "availableQuestions": "可用题目:{{count}} 道", + "useAllQuestions": "使用全部筛选结果", + "randomSample": "将随机抽取 {{count}} 道题目", + "startBlindTest": "开始盲测", + "creating": "创建中...", + "noModelsAvailable": "暂无可用模型,请先在设置中配置模型", + "errorSelectModelA": "请选择模型A", + "errorSelectModelB": "请选择模型B", + "errorSameModel": "两个模型不能相同", + "errorNoQuestions": "没有符合条件的题目", + "statusProcessing": "进行中", + "statusCompleted": "已完成", + "statusFailed": "失败", + "statusInterrupted": "已中断", + "progress": "进度", + "viewDetails": "查看详情", + "continue": "继续盲测", + "interrupt": "中断任务", + "deleteConfirmTitle": "确认删除", + "deleteConfirmMessage": "确定要删除这个盲测任务吗?此操作不可撤销。", + "interruptConfirmTitle": "确认中断", + "interruptConfirmMessage": "确定要中断这个盲测任务吗?已完成的评判结果将保留。", + "inProgress": "盲测进行中", + "generatingAnswers": "正在生成回答...", + "question": "问题", + "answerA": "回答 A", + "answerB": "回答 B", + "duration": "耗时", + "whichBetter": "哪个回答更好?", + "leftBetter": "左边更好", + "rightBetter": "右边更好", + "bothGood": "都好", + "bothBad": "都不好", + "loadQuestion": "加载题目", + "taskNotFound": "任务不存在", + "resultTitle": "盲测结果", + "resultSummary": "评测结果汇总", + "wins": "胜出", + "times": "次", + "totalQuestions": "总题数", + "ties": "平局", + "detailResults": "详细结果", + "left": "左", + "right": "右" + } +} diff --git a/easy-dataset-main/next.config.js b/easy-dataset-main/next.config.js new file mode 100644 index 0000000..37f271f --- /dev/null +++ b/easy-dataset-main/next.config.js @@ -0,0 +1,19 @@ +// 最佳实践配置示例 +module.exports = { + experimental: { + serverComponentsExternalPackages: ['@opendocsg/pdf2md', 'pdfjs-dist', '@hyzyla/pdfium'], + esmExternals: 'loose' + }, + webpack: (config, { isServer }) => { + if (!isServer) { + config.externals.push({ + unpdf: 'window.unpdf', + 'pdfjs-dist': 'window.pdfjsLib' + }); + } else { + config.externals.push('pdfjs-dist'); + config.externals.push('@hyzyla/pdfium'); + } + return config; + } +}; diff --git a/easy-dataset-main/package-lock.json b/easy-dataset-main/package-lock.json new file mode 100644 index 0000000..45b89a7 --- /dev/null +++ b/easy-dataset-main/package-lock.json @@ -0,0 +1,18891 @@ +{ + "name": "easy-dataset", + "version": "1.7.2", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "easy-dataset", + "version": "1.7.2", + "license": "AGPL 3.0", + "dependencies": { + "@ai-sdk/openai": "^1.3.9", + "@ai-sdk/openai-compatible": "^1.0.22", + "@emotion/react": "^11.11.3", + "@emotion/styled": "^11.11.0", + "@fontsource/inter": "^5.0.16", + "@fontsource/jetbrains-mono": "^5.0.18", + "@huggingface/hub": "^2.0.2", + "@lobehub/icons": "^1.96.0", + "@mui/icons-material": "5.16.14", + "@mui/lab": "5.0.0-alpha.175", + "@mui/material": "5.16.14", + "@opendocsg/pdf2md": "^0.2.1", + "@openrouter/ai-sdk-provider": "^0.4.5", + "@prisma/client": "^6.6.0", + "adm-zip": "^0.5.16", + "ai": "^4.3.4", + "axios": "^1.8.4", + "electron-build": "^0.0.3", + "electron-updater": "^6.3.9", + "formidable": "^3.5.2", + "framer-motion": "^12.4.10", + "github-markdown-css": "^5.8.1", + "i18next": "^24.2.2", + "i18next-browser-languagedetector": "^8.0.4", + "image-size": "^2.0.2", + "jotai": "^2.12.3", + "jsonrepair": "^3.13.1", + "jszip": "^3.10.1", + "langchain": "^0.3.24", + "mammoth": "^1.9.0", + "nanoid": "^5.1.5", + "next": "^14.2.29", + "next-themes": "^0.2.1", + "ollama-ai-provider": "^1.2.0", + "opener": "^1.5.2", + "pdf2md-js": "1.0.8", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-i18next": "^15.4.1", + "react-markdown": "^10.0.1", + "recharts": "^3.6.0", + "sharp": "^0.33.1", + "sonner": "^2.0.3", + "turndown": "^7.2.0", + "xlsx": "^0.18.5", + "xmldom": "^0.6.0", + "zhipu-ai-provider": "^0.1.1", + "zod": "^3.25.76" + }, + "bin": { + "easy-dataset": "desktop/server.js" + }, + "devDependencies": { + "@commitlint/cli": "^19.8.0", + "@commitlint/config-conventional": "^19.8.0", + "concurrently": "^8.2.2", + "electron": "^35.0.0", + "electron-builder": "^24.13.3", + "husky": "^9.1.7", + "lint-staged": "15.5.2", + "pkg": "^5.8.1", + "prisma": "^6.6.0", + "wait-on": "^7.2.0" + } + }, + "node_modules/@ai-sdk/openai": { + "version": "1.3.22", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-1.3.22.tgz", + "integrity": "sha512-QwA+2EkG0QyjVR+7h6FE7iOu2ivNqAVMm9UJZkVxxTk5OIq5fFJDTEI/zICEMuHImTTXR2JjsL6EirJ28Jc4cw==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "1.1.3", + "@ai-sdk/provider-utils": "2.2.8" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.0.0" + } + }, + "node_modules/@ai-sdk/openai-compatible": { + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-1.0.22.tgz", + "integrity": "sha512-Q+lwBIeMprc/iM+vg1yGjvzRrp74l316wDpqWdbmd4VXXlllblzGsUgBLTeKvcEapFTgqk0FRETvSb58Y6dsfA==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.12" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/openai-compatible/node_modules/@ai-sdk/provider": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.0.tgz", + "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==", + "license": "Apache-2.0", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/openai-compatible/node_modules/@ai-sdk/provider-utils": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.12.tgz", + "integrity": "sha512-ZtbdvYxdMoria+2SlNarEk6Hlgyf+zzcznlD55EAl+7VZvJaSg2sqPvwArY7L6TfDEDJsnCq0fdhBSkYo0Xqdg==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@standard-schema/spec": "^1.0.0", + "eventsource-parser": "^3.0.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/provider": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-1.1.3.tgz", + "integrity": "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg==", + "license": "Apache-2.0", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/provider-utils": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-2.2.8.tgz", + "integrity": "sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "1.1.3", + "nanoid": "^3.3.8", + "secure-json-parse": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.23.8" + } + }, + "node_modules/@ai-sdk/provider-utils/node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/@ai-sdk/react": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@ai-sdk/react/-/react-1.2.12.tgz", + "integrity": "sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider-utils": "2.2.8", + "@ai-sdk/ui-utils": "1.2.11", + "swr": "^2.2.5", + "throttleit": "2.1.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/@ai-sdk/ui-utils": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@ai-sdk/ui-utils/-/ui-utils-1.2.11.tgz", + "integrity": "sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "1.1.3", + "@ai-sdk/provider-utils": "2.2.8", + "zod-to-json-schema": "^3.24.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.23.8" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@ant-design/colors": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-7.2.1.tgz", + "integrity": "sha512-lCHDcEzieu4GA3n8ELeZ5VQ8pKQAWcGGLRTQ50aQM2iqPpq2evTxER84jfdPvsPAtEcZ7m44NI45edFMo8oOYQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@ant-design/fast-color": "^2.0.6" + } + }, + "node_modules/@ant-design/cssinjs": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@ant-design/cssinjs/-/cssinjs-1.23.0.tgz", + "integrity": "sha512-7GAg9bD/iC9ikWatU9ym+P9ugJhi/WbsTWzcKN6T4gU0aehsprtke1UAaaSxxkjjmkJb3llet/rbUSLPgwlY4w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "@emotion/hash": "^0.8.0", + "@emotion/unitless": "^0.7.5", + "classnames": "^2.3.1", + "csstype": "^3.1.3", + "rc-util": "^5.35.0", + "stylis": "^4.3.4" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@ant-design/cssinjs-utils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@ant-design/cssinjs-utils/-/cssinjs-utils-1.1.3.tgz", + "integrity": "sha512-nOoQMLW1l+xR1Co8NFVYiP8pZp3VjIIzqV6D6ShYF2ljtdwWJn5WSsH+7kvCktXL/yhEtWURKOfH5Xz/gzlwsg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@ant-design/cssinjs": "^1.21.0", + "@babel/runtime": "^7.23.2", + "rc-util": "^5.38.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@ant-design/cssinjs/node_modules/@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==", + "license": "MIT" + }, + "node_modules/@ant-design/cssinjs/node_modules/@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==", + "license": "MIT" + }, + "node_modules/@ant-design/cssinjs/node_modules/stylis": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", + "license": "MIT" + }, + "node_modules/@ant-design/fast-color": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@ant-design/fast-color/-/fast-color-2.0.6.tgz", + "integrity": "sha512-y2217gk4NqL35giHl72o6Zzqji9O7vHh9YmhUVkPtAOpoTCH4uWxo/pr4VE8t0+ChEPs0qo4eJRC5Q1eXWo3vA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.24.7" + }, + "engines": { + "node": ">=8.x" + } + }, + "node_modules/@ant-design/icons": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-5.6.1.tgz", + "integrity": "sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@ant-design/colors": "^7.0.0", + "@ant-design/icons-svg": "^4.4.0", + "@babel/runtime": "^7.24.8", + "classnames": "^2.2.6", + "rc-util": "^5.31.1" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@ant-design/icons-svg": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz", + "integrity": "sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==", + "license": "MIT", + "peer": true + }, + "node_modules/@ant-design/react-slick": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-1.1.2.tgz", + "integrity": "sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.4", + "classnames": "^2.2.5", + "json2mq": "^0.2.0", + "resize-observer-polyfill": "^1.5.1", + "throttle-debounce": "^5.0.0" + }, + "peerDependencies": { + "react": ">=16.9.0" + } + }, + "node_modules/@antfu/install-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", + "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==", + "license": "MIT", + "dependencies": { + "package-manager-detector": "^1.3.0", + "tinyexec": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@antfu/utils": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-8.1.1.tgz", + "integrity": "sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.5.tgz", + "integrity": "sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.4.tgz", + "integrity": "sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.4", + "@babel/parser": "^7.27.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.27.4", + "@babel/types": "^7.27.3", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT", + "peer": true + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "peer": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.5.tgz", + "integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.5", + "@babel/types": "^7.27.3", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC" + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.4.tgz", + "integrity": "sha512-jljfR1rGnXXNWnmQg2K3+bvhkxB51Rl32QRaOTuwwjviGrHzIbSc8+x9CpraDtbT7mfyjXObULP4w/adunNwAw==", + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", + "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz", + "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.27.4.tgz", + "integrity": "sha512-D68nR5zxU64EUzV8i7T3R5XP0Xhrou/amNnddsRQssx6GrTLdZl1rLxyjtVZBd+v/NVX4AbTPOB5aU8thAZV1A==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.11.0", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/runtime": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", + "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.4.tgz", + "integrity": "sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.3", + "@babel/parser": "^7.27.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.3", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz", + "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@braintree/sanitize-url": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.1.tgz", + "integrity": "sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw==", + "license": "MIT" + }, + "node_modules/@cfworker/json-schema": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@cfworker/json-schema/-/json-schema-4.1.1.tgz", + "integrity": "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==", + "license": "MIT", + "peer": true + }, + "node_modules/@chevrotain/cst-dts-gen": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz", + "integrity": "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==", + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/gast": "11.0.3", + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/@chevrotain/gast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.0.3.tgz", + "integrity": "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==", + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/@chevrotain/regexp-to-ast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.0.3.tgz", + "integrity": "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==", + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/types": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.0.3.tgz", + "integrity": "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==", + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/utils": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz", + "integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==", + "license": "Apache-2.0" + }, + "node_modules/@commitlint/cli": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-19.8.1.tgz", + "integrity": "sha512-LXUdNIkspyxrlV6VDHWBmCZRtkEVRpBKxi2Gtw3J54cGWhLCTouVD/Q6ZSaSvd2YaDObWK8mDjrz3TIKtaQMAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/format": "^19.8.1", + "@commitlint/lint": "^19.8.1", + "@commitlint/load": "^19.8.1", + "@commitlint/read": "^19.8.1", + "@commitlint/types": "^19.8.1", + "tinyexec": "^1.0.0", + "yargs": "^17.0.0" + }, + "bin": { + "commitlint": "cli.js" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/config-conventional": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-19.8.1.tgz", + "integrity": "sha512-/AZHJL6F6B/G959CsMAzrPKKZjeEiAVifRyEwXxcT6qtqbPwGw+iQxmNS+Bu+i09OCtdNRW6pNpBvgPrtMr9EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^19.8.1", + "conventional-changelog-conventionalcommits": "^7.0.2" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/config-validator": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-19.8.1.tgz", + "integrity": "sha512-0jvJ4u+eqGPBIzzSdqKNX1rvdbSU1lPNYlfQQRIFnBgLy26BtC0cFnr7c/AyuzExMxWsMOte6MkTi9I3SQ3iGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^19.8.1", + "ajv": "^8.11.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/ensure": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-19.8.1.tgz", + "integrity": "sha512-mXDnlJdvDzSObafjYrOSvZBwkD01cqB4gbnnFuVyNpGUM5ijwU/r/6uqUmBXAAOKRfyEjpkGVZxaDsCVnHAgyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^19.8.1", + "lodash.camelcase": "^4.3.0", + "lodash.kebabcase": "^4.1.1", + "lodash.snakecase": "^4.1.1", + "lodash.startcase": "^4.4.0", + "lodash.upperfirst": "^4.3.1" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/execute-rule": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-19.8.1.tgz", + "integrity": "sha512-YfJyIqIKWI64Mgvn/sE7FXvVMQER/Cd+s3hZke6cI1xgNT/f6ZAz5heND0QtffH+KbcqAwXDEE1/5niYayYaQA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/format": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-19.8.1.tgz", + "integrity": "sha512-kSJj34Rp10ItP+Eh9oCItiuN/HwGQMXBnIRk69jdOwEW9llW9FlyqcWYbHPSGofmjsqeoxa38UaEA5tsbm2JWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^19.8.1", + "chalk": "^5.3.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/is-ignored": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-19.8.1.tgz", + "integrity": "sha512-AceOhEhekBUQ5dzrVhDDsbMaY5LqtN8s1mqSnT2Kz1ERvVZkNihrs3Sfk1Je/rxRNbXYFzKZSHaPsEJJDJV8dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^19.8.1", + "semver": "^7.6.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/lint": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-19.8.1.tgz", + "integrity": "sha512-52PFbsl+1EvMuokZXLRlOsdcLHf10isTPlWwoY1FQIidTsTvjKXVXYb7AvtpWkDzRO2ZsqIgPK7bI98x8LRUEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/is-ignored": "^19.8.1", + "@commitlint/parse": "^19.8.1", + "@commitlint/rules": "^19.8.1", + "@commitlint/types": "^19.8.1" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/load": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-19.8.1.tgz", + "integrity": "sha512-9V99EKG3u7z+FEoe4ikgq7YGRCSukAcvmKQuTtUyiYPnOd9a2/H9Ak1J9nJA1HChRQp9OA/sIKPugGS+FK/k1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/config-validator": "^19.8.1", + "@commitlint/execute-rule": "^19.8.1", + "@commitlint/resolve-extends": "^19.8.1", + "@commitlint/types": "^19.8.1", + "chalk": "^5.3.0", + "cosmiconfig": "^9.0.0", + "cosmiconfig-typescript-loader": "^6.1.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "lodash.uniq": "^4.5.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/message": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-19.8.1.tgz", + "integrity": "sha512-+PMLQvjRXiU+Ae0Wc+p99EoGEutzSXFVwQfa3jRNUZLNW5odZAyseb92OSBTKCu+9gGZiJASt76Cj3dLTtcTdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/parse": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-19.8.1.tgz", + "integrity": "sha512-mmAHYcMBmAgJDKWdkjIGq50X4yB0pSGpxyOODwYmoexxxiUCy5JJT99t1+PEMK7KtsCtzuWYIAXYAiKR+k+/Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^19.8.1", + "conventional-changelog-angular": "^7.0.0", + "conventional-commits-parser": "^5.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/read": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-19.8.1.tgz", + "integrity": "sha512-03Jbjb1MqluaVXKHKRuGhcKWtSgh3Jizqy2lJCRbRrnWpcM06MYm8th59Xcns8EqBYvo0Xqb+2DoZFlga97uXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/top-level": "^19.8.1", + "@commitlint/types": "^19.8.1", + "git-raw-commits": "^4.0.0", + "minimist": "^1.2.8", + "tinyexec": "^1.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/resolve-extends": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-19.8.1.tgz", + "integrity": "sha512-GM0mAhFk49I+T/5UCYns5ayGStkTt4XFFrjjf0L4S26xoMTSkdCf9ZRO8en1kuopC4isDFuEm7ZOm/WRVeElVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/config-validator": "^19.8.1", + "@commitlint/types": "^19.8.1", + "global-directory": "^4.0.1", + "import-meta-resolve": "^4.0.0", + "lodash.mergewith": "^4.6.2", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/rules": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-19.8.1.tgz", + "integrity": "sha512-Hnlhd9DyvGiGwjfjfToMi1dsnw1EXKGJNLTcsuGORHz6SS9swRgkBsou33MQ2n51/boIDrbsg4tIBbRpEWK2kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/ensure": "^19.8.1", + "@commitlint/message": "^19.8.1", + "@commitlint/to-lines": "^19.8.1", + "@commitlint/types": "^19.8.1" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/to-lines": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-19.8.1.tgz", + "integrity": "sha512-98Mm5inzbWTKuZQr2aW4SReY6WUukdWXuZhrqf1QdKPZBCCsXuG87c+iP0bwtD6DBnmVVQjgp4whoHRVixyPBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/top-level": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-19.8.1.tgz", + "integrity": "sha512-Ph8IN1IOHPSDhURCSXBz44+CIu+60duFwRsg6HqaISFHQHbmBtxVw4ZrFNIYUzEP7WwrNPxa2/5qJ//NK1FGcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^7.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/types": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-19.8.1.tgz", + "integrity": "sha512-/yCrWGCoA1SVKOks25EGadP9Pnj0oAIHGpl2wH2M2Y46dPM2ueb8wyCVOD7O3WCTkaJ0IkKvzhl1JY7+uCT2Dw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/conventional-commits-parser": "^5.0.0", + "chalk": "^5.3.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@develar/schema-utils": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz", + "integrity": "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.0", + "ajv-keywords": "^3.4.1" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/@develar/schema-utils/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@develar/schema-utils/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/@develar/schema-utils/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@dnd-kit/accessibility": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz", + "integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/core": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", + "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", + "license": "MIT", + "dependencies": { + "@dnd-kit/accessibility": "^3.1.1", + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/modifiers": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@dnd-kit/modifiers/-/modifiers-9.0.0.tgz", + "integrity": "sha512-ybiLc66qRGuZoC20wdSSG6pDXFikui/dCNGthxv4Ndy8ylErY0N3KVxY2bgo7AWwIbxDmXDg3ylAFmnrjcbVvw==", + "license": "MIT", + "dependencies": { + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@dnd-kit/core": "^6.3.0", + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/sortable": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-10.0.0.tgz", + "integrity": "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==", + "license": "MIT", + "dependencies": { + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@dnd-kit/core": "^6.3.0", + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/utilities": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz", + "integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@electron/asar": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.4.1.tgz", + "integrity": "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^5.0.0", + "glob": "^7.1.6", + "minimatch": "^3.0.4" + }, + "bin": { + "asar": "bin/asar.js" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/@electron/asar/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@electron/asar/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@electron/get": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz", + "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^11.8.5", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "global-agent": "^3.0.0" + } + }, + "node_modules/@electron/get/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@electron/notarize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.2.1.tgz", + "integrity": "sha512-aL+bFMIkpR0cmmj5Zgy0LMKEpgy43/hw5zadEArgmAMWWlKc5buwFvFT9G/o/YJkvXAJm5q3iuTuLaiaXW39sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^9.0.1", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/notarize/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@electron/notarize/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/notarize/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/osx-sign": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.0.5.tgz", + "integrity": "sha512-k9ZzUQtamSoweGQDV2jILiRIHUu7lYlJ3c6IEmjv1hC17rclE+eb9U+f6UFlOOETo0JzY1HNlXy4YOlCvl+Lww==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "compare-version": "^0.1.2", + "debug": "^4.3.4", + "fs-extra": "^10.0.0", + "isbinaryfile": "^4.0.8", + "minimist": "^1.2.6", + "plist": "^3.0.5" + }, + "bin": { + "electron-osx-flat": "bin/electron-osx-flat.js", + "electron-osx-sign": "bin/electron-osx-sign.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@electron/osx-sign/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@electron/osx-sign/node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/@electron/osx-sign/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/osx-sign/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/universal": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.5.1.tgz", + "integrity": "sha512-kbgXxyEauPJiQQUNG2VgUeyfQNFk6hBF11ISN2PNI6agUgPl55pv4eQmaqHzTAzchBvqZ2tQuRVaPStGf0mxGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron/asar": "^3.2.1", + "@malept/cross-spawn-promise": "^1.1.0", + "debug": "^4.3.1", + "dir-compare": "^3.0.0", + "fs-extra": "^9.0.1", + "minimatch": "^3.0.4", + "plist": "^3.0.4" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/@electron/universal/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@electron/universal/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@electron/universal/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/universal/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@electron/universal/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-0.44.0.tgz", + "integrity": "sha512-ZX/etZEZw8DR7zAB1eVQT40lNo0jeqpb6dCgOvctB6FIQ5PoXfMuNY8+ayQfu8tNQbAB8gQWSSJupR8NxeiZXw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emoji-mart/data": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emoji-mart/data/-/data-1.2.1.tgz", + "integrity": "sha512-no2pQMWiBy6gpBEiqGeU77/bFejDqUTRY7KX+0+iur13op3bqUsXdnwoZs6Xb1zbv0gAj5VvS1PWoUUckSr5Dw==", + "license": "MIT" + }, + "node_modules/@emoji-mart/react": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@emoji-mart/react/-/react-1.1.1.tgz", + "integrity": "sha512-NMlFNeWgv1//uPsvLxvGQoIerPuVdXwK/EUek8OOkJ6wVOWPUizRBJU0hDqWZCOROVpfBgCemaC3m6jDOXi03g==", + "license": "MIT", + "peerDependencies": { + "emoji-mart": "^5.2", + "react": "^16.8 || ^17 || ^18" + } + }, + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/css": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/css/-/css-11.13.5.tgz", + "integrity": "sha512-wQdD0Xhkn3Qy2VNcIzbLP9MR8TafI0MJb7BEAXKp+w4+XqErksWR4OXomuDzPsN4InLdGhVe6EYcn2ZIUCpB8w==", + "license": "MIT", + "dependencies": { + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.13.5", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz", + "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" + }, + "node_modules/@emotion/styled": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz", + "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" + }, + "node_modules/@floating-ui/core": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.1.tgz", + "integrity": "sha512-azI0DrjMMfIug/ExbBaeDVJXcY0a7EPvPjb2xAJPa4HeimBX+Z18HK8QQR3jb6356SnDDdxx+hinMLcJEDdOjw==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.1.tgz", + "integrity": "sha512-cwsmW/zyw5ltYTUeeYJ60CnQuPqmGwuGVhG9w0PRaRKkAyi38BT5CKrpIbb+jtahSwUl04cWzSx9ZOIxeS6RsQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.1", + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/react": { + "version": "0.27.12", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.12.tgz", + "integrity": "sha512-kKlWNrpIQxF1B/a2MZvE0/uyKby4960yjO91W7nVyNKmmfNi62xU9HCjL1M1eWzx/LFj/VPSwJVbwQk9Pq/68A==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.1.3", + "@floating-ui/utils": "^0.2.9", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.3.tgz", + "integrity": "sha512-huMBfiU9UnQ2oBwIhgzyIiSpVgvlDstU8CX0AF+wS+KzmYMs0J2a3GwuFHV1Lz+jlrQGeC1fF+Nv0QoumyV0bA==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", + "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", + "license": "MIT" + }, + "node_modules/@fontsource/inter": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/@fontsource/inter/-/inter-5.2.6.tgz", + "integrity": "sha512-CZs9S1CrjD0jPwsNy9W6j0BhsmRSQrgwlTNkgQXTsAeDRM42LBRLo3eo9gCzfH4GvV7zpyf78Ozfl773826csw==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/jetbrains-mono": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/@fontsource/jetbrains-mono/-/jetbrains-mono-5.2.6.tgz", + "integrity": "sha512-nz//dBr99hXZmHp10wgNI00qThWImkzRR5PQjvRM+rpmuHO5rYBJCqPPWufidCvmkkryXx/GOP/lgqsM3R3Org==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@giscus/react": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@giscus/react/-/react-3.1.0.tgz", + "integrity": "sha512-0TCO2TvL43+oOdyVVGHDItwxD1UMKP2ZYpT6gXmhFOqfAJtZxTzJ9hkn34iAF/b6YzyJ4Um89QIt9z/ajmAEeg==", + "dependencies": { + "giscus": "^1.6.0" + }, + "peerDependencies": { + "react": "^16 || ^17 || ^18 || ^19", + "react-dom": "^16 || ^17 || ^18 || ^19" + } + }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@huggingface/hub": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@huggingface/hub/-/hub-2.2.0.tgz", + "integrity": "sha512-G+VS1eMp80KovIHBlsiEigS6I6qmI4j+VQ1UZ8CaXT+pw2A7tj6e/crfxFdKNE2uOK5oQkRFiCBJykMwrWQ8OA==", + "license": "MIT", + "dependencies": { + "@huggingface/tasks": "^0.19.11" + }, + "bin": { + "hfjs": "dist/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@huggingface/tasks": { + "version": "0.19.14", + "resolved": "https://registry.npmjs.org/@huggingface/tasks/-/tasks-0.19.14.tgz", + "integrity": "sha512-TlyCOsUc+7y/uILTF8z61JzqBKfK50hIs0nDNmp+FiwXfQt4fR7Z0eUduq5JUm8uK5vL/l5pn9bjmXnDawYUOQ==", + "license": "MIT" + }, + "node_modules/@hyzyla/pdfium": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@hyzyla/pdfium/-/pdfium-2.1.7.tgz", + "integrity": "sha512-GQ0mxuVY15RHAyxVRC8IUREAeuhZ+Efab8czhvwiw3mveNmBQAtaV3x2O70NaQlu5juzaP0DXK3Jq8bh6Jgjjw==", + "license": "MIT" + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "license": "MIT" + }, + "node_modules/@iconify/utils": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-2.3.0.tgz", + "integrity": "sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==", + "license": "MIT", + "dependencies": { + "@antfu/install-pkg": "^1.0.0", + "@antfu/utils": "^8.1.0", + "@iconify/types": "^2.0.0", + "debug": "^4.4.0", + "globals": "^15.14.0", + "kolorist": "^1.8.0", + "local-pkg": "^1.0.0", + "mlly": "^1.7.4" + } + }, + "node_modules/@iconify/utils/node_modules/globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.1.tgz", + "integrity": "sha512-esr2BZ1x0bo+wl7Gx2hjssYhjrhUsD88VQulI0FrG8/otRQUOxLWHMBd1Y1qo2Gfg2KUvXNpT0ASnV9BzJCexw==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.0" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.1.tgz", + "integrity": "sha512-YrnuB3bXuWdG+hJlXtq7C73lF8ampkhU3tMxg5Hh+E7ikxbUVOU9nlNtVTloDXz6pRHt2y2oKJq7DY/yt+UXYw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.0" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.0.tgz", + "integrity": "sha512-VzYd6OwnUR81sInf3alj1wiokY50DjsHz5bvfnsFpxs5tqQxESoHtJO6xyksDs3RIkyhMWq2FufXo6GNSU9BMw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "macos": ">=11", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.0.tgz", + "integrity": "sha512-dD9OznTlHD6aovRswaPNEy8dKtSAmNo4++tO7uuR4o5VxbVAOoEQ1uSmN4iFAdQneTHws1lkTZeiXPrcCkh6IA==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "macos": ">=10.13", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.0.tgz", + "integrity": "sha512-VwgD2eEikDJUk09Mn9Dzi1OW2OJFRQK+XlBTkUNmAWPrtj8Ly0yq05DFgu1VCMx2/DqCGQVi5A1dM9hTmxf3uw==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.28", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.0.tgz", + "integrity": "sha512-xTYThiqEZEZc0PRU90yVtM3KE7lw1bKdnDQ9kCTHWbqWyHOe4NpPOtMGy27YnN51q0J5dqRrvicfPbALIOeAZA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.26", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.0.tgz", + "integrity": "sha512-o9E46WWBC6JsBlwU4QyU9578G77HBDT1NInd+aERfxeOPbk0qBZHgoDsQmA2v9TbqJRWzoBPx1aLOhprBMgPjw==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.28", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.0.tgz", + "integrity": "sha512-naldaJy4hSVhWBgEjfdBY85CAa4UO+W1nx6a1sWStHZ7EUfNiuBTTN2KUYT5dH1+p/xij1t2QSXfCiFJoC5S/Q==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.26", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.0.tgz", + "integrity": "sha512-OdorplCyvmSAPsoJLldtLh3nLxRrkAAAOHsGWGDYfN0kh730gifK+UZb3dWORRa6EusNqCTjfXV4GxvgJ/nPDQ==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "musl": ">=1.2.2", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.0.tgz", + "integrity": "sha512-FW8iK6rJrg+X2jKD0Ajhjv6y74lToIBEvkZhl42nZt563FfxkCYacrXZtd+q/sRQDypQLzY5WdLkVTbJoPyqNg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "musl": ">=1.2.2", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.1.tgz", + "integrity": "sha512-Ii4X1vnzzI4j0+cucsrYA5ctrzU9ciXERfJR633S2r39CiD8npqH2GMj63uFZRCFt3E687IenAdbwIpQOJ5BNA==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.28", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.0" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.1.tgz", + "integrity": "sha512-59B5GRO2d5N3tIfeGHAbJps7cLpuWEQv/8ySd9109ohQ3kzyCACENkFVAnGPX00HwPTQcaBNF7HQYEfZyZUFfw==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.0" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.1.tgz", + "integrity": "sha512-tRGrb2pHnFUXpOAj84orYNxHADBDIr0J7rrjwQrTNMQMWA4zy3StKmMvwsI7u3dEZcgwuMMooIIGWEWOjnmG8A==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.28", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.0" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.1.tgz", + "integrity": "sha512-4y8osC0cAc1TRpy02yn5omBeloZZwS62fPZ0WUAYQiLhSFSpWJfY/gMrzKzLcHB9ulUV6ExFiu2elMaixKDbeg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.0" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.1.tgz", + "integrity": "sha512-D3lV6clkqIKUizNS8K6pkuCKNGmWoKlBGh5p0sLO2jQERzbakhu4bVX1Gz+RS4vTZBprKlWaf+/Rdp3ni2jLfA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "musl": ">=1.2.2", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.0" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.1.tgz", + "integrity": "sha512-LOGKNu5w8uu1evVqUAUKTix2sQu1XDRIYbsi5Q0c/SrXhvJ4QyOx+GaajxmOg5PZSsSnCYPSmhjHHsRBx06/wQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "musl": ">=1.2.2", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.0" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.1.tgz", + "integrity": "sha512-vWI/sA+0p+92DLkpAMb5T6I8dg4z2vzCUnp8yvxHlwBpzN8CIcO3xlSXrLltSvK6iMsVMNswAv+ub77rsf25lA==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^0.44.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.1.tgz", + "integrity": "sha512-/xhYkylsKL05R+NXGJc9xr2Tuw6WIVl2lubFJaFYfW4/MQ4J+dgjIo/T4qjNRizrqs/szF/lC9a5+updmY9jaQ==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.1.tgz", + "integrity": "sha512-XaM69X0n6kTEsp9tVYYLhXdg7Qj32vYJlAKRutxUsm1UlgQNx6BOhHwZPwukCGXBU2+tH87ip2eV1I/E8MQnZg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@langchain/core": { + "version": "0.3.57", + "resolved": "https://registry.npmjs.org/@langchain/core/-/core-0.3.57.tgz", + "integrity": "sha512-jz28qCTKJmi47b6jqhQ6vYRTG5jRpqhtPQjriRTB5wR8mgvzo6xKs0fG/kExS3ZvM79ytD1npBvgf8i19xOo9Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "@cfworker/json-schema": "^4.0.2", + "ansi-styles": "^5.0.0", + "camelcase": "6", + "decamelize": "1.2.0", + "js-tiktoken": "^1.0.12", + "langsmith": "^0.3.29", + "mustache": "^4.2.0", + "p-queue": "^6.6.2", + "p-retry": "4", + "uuid": "^10.0.0", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@langchain/openai": { + "version": "0.5.12", + "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-0.5.12.tgz", + "integrity": "sha512-k7rxBY3ed/HIiMLd6HBqFibsfB0+L6c82H8JgXDqKeyUoACJIi1JaKHXmofmCeF2SBXBU9dog4gEGpHfcUDGUA==", + "license": "MIT", + "dependencies": { + "js-tiktoken": "^1.0.12", + "openai": "^4.96.0", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@langchain/core": ">=0.3.48 <0.4.0" + } + }, + "node_modules/@langchain/textsplitters": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@langchain/textsplitters/-/textsplitters-0.1.0.tgz", + "integrity": "sha512-djI4uw9rlkAb5iMhtLED+xJebDdAG935AdP4eRTB02R7OB/act55Bj9wsskhZsvuyQRpO4O1wQOp85s6T6GWmw==", + "license": "MIT", + "dependencies": { + "js-tiktoken": "^1.0.12" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@langchain/core": ">=0.2.21 <0.4.0" + } + }, + "node_modules/@lit-labs/ssr-dom-shim": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.3.0.tgz", + "integrity": "sha512-nQIWonJ6eFAvUUrSlwyHDm/aE8PBDu5kRpL0vHMg6K8fK3Diq1xdPjTnsJSwxABhaZ+5eBi1btQB5ShUTKo4nQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@lit/reactive-element": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.1.0.tgz", + "integrity": "sha512-L2qyoZSQClcBmq0qajBVbhYEcG6iK0XfLn66ifLe/RfC0/ihpc+pl0Wdn8bJ8o+hj38cG0fGXRgSS20MuXn7qA==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.2.0" + } + }, + "node_modules/@lobehub/emojilib": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@lobehub/emojilib/-/emojilib-1.0.0.tgz", + "integrity": "sha512-s9KnjaPjsEefaNv150G3aifvB+J3P4eEKG+epY9zDPS2BeB6+V2jELWqAZll+nkogMaVovjEE813z3V751QwGw==", + "license": "MIT" + }, + "node_modules/@lobehub/fluent-emoji": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@lobehub/fluent-emoji/-/fluent-emoji-1.2.0.tgz", + "integrity": "sha512-l2Ed3kyNQpiquRVROYO/frFifuuGoXizkC7e93UIJ9lRLnwLx2XKi0EqGse0Qld/BHsfJfrlXhbwNBYd2RDZqQ==", + "license": "MIT", + "dependencies": { + "@lobehub/emojilib": "^1.0.0", + "@lobehub/ui": "^1.161.0", + "antd-style": "^3.7.1", + "emoji-regex": "^10.4.0", + "lodash-es": "^4.17.21", + "lucide-react": "^0.469.0", + "react-layout-kit": "^1.9.1", + "url-join": "^5.0.0" + }, + "peerDependencies": { + "antd": "^5.23.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/@lobehub/icons": { + "version": "1.97.2", + "resolved": "https://registry.npmjs.org/@lobehub/icons/-/icons-1.97.2.tgz", + "integrity": "sha512-b6aGDVc+mgyyvfmhclX0XvH1vcaXb7oG6HQ8oGFaeU1CHghI/suiG40YQOWYYt5u8vqyVVbZVKm+i+KEp75j6A==", + "license": "MIT", + "dependencies": { + "@lobehub/ui": "^1.168.7", + "antd-style": "^3.7.1", + "lucide-react": "^0.469.0", + "polished": "^4.3.1", + "react-layout-kit": "^1.9.1" + }, + "peerDependencies": { + "antd": "^5.23.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/@lobehub/ui": { + "version": "1.171.0", + "resolved": "https://registry.npmjs.org/@lobehub/ui/-/ui-1.171.0.tgz", + "integrity": "sha512-MP1AUHmV15lCcxps7Ncw9QEeWF4Zlj/o1Lvi0MoEWVsdE1myv6I/DRlXcyJLKXgU/FKrMX6EAcFrE+NwlXUtfw==", + "license": "MIT", + "dependencies": { + "@ant-design/cssinjs": "^1.23.0", + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/modifiers": "^9.0.0", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", + "@emoji-mart/data": "^1.2.1", + "@emoji-mart/react": "^1.1.1", + "@floating-ui/react": "^0.27.5", + "@giscus/react": "^3.1.0", + "@lobehub/fluent-emoji": "^1.2.0", + "@lobehub/icons": "^1.94.0", + "@mdx-js/mdx": "^3.1.0", + "@mdx-js/react": "^3.1.0", + "@radix-ui/react-slot": "^1.1.2", + "@shikijs/transformers": "^3.2.1", + "@splinetool/runtime": "0.9.526", + "ahooks": "^3.8.4", + "antd-style": "^3.7.1", + "chroma-js": "^3.1.2", + "class-variance-authority": "^0.7.1", + "dayjs": "^1.11.13", + "emoji-mart": "^5.6.0", + "fast-deep-equal": "^3.1.3", + "framer-motion": "^11.18.2", + "immer": "^10.1.1", + "leva": "^0.10.0", + "lodash-es": "^4.17.21", + "lucide-react": "^0.484.0", + "mermaid": "^11.6.0", + "numeral": "^2.0.6", + "polished": "^4.3.1", + "query-string": "^9.1.1", + "rc-footer": "^0.6.8", + "re-resizable": "^6.11.2", + "react-avatar-editor": "^13.0.2", + "react-error-boundary": "^5.0.0", + "react-hotkeys-hook": "^4.6.1", + "react-layout-kit": "^1.9.1", + "react-markdown": "^10.1.0", + "react-merge-refs": "^2.1.1", + "react-rnd": "^10.5.2", + "react-zoom-pan-pinch": "^3.7.0", + "rehype-katex": "^7.0.1", + "rehype-raw": "^7.0.0", + "remark-breaks": "^4.0.0", + "remark-gfm": "^4.0.1", + "remark-math": "^6.0.0", + "shiki": "^3.2.1", + "swr": "^2.3.3", + "ts-md5": "^1.3.1", + "unified": "^11.0.5", + "url-join": "^5.0.0", + "use-merge-value": "^1.2.0", + "uuid": "^11.1.0" + }, + "peerDependencies": { + "antd": "^5.23.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/@lobehub/ui/node_modules/framer-motion": { + "version": "11.18.2", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz", + "integrity": "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==", + "license": "MIT", + "dependencies": { + "motion-dom": "^11.18.1", + "motion-utils": "^11.18.1", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@lobehub/ui/node_modules/lucide-react": { + "version": "0.484.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.484.0.tgz", + "integrity": "sha512-oZy8coK9kZzvqhSgfbGkPtTgyjpBvs3ukLgDPv14dSOZtBtboryWF5o8i3qen7QbGg7JhiJBz5mK1p8YoMZTLQ==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@lobehub/ui/node_modules/motion-dom": { + "version": "11.18.1", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz", + "integrity": "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==", + "license": "MIT", + "dependencies": { + "motion-utils": "^11.18.1" + } + }, + "node_modules/@lobehub/ui/node_modules/motion-utils": { + "version": "11.18.1", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.18.1.tgz", + "integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==", + "license": "MIT" + }, + "node_modules/@lobehub/ui/node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/@malept/cross-spawn-promise": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", + "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" + } + ], + "license": "Apache-2.0", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@malept/flatpak-bundler": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz", + "integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^9.0.0", + "lodash": "^4.17.15", + "tmp-promise": "^3.0.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@malept/flatpak-bundler/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@malept/flatpak-bundler/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@malept/flatpak-bundler/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@mdx-js/mdx": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.0.tgz", + "integrity": "sha512-/QxEhPAvGwbQmy1Px8F899L5Uc2KZ6JtXwlCgJmjSTBedwOZkByYcBG4GceIGPXRDsmfxhHazuS+hlOShRLeDw==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdx": "^2.0.0", + "collapse-white-space": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-util-scope": "^1.0.0", + "estree-walker": "^3.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "markdown-extensions": "^2.0.0", + "recma-build-jsx": "^1.0.0", + "recma-jsx": "^1.0.0", + "recma-stringify": "^1.0.0", + "rehype-recma": "^1.0.0", + "remark-mdx": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "source-map": "^0.7.0", + "unified": "^11.0.0", + "unist-util-position-from-estree": "^2.0.0", + "unist-util-stringify-position": "^4.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mdx-js/mdx/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@mdx-js/react": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.0.tgz", + "integrity": "sha512-QjHtSaoameoalGnKDT3FoIl4+9RwyTmo9ZJGBdLOks/YOiWHoRDI3PUwEzOE7kEmGcV3AFcp9K6dYu9rEuKLAQ==", + "license": "MIT", + "dependencies": { + "@types/mdx": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=16", + "react": ">=16" + } + }, + "node_modules/@mermaid-js/parser": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.4.0.tgz", + "integrity": "sha512-wla8XOWvQAwuqy+gxiZqY+c7FokraOTHRWMsbB4AgRx9Sy7zKslNyejy7E+a77qHfey5GXw/ik3IXv/NHMJgaA==", + "license": "MIT", + "dependencies": { + "langium": "3.3.1" + } + }, + "node_modules/@mixmark-io/domino": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@mixmark-io/domino/-/domino-2.2.0.tgz", + "integrity": "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==", + "license": "BSD-2-Clause" + }, + "node_modules/@mui/base": { + "version": "5.0.0-beta.40-0", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40-0.tgz", + "integrity": "sha512-hG3atoDUxlvEy+0mqdMpWd04wca8HKr2IHjW/fAjlkCHQolSLazhZM46vnHjOf15M4ESu25mV/3PgjczyjVM4w==", + "deprecated": "This package has been replaced by @base-ui-components/react", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@floating-ui/react-dom": "^2.0.8", + "@mui/types": "^7.2.15", + "@mui/utils": "^5.16.12", + "@popperjs/core": "^2.11.8", + "clsx": "^2.1.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/core-downloads-tracker": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.17.1.tgz", + "integrity": "sha512-OcZj+cs6EfUD39IoPBOgN61zf1XFVY+imsGoBDwXeSq2UHJZE3N59zzBOVjclck91Ne3e9gudONOeILvHCIhUA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/icons-material": { + "version": "5.16.14", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.16.14.tgz", + "integrity": "sha512-heL4S+EawrP61xMXBm59QH6HODsu0gxtZi5JtnXF2r+rghzyU/3Uftlt1ij8rmJh+cFdKTQug1L9KkZB5JgpMQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.9" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@mui/material": "^5.0.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/lab": { + "version": "5.0.0-alpha.175", + "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-5.0.0-alpha.175.tgz", + "integrity": "sha512-AvM0Nvnnj7vHc9+pkkQkoE1i+dEbr6gsMdnSfy7X4w3Ljgcj1yrjZhIt3jGTCLzyKVLa6uve5eLluOcGkvMqUA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/base": "5.0.0-beta.40-0", + "@mui/system": "^5.16.12", + "@mui/types": "^7.2.15", + "@mui/utils": "^5.16.12", + "clsx": "^2.1.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material": ">=5.15.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material": { + "version": "5.16.14", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.16.14.tgz", + "integrity": "sha512-eSXQVCMKU2xc7EcTxe/X/rC9QsV2jUe8eLM3MUCPYbo6V52eCE436akRIvELq/AqZpxx2bwkq7HC0cRhLB+yaw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/core-downloads-tracker": "^5.16.14", + "@mui/system": "^5.16.14", + "@mui/types": "^7.2.15", + "@mui/utils": "^5.16.14", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.10", + "clsx": "^2.1.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1", + "react-is": "^19.0.0", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/private-theming": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.17.1.tgz", + "integrity": "sha512-XMxU0NTYcKqdsG8LRmSoxERPXwMbp16sIXPcLVgLGII/bVNagX0xaheWAwFv8+zDK7tI3ajllkuD3GZZE++ICQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/utils": "^5.17.1", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "5.16.14", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.16.14.tgz", + "integrity": "sha512-UAiMPZABZ7p8mUW4akDV6O7N3+4DatStpXMZwPlt+H/dA0lt67qawN021MNND+4QTpjaiMYxbhKZeQcyWCbuKw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@emotion/cache": "^11.13.5", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.17.1.tgz", + "integrity": "sha512-aJrmGfQpyF0U4D4xYwA6ueVtQcEMebET43CUmKMP7e7iFh3sMIF3sBR0l8Urb4pqx1CBjHAaWgB0ojpND4Q3Jg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/private-theming": "^5.17.1", + "@mui/styled-engine": "^5.16.14", + "@mui/types": "~7.2.15", + "@mui/utils": "^5.17.1", + "clsx": "^2.1.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/system/node_modules/@mui/types": { + "version": "7.2.24", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.24.tgz", + "integrity": "sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.3.tgz", + "integrity": "sha512-2UCEiK29vtiZTeLdS2d4GndBKacVyxGvReznGXGr+CzW/YhjIX+OHUdCIczZjzcRAgKBGmE9zCIgoV9FleuyRQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.17.1.tgz", + "integrity": "sha512-jEZ8FTqInt2WzxDV8bhImWBqeQRD99c/id/fq83H0ER9tFl+sfZlaAoCdznGvbSQQ9ividMxqSV2c7cC1vBcQg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/types": "~7.2.15", + "@types/prop-types": "^15.7.12", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils/node_modules/@mui/types": { + "version": "7.2.24", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.24.tgz", + "integrity": "sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@next/env": { + "version": "14.2.29", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.29.tgz", + "integrity": "sha512-UzgLR2eBfhKIQt0aJ7PWH7XRPYw7SXz0Fpzdl5THjUnvxy4kfBk9OU4RNPNiETewEEtaBcExNFNn1QWH8wQTjg==", + "license": "MIT" + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "14.2.29", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.29.tgz", + "integrity": "sha512-wWtrAaxCVMejxPHFb1SK/PVV1WDIrXGs9ki0C/kUM8ubKHQm+3hU9MouUywCw8Wbhj3pewfHT2wjunLEr/TaLA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "14.2.29", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.29.tgz", + "integrity": "sha512-7Z/jk+6EVBj4pNLw/JQrvZVrAh9Bv8q81zCFSfvTMZ51WySyEHWVpwCEaJY910LyBftv2F37kuDPQm0w9CEXyg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "14.2.29", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.29.tgz", + "integrity": "sha512-o6hrz5xRBwi+G7JFTHc+RUsXo2lVXEfwh4/qsuWBMQq6aut+0w98WEnoNwAwt7hkEqegzvazf81dNiwo7KjITw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "14.2.29", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.29.tgz", + "integrity": "sha512-9i+JEHBOVgqxQ92HHRFlSW1EQXqa/89IVjtHgOqsShCcB/ZBjTtkWGi+SGCJaYyWkr/lzu51NTMCfKuBf7ULNw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "14.2.29", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.29.tgz", + "integrity": "sha512-B7JtMbkUwHijrGBOhgSQu2ncbCYq9E7PZ7MX58kxheiEOwdkM+jGx0cBb+rN5AeqF96JypEppK6i/bEL9T13lA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "14.2.29", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.29.tgz", + "integrity": "sha512-yCcZo1OrO3aQ38B5zctqKU1Z3klOohIxug6qdiKO3Q3qNye/1n6XIs01YJ+Uf+TdpZQ0fNrOQI2HrTLF3Zprnw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "14.2.29", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.29.tgz", + "integrity": "sha512-WnrfeOEtTVidI9Z6jDLy+gxrpDcEJtZva54LYC0bSKQqmyuHzl0ego+v0F/v2aXq0am67BRqo/ybmmt45Tzo4A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.2.29", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.29.tgz", + "integrity": "sha512-vkcriFROT4wsTdSeIzbxaZjTNTFKjSYmLd8q/GVH3Dn8JmYjUKOuKXHK8n+lovW/kdcpIvydO5GtN+It2CvKWA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "14.2.29", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.29.tgz", + "integrity": "sha512-iPPwUEKnVs7pwR0EBLJlwxLD7TTHWS/AoVZx1l9ZQzfQciqaFEr5AlYzA2uB6Fyby1IF18t4PL0nTpB+k4Tzlw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@opendocsg/pdf2md": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@opendocsg/pdf2md/-/pdf2md-0.2.1.tgz", + "integrity": "sha512-k/yvfrTb+GPTIIm/bMm5IsenTqAFl+IqvkBgFwFlmflS5TT7FOfyRLp8MypVWLAG4G9AnT7AZFbdQYgN/CR5BA==", + "license": "MIT", + "dependencies": { + "enumify": "^1.0.4", + "minimist": "^1.2.5", + "unpdf": "^0.12.1" + }, + "bin": { + "pdf2md": "lib/pdf2md-cli.js" + } + }, + "node_modules/@openrouter/ai-sdk-provider": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/@openrouter/ai-sdk-provider/-/ai-sdk-provider-0.4.6.tgz", + "integrity": "sha512-oUa8xtssyUhiKEU/aW662lsZ0HUvIUTRk8vVIF3Ha3KI/DnqX54zmVIuzYnaDpermqhy18CHqblAY4dDt1JW3g==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "1.0.9", + "@ai-sdk/provider-utils": "2.1.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.0.0" + } + }, + "node_modules/@openrouter/ai-sdk-provider/node_modules/@ai-sdk/provider": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-1.0.9.tgz", + "integrity": "sha512-jie6ZJT2ZR0uVOVCDc9R2xCX5I/Dum/wEK28lx21PJx6ZnFAN9EzD2WsPhcDWfCgGx3OAZZ0GyM3CEobXpa9LA==", + "license": "Apache-2.0", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@openrouter/ai-sdk-provider/node_modules/@ai-sdk/provider-utils": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-2.1.10.tgz", + "integrity": "sha512-4GZ8GHjOFxePFzkl3q42AU0DQOtTQ5w09vmaWUf/pKFXJPizlnzKSUkF0f+VkapIUfDugyMqPMT1ge8XQzVI7Q==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "1.0.9", + "eventsource-parser": "^3.0.0", + "nanoid": "^3.3.8", + "secure-json-parse": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/@openrouter/ai-sdk-provider/node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@paralleldrive/cuid2": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", + "integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.1.5" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@prisma/client": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.9.0.tgz", + "integrity": "sha512-Gg7j1hwy3SgF1KHrh0PZsYvAaykeR0PaxusnLXydehS96voYCGt1U5zVR31NIouYc63hWzidcrir1a7AIyCsNQ==", + "hasInstallScript": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "prisma": "*", + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@prisma/config": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.9.0.tgz", + "integrity": "sha512-Wcfk8/lN3WRJd5w4jmNQkUwhUw0eksaU/+BlAJwPQKW10k0h0LC9PD/6TQFmqKVbHQL0vG2z266r0S1MPzzhbA==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "jiti": "2.4.2" + } + }, + "node_modules/@prisma/debug": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.9.0.tgz", + "integrity": "sha512-bFeur/qi/Q+Mqk4JdQ3R38upSYPebv5aOyD1RKywVD+rAMLtRkmTFn28ZuTtVOnZHEdtxnNOCH+bPIeSGz1+Fg==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.9.0.tgz", + "integrity": "sha512-im0X0bwDLA0244CDf8fuvnLuCQcBBdAGgr+ByvGfQY9wWl6EA+kRGwVk8ZIpG65rnlOwtaWIr/ZcEU5pNVvq9g==", + "devOptional": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.9.0", + "@prisma/engines-version": "6.9.0-10.81e4af48011447c3cc503a190e86995b66d2a28e", + "@prisma/fetch-engine": "6.9.0", + "@prisma/get-platform": "6.9.0" + } + }, + "node_modules/@prisma/engines-version": { + "version": "6.9.0-10.81e4af48011447c3cc503a190e86995b66d2a28e", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.9.0-10.81e4af48011447c3cc503a190e86995b66d2a28e.tgz", + "integrity": "sha512-Qp9gMoBHgqhKlrvumZWujmuD7q4DV/gooEyPCLtbkc13EZdSz2RsGUJ5mHb3RJgAbk+dm6XenqG7obJEhXcJ6Q==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/fetch-engine": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.9.0.tgz", + "integrity": "sha512-PMKhJdl4fOdeE3J3NkcWZ+tf3W6rx3ht/rLU8w4SXFRcLhd5+3VcqY4Kslpdm8osca4ej3gTfB3+cSk5pGxgFg==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.9.0", + "@prisma/engines-version": "6.9.0-10.81e4af48011447c3cc503a190e86995b66d2a28e", + "@prisma/get-platform": "6.9.0" + } + }, + "node_modules/@prisma/get-platform": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.9.0.tgz", + "integrity": "sha512-/B4n+5V1LI/1JQcHp+sUpyRT1bBgZVPHbsC4lt4/19Xp4jvNIVcq5KYNtQDk5e/ukTSjo9PZVAxxy9ieFtlpTQ==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.9.0" + } + }, + "node_modules/@radix-ui/primitive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.0.tgz", + "integrity": "sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.2.tgz", + "integrity": "sha512-fqYwhhI9IarZ0ll2cUSfKuXHlJK0qE4AfnRrPBbRwEH/4mGQn04/QFGomLi8TXWIdv9WJk//KgGm+aDxVIr1wA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.2" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.0.tgz", + "integrity": "sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.3.tgz", + "integrity": "sha512-nXZOvFjOuHS1ovumntGV7NNoLaEp9JEvTht3MBjP44NSW5hUKj/8OnfN3+8WmB+CEhN44XaGhpHoSsUIEl5P7Q==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.0", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-primitive": "1.0.2", + "@radix-ui/react-use-callback-ref": "1.0.0", + "@radix-ui/react-use-escape-keydown": "1.0.2" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/react-compose-refs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz", + "integrity": "sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.0.tgz", + "integrity": "sha512-Q6iAB/U7Tq3NTolBBQbHTgclPmGWE3OlktGGqrClPozSw4vkQ1DfQAOtzgRPecKsMdJINE05iaoDUG8tRzCBjw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.1.tgz", + "integrity": "sha512-keYDcdMPNMjSC8zTsZ8wezUMiWM9Yj14wtF3s0PTIs9srnEPC9Kt2Gny1T3T81mmSeyDjZxsD9N5WCwNNb712w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@floating-ui/react-dom": "0.7.2", + "@radix-ui/react-arrow": "1.0.2", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-context": "1.0.0", + "@radix-ui/react-primitive": "1.0.2", + "@radix-ui/react-use-callback-ref": "1.0.0", + "@radix-ui/react-use-layout-effect": "1.0.0", + "@radix-ui/react-use-rect": "1.0.0", + "@radix-ui/react-use-size": "1.0.0", + "@radix-ui/rect": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-popper/node_modules/@floating-ui/core": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-0.7.3.tgz", + "integrity": "sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-popper/node_modules/@floating-ui/dom": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-0.5.4.tgz", + "integrity": "sha512-419BMceRLq0RrmTSDxn8hf9R3VCJv2K9PUfugh5JyEFmdjzDo+e8U5EdR8nzKq8Yj1htzLm3b6eQEEam3/rrtg==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^0.7.3" + } + }, + "node_modules/@radix-ui/react-popper/node_modules/@floating-ui/react-dom": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-0.7.2.tgz", + "integrity": "sha512-1T0sJcpHgX/u4I1OzIEhlcrvkUN8ln39nz7fMoE/2HDHrPiMFoOGR7++GYyfUmIQHkkrTinaeQsO3XWubjSvGg==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^0.5.3", + "use-isomorphic-layout-effect": "^1.1.1" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-compose-refs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz", + "integrity": "sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.2.tgz", + "integrity": "sha512-swu32idoCW7KA2VEiUZGBSu9nB6qwGdV6k6HYhUoOo3M1FFpD+VgLzUqtt3mwL1ssz7r2x8MggpLSQach2Xy/Q==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.2" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.0.tgz", + "integrity": "sha512-A+6XEvN01NfVWiKu38ybawfHsBjWum42MRPnEuqPsBZ4eV7e/7K321B5VgYMPv3Xx5An6o1/l9ZuDBgmcmWK3w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-use-layout-effect": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-presence/node_modules/@radix-ui/react-compose-refs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz", + "integrity": "sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.2.tgz", + "integrity": "sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.1" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-compose-refs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz", + "integrity": "sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", + "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.0.5.tgz", + "integrity": "sha512-cDKVcfzyO6PpckZekODJZDe5ZxZ2fCZlzKzTmPhe4mX9qTHRfLcKgqb0OKf22xLwDequ2tVleim+ZYx3rabD5w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.0", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-context": "1.0.0", + "@radix-ui/react-dismissable-layer": "1.0.3", + "@radix-ui/react-id": "1.0.0", + "@radix-ui/react-popper": "1.1.1", + "@radix-ui/react-portal": "1.0.2", + "@radix-ui/react-presence": "1.0.0", + "@radix-ui/react-primitive": "1.0.2", + "@radix-ui/react-slot": "1.0.1", + "@radix-ui/react-use-controllable-state": "1.0.0", + "@radix-ui/react-visually-hidden": "1.0.2" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-compose-refs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz", + "integrity": "sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", + "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.0.tgz", + "integrity": "sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.0.tgz", + "integrity": "sha512-FohDoZvk3mEXh9AWAVyRTYR4Sq7/gavuofglmiXB2g1aKyboUD4YtgWxKj8O5n+Uak52gXQ4wKz5IFST4vtJHg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.2.tgz", + "integrity": "sha512-DXGim3x74WgUv+iMNCF+cAo8xUHHeqvjx8zs7trKf+FkQKPQXLk2sX7Gx1ysH7Q76xCpZuxIJE7HLPxRE+Q+GA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.0.tgz", + "integrity": "sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.0.tgz", + "integrity": "sha512-TB7pID8NRMEHxb/qQJpvSt3hQU4sqNPM1VCTjTRjEOa7cEop/QMuq8S6fb/5Tsz64kqSvB9WnwsDHtjnrM9qew==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/rect": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.0.0.tgz", + "integrity": "sha512-imZ3aYcoYCKhhgNpkNDh/aTiU05qw9hX+HHI1QDBTyIlcFjgeFlKKySNGMwTp7nYFLQg/j0VA2FmCY4WPDDHMg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.2.tgz", + "integrity": "sha512-qirnJxtYn73HEk1rXL12/mXnu2rwsNHDID10th2JGtdK25T9wX+mxRmGt7iPSahw512GbZOc0syZX1nLQGoEOg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.2" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.0.tgz", + "integrity": "sha512-d0O68AYy/9oeEy1DdC07bz1/ZXX+DqCskRd3i4JzLSTXwefzaepQrKjXC7aNM8lTHjFLDO0pDgaEiQ7jEk+HVg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, + "node_modules/@rc-component/async-validator": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@rc-component/async-validator/-/async-validator-5.0.4.tgz", + "integrity": "sha512-qgGdcVIF604M9EqjNF0hbUTz42bz/RDtxWdWuU5EQe3hi7M8ob54B6B35rOsvX5eSvIHIzT9iH1R3n+hk3CGfg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.24.4" + }, + "engines": { + "node": ">=14.x" + } + }, + "node_modules/@rc-component/color-picker": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@rc-component/color-picker/-/color-picker-2.0.1.tgz", + "integrity": "sha512-WcZYwAThV/b2GISQ8F+7650r5ZZJ043E57aVBFkQ+kSY4C6wdofXgB0hBx+GPGpIU0Z81eETNoDUJMr7oy/P8Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "@ant-design/fast-color": "^2.0.6", + "@babel/runtime": "^7.23.6", + "classnames": "^2.2.6", + "rc-util": "^5.38.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/context": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@rc-component/context/-/context-1.4.0.tgz", + "integrity": "sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.1", + "rc-util": "^5.27.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/mini-decimal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rc-component/mini-decimal/-/mini-decimal-1.1.0.tgz", + "integrity": "sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.18.0" + }, + "engines": { + "node": ">=8.x" + } + }, + "node_modules/@rc-component/mutate-observer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rc-component/mutate-observer/-/mutate-observer-1.1.0.tgz", + "integrity": "sha512-QjrOsDXQusNwGZPf4/qRQasg7UFEj06XiCJ8iuiq/Io7CrHrgVi6Uuetw60WAMG1799v+aM8kyc+1L/GBbHSlw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.18.0", + "classnames": "^2.3.2", + "rc-util": "^5.24.4" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/portal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@rc-component/portal/-/portal-1.1.2.tgz", + "integrity": "sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.18.0", + "classnames": "^2.3.2", + "rc-util": "^5.24.4" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/qrcode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rc-component/qrcode/-/qrcode-1.0.0.tgz", + "integrity": "sha512-L+rZ4HXP2sJ1gHMGHjsg9jlYBX/SLN2D6OxP9Zn3qgtpMWtO2vUfxVFwiogHpAIqs54FnALxraUy/BCO1yRIgg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.24.7", + "classnames": "^2.3.2", + "rc-util": "^5.38.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/tour": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@rc-component/tour/-/tour-1.15.1.tgz", + "integrity": "sha512-Tr2t7J1DKZUpfJuDZWHxyxWpfmj8EZrqSgyMZ+BCdvKZ6r1UDsfU46M/iWAAFBy961Ssfom2kv5f3UcjIL2CmQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.18.0", + "@rc-component/portal": "^1.0.0-9", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.3.2", + "rc-util": "^5.24.4" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/trigger": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@rc-component/trigger/-/trigger-2.2.6.tgz", + "integrity": "sha512-/9zuTnWwhQ3S3WT1T8BubuFTT46kvnXgaERR9f4BTKyn61/wpf/BvbImzYBubzJibU707FxwbKszLlHjcLiv1Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.23.2", + "@rc-component/portal": "^1.1.0", + "classnames": "^2.3.2", + "rc-motion": "^2.0.0", + "rc-resize-observer": "^1.3.1", + "rc-util": "^5.44.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@reduxjs/toolkit": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz", + "integrity": "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@standard-schema/utils": "^0.3.0", + "immer": "^11.0.0", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", + "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, + "node_modules/@reduxjs/toolkit/node_modules/immer": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.0.tgz", + "integrity": "sha512-dlzb07f5LDY+tzs+iLCSXV2yuhaYfezqyZQc+n6baLECWkOMEWxkECAOnXL0ba7lsA25fM9b2jtzpu/uxo1a7g==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/@shikijs/core": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.6.0.tgz", + "integrity": "sha512-9By7Xb3olEX0o6UeJyPLI1PE1scC4d3wcVepvtv2xbuN9/IThYN4Wcwh24rcFeASzPam11MCq8yQpwwzCgSBRw==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.6.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.5" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.6.0.tgz", + "integrity": "sha512-7YnLhZG/TU05IHMG14QaLvTW/9WiK8SEYafceccHUSXs2Qr5vJibUwsDfXDLmRi0zHdzsxrGKpSX6hnqe0k8nA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.6.0", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^4.3.3" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.6.0.tgz", + "integrity": "sha512-nmOhIZ9yT3Grd+2plmW/d8+vZ2pcQmo/UnVwXMUXAKTXdi+LK0S08Ancrz5tQQPkxvjBalpMW2aKvwXfelauvA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.6.0", + "@shikijs/vscode-textmate": "^10.0.2" + } + }, + "node_modules/@shikijs/langs": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.6.0.tgz", + "integrity": "sha512-IdZkQJaLBu1LCYCwkr30hNuSDfllOT8RWYVZK1tD2J03DkiagYKRxj/pDSl8Didml3xxuyzUjgtioInwEQM/TA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.6.0" + } + }, + "node_modules/@shikijs/themes": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.6.0.tgz", + "integrity": "sha512-Fq2j4nWr1DF4drvmhqKq8x5vVQ27VncF8XZMBuHuQMZvUSS3NBgpqfwz/FoGe36+W6PvniZ1yDlg2d4kmYDU6w==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.6.0" + } + }, + "node_modules/@shikijs/transformers": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-3.6.0.tgz", + "integrity": "sha512-PYkU54lYV0RCaUG8n2FNTF+YWiU3uPhcjLGq2x/C8lIrUX9GVnRb3bK+R5xtdFHbuctntATKm7ondp/H/dux9Q==", + "license": "MIT", + "dependencies": { + "@shikijs/core": "3.6.0", + "@shikijs/types": "3.6.0" + } + }, + "node_modules/@shikijs/types": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.6.0.tgz", + "integrity": "sha512-cLWFiToxYu0aAzJqhXTQsFiJRTFDAGl93IrMSBNaGSzs7ixkLfdG6pH11HipuWFGW5vyx4X47W8HDQ7eSrmBUg==", + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "license": "MIT" + }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@splinetool/runtime": { + "version": "0.9.526", + "resolved": "https://registry.npmjs.org/@splinetool/runtime/-/runtime-0.9.526.tgz", + "integrity": "sha512-qznHbXA5aKwDbCgESAothCNm1IeEZcmNWG145p5aXj4w5uoqR1TZ9qkTHTKLTsUbHeitCwdhzmRqan1kxboLgQ==", + "dependencies": { + "on-change": "^4.0.0", + "semver-compare": "^1.0.0" + } + }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "license": "MIT" + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, + "node_modules/@stitches/react": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@stitches/react/-/react-1.2.8.tgz", + "integrity": "sha512-9g9dWI4gsSVe8bNLlb+lMkBYsnIKCZTmvqvDG+Avnn69XfmHZKiaMrx7cgTaddq7aTPPmXiTsbFcUy0xgI4+wA==", + "license": "MIT", + "peerDependencies": { + "react": ">= 16.3.0" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "license": "Apache-2.0" + }, + "node_modules/@swc/helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", + "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/conventional-commits-parser": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.1.tgz", + "integrity": "sha512-7uz5EHdzz2TqoMfV7ee61Egf5y6NkcO4FB/1iCCQnbeiI1F3xzv3vK5dBCXUCLQgGYS+mUeigK1iKQzvED+QnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==", + "license": "MIT" + }, + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", + "license": "MIT" + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz", + "integrity": "sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==", + "license": "MIT" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "license": "MIT", + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", + "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==", + "license": "MIT" + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", + "license": "MIT" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", + "license": "MIT" + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", + "license": "MIT" + }, + "node_modules/@types/d3-random": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", + "license": "MIT" + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", + "license": "MIT" + }, + "node_modules/@types/d3-shape": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", + "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "license": "MIT", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/diff-match-patch": { + "version": "1.0.36", + "resolved": "https://registry.npmjs.org/@types/diff-match-patch/-/diff-match-patch-1.0.36.tgz", + "integrity": "sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==", + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/fs-extra": { + "version": "9.0.13", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", + "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/katex": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.7.tgz", + "integrity": "sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==", + "license": "MIT" + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdx": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", + "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==", + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.0.tgz", + "integrity": "sha512-yZQa2zm87aRVcqDyH5+4Hv9KYgSdgwX1rFnGvpbzMaC7YAljmhBET93TPiTd3ObwTL+gSpIzPKg5BqVxdCvxKg==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.8.0" + } + }, + "node_modules/@types/node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" + }, + "node_modules/@types/plist": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz", + "integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*", + "xmlbuilder": ">=11.0.1" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.1.7", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.7.tgz", + "integrity": "sha512-BnsPLV43ddr05N71gaGzyZ5hzkCmGwhMvYc8zmvI8Ci1bRkkDSzDDVfAXfN2tk748OwI7ediiPX6PfT9p0QGVg==", + "license": "MIT", + "peer": true, + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "license": "MIT" + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT" + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" + }, + "node_modules/@types/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "license": "MIT" + }, + "node_modules/@types/verror": { + "version": "1.10.11", + "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.11.tgz", + "integrity": "sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "license": "ISC" + }, + "node_modules/@use-gesture/core": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.3.1.tgz", + "integrity": "sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==", + "license": "MIT" + }, + "node_modules/@use-gesture/react": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.3.1.tgz", + "integrity": "sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==", + "license": "MIT", + "dependencies": { + "@use-gesture/core": "10.3.1" + }, + "peerDependencies": { + "react": ">= 16.8.0" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/7zip-bin": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.2.0.tgz", + "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "license": "ISC", + "optional": true + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/adler-32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz", + "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/adm-zip": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", + "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", + "license": "MIT", + "engines": { + "node": ">=12.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/ahooks": { + "version": "3.8.5", + "resolved": "https://registry.npmjs.org/ahooks/-/ahooks-3.8.5.tgz", + "integrity": "sha512-Y+MLoJpBXVdjsnnBjE5rOSPkQ4DK+8i5aPDzLJdIOsCpo/fiAeXcBY1Y7oWgtOK0TpOz0gFa/XcyO1UGdoqLcw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.21.0", + "dayjs": "^1.9.1", + "intersection-observer": "^0.12.0", + "js-cookie": "^3.0.5", + "lodash": "^4.17.21", + "react-fast-compare": "^3.2.2", + "resize-observer-polyfill": "^1.5.1", + "screenfull": "^5.0.0", + "tslib": "^2.4.1" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/ai": { + "version": "4.3.16", + "resolved": "https://registry.npmjs.org/ai/-/ai-4.3.16.tgz", + "integrity": "sha512-KUDwlThJ5tr2Vw0A1ZkbDKNME3wzWhuVfAOwIvFUzl1TPVDFAXDFTXio3p+jaKneB+dKNCvFFlolYmmgHttG1g==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "1.1.3", + "@ai-sdk/provider-utils": "2.2.8", + "@ai-sdk/react": "1.2.12", + "@ai-sdk/ui-utils": "1.2.11", + "@opentelemetry/api": "1.9.0", + "jsondiffpatch": "0.6.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/antd": { + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/antd/-/antd-5.26.0.tgz", + "integrity": "sha512-iMPYKFTo2HvIRGutUOuN5AG+Uf+B2QaqcGQbdPp/100fqV3FAil6vFZLVuV3C4XEUOlDNkkUlJKhLR9V5rzIEg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@ant-design/colors": "^7.2.1", + "@ant-design/cssinjs": "^1.23.0", + "@ant-design/cssinjs-utils": "^1.1.3", + "@ant-design/fast-color": "^2.0.6", + "@ant-design/icons": "^5.6.1", + "@ant-design/react-slick": "~1.1.2", + "@babel/runtime": "^7.26.0", + "@rc-component/color-picker": "~2.0.1", + "@rc-component/mutate-observer": "^1.1.0", + "@rc-component/qrcode": "~1.0.0", + "@rc-component/tour": "~1.15.1", + "@rc-component/trigger": "^2.2.6", + "classnames": "^2.5.1", + "copy-to-clipboard": "^3.3.3", + "dayjs": "^1.11.11", + "rc-cascader": "~3.34.0", + "rc-checkbox": "~3.5.0", + "rc-collapse": "~3.9.0", + "rc-dialog": "~9.6.0", + "rc-drawer": "~7.3.0", + "rc-dropdown": "~4.2.1", + "rc-field-form": "~2.7.0", + "rc-image": "~7.12.0", + "rc-input": "~1.8.0", + "rc-input-number": "~9.5.0", + "rc-mentions": "~2.20.0", + "rc-menu": "~9.16.1", + "rc-motion": "^2.9.5", + "rc-notification": "~5.6.4", + "rc-pagination": "~5.1.0", + "rc-picker": "~4.11.3", + "rc-progress": "~4.0.0", + "rc-rate": "~2.13.1", + "rc-resize-observer": "^1.4.3", + "rc-segmented": "~2.7.0", + "rc-select": "~14.16.8", + "rc-slider": "~11.1.8", + "rc-steps": "~6.0.1", + "rc-switch": "~4.1.0", + "rc-table": "~7.51.0", + "rc-tabs": "~15.6.1", + "rc-textarea": "~1.10.0", + "rc-tooltip": "~6.4.0", + "rc-tree": "~5.13.1", + "rc-tree-select": "~5.27.0", + "rc-upload": "~4.9.2", + "rc-util": "^5.44.4", + "scroll-into-view-if-needed": "^3.1.0", + "throttle-debounce": "^5.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ant-design" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/antd-style": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/antd-style/-/antd-style-3.7.1.tgz", + "integrity": "sha512-CQOfddVp4aOvBfCepa+Kj2e7ap+2XBINg1Kn2osdE3oQvrD7KJu/K0sfnLcFLkgCJygbxmuazYdWLKb+drPDYA==", + "license": "MIT", + "dependencies": { + "@ant-design/cssinjs": "^1.21.1", + "@babel/runtime": "^7.24.1", + "@emotion/cache": "^11.11.0", + "@emotion/css": "^11.11.2", + "@emotion/react": "^11.11.4", + "@emotion/serialize": "^1.1.3", + "@emotion/utils": "^1.2.1", + "use-merge-value": "^1.2.0" + }, + "peerDependencies": { + "antd": ">=5.8.1", + "react": ">=18" + } + }, + "node_modules/app-builder-bin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-4.0.0.tgz", + "integrity": "sha512-xwdG0FJPQMe0M0UA4Tz0zEB8rBJTRA5a476ZawAqiBkMv16GRK5xpXThOjMaEOFnZ6zabejjG4J3da0SXG63KA==", + "dev": true, + "license": "MIT" + }, + "node_modules/app-builder-lib": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-24.13.3.tgz", + "integrity": "sha512-FAzX6IBit2POXYGnTCT8YHFO/lr5AapAII6zzhQO3Rw4cEDOgK+t1xhLc5tNcKlicTHlo9zxIwnYCX9X2DLkig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@develar/schema-utils": "~2.6.5", + "@electron/notarize": "2.2.1", + "@electron/osx-sign": "1.0.5", + "@electron/universal": "1.5.1", + "@malept/flatpak-bundler": "^0.4.0", + "@types/fs-extra": "9.0.13", + "async-exit-hook": "^2.0.1", + "bluebird-lst": "^1.0.9", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "chromium-pickle-js": "^0.2.0", + "debug": "^4.3.4", + "ejs": "^3.1.8", + "electron-publish": "24.13.1", + "form-data": "^4.0.0", + "fs-extra": "^10.1.0", + "hosted-git-info": "^4.1.0", + "is-ci": "^3.0.0", + "isbinaryfile": "^5.0.0", + "js-yaml": "^4.1.0", + "lazy-val": "^1.0.5", + "minimatch": "^5.1.1", + "read-config-file": "6.3.2", + "sanitize-filename": "^1.6.3", + "semver": "^7.3.8", + "tar": "^6.1.12", + "temp-file": "^3.4.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "dmg-builder": "24.13.3", + "electron-builder-squirrel-windows": "24.13.3" + } + }, + "node_modules/app-builder-lib/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/app-builder-lib/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/app-builder-lib/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "license": "ISC", + "optional": true + }, + "node_modules/archiver": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "archiver-utils": "^2.1.0", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^2.2.0", + "zip-stream": "^4.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/archiver-utils/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/archiver-utils/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/archiver-utils/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "dev": true, + "license": "MIT" + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "license": "MIT" + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/astring": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", + "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", + "license": "MIT", + "bin": { + "astring": "bin/astring" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-exit-hook": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", + "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/attr-accept": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.5.tgz", + "integrity": "sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/aws-sign2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha512-JnJpAS0p9RmixkOvW2XwDxxzs1bd4/VAGIl6Q0EC5YOo+p+hqIhtDhn/nmFnB/xUNXbLkpE2mOjgVIBRKD4xYw==", + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", + "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/babel-plugin-macros/node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/babel-plugin-macros/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.13", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.13.tgz", + "integrity": "sha512-3sX/eOms8kd3q2KZ6DAhKPc0dgm525Gqq5NtWKZ7QYYZEv57OQ54KtblzJzH1lQF/eQxO8KjWGIK9IPUJNus5g==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.6.4", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz", + "integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.3", + "core-js-compat": "^3.40.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.4.tgz", + "integrity": "sha512-7gD3pRadPrbjhjLyxebmx/WrFYcuSjZ0XbdUujQMZ/fcE9oeewk2U/7PCvez84UeuK3oSjmPZ0Ch0dlupQvGzw==", + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.4" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/bluebird-lst": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/bluebird-lst/-/bluebird-lst-1.0.9.tgz", + "integrity": "sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bluebird": "^3.5.5" + } + }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha512-KbiZEa9/vofNcVJXGwdWWn25reQ3V3dHBWbS07FTF3/TOehLnm9GEhJV4T6ZvGPkShRpmUqYwnaCrkj0mRnP6Q==", + "deprecated": "This version has been deprecated in accordance with the hapi support policy (hapi.im/support). Please upgrade to the latest version to get the best features, bug fixes, and security patches. If you are unable to upgrade at this time, paid support is available for older versions (hapi.im/commercial).", + "license": "BSD-3-Clause", + "dependencies": { + "hoek": "2.x.x" + }, + "engines": { + "node": ">=0.10.40" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", + "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001718", + "electron-to-chromium": "^1.5.160", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz", + "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/builder-util": { + "version": "24.13.1", + "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-24.13.1.tgz", + "integrity": "sha512-NhbCSIntruNDTOVI9fdXz0dihaqX2YuE1D6zZMrwiErzH4ELZHE6mdiB40wEgZNprDia+FghRFgKoAqMZRRjSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/debug": "^4.1.6", + "7zip-bin": "~5.2.0", + "app-builder-bin": "4.0.0", + "bluebird-lst": "^1.0.9", + "builder-util-runtime": "9.2.4", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "debug": "^4.3.4", + "fs-extra": "^10.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-ci": "^3.0.0", + "js-yaml": "^4.1.0", + "source-map-support": "^0.5.19", + "stat-mode": "^1.0.0", + "temp-file": "^3.4.0" + } + }, + "node_modules/builder-util-runtime": { + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.4.tgz", + "integrity": "sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "sax": "^1.2.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/builder-util/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/builder-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/builder-util/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/builder-util/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/builder-util/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/builder-util/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001722", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001722.tgz", + "integrity": "sha512-DCQHBBZtiK6JVkAGw7drvAMK0Q0POD/xZvEmDp6baiMMP6QXXk9HpD6mNYBZWhOPG6LvIDb82ITqtWjhDckHCA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/canvas": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz", + "integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.0", + "nan": "^2.17.0", + "simple-get": "^3.0.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/canvas/node_modules/decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "license": "MIT", + "optional": true, + "dependencies": { + "mimic-response": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/canvas/node_modules/mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/canvas/node_modules/simple-get": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", + "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", + "license": "MIT", + "optional": true, + "dependencies": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/caseless": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", + "integrity": "sha512-ODLXH644w9C2fMPAm7bMDQ3GRvipZWZfKc+8As6hIadRIelE0n0xZuN38NS6kiK3KPEVrpymmQD8bvncAHWQkQ==", + "license": "Apache-2.0" + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/cfb": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz", + "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chevrotain": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz", + "integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==", + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/cst-dts-gen": "11.0.3", + "@chevrotain/gast": "11.0.3", + "@chevrotain/regexp-to-ast": "11.0.3", + "@chevrotain/types": "11.0.3", + "@chevrotain/utils": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/chevrotain-allstar": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.3.1.tgz", + "integrity": "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==", + "license": "MIT", + "dependencies": { + "lodash-es": "^4.17.21" + }, + "peerDependencies": { + "chevrotain": "^11.0.0" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "devOptional": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/chroma-js": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-3.1.2.tgz", + "integrity": "sha512-IJnETTalXbsLx1eKEgx19d5L6SRM7cH4vINw/99p/M11HCuXGRWL+6YmCm7FWFGIo6dtWuQoQi1dc5yQ7ESIHg==", + "license": "(BSD-3-Clause AND Apache-2.0)" + }, + "node_modules/chromium-pickle-js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", + "integrity": "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/co-prompt": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/co-prompt/-/co-prompt-1.0.0.tgz", + "integrity": "sha512-uKmEbjDnL9SJTb+TNfIFsATe1F3IsNsR7KDGUG1hq7ColkMV0MSn7dg3eKVS+3wwtyvVqrgfIwi39NOJiknO7Q==", + "license": "MIT", + "dependencies": { + "keypress": "~0.2.1" + } + }, + "node_modules/codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/collapse-white-space": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", + "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "license": "ISC", + "optional": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "node_modules/compare-version": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", + "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/compress-commons": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", + "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/compute-scroll-into-view": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz", + "integrity": "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==", + "license": "MIT", + "peer": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/concurrently": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz", + "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "date-fns": "^2.30.0", + "lodash": "^4.17.21", + "rxjs": "^7.8.1", + "shell-quote": "^1.8.1", + "spawn-command": "0.0.2", + "supports-color": "^8.1.1", + "tree-kill": "^1.2.2", + "yargs": "^17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": "^14.13.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/confbox": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", + "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "license": "MIT" + }, + "node_modules/config-file-ts": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/config-file-ts/-/config-file-ts-0.2.6.tgz", + "integrity": "sha512-6boGVaglwblBgJqGyxm4+xCmEGcWgnWHSWHY5jad58awQhB6gftq0G8HbzU39YqCIYHMLAiL1yjwiZ36m/CL8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "^10.3.10", + "typescript": "^5.3.3" + } + }, + "node_modules/config-file-ts/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/config-file-ts/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/config-file-ts/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "license": "ISC", + "optional": true + }, + "node_modules/console-table-printer": { + "version": "2.14.3", + "resolved": "https://registry.npmjs.org/console-table-printer/-/console-table-printer-2.14.3.tgz", + "integrity": "sha512-X5OCFnjYlXzRuC8ac5hPA2QflRjJvNKJocMhlnqK/Ap7q3DHXr0NJ0TGzwmEKOiOdJrjsSwEd0m+a32JAYPrKQ==", + "license": "MIT", + "dependencies": { + "simple-wcswidth": "^1.0.1" + } + }, + "node_modules/conventional-changelog-angular": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz", + "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/conventional-changelog-conventionalcommits": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-7.0.2.tgz", + "integrity": "sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/conventional-commits-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", + "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-text-path": "^2.0.0", + "JSONStream": "^1.3.5", + "meow": "^12.0.1", + "split2": "^4.0.0" + }, + "bin": { + "conventional-commits-parser": "cli.mjs" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, + "node_modules/copy-to-clipboard": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", + "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", + "license": "MIT", + "peer": true, + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, + "node_modules/core-js-compat": { + "version": "3.43.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.43.0.tgz", + "integrity": "sha512-2GML2ZsCc5LR7hZYz4AXmjQw8zuy2T//2QntwdnpuYI7jteT6GVYJL7F6C2C57R7gSYrcqVW3lAALefdbhBLDA==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.25.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "license": "MIT" + }, + "node_modules/cose-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", + "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", + "license": "MIT", + "dependencies": { + "layout-base": "^1.0.0" + } + }, + "node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cosmiconfig-typescript-loader": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.1.0.tgz", + "integrity": "sha512-tJ1w35ZRUiM5FeTzT7DtYWAFFv37ZLqSRkGi2oeCK1gPhvaWjkAtfXvLmvE1pRfxxp9aQo6ba/Pvg1dKj05D4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "jiti": "^2.4.1" + }, + "engines": { + "node": ">=v18" + }, + "peerDependencies": { + "@types/node": "*", + "cosmiconfig": ">=9", + "typescript": ">=5" + } + }, + "node_modules/crc": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", + "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "buffer": "^5.1.0" + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", + "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cryptiles": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha512-FFN5KwpvvQTTS5hWPxrU8/QE4kQUc6uwZcrnlMBN82t1MgAtq8mnoDwINBly9Tdr02seeIIhtdF+UH1feBYGog==", + "deprecated": "This version has been deprecated in accordance with the hapi support policy (hapi.im/support). Please upgrade to the latest version to get the best features, bug fixes, and security patches. If you are unable to upgrade at this time, paid support is available for older versions (hapi.im/commercial).", + "license": "BSD-3-Clause", + "dependencies": { + "boom": "2.x.x" + }, + "engines": { + "node": ">=0.10.40" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/cytoscape": { + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.32.0.tgz", + "integrity": "sha512-5JHBC9n75kz5851jeklCPmZWcg3hUe6sjqJvyk3+hVqFaKcHwHgxsjeN1yLmggoUc6STbtm9/NQyabQehfjvWQ==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cytoscape-cose-bilkent": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", + "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", + "license": "MIT", + "dependencies": { + "cose-base": "^1.0.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", + "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", + "license": "MIT", + "dependencies": { + "cose-base": "^2.2.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/cose-base": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", + "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", + "license": "MIT", + "dependencies": { + "layout-base": "^2.0.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/layout-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", + "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==", + "license": "MIT" + }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "license": "ISC", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "license": "ISC", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "license": "ISC", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "license": "ISC", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "license": "ISC", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "license": "ISC", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-sankey": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", + "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "1 - 2", + "d3-shape": "^1.2.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "license": "BSD-3-Clause", + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", + "license": "BSD-3-Clause" + }, + "node_modules/d3-sankey/node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-sankey/node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", + "license": "ISC" + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dagre-d3-es": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.11.tgz", + "integrity": "sha512-tvlJLyQf834SylNKax8Wkzco/1ias1OPw8DcUMDE7oUIoSEW25riQVuiu/0OWEFqT0cxHT3Pa9/D82Jr47IONw==", + "license": "MIT", + "dependencies": { + "d3": "^7.9.0", + "lodash-es": "^4.17.21" + } + }, + "node_modules/dargs": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-8.1.0.tgz", + "integrity": "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", + "license": "MIT" + }, + "node_modules/decode-named-character-reference": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.1.0.tgz", + "integrity": "sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/decode-uri-component": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.4.1.tgz", + "integrity": "sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==", + "license": "MIT", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "license": "ISC", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "license": "MIT", + "optional": true + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "license": "ISC", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/diff-match-patch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", + "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==", + "license": "Apache-2.0" + }, + "node_modules/dingbat-to-unicode": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dingbat-to-unicode/-/dingbat-to-unicode-1.0.1.tgz", + "integrity": "sha512-98l0sW87ZT58pU4i61wa2OHwxbiYSbuxsCBozaVnYX2iCnr3bLM3fIes1/ej7h1YdOKuKt/MLs706TVnALA65w==", + "license": "BSD-2-Clause" + }, + "node_modules/dir-compare": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-3.3.0.tgz", + "integrity": "sha512-J7/et3WlGUCxjdnD3HAAzQ6nsnc0WL6DD7WcwJb7c39iH1+AWfg+9OqzJNaI6PkBwBvm1mhZNL9iY/nRiZXlPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal": "^1.0.0", + "minimatch": "^3.0.4" + } + }, + "node_modules/dir-compare/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/dir-compare/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dmg-builder": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-24.13.3.tgz", + "integrity": "sha512-rcJUkMfnJpfCboZoOOPf4L29TRtEieHNOeAbYPWPxlaBw/Z1RKrRA86dOI9rwaI4tQSc/RD82zTNHprfUHXsoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "app-builder-lib": "24.13.3", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "fs-extra": "^10.1.0", + "iconv-lite": "^0.6.2", + "js-yaml": "^4.1.0" + }, + "optionalDependencies": { + "dmg-license": "^1.0.11" + } + }, + "node_modules/dmg-builder/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dmg-builder/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/dmg-builder/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/dmg-license": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz", + "integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "@types/plist": "^3.0.1", + "@types/verror": "^1.10.3", + "ajv": "^6.10.0", + "crc": "^3.8.0", + "iconv-corefoundation": "^1.1.7", + "plist": "^3.0.4", + "smart-buffer": "^4.0.2", + "verror": "^1.10.0" + }, + "bin": { + "dmg-license": "bin/dmg-license.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dmg-license/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/dmg-license/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dompurify": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.6.tgz", + "integrity": "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-9.0.2.tgz", + "integrity": "sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=10" + } + }, + "node_modules/dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/duck": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/duck/-/duck-0.1.12.tgz", + "integrity": "sha512-wkctla1O6VfP89gQ+J/yDesM0S7B7XLXjKGzXxMDVFg7uEn706niAtyYovKbyq1oT9YwDcly721/iUWoc8MVRg==", + "license": "BSD", + "dependencies": { + "underscore": "^1.13.1" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "license": "MIT", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron": { + "version": "35.5.1", + "resolved": "https://registry.npmjs.org/electron/-/electron-35.5.1.tgz", + "integrity": "sha512-kkbGXz56safvXcxqAZyMS2nJGYK9NFG/iKOJsAO5e0HPH8y3EnV4Fi87tvbfFpeLiaruFS+eN0YFy6f1StpSgQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@electron/get": "^2.0.0", + "@types/node": "^22.7.7", + "extract-zip": "^2.0.1" + }, + "bin": { + "electron": "cli.js" + }, + "engines": { + "node": ">= 12.20.55" + } + }, + "node_modules/electron-build": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/electron-build/-/electron-build-0.0.3.tgz", + "integrity": "sha512-gJ+Civsa9MfR4TssR1+XiJczYylYGoSD2mFMHcOimRV0JxeHA2bbqYJ1Usb9b61F+QuG6dN+l+wLGh7680zH+A==", + "license": "MIT", + "dependencies": { + "co": "4.6.0", + "co-prompt": "1.0.0", + "commander": "2.9.0", + "jszip": "2.5.0", + "path": "0.12.7", + "progress": "1.1.8", + "q": "1.4.1", + "request": "2.72.0", + "url": "0.11.0" + }, + "bin": { + "ebuild": "index.js" + } + }, + "node_modules/electron-build/node_modules/commander": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A==", + "license": "MIT", + "dependencies": { + "graceful-readlink": ">= 1.0.0" + }, + "engines": { + "node": ">= 0.6.x" + } + }, + "node_modules/electron-build/node_modules/jszip": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-2.5.0.tgz", + "integrity": "sha512-IRoyf8JSYY3nx+uyh5xPc0qdy8pUDTp2UkHOWYNF/IO/3D8nx7899UlSAjD8rf8wUgOmm0lACWx/GbW3EaxIXQ==", + "license": "MIT or GPLv3", + "dependencies": { + "pako": "~0.2.5" + } + }, + "node_modules/electron-build/node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", + "license": "MIT" + }, + "node_modules/electron-build/node_modules/progress": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "integrity": "sha512-UdA8mJ4weIkUBO224tIarHzuHs4HuYiJvsuGT7j/SPQiUJVjYvNDBIPa0hAorduOfjGohB/qHWRa/lrrWX/mXw==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/electron-builder": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-24.13.3.tgz", + "integrity": "sha512-yZSgVHft5dNVlo31qmJAe4BVKQfFdwpRw7sFp1iQglDRCDD6r22zfRJuZlhtB5gp9FHUxCMEoWGq10SkCnMAIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "app-builder-lib": "24.13.3", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "chalk": "^4.1.2", + "dmg-builder": "24.13.3", + "fs-extra": "^10.1.0", + "is-ci": "^3.0.0", + "lazy-val": "^1.0.5", + "read-config-file": "6.3.2", + "simple-update-notifier": "2.0.0", + "yargs": "^17.6.2" + }, + "bin": { + "electron-builder": "cli.js", + "install-app-deps": "install-app-deps.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/electron-builder-squirrel-windows": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-24.13.3.tgz", + "integrity": "sha512-oHkV0iogWfyK+ah9ZIvMDpei1m9ZRpdXcvde1wTpra2U8AFDNNpqJdnin5z+PM1GbQ5BoaKCWas2HSjtR0HwMg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "app-builder-lib": "24.13.3", + "archiver": "^5.3.1", + "builder-util": "24.13.1", + "fs-extra": "^10.1.0" + } + }, + "node_modules/electron-builder-squirrel-windows/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-builder-squirrel-windows/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-builder-squirrel-windows/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/electron-builder/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/electron-builder/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/electron-builder/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-builder/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-builder/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-builder/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/electron-publish": { + "version": "24.13.1", + "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-24.13.1.tgz", + "integrity": "sha512-2ZgdEqJ8e9D17Hwp5LEq5mLQPjqU3lv/IALvgp+4W8VeNhryfGhYEQC/PgDPMrnWUp+l60Ou5SJLsu+k4mhQ8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/fs-extra": "^9.0.11", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "chalk": "^4.1.2", + "fs-extra": "^10.1.0", + "lazy-val": "^1.0.5", + "mime": "^2.5.2" + } + }, + "node_modules/electron-publish/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/electron-publish/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/electron-publish/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-publish/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-publish/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-publish/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.166", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.166.tgz", + "integrity": "sha512-QPWqHL0BglzPYyJJ1zSSmwFFL6MFXhbACOCcsCdUMCkzPdS9/OIBVxg516X/Ado2qwAq8k0nJJ7phQPCqiaFAw==", + "license": "ISC" + }, + "node_modules/electron-updater": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-6.6.2.tgz", + "integrity": "sha512-Cr4GDOkbAUqRHP5/oeOmH/L2Bn6+FQPxVLZtPbcmKZC63a1F3uu5EefYOssgZXG3u/zBlubbJ5PJdITdMVggbw==", + "license": "MIT", + "dependencies": { + "builder-util-runtime": "9.3.1", + "fs-extra": "^10.1.0", + "js-yaml": "^4.1.0", + "lazy-val": "^1.0.5", + "lodash.escaperegexp": "^4.1.2", + "lodash.isequal": "^4.5.0", + "semver": "^7.6.3", + "tiny-typed-emitter": "^2.1.0" + } + }, + "node_modules/electron-updater/node_modules/builder-util-runtime": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.3.1.tgz", + "integrity": "sha512-2/egrNDDnRaxVwK3A+cJq6UOlqOdedGA7JPqCeJjN2Zjk1/QB/6QUi3b714ScIGS7HafFXTyzJEOr5b44I3kvQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "sax": "^1.2.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/electron-updater/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-updater/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-updater/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/electron/node_modules/@types/node": { + "version": "22.15.31", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.31.tgz", + "integrity": "sha512-jnVe5ULKl6tijxUhvQeNbQG/84fHfg+yMak02cT8QVhBx/F05rAVxCGBYYTh2EKz22D6JF5ktXuNwdx7b9iEGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/electron/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-mart": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/emoji-mart/-/emoji-mart-5.6.0.tgz", + "integrity": "sha512-eJp3QRe79pjwa+duv+n7+5YsNhRcMl812EcFVwrnRvYKoNPoQb5qxU8DG6Bgwji0akHdp6D4Ln6tYLG58MFSow==", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "license": "MIT" + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/enumify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/enumify/-/enumify-1.0.4.tgz", + "integrity": "sha512-5mwWXaVzJaqyUdOW/PDH5QySRgmQ8VvujmxmvXoXj9w0n+6omhVuyD56eI37FMqy/LxueJzsQ4DrHVQzuT/TXg==", + "license": "MIT" + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-toolkit": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.43.0.tgz", + "integrity": "sha512-SKCT8AsWvYzBBuUqMk4NPwFlSdqLpJwmy6AP322ERn8W2YLIB6JBXnwMI2Qsh2gfphT3q7EKAxKb23cvFHFwKA==", + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks" + ] + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/esast-util-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz", + "integrity": "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/esast-util-from-js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz", + "integrity": "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "acorn": "^8.0.0", + "esast-util-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/estree-util-attach-comments": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", + "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-build-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", + "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-walker": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-scope": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/estree-util-scope/-/estree-util-scope-1.0.0.tgz", + "integrity": "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-to-js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", + "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "astring": "^1.8.0", + "source-map": "^0.7.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-to-js/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/estree-util-visit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", + "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, + "node_modules/eventsource-parser": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", + "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "node_modules/exsolve": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.5.tgz", + "integrity": "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==", + "license": "MIT" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extend-shallow/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extsprintf": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", + "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "optional": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/file-selector": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.5.0.tgz", + "integrity": "sha512-s8KNnmIDTBoD0p9uJ9uD0XY38SCeBOtj0UMXyQSLg1Ypfrfj8+dAvwsLjYQkQ2GjhVtp2HrnF5cJzMhBjfD8HA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/filter-obj": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-5.1.0.tgz", + "integrity": "sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" + }, + "node_modules/find-up": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", + "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^7.2.0", + "path-exists": "^5.0.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", + "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", + "license": "MIT" + }, + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "license": "MIT", + "dependencies": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "engines": { + "node": ">= 12.20" + } + }, + "node_modules/formidable": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", + "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", + "license": "MIT", + "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", + "dezalgo": "^1.0.4", + "once": "^1.4.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, + "node_modules/frac": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/framer-motion": { + "version": "12.17.0", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.17.0.tgz", + "integrity": "sha512-2hISKgDk49yCLStwG1wf4Kdy/D6eBw9/eRNaWFIYoI9vMQ/Mqd1Fz+gzVlEtxJmtQ9y4IWnXm19/+UXD3dAYAA==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.17.0", + "motion-utils": "^12.12.1", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "node_modules/from2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/from2/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/from2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "license": "MIT" + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "devOptional": true, + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gauge/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/gauge/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC", + "optional": true + }, + "node_modules/gauge/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "license": "MIT", + "dependencies": { + "is-property": "^1.0.2" + } + }, + "node_modules/generate-object-property": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", + "integrity": "sha512-TuOwZWgJ2VAMEGJvAyPWvpqxSANF0LDpmyHauMjFYzaACvn+QTT/AZomvPCzVBV7yDN3OmwHQ5OvHaeLKre3JQ==", + "license": "MIT", + "dependencies": { + "is-property": "^1.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/giscus": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/giscus/-/giscus-1.6.0.tgz", + "integrity": "sha512-Zrsi8r4t1LVW950keaWcsURuZUQwUaMKjvJgTCY125vkW6OiEBkatE7ScJDbpqKHdZwb///7FVC21SE3iFK3PQ==", + "license": "MIT", + "dependencies": { + "lit": "^3.2.1" + } + }, + "node_modules/git-raw-commits": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz", + "integrity": "sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dargs": "^8.0.0", + "meow": "^12.0.1", + "split2": "^4.0.0" + }, + "bin": { + "git-raw-commits": "cli.mjs" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/github-markdown-css": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/github-markdown-css/-/github-markdown-css-5.8.1.tgz", + "integrity": "sha512-8G+PFvqigBQSWLQjyzgpa2ThD9bo7+kDsriUIidGcRhXgmcaAWUIpCZf8DavJgc+xifjbCG+GvMyWr0XMXmc7g==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "devOptional": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/global-directory": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", + "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "4.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w==", + "license": "MIT" + }, + "node_modules/hachure-fill": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz", + "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==", + "license": "MIT" + }, + "node_modules/har-validator": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", + "integrity": "sha512-P6tFV+wCcUL3nbyTDAvveDySfbhy0XkDtAIfZP6HITjM2WUsiPna/Eg1Yy93SFXvahqoX+kt0n+6xlXKDXYowA==", + "deprecated": "this library is no longer supported", + "license": "ISC", + "dependencies": { + "chalk": "^1.1.1", + "commander": "^2.9.0", + "is-my-json-valid": "^2.12.4", + "pinkie-promise": "^2.0.0" + }, + "bin": { + "har-validator": "bin/har-validator" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/har-validator/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/har-validator/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/har-validator/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/har-validator/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, + "node_modules/har-validator/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/har-validator/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/har-validator/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/has": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", + "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "license": "ISC", + "optional": true + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hast-util-from-dom": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/hast-util-from-dom/-/hast-util-from-dom-5.0.1.tgz", + "integrity": "sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q==", + "license": "ISC", + "dependencies": { + "@types/hast": "^3.0.0", + "hastscript": "^9.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", + "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html-isomorphic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hast-util-from-html-isomorphic/-/hast-util-from-html-isomorphic-2.0.0.tgz", + "integrity": "sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-from-dom": "^5.0.0", + "hast-util-from-html": "^2.0.0", + "unist-util-remove-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", + "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^7.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", + "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-estree": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.3.tgz", + "integrity": "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-attach-comments": "^3.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-parse5": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz", + "integrity": "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-parse5/node_modules/property-information": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/hast-util-to-text": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", + "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "unist-util-find-after": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hawk": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha512-X8xbmTc1cbPXcQV4WkLcRMALuyoxhfpFATmyuCxJPOAvrDS4DNnsTAOmKUxMTOWU6TzrTOkxPKwIx5ZOpJVSrg==", + "deprecated": "This module moved to @hapi/hawk. Please make sure to switch over as this distribution is no longer supported and may contain bugs and critical security issues.", + "license": "BSD-3-Clause", + "dependencies": { + "boom": "2.x.x", + "cryptiles": "2.x.x", + "hoek": "2.x.x", + "sntp": "1.x.x" + }, + "engines": { + "node": ">=0.10.32" + } + }, + "node_modules/hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha512-V6Yw1rIcYV/4JsnggjBU0l4Kr+EXhpwqXRusENU1Xx6ro00IHPHYNynCuBTOZAPlr3AAmLvchH9I7N/VUdvOwQ==", + "deprecated": "This version has been deprecated in accordance with the hapi support policy (hapi.im/support). Please upgrade to the latest version to get the best features, bug fixes, and security patches. If you are unable to upgrade at this time, paid support is available for older versions (hapi.im/commercial).", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.40" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "license": "MIT", + "dependencies": { + "void-elements": "3.1.0" + } + }, + "node_modules/html-url-attributes": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", + "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-signature": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha512-iUn0NcRULlDGtqNLN1Jxmzayk8ogm7NToldASyZBpM2qggbphjXzNOiw3piN8tgz+e/DRs6X5gAzFwTI6BCRcg==", + "license": "MIT", + "dependencies": { + "assert-plus": "^0.2.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/http-signature/node_modules/assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha512-u1L0ZLywRziOVjUhRxI0Qg9G+4RnFB9H/Rq40YWn0dieDgO7vAYeJz6jKAO6t/aruzlDFLAPkQTT87e+f8Imaw==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/i18next": { + "version": "24.2.3", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-24.2.3.tgz", + "integrity": "sha512-lfbf80OzkocvX7nmZtu7nSTNbrTYR52sLWxPtlXX1zAhVw8WEnFk4puUkCR4B1dNQwbSpEHHHemcZu//7EcB7A==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.10" + }, + "peerDependencies": { + "typescript": "^5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/i18next-browser-languagedetector": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.2.0.tgz", + "integrity": "sha512-P+3zEKLnOF0qmiesW383vsLdtQVyKtCNA9cjSoKCppTKPQVfKd2W8hbVo5ZhNJKDqeM7BOcvNoKJOjpHh4Js9g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, + "node_modules/iconv-corefoundation": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", + "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "cli-truncate": "^2.1.0", + "node-addon-api": "^1.6.3" + }, + "engines": { + "node": "^8.11.2 || >=10" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/image-size": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-2.0.2.tgz", + "integrity": "sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==", + "license": "MIT", + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=16.x" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, + "node_modules/immer": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz", + "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", + "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "devOptional": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/inline-style-parser": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", + "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", + "license": "MIT" + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/intersection-observer": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.12.2.tgz", + "integrity": "sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==", + "license": "Apache-2.0" + }, + "node_modules/into-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-6.0.0.tgz", + "integrity": "sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "from2": "^2.3.0", + "p-is-promise": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "node_modules/is-ci": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", + "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ci-info": "^3.2.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-core-module": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-my-ip-valid": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.1.tgz", + "integrity": "sha512-jxc8cBcOWbNK2i2aTkCZP6i7wkHF1bqKFrwEHuN5Jtg5BSaZHUZQ/JTOJwoV41YvHnOaRyWWh72T/KvfNz9DJg==", + "license": "MIT" + }, + "node_modules/is-my-json-valid": { + "version": "2.20.6", + "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.20.6.tgz", + "integrity": "sha512-1JQwulVNjx8UqkPE/bqDaxtH4PXCe/2VRh/y3p99heOV87HG4Id5/VfDswd+YiAfHcRTfDlWgISycnHuhZq1aw==", + "license": "MIT", + "dependencies": { + "generate-function": "^2.0.0", + "generate-object-property": "^1.1.0", + "is-my-ip-valid": "^1.0.0", + "jsonpointer": "^5.0.0", + "xtend": "^4.0.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", + "license": "MIT" + }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-text-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", + "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "text-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "license": "MIT" + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/isbinaryfile": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.4.tgz", + "integrity": "sha512-YKBKVkKhty7s8rxddb40oOkuP0NbaeXrQvLin6QMHL7Ypiy2RW9LwOVrVgZRyOrhQlayMd9t+D8yDy8MKFTSDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "license": "MIT" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jake/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jake/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/jake/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jake/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/jake/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "devOptional": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/joi": { + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, + "node_modules/jotai": { + "version": "2.12.5", + "resolved": "https://registry.npmjs.org/jotai/-/jotai-2.12.5.tgz", + "integrity": "sha512-G8m32HW3lSmcz/4mbqx0hgJIQ0ekndKWiYP7kWVKi0p6saLXdSoye+FZiOFyonnd7Q482LCzm8sMDl7Ar1NWDw==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=17.0.0", + "react": ">=17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/js-tiktoken": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.20.tgz", + "integrity": "sha512-Xlaqhhs8VfCd6Sh7a1cFkZHQbYTLCwVJJWiHVxBYzLPxW0XsoxBy1hitmjkdIjD3Aon5BXLHFwU5O8WUx6HH+A==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.5.1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "license": "ISC" + }, + "node_modules/json2mq": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz", + "integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==", + "license": "MIT", + "peer": true, + "dependencies": { + "string-convert": "^0.2.0" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsondiffpatch": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/jsondiffpatch/-/jsondiffpatch-0.6.0.tgz", + "integrity": "sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==", + "license": "MIT", + "dependencies": { + "@types/diff-match-patch": "^1.0.36", + "chalk": "^5.3.0", + "diff-match-patch": "^1.0.5" + }, + "bin": { + "jsondiffpatch": "bin/jsondiffpatch.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" + }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jsonrepair": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/jsonrepair/-/jsonrepair-3.13.1.tgz", + "integrity": "sha512-WJeiE0jGfxYmtLwBTEk8+y/mYcaleyLXWaqp5bJu0/ZTSeG0KQq/wWQ8pmnkKenEdN6pdnn6QtcoSUkbqDHWNw==", + "license": "ISC", + "bin": { + "jsonrepair": "bin/cli.js" + } + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "license": "(MIT OR Apache-2.0)", + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "license": "MIT", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/jsprim/node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "engines": [ + "node >=0.6.0" + ], + "license": "MIT" + }, + "node_modules/jsprim/node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/katex": { + "version": "0.16.22", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.22.tgz", + "integrity": "sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/keypress": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/keypress/-/keypress-0.2.1.tgz", + "integrity": "sha512-HjorDJFNhnM4SicvaUXac0X77NiskggxJdesG72+O5zBKpSqKFCrqmndKVqpu3pFqkla0St6uGk8Ju0sCurrmg==", + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/khroma": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", + "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" + }, + "node_modules/kolorist": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", + "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", + "license": "MIT" + }, + "node_modules/langchain": { + "version": "0.3.27", + "resolved": "https://registry.npmjs.org/langchain/-/langchain-0.3.27.tgz", + "integrity": "sha512-XfOuXetMSpkS11Mt6YJkDmvuSGTMPUsks5DJz4RCZ3y2dcbLkOe5kecjx2SWVJYqQIqcMMwsjsve3/ZjnRe7rQ==", + "license": "MIT", + "dependencies": { + "@langchain/openai": ">=0.1.0 <0.6.0", + "@langchain/textsplitters": ">=0.0.0 <0.2.0", + "js-tiktoken": "^1.0.12", + "js-yaml": "^4.1.0", + "jsonpointer": "^5.0.1", + "langsmith": "^0.3.29", + "openapi-types": "^12.1.3", + "p-retry": "4", + "uuid": "^10.0.0", + "yaml": "^2.2.1", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@langchain/anthropic": "*", + "@langchain/aws": "*", + "@langchain/cerebras": "*", + "@langchain/cohere": "*", + "@langchain/core": ">=0.2.21 <0.4.0", + "@langchain/deepseek": "*", + "@langchain/google-genai": "*", + "@langchain/google-vertexai": "*", + "@langchain/google-vertexai-web": "*", + "@langchain/groq": "*", + "@langchain/mistralai": "*", + "@langchain/ollama": "*", + "@langchain/xai": "*", + "axios": "*", + "cheerio": "*", + "handlebars": "^4.7.8", + "peggy": "^3.0.2", + "typeorm": "*" + }, + "peerDependenciesMeta": { + "@langchain/anthropic": { + "optional": true + }, + "@langchain/aws": { + "optional": true + }, + "@langchain/cerebras": { + "optional": true + }, + "@langchain/cohere": { + "optional": true + }, + "@langchain/deepseek": { + "optional": true + }, + "@langchain/google-genai": { + "optional": true + }, + "@langchain/google-vertexai": { + "optional": true + }, + "@langchain/google-vertexai-web": { + "optional": true + }, + "@langchain/groq": { + "optional": true + }, + "@langchain/mistralai": { + "optional": true + }, + "@langchain/ollama": { + "optional": true + }, + "@langchain/xai": { + "optional": true + }, + "axios": { + "optional": true + }, + "cheerio": { + "optional": true + }, + "handlebars": { + "optional": true + }, + "peggy": { + "optional": true + }, + "typeorm": { + "optional": true + } + } + }, + "node_modules/langium": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/langium/-/langium-3.3.1.tgz", + "integrity": "sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w==", + "license": "MIT", + "dependencies": { + "chevrotain": "~11.0.3", + "chevrotain-allstar": "~0.3.0", + "vscode-languageserver": "~9.0.1", + "vscode-languageserver-textdocument": "~1.0.11", + "vscode-uri": "~3.0.8" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/langsmith": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.3.31.tgz", + "integrity": "sha512-9lwuLZuN3tXFYQ6eMg0rmbBw7oxQo4bu1NYeylbjz27bOdG1XB9XNoxaiIArkK4ciLdOIOhPMBXP4bkvZOgHRw==", + "license": "MIT", + "dependencies": { + "@types/uuid": "^10.0.0", + "chalk": "^4.1.2", + "console-table-printer": "^2.12.1", + "p-queue": "^6.6.2", + "p-retry": "4", + "semver": "^7.6.3", + "uuid": "^10.0.0" + }, + "peerDependencies": { + "openai": "*" + }, + "peerDependenciesMeta": { + "openai": { + "optional": true + } + } + }, + "node_modules/langsmith/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/langsmith/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/langsmith/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/layout-base": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", + "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==", + "license": "MIT" + }, + "node_modules/lazy-val": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", + "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==", + "license": "MIT" + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/leva": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/leva/-/leva-0.10.0.tgz", + "integrity": "sha512-RiNJWmeqQdKIeHuVXgshmxIHu144a2AMYtLxKf8Nm1j93pisDPexuQDHKNdQlbo37wdyDQibLjY9JKGIiD7gaw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-portal": "1.0.2", + "@radix-ui/react-tooltip": "1.0.5", + "@stitches/react": "^1.2.8", + "@use-gesture/react": "^10.2.5", + "colord": "^2.9.2", + "dequal": "^2.0.2", + "merge-value": "^1.0.0", + "react-colorful": "^5.5.1", + "react-dropzone": "^12.0.0", + "v8n": "^1.3.3", + "zustand": "^3.6.9" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/lint-staged": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.5.2.tgz", + "integrity": "sha512-YUSOLq9VeRNAo/CTaVmhGDKG+LBtA8KF1X4K5+ykMSwWST1vDxJRB2kv2COgLb1fvpCo+A/y9A0G0znNVmdx4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.4.1", + "commander": "^13.1.0", + "debug": "^4.4.0", + "execa": "^8.0.1", + "lilconfig": "^3.1.3", + "listr2": "^8.2.5", + "micromatch": "^4.0.8", + "pidtree": "^0.6.0", + "string-argv": "^0.3.2", + "yaml": "^2.7.0" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/listr2": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.3.3.tgz", + "integrity": "sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/listr2/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/listr2/node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/listr2/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lit": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lit/-/lit-3.3.0.tgz", + "integrity": "sha512-DGVsqsOIHBww2DqnuZzW7QsuCdahp50ojuDaBPC7jUDRpYoH0z7kHBBYZewRzer75FwtrkmkKk7iOAwSaWdBmw==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit/reactive-element": "^2.1.0", + "lit-element": "^4.2.0", + "lit-html": "^3.3.0" + } + }, + "node_modules/lit-element": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.2.0.tgz", + "integrity": "sha512-MGrXJVAI5x+Bfth/pU9Kst1iWID6GHDLEzFEnyULB/sFiRLgkd8NPK/PeeXxktA3T6EIIaq8U3KcbTU5XFcP2Q==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.2.0", + "@lit/reactive-element": "^2.1.0", + "lit-html": "^3.3.0" + } + }, + "node_modules/lit-html": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.3.0.tgz", + "integrity": "sha512-RHoswrFAxY2d8Cf2mm4OZ1DgzCoBKUKSPvA1fhtSELxUERq2aQQ2h05pO9j81gS1o7RIRJ+CePLogfyahwmynw==", + "license": "BSD-3-Clause", + "dependencies": { + "@types/trusted-types": "^2.0.2" + } + }, + "node_modules/local-pkg": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.1.tgz", + "integrity": "sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==", + "license": "MIT", + "dependencies": { + "mlly": "^1.7.4", + "pkg-types": "^2.0.1", + "quansync": "^0.2.8" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", + "license": "MIT" + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.startcase": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", + "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.upperfirst": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", + "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lop": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/lop/-/lop-0.4.2.tgz", + "integrity": "sha512-RefILVDQ4DKoRZsJ4Pj22TxE3omDO47yFpkIBoDKzkqPRISs5U1cnAdg/5583YPkWPaLIYHOKRMQSvjFsO26cw==", + "license": "BSD-2-Clause", + "dependencies": { + "duck": "^0.1.12", + "option": "~0.2.1", + "underscore": "^1.13.1" + } + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lucide-react": { + "version": "0.469.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.469.0.tgz", + "integrity": "sha512-28vvUnnKQ/dBwiCQtwJw7QauYnE7yd2Cyp4tTTJpvglX4EMpbflcdBgrgToX2j71B3YvugK/NH3BGUk+E/p/Fw==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "license": "MIT", + "optional": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/mammoth": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/mammoth/-/mammoth-1.9.1.tgz", + "integrity": "sha512-4S2v1eP4Yo4so0zGNicJKcP93su3wDPcUk+xvkjSG75nlNjSkDJu8BhWQ+e54BROM0HfA6nPzJn12S6bq2Ko6w==", + "license": "BSD-2-Clause", + "dependencies": { + "@xmldom/xmldom": "^0.8.6", + "argparse": "~1.0.3", + "base64-js": "^1.5.1", + "bluebird": "~3.4.0", + "dingbat-to-unicode": "^1.0.1", + "jszip": "^3.7.1", + "lop": "^0.4.2", + "path-is-absolute": "^1.0.0", + "underscore": "^1.13.1", + "xmlbuilder": "^10.0.0" + }, + "bin": { + "mammoth": "bin/mammoth" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/mammoth/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/mammoth/node_modules/bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", + "license": "MIT" + }, + "node_modules/mammoth/node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" + }, + "node_modules/mammoth/node_modules/xmlbuilder": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-10.1.1.tgz", + "integrity": "sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg==", + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/markdown-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", + "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/marked": { + "version": "15.0.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", + "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-math": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-math/-/mdast-util-math-3.0.0.tgz", + "integrity": "sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "longest-streak": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.1.0", + "unist-util-remove-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", + "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-newline-to-break": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-newline-to-break/-/mdast-util-newline-to-break-2.0.0.tgz", + "integrity": "sha512-MbgeFca0hLYIEx/2zGsszCSEJJ1JSCdiY5xQxRcLDDGa8EPvlLPupJ4DSajbMPAnC0je8jfb9TiUATnxxrHUog==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-find-and-replace": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", + "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/meow": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", + "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/merge-value/-/merge-value-1.0.0.tgz", + "integrity": "sha512-fJMmvat4NeKz63Uv9iHWcPDjCWcCkoiRoajRTEO8hlhUC6rwaHg0QCF9hBOTjZmm4JuglPckPSTtcuJL5kp0TQ==", + "license": "MIT", + "dependencies": { + "get-value": "^2.0.6", + "is-extendable": "^1.0.0", + "mixin-deep": "^1.2.0", + "set-value": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/mermaid": { + "version": "11.6.0", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.6.0.tgz", + "integrity": "sha512-PE8hGUy1LDlWIHWBP05SFdqUHGmRcCcK4IzpOKPE35eOw+G9zZgcnMpyunJVUEOgb//KBORPjysKndw8bFLuRg==", + "license": "MIT", + "dependencies": { + "@braintree/sanitize-url": "^7.0.4", + "@iconify/utils": "^2.1.33", + "@mermaid-js/parser": "^0.4.0", + "@types/d3": "^7.4.3", + "cytoscape": "^3.29.3", + "cytoscape-cose-bilkent": "^4.1.0", + "cytoscape-fcose": "^2.2.0", + "d3": "^7.9.0", + "d3-sankey": "^0.12.3", + "dagre-d3-es": "7.0.11", + "dayjs": "^1.11.13", + "dompurify": "^3.2.4", + "katex": "^0.16.9", + "khroma": "^2.1.0", + "lodash-es": "^4.17.21", + "marked": "^15.0.7", + "roughjs": "^4.6.6", + "stylis": "^4.3.6", + "ts-dedent": "^2.2.0", + "uuid": "^11.1.0" + } + }, + "node_modules/mermaid/node_modules/stylis": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", + "license": "MIT" + }, + "node_modules/mermaid/node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-math": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz", + "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==", + "license": "MIT", + "dependencies": { + "@types/katex": "^0.16.0", + "devlop": "^1.0.0", + "katex": "^0.16.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-expression": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz", + "integrity": "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-jsx": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.2.tgz", + "integrity": "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-md": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", + "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", + "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", + "license": "MIT", + "dependencies": { + "acorn": "^8.0.0", + "acorn-jsx": "^5.0.0", + "micromark-extension-mdx-expression": "^3.0.0", + "micromark-extension-mdx-jsx": "^3.0.0", + "micromark-extension-mdx-md": "^2.0.0", + "micromark-extension-mdxjs-esm": "^3.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs-esm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", + "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-mdx-expression": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.3.tgz", + "integrity": "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-events-to-acorn": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.3.tgz", + "integrity": "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "devOptional": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "license": "MIT", + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "devOptional": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/mlly": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz", + "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==", + "license": "MIT", + "dependencies": { + "acorn": "^8.14.0", + "pathe": "^2.0.1", + "pkg-types": "^1.3.0", + "ufo": "^1.5.4" + } + }, + "node_modules/mlly/node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "license": "MIT" + }, + "node_modules/mlly/node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/motion-dom": { + "version": "12.17.0", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.17.0.tgz", + "integrity": "sha512-FA6/c70R9NKs3g41XDVONzmUUrEmyaifLVGCWtAmHP0usDnX9W+RN/tmbC4EUl0w6yLGvMTOwnWCFVgA5luhRg==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.12.1" + } + }, + "node_modules/motion-utils": { + "version": "12.12.1", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.12.1.tgz", + "integrity": "sha512-f9qiqUHm7hWSLlNW8gS9pisnsN7CRFRD58vNjptKdsqFLpkVnX00TNeD6Q0d27V9KzT7ySFyK1TZ/DShfVOv6w==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/multistream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/multistream/-/multistream-4.1.0.tgz", + "integrity": "sha512-J1XDiAmmNpRCBfIWJv+n0ymC4ABcf/Pl+5YvC5B/D2f/2+8PtHvCNxMPKiQcZyi922Hq69J2YOpb1pTywfifyw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "once": "^1.4.0", + "readable-stream": "^3.6.0" + } + }, + "node_modules/mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "license": "MIT", + "peer": true, + "bin": { + "mustache": "bin/mustache" + } + }, + "node_modules/nan": { + "version": "2.22.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.2.tgz", + "integrity": "sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ==", + "license": "MIT", + "optional": true + }, + "node_modules/nanoid": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.5.tgz", + "integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, + "node_modules/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "dev": true, + "license": "MIT" + }, + "node_modules/next": { + "version": "14.2.29", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.29.tgz", + "integrity": "sha512-s98mCOMOWLGGpGOfgKSnleXLuegvvH415qtRZXpSp00HeEgdmrxmwL9cgKU+h4XrhB16zEI5d/7BnkS3ATInsA==", + "license": "MIT", + "dependencies": { + "@next/env": "14.2.29", + "@swc/helpers": "0.5.5", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001579", + "graceful-fs": "^4.2.11", + "postcss": "8.4.31", + "styled-jsx": "5.1.1" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=18.17.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "14.2.29", + "@next/swc-darwin-x64": "14.2.29", + "@next/swc-linux-arm64-gnu": "14.2.29", + "@next/swc-linux-arm64-musl": "14.2.29", + "@next/swc-linux-x64-gnu": "14.2.29", + "@next/swc-linux-x64-musl": "14.2.29", + "@next/swc-win32-arm64-msvc": "14.2.29", + "@next/swc-win32-ia32-msvc": "14.2.29", + "@next/swc-win32-x64-msvc": "14.2.29" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.41.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next-themes": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.2.1.tgz", + "integrity": "sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==", + "license": "MIT", + "peerDependencies": { + "next": "*", + "react": "*", + "react-dom": "*" + } + }, + "node_modules/node-abi": { + "version": "3.75.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz", + "integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", + "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "license": "MIT" + }, + "node_modules/node-uuid": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", + "integrity": "sha512-TkCET/3rr9mUuRp+CpO7qfgT++aAxfDRaalQhwPFzI9BY/2rCDn6OfpZOVggi1AXfTPpfkTrg5f5WQx5G1uLxA==", + "deprecated": "Use uuid module instead", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/numeral": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", + "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha512-VlF07iu3VV3+BTXj43Nmp6Irt/G7j/NgEctUS6IweH1RGhURjjCc2NWtzXFPXXWWfc7hgbXQdtiQu2LGp6MxUg==", + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ollama-ai-provider": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ollama-ai-provider/-/ollama-ai-provider-1.2.0.tgz", + "integrity": "sha512-jTNFruwe3O/ruJeppI/quoOUxG7NA6blG3ZyQj3lei4+NnJo7bi3eIRWqlVpRlu/mbzbFXeJSBuYQWF6pzGKww==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "^1.0.0", + "@ai-sdk/provider-utils": "^2.0.0", + "partial-json": "0.1.7" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/on-change": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/on-change/-/on-change-4.0.2.tgz", + "integrity": "sha512-cMtCyuJmTx/bg2HCpHo3ZLeF7FZnBOapLqZHr2AlLeJ5Ul0Zu2mUJJz051Fdwu/Et2YW04ZD+TtU+gVy0ACNCA==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/on-change?sponsor=1" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/oniguruma-parser": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz", + "integrity": "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==", + "license": "MIT" + }, + "node_modules/oniguruma-to-es": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.3.tgz", + "integrity": "sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg==", + "license": "MIT", + "dependencies": { + "oniguruma-parser": "^0.12.1", + "regex": "^6.0.1", + "regex-recursion": "^6.0.2" + } + }, + "node_modules/openai": { + "version": "4.104.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.104.0.tgz", + "integrity": "sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==", + "license": "Apache-2.0", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + }, + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/openai/node_modules/@types/node": { + "version": "18.19.111", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.111.tgz", + "integrity": "sha512-90sGdgA+QLJr1F9X79tQuEut0gEYIfkX9pydI4XGRgvFo9g2JWswefI+WUSUHPYVBHYSEfTEqBxA5hQvAZB3Mw==", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/openai/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT" + }, + "node_modules/openapi-types": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", + "license": "MIT" + }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "license": "(WTFPL OR MIT)", + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/option": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/option/-/option-0.2.4.tgz", + "integrity": "sha512-pkEqbDyl8ou5cpq+VsnQbe/WlEy5qS7xPzMS1U55OCG9KPvwFD46zDbxQIj3egJSFc3D+XhYOPUzz49zQAVy7A==", + "license": "BSD-2-Clause" + }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-is-promise": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", + "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "license": "MIT", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "license": "MIT", + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/package-manager-detector": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.3.0.tgz", + "integrity": "sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==", + "license": "MIT" + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/partial-json": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/partial-json/-/partial-json-0.1.7.tgz", + "integrity": "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==", + "license": "MIT" + }, + "node_modules/path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", + "license": "MIT", + "dependencies": { + "process": "^0.11.1", + "util": "^0.10.3" + } + }, + "node_modules/path-data-parser": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz", + "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==", + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "license": "MIT" + }, + "node_modules/pdf2md-js": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/pdf2md-js/-/pdf2md-js-1.0.8.tgz", + "integrity": "sha512-cjKv46RzWmUNCt0mOgr/HGOJJl1fJmNOaldx+tdwlxTiZojZVBkGwI27HLPbms5zH+XRdJiCwjynnZZuKQygvQ==", + "license": "MIT", + "dependencies": { + "@hyzyla/pdfium": "^2.1.7", + "fs-extra": "^11.3.0", + "sharp": "^0.33.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/pdf2md-js/node_modules/fs-extra": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/pdf2md-js/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/pdf2md-js/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "license": "MIT", + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pkg": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/pkg/-/pkg-5.8.1.tgz", + "integrity": "sha512-CjBWtFStCfIiT4Bde9QpJy0KeH19jCfwZRJqHFDFXfhUklCx8JoFmMj3wgnEYIwGmZVNkhsStPHEOnrtrQhEXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/generator": "7.18.2", + "@babel/parser": "7.18.4", + "@babel/types": "7.19.0", + "chalk": "^4.1.2", + "fs-extra": "^9.1.0", + "globby": "^11.1.0", + "into-stream": "^6.0.0", + "is-core-module": "2.9.0", + "minimist": "^1.2.6", + "multistream": "^4.1.0", + "pkg-fetch": "3.4.2", + "prebuild-install": "7.1.1", + "resolve": "^1.22.0", + "stream-meter": "^1.0.4" + }, + "bin": { + "pkg": "lib-es5/bin.js" + }, + "peerDependencies": { + "node-notifier": ">=9.0.1" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/pkg-fetch": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/pkg-fetch/-/pkg-fetch-3.4.2.tgz", + "integrity": "sha512-0+uijmzYcnhC0hStDjm/cl2VYdrmVVBpe7Q8k9YBojxmR5tG8mvR9/nooQq3QSXiQqORDVOTY3XqMEqJVIzkHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "fs-extra": "^9.1.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.6", + "progress": "^2.0.3", + "semver": "^7.3.5", + "tar-fs": "^2.1.1", + "yargs": "^16.2.0" + }, + "bin": { + "pkg-fetch": "lib-es5/bin.js" + } + }, + "node_modules/pkg-fetch/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-fetch/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pkg-fetch/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/pkg-fetch/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/pkg-fetch/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pkg-fetch/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/pkg-fetch/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-fetch/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-fetch/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/pkg-fetch/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/pkg-fetch/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pkg-fetch/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/pkg-types": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.1.0.tgz", + "integrity": "sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A==", + "license": "MIT", + "dependencies": { + "confbox": "^0.2.1", + "exsolve": "^1.0.1", + "pathe": "^2.0.3" + } + }, + "node_modules/pkg/node_modules/@babel/generator": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.2.tgz", + "integrity": "sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.18.2", + "@jridgewell/gen-mapping": "^0.3.0", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/pkg/node_modules/@babel/parser": { + "version": "7.18.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.4.tgz", + "integrity": "sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow==", + "dev": true, + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/pkg/node_modules/@babel/types": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.0.tgz", + "integrity": "sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.18.10", + "@babel/helper-validator-identifier": "^7.18.6", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/pkg/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pkg/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/pkg/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pkg/node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/pkg/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/plist": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", + "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.8.8", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" + }, + "engines": { + "node": ">=10.4.0" + } + }, + "node_modules/points-on-curve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", + "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==", + "license": "MIT" + }, + "node_modules/points-on-path": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz", + "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==", + "license": "MIT", + "dependencies": { + "path-data-parser": "0.1.0", + "points-on-curve": "0.2.0" + } + }, + "node_modules/polished": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/polished/-/polished-4.3.1.tgz", + "integrity": "sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.17.8" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss/node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", + "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prisma": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.9.0.tgz", + "integrity": "sha512-resJAwMyZREC/I40LF6FZ6rZTnlrlrYrb63oW37Gq+U+9xHwbyMSPJjKtM7VZf3gTO86t/Oyz+YeSXr3CmAY1Q==", + "devOptional": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/config": "6.9.0", + "@prisma/engines": "6.9.0" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/promise-retry/node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/q": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", + "integrity": "sha512-/CdEdaw49VZVmyIDGUQKDDT53c7qBkO6g5CefWz91Ae+l4+cRtcDYwMTXh6me4O8TMldeGHG3N2Bl84V78Ywbg==", + "deprecated": "You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.\n\n(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)", + "license": "MIT", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qs": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.1.2.tgz", + "integrity": "sha512-vkyEo9cSlcgr1xj5n14ykoPWKE36R8wkxK2fQkbGACZNv4zDGFw/juEwFFUs9/APU7DaTMRlRNTYISLPD0Z4Qw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/quansync": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.10.tgz", + "integrity": "sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, + "node_modules/query-string": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-9.2.0.tgz", + "integrity": "sha512-YIRhrHujoQxhexwRLxfy3VSjOXmvZRd2nyw1PwL1UUqZ/ys1dEZd1+NSgXkne2l/4X/7OXkigEAuhTX0g/ivJQ==", + "license": "MIT", + "dependencies": { + "decode-uri-component": "^0.4.1", + "filter-obj": "^5.1.0", + "split-on-first": "^3.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc-cascader": { + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.34.0.tgz", + "integrity": "sha512-KpXypcvju9ptjW9FaN2NFcA2QH9E9LHKq169Y0eWtH4e/wHQ5Wh5qZakAgvb8EKZ736WZ3B0zLLOBsrsja5Dag==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.25.7", + "classnames": "^2.3.1", + "rc-select": "~14.16.2", + "rc-tree": "~5.13.0", + "rc-util": "^5.43.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-checkbox": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/rc-checkbox/-/rc-checkbox-3.5.0.tgz", + "integrity": "sha512-aOAQc3E98HteIIsSqm6Xk2FPKIER6+5vyEFMZfo73TqM+VVAIqOkHoPjgKLqSNtVLWScoaM7vY2ZrGEheI79yg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.3.2", + "rc-util": "^5.25.2" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-collapse": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/rc-collapse/-/rc-collapse-3.9.0.tgz", + "integrity": "sha512-swDdz4QZ4dFTo4RAUMLL50qP0EY62N2kvmk2We5xYdRwcRn8WcYtuetCJpwpaCbUfUt5+huLpVxhvmnK+PHrkA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-motion": "^2.3.4", + "rc-util": "^5.27.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-dialog": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/rc-dialog/-/rc-dialog-9.6.0.tgz", + "integrity": "sha512-ApoVi9Z8PaCQg6FsUzS8yvBEQy0ZL2PkuvAgrmohPkN3okps5WZ5WQWPc1RNuiOKaAYv8B97ACdsFU5LizzCqg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/portal": "^1.0.0-8", + "classnames": "^2.2.6", + "rc-motion": "^2.3.0", + "rc-util": "^5.21.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-drawer": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/rc-drawer/-/rc-drawer-7.3.0.tgz", + "integrity": "sha512-DX6CIgiBWNpJIMGFO8BAISFkxiuKitoizooj4BDyee8/SnBn0zwO2FHrNDpqqepj0E/TFTDpmEBCyFuTgC7MOg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "@rc-component/portal": "^1.1.1", + "classnames": "^2.2.6", + "rc-motion": "^2.6.1", + "rc-util": "^5.38.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-dropdown": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/rc-dropdown/-/rc-dropdown-4.2.1.tgz", + "integrity": "sha512-YDAlXsPv3I1n42dv1JpdM7wJ+gSUBfeyPK59ZpBD9jQhK9jVuxpjj3NmWQHOBceA1zEPVX84T2wbdb2SD0UjmA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.18.3", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.2.6", + "rc-util": "^5.44.1" + }, + "peerDependencies": { + "react": ">=16.11.0", + "react-dom": ">=16.11.0" + } + }, + "node_modules/rc-field-form": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/rc-field-form/-/rc-field-form-2.7.0.tgz", + "integrity": "sha512-hgKsCay2taxzVnBPZl+1n4ZondsV78G++XVsMIJCAoioMjlMQR9YwAp7JZDIECzIu2Z66R+f4SFIRrO2DjDNAA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.18.0", + "@rc-component/async-validator": "^5.0.3", + "rc-util": "^5.32.2" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-footer": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/rc-footer/-/rc-footer-0.6.8.tgz", + "integrity": "sha512-JBZ+xcb6kkex8XnBd4VHw1ZxjV6kmcwUumSHaIFdka2qzMCo7Klcy4sI6G0XtUpG/vtpislQCc+S9Bc+NLHYMg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/rc-image": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/rc-image/-/rc-image-7.12.0.tgz", + "integrity": "sha512-cZ3HTyyckPnNnUb9/DRqduqzLfrQRyi+CdHjdqgsyDpI3Ln5UX1kXnAhPBSJj9pVRzwRFgqkN7p9b6HBDjmu/Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.11.2", + "@rc-component/portal": "^1.0.2", + "classnames": "^2.2.6", + "rc-dialog": "~9.6.0", + "rc-motion": "^2.6.2", + "rc-util": "^5.34.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-input": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/rc-input/-/rc-input-1.8.0.tgz", + "integrity": "sha512-KXvaTbX+7ha8a/k+eg6SYRVERK0NddX8QX7a7AnRvUa/rEH0CNMlpcBzBkhI0wp2C8C4HlMoYl8TImSN+fuHKA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-util": "^5.18.1" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/rc-input-number": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/rc-input-number/-/rc-input-number-9.5.0.tgz", + "integrity": "sha512-bKaEvB5tHebUURAEXw35LDcnRZLq3x1k7GxfAqBMzmpHkDGzjAtnUL8y4y5N15rIFIg5IJgwr211jInl3cipag==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/mini-decimal": "^1.0.1", + "classnames": "^2.2.5", + "rc-input": "~1.8.0", + "rc-util": "^5.40.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-mentions": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/rc-mentions/-/rc-mentions-2.20.0.tgz", + "integrity": "sha512-w8HCMZEh3f0nR8ZEd466ATqmXFCMGMN5UFCzEUL0bM/nGw/wOS2GgRzKBcm19K++jDyuWCOJOdgcKGXU3fXfbQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.22.5", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.2.6", + "rc-input": "~1.8.0", + "rc-menu": "~9.16.0", + "rc-textarea": "~1.10.0", + "rc-util": "^5.34.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-menu": { + "version": "9.16.1", + "resolved": "https://registry.npmjs.org/rc-menu/-/rc-menu-9.16.1.tgz", + "integrity": "sha512-ghHx6/6Dvp+fw8CJhDUHFHDJ84hJE3BXNCzSgLdmNiFErWSOaZNsihDAsKq9ByTALo/xkNIwtDFGIl6r+RPXBg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/trigger": "^2.0.0", + "classnames": "2.x", + "rc-motion": "^2.4.3", + "rc-overflow": "^1.3.1", + "rc-util": "^5.27.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-motion": { + "version": "2.9.5", + "resolved": "https://registry.npmjs.org/rc-motion/-/rc-motion-2.9.5.tgz", + "integrity": "sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-util": "^5.44.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-notification": { + "version": "5.6.4", + "resolved": "https://registry.npmjs.org/rc-notification/-/rc-notification-5.6.4.tgz", + "integrity": "sha512-KcS4O6B4qzM3KH7lkwOB7ooLPZ4b6J+VMmQgT51VZCeEcmghdeR4IrMcFq0LG+RPdnbe/ArT086tGM8Snimgiw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-motion": "^2.9.0", + "rc-util": "^5.20.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-overflow": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rc-overflow/-/rc-overflow-1.4.1.tgz", + "integrity": "sha512-3MoPQQPV1uKyOMVNd6SZfONi+f3st0r8PksexIdBTeIYbMX0Jr+k7pHEDvsXtR4BpCv90/Pv2MovVNhktKrwvw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.37.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-pagination": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/rc-pagination/-/rc-pagination-5.1.0.tgz", + "integrity": "sha512-8416Yip/+eclTFdHXLKTxZvn70duYVGTvUUWbckCCZoIl3jagqke3GLsFrMs0bsQBikiYpZLD9206Ej4SOdOXQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.3.2", + "rc-util": "^5.38.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-picker": { + "version": "4.11.3", + "resolved": "https://registry.npmjs.org/rc-picker/-/rc-picker-4.11.3.tgz", + "integrity": "sha512-MJ5teb7FlNE0NFHTncxXQ62Y5lytq6sh5nUw0iH8OkHL/TjARSEvSHpr940pWgjGANpjCwyMdvsEV55l5tYNSg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.24.7", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.2.1", + "rc-overflow": "^1.3.2", + "rc-resize-observer": "^1.4.0", + "rc-util": "^5.43.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "date-fns": ">= 2.x", + "dayjs": ">= 1.x", + "luxon": ">= 3.x", + "moment": ">= 2.x", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + }, + "peerDependenciesMeta": { + "date-fns": { + "optional": true + }, + "dayjs": { + "optional": true + }, + "luxon": { + "optional": true + }, + "moment": { + "optional": true + } + } + }, + "node_modules/rc-progress": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/rc-progress/-/rc-progress-4.0.0.tgz", + "integrity": "sha512-oofVMMafOCokIUIBnZLNcOZFsABaUw8PPrf1/y0ZBvKZNpOiu5h4AO9vv11Sw0p4Hb3D0yGWuEattcQGtNJ/aw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.6", + "rc-util": "^5.16.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-rate": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/rc-rate/-/rc-rate-2.13.1.tgz", + "integrity": "sha512-QUhQ9ivQ8Gy7mtMZPAjLbxBt5y9GRp65VcUyGUMF3N3fhiftivPHdpuDIaWIMOTEprAjZPC08bls1dQB+I1F2Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.5", + "rc-util": "^5.0.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-resize-observer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/rc-resize-observer/-/rc-resize-observer-1.4.3.tgz", + "integrity": "sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.20.7", + "classnames": "^2.2.1", + "rc-util": "^5.44.1", + "resize-observer-polyfill": "^1.5.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-segmented": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/rc-segmented/-/rc-segmented-2.7.0.tgz", + "integrity": "sha512-liijAjXz+KnTRVnxxXG2sYDGd6iLL7VpGGdR8gwoxAXy2KglviKCxLWZdjKYJzYzGSUwKDSTdYk8brj54Bn5BA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-motion": "^2.4.4", + "rc-util": "^5.17.0" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/rc-select": { + "version": "14.16.8", + "resolved": "https://registry.npmjs.org/rc-select/-/rc-select-14.16.8.tgz", + "integrity": "sha512-NOV5BZa1wZrsdkKaiK7LHRuo5ZjZYMDxPP6/1+09+FB4KoNi8jcG1ZqLE3AVCxEsYMBe65OBx71wFoHRTP3LRg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/trigger": "^2.1.1", + "classnames": "2.x", + "rc-motion": "^2.0.1", + "rc-overflow": "^1.3.1", + "rc-util": "^5.16.1", + "rc-virtual-list": "^3.5.2" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/rc-slider": { + "version": "11.1.8", + "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-11.1.8.tgz", + "integrity": "sha512-2gg/72YFSpKP+Ja5AjC5DPL1YnV8DEITDQrcc1eASrUYjl0esptaBVJBh5nLTXCCp15eD8EuGjwezVGSHhs9tQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.5", + "rc-util": "^5.36.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-steps": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rc-steps/-/rc-steps-6.0.1.tgz", + "integrity": "sha512-lKHL+Sny0SeHkQKKDJlAjV5oZ8DwCdS2hFhAkIjuQt1/pB81M0cA0ErVFdHq9+jmPmFw1vJB2F5NBzFXLJxV+g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.16.7", + "classnames": "^2.2.3", + "rc-util": "^5.16.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-switch": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/rc-switch/-/rc-switch-4.1.0.tgz", + "integrity": "sha512-TI8ufP2Az9oEbvyCeVE4+90PDSljGyuwix3fV58p7HV2o4wBnVToEyomJRVyTaZeqNPAp+vqeo4Wnj5u0ZZQBg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.21.0", + "classnames": "^2.2.1", + "rc-util": "^5.30.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-table": { + "version": "7.51.0", + "resolved": "https://registry.npmjs.org/rc-table/-/rc-table-7.51.0.tgz", + "integrity": "sha512-7ZlvW6lB0IDKaSFInD6OfJsCepSJJtfsQv2PZLtzEeZd/PLzQnKliXPaoZqkqDdLdJ3jxE2x4sane4DjxcAg+g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/context": "^1.4.0", + "classnames": "^2.2.5", + "rc-resize-observer": "^1.1.0", + "rc-util": "^5.44.3", + "rc-virtual-list": "^3.14.2" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-tabs": { + "version": "15.6.1", + "resolved": "https://registry.npmjs.org/rc-tabs/-/rc-tabs-15.6.1.tgz", + "integrity": "sha512-/HzDV1VqOsUWyuC0c6AkxVYFjvx9+rFPKZ32ejxX0Uc7QCzcEjTA9/xMgv4HemPKwzBNX8KhGVbbumDjnj92aA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.11.2", + "classnames": "2.x", + "rc-dropdown": "~4.2.0", + "rc-menu": "~9.16.0", + "rc-motion": "^2.6.2", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.34.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-textarea": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/rc-textarea/-/rc-textarea-1.10.0.tgz", + "integrity": "sha512-ai9IkanNuyBS4x6sOL8qu/Ld40e6cEs6pgk93R+XLYg0mDSjNBGey6/ZpDs5+gNLD7urQ14po3V6Ck2dJLt9SA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.1", + "rc-input": "~1.8.0", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.27.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-tooltip": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/rc-tooltip/-/rc-tooltip-6.4.0.tgz", + "integrity": "sha512-kqyivim5cp8I5RkHmpsp1Nn/Wk+1oeloMv9c7LXNgDxUpGm+RbXJGL+OPvDlcRnx9DBeOe4wyOIl4OKUERyH1g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.11.2", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.3.1", + "rc-util": "^5.44.3" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-tree": { + "version": "5.13.1", + "resolved": "https://registry.npmjs.org/rc-tree/-/rc-tree-5.13.1.tgz", + "integrity": "sha512-FNhIefhftobCdUJshO7M8uZTA9F4OPGVXqGfZkkD/5soDeOhwO06T/aKTrg0WD8gRg/pyfq+ql3aMymLHCTC4A==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-motion": "^2.0.1", + "rc-util": "^5.16.1", + "rc-virtual-list": "^3.5.1" + }, + "engines": { + "node": ">=10.x" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/rc-tree-select": { + "version": "5.27.0", + "resolved": "https://registry.npmjs.org/rc-tree-select/-/rc-tree-select-5.27.0.tgz", + "integrity": "sha512-2qTBTzwIT7LRI1o7zLyrCzmo5tQanmyGbSaGTIf7sYimCklAToVVfpMC6OAldSKolcnjorBYPNSKQqJmN3TCww==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.25.7", + "classnames": "2.x", + "rc-select": "~14.16.2", + "rc-tree": "~5.13.0", + "rc-util": "^5.43.0" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/rc-upload": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/rc-upload/-/rc-upload-4.9.2.tgz", + "integrity": "sha512-nHx+9rbd1FKMiMRYsqQ3NkXUv7COHPBo3X1Obwq9SWS6/diF/A0aJ5OHubvwUAIDs+4RMleljV0pcrNUc823GQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.18.3", + "classnames": "^2.2.5", + "rc-util": "^5.2.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-util": { + "version": "5.44.4", + "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.44.4.tgz", + "integrity": "sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "react-is": "^18.2.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-util/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, + "node_modules/rc-virtual-list": { + "version": "3.18.6", + "resolved": "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.18.6.tgz", + "integrity": "sha512-TQ5SsutL3McvWmmxqQtMIbfeoE3dGjJrRSfKekgby7WQMpPIFvv4ghytp5Z0s3D8Nik9i9YNOCqHBfk86AwgAA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.20.0", + "classnames": "^2.2.6", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.36.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/re-resizable": { + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/re-resizable/-/re-resizable-6.11.2.tgz", + "integrity": "sha512-2xI2P3OHs5qw7K0Ud1aLILK6MQxW50TcO+DetD9eIV58j84TqYeHoZcL9H4GXFXXIh7afhH8mv5iUCXII7OW7A==", + "license": "MIT", + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-avatar-editor": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/react-avatar-editor/-/react-avatar-editor-13.0.2.tgz", + "integrity": "sha512-a4ajbi7lwDh98kgEtSEeKMu0vs0CHTczkq4Xcxr1EiwMFH1GlgHCEtwGU8q/H5W8SeLnH4KPK8LUjEEaZXklxQ==", + "license": "MIT", + "dependencies": { + "@babel/plugin-transform-runtime": "^7.12.1", + "@babel/runtime": "^7.12.5", + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "react": "^0.14.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^0.14.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-colorful": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz", + "integrity": "sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-draggable": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.6.tgz", + "integrity": "sha512-LtY5Xw1zTPqHkVmtM3X8MUOxNDOUhv/khTgBgrUvwaS064bwVvxT+q5El0uUFNx5IEPKXuRejr7UqLwBIg5pdw==", + "license": "MIT", + "dependencies": { + "clsx": "^1.1.1", + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "react": ">= 16.3.0", + "react-dom": ">= 16.3.0" + } + }, + "node_modules/react-draggable/node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react-dropzone": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-12.1.0.tgz", + "integrity": "sha512-iBYHA1rbopIvtzokEX4QubO6qk5IF/x3BtKGu74rF2JkQDXnwC4uO/lHKpaw4PJIV6iIAYOlwLv2FpiGyqHNog==", + "license": "MIT", + "dependencies": { + "attr-accept": "^2.2.2", + "file-selector": "^0.5.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">= 10.13" + }, + "peerDependencies": { + "react": ">= 16.8" + } + }, + "node_modules/react-error-boundary": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-5.0.0.tgz", + "integrity": "sha512-tnjAxG+IkpLephNcePNA7v6F/QpWLH8He65+DmedchDwg162JZqx4NmbXj0mlAYVVEd81OW7aFhmbsScYfiAFQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "peerDependencies": { + "react": ">=16.13.1" + } + }, + "node_modules/react-fast-compare": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", + "license": "MIT" + }, + "node_modules/react-hotkeys-hook": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/react-hotkeys-hook/-/react-hotkeys-hook-4.6.2.tgz", + "integrity": "sha512-FmP+ZriY3EG59Ug/lxNfrObCnW9xQShgk7Nb83+CkpfkcCpfS95ydv+E9JuXA5cp8KtskU7LGlIARpkc92X22Q==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.1", + "react-dom": ">=16.8.1" + } + }, + "node_modules/react-i18next": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.5.2.tgz", + "integrity": "sha512-ePODyXgmZQAOYTbZXQn5rRsSBu3Gszo69jxW6aKmlSgxKAI1fOhDwSu6bT4EKHciWPKQ7v7lPrjeiadR6Gi+1A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.25.0", + "html-parse-stringify": "^3.0.1" + }, + "peerDependencies": { + "i18next": ">= 23.2.3", + "react": ">= 16.8.0", + "typescript": "^5" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/react-is": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz", + "integrity": "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==", + "license": "MIT" + }, + "node_modules/react-layout-kit": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/react-layout-kit/-/react-layout-kit-1.9.1.tgz", + "integrity": "sha512-tQO5J+Ajppu2JCdhgFaFbWCg01WJXXaQ5vg8cxzsv8vVeogJKGFgoJm9OI2saDFchfKP3RABd+aRY5vB++poqw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7", + "@emotion/css": "^11" + }, + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/react-markdown": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", + "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, + "node_modules/react-merge-refs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/react-merge-refs/-/react-merge-refs-2.1.1.tgz", + "integrity": "sha512-jLQXJ/URln51zskhgppGJ2ub7b2WFKGq3cl3NYKtlHoTG+dN2q7EzWrn3hN3EgPsTMvpR9tpq5ijdp7YwFZkag==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/react-redux": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", + "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", + "license": "MIT", + "dependencies": { + "@types/use-sync-external-store": "^0.0.6", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "@types/react": "^18.2.25 || ^19", + "react": "^18.0 || ^19", + "redux": "^5.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "node_modules/react-rnd": { + "version": "10.5.2", + "resolved": "https://registry.npmjs.org/react-rnd/-/react-rnd-10.5.2.tgz", + "integrity": "sha512-0Tm4x7k7pfHf2snewJA8x7Nwgt3LV+58MVEWOVsFjk51eYruFEa6Wy7BNdxt4/lH0wIRsu7Gm3KjSXY2w7YaNw==", + "license": "MIT", + "dependencies": { + "re-resizable": "6.11.2", + "react-draggable": "4.4.6", + "tslib": "2.6.2" + }, + "peerDependencies": { + "react": ">=16.3.0", + "react-dom": ">=16.3.0" + } + }, + "node_modules/react-rnd/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "license": "0BSD" + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/react-zoom-pan-pinch": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/react-zoom-pan-pinch/-/react-zoom-pan-pinch-3.7.0.tgz", + "integrity": "sha512-UmReVZ0TxlKzxSbYiAj+LeGRW8s8LraAFTXRAxzMYnNRgGPsxCudwZKVkjvGmjtx7SW/hZamt69NUmGf4xrkXA==", + "license": "MIT", + "engines": { + "node": ">=8", + "npm": ">=5" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/read-config-file": { + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.3.2.tgz", + "integrity": "sha512-M80lpCjnE6Wt6zb98DoW8WHR09nzMSpu8XHtPkiTHrJ5Az9CybfeQhTJ8D7saeBHpGhLPIVyA8lcL6ZmdKwY6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "config-file-ts": "^0.2.4", + "dotenv": "^9.0.2", + "dotenv-expand": "^5.1.0", + "js-yaml": "^4.1.0", + "json5": "^2.2.0", + "lazy-val": "^1.0.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/recharts": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.6.0.tgz", + "integrity": "sha512-L5bjxvQRAe26RlToBAziKUB7whaGKEwD3znoM6fz3DrTowCIC/FnJYnuq1GEzB8Zv2kdTfaxQfi5GoH0tBinyg==", + "license": "MIT", + "workspaces": [ + "www" + ], + "dependencies": { + "@reduxjs/toolkit": "1.x.x || 2.x.x", + "clsx": "^2.1.1", + "decimal.js-light": "^2.5.1", + "es-toolkit": "^1.39.3", + "eventemitter3": "^5.0.1", + "immer": "^10.1.1", + "react-redux": "8.x.x || 9.x.x", + "reselect": "5.1.1", + "tiny-invariant": "^1.3.3", + "use-sync-external-store": "^1.2.2", + "victory-vendor": "^37.0.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/recma-build-jsx": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz", + "integrity": "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-util-build-jsx": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-jsx": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-jsx/-/recma-jsx-1.0.0.tgz", + "integrity": "sha512-5vwkv65qWwYxg+Atz95acp8DMu1JDSqdGkA2Of1j6rCreyFUE/gp15fC8MnGEuG1W68UKjM6x6+YTWIh7hZM/Q==", + "license": "MIT", + "dependencies": { + "acorn-jsx": "^5.0.0", + "estree-util-to-js": "^2.0.0", + "recma-parse": "^1.0.0", + "recma-stringify": "^1.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-parse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-parse/-/recma-parse-1.0.0.tgz", + "integrity": "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "esast-util-from-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-stringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-stringify/-/recma-stringify-1.0.0.tgz", + "integrity": "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-util-to-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", + "license": "MIT" + }, + "node_modules/redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "license": "MIT", + "peerDependencies": { + "redux": "^5.0.0" + } + }, + "node_modules/regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.0.1.tgz", + "integrity": "sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "license": "MIT" + }, + "node_modules/rehype-katex": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/rehype-katex/-/rehype-katex-7.0.1.tgz", + "integrity": "sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/katex": "^0.16.0", + "hast-util-from-html-isomorphic": "^2.0.0", + "hast-util-to-text": "^4.0.0", + "katex": "^0.16.0", + "unist-util-visit-parents": "^6.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-raw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-recma": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rehype-recma/-/rehype-recma-1.0.0.tgz", + "integrity": "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "hast-util-to-estree": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-breaks": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/remark-breaks/-/remark-breaks-4.0.0.tgz", + "integrity": "sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-newline-to-break": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-math": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/remark-math/-/remark-math-6.0.0.tgz", + "integrity": "sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-math": "^3.0.0", + "micromark-extension-math": "^3.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-mdx": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.0.tgz", + "integrity": "sha512-Ngl/H3YXyBV9RcRNdlYsZujAmhsxwzxpDzpDEhFBVAGthS4GDgnctpDjgFl/ULx5UEDzqtW1cyBSNKqYYrqLBA==", + "license": "MIT", + "dependencies": { + "mdast-util-mdx": "^3.0.0", + "micromark-extension-mdxjs": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/request": { + "version": "2.72.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.72.0.tgz", + "integrity": "sha512-rQiQ3Eza3HNC+gBlzKxXaPwG1rQIcO0/7TKGIgA9D/obvFK//H+pzkCS4CctQ7aFk6LboTvyFXHMEdf8P4pSxg==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "license": "Apache-2.0", + "dependencies": { + "aws-sign2": "~0.6.0", + "aws4": "^1.2.1", + "bl": "~1.1.2", + "caseless": "~0.11.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.0", + "forever-agent": "~0.6.1", + "form-data": "~1.0.0-rc3", + "har-validator": "~2.0.6", + "hawk": "~3.1.3", + "http-signature": "~1.1.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.7", + "node-uuid": "~1.4.7", + "oauth-sign": "~0.8.1", + "qs": "~6.1.0", + "stringstream": "~0.0.4", + "tough-cookie": "~2.2.0", + "tunnel-agent": "~0.4.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/request/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/request/node_modules/bl": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.1.2.tgz", + "integrity": "sha512-uVVYHEQk+OuWvCi5U+iquVXvvGCWXKawjwELIR2XMLsqfV/e2sGDClVBs8OlGIgGsStPRY/Es311YKYIlYCWAg==", + "license": "MIT", + "dependencies": { + "readable-stream": "~2.0.5" + } + }, + "node_modules/request/node_modules/form-data": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-1.0.1.tgz", + "integrity": "sha512-M4Yhq2mLogpCtpUmfopFlTTuIe6mSCTgKvnlMhDj3NcgVhA1uS20jT0n+xunKPzpmL5w2erSVtp+SKiJf1TlWg==", + "license": "MIT", + "dependencies": { + "async": "^2.0.1", + "combined-stream": "^1.0.5", + "mime-types": "^2.1.11" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/request/node_modules/process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha512-yN0WQmuCX63LP/TMvAg31nvT6m4vDqJEiiv2CAZqWOGNWutc9DfDk1NPYYmKUFmaVM2UwDowH4u5AHWYP/jxKw==", + "license": "MIT" + }, + "node_modules/request/node_modules/readable-stream": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha512-TXcFfb63BQe1+ySzsHZI/5v1aJPCShfqvWJ64ayNImXMsN1Cd0YGk/wm8KB7/OeessgPc9QvS9Zou8QTkFzsLw==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/request/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "license": "MIT" + }, + "node_modules/request/node_modules/tunnel-agent": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", + "integrity": "sha512-e0IoVDWx8SDHc/hwFTqJDQ7CCDTEeGhmcT9jkWJjoGQSpgBz20nAMr80E3Tpk7PatJ1b37DQDgJR3CNSzcMOZQ==", + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/reselect": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", + "license": "MIT" + }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve/node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "optional": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", + "license": "Unlicense" + }, + "node_modules/roughjs": { + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz", + "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==", + "license": "MIT", + "dependencies": { + "hachure-fill": "^0.5.2", + "path-data-parser": "^0.1.0", + "points-on-curve": "^0.2.0", + "points-on-path": "^0.2.1" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "license": "BSD-3-Clause" + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "devOptional": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/sanitize-filename": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", + "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", + "dev": true, + "license": "WTFPL OR ISC", + "dependencies": { + "truncate-utf8-bytes": "^1.0.0" + } + }, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "license": "ISC" + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/screenfull": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/screenfull/-/screenfull-5.2.0.tgz", + "integrity": "sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/scroll-into-view-if-needed": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz", + "integrity": "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "compute-scroll-into-view": "^3.0.2" + } + }, + "node_modules/secure-json-parse": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", + "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==", + "license": "BSD-3-Clause" + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "license": "MIT" + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC", + "optional": true + }, + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "license": "MIT", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, + "node_modules/sharp": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.1.tgz", + "integrity": "sha512-iAYUnOdTqqZDb3QjMneBKINTllCJDZ3em6WaWy7NPECM4aHncvqHRm0v0bN9nqJxMiwamv5KIdauJ6lUzKDpTQ==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.2", + "semver": "^7.5.4" + }, + "engines": { + "libvips": ">=8.15.0", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.1", + "@img/sharp-darwin-x64": "0.33.1", + "@img/sharp-libvips-darwin-arm64": "1.0.0", + "@img/sharp-libvips-darwin-x64": "1.0.0", + "@img/sharp-libvips-linux-arm": "1.0.0", + "@img/sharp-libvips-linux-arm64": "1.0.0", + "@img/sharp-libvips-linux-s390x": "1.0.0", + "@img/sharp-libvips-linux-x64": "1.0.0", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.0", + "@img/sharp-libvips-linuxmusl-x64": "1.0.0", + "@img/sharp-linux-arm": "0.33.1", + "@img/sharp-linux-arm64": "0.33.1", + "@img/sharp-linux-s390x": "0.33.1", + "@img/sharp-linux-x64": "0.33.1", + "@img/sharp-linuxmusl-arm64": "0.33.1", + "@img/sharp-linuxmusl-x64": "0.33.1", + "@img/sharp-wasm32": "0.33.1", + "@img/sharp-win32-ia32": "0.33.1", + "@img/sharp-win32-x64": "0.33.1" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/shiki": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.6.0.tgz", + "integrity": "sha512-tKn/Y0MGBTffQoklaATXmTqDU02zx8NYBGQ+F6gy87/YjKbizcLd+Cybh/0ZtOBX9r1NEnAy/GTRDKtOsc1L9w==", + "license": "MIT", + "dependencies": { + "@shikijs/core": "3.6.0", + "@shikijs/engine-javascript": "3.6.0", + "@shikijs/engine-oniguruma": "3.6.0", + "@shikijs/langs": "3.6.0", + "@shikijs/themes": "3.6.0", + "@shikijs/types": "3.6.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "devOptional": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "license": "MIT" + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/simple-wcswidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-wcswidth/-/simple-wcswidth-1.0.1.tgz", + "integrity": "sha512-xMO/8eNREtaROt7tJvWJqHBDTMFN4eiQ5I4JRMuilwfnFcV5W9u7RUkueNkdw0jPqGMX36iCywelS5yilTuOxg==", + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha512-7bgVOAnPj3XjrKY577S+puCKGCRlUrcrEdsMeRXlg9Ghf5df/xNi6sONUa43WrHUd3TjJBF7O04jYoiY0FVa0A==", + "deprecated": "This module moved to @hapi/sntp. Please make sure to switch over as this distribution is no longer supported and may contain bugs and critical security issues.", + "dependencies": { + "hoek": "2.x.x" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/sonner": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.5.tgz", + "integrity": "sha512-YwbHQO6cSso3HBXlbCkgrgzDNIhws14r4MO87Ofy+cV2X7ES4pOoAK3+veSmVTvqNx1BWUxlhPmZzP00Crk2aQ==", + "license": "MIT", + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/spawn-command": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", + "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", + "dev": true + }, + "node_modules/split-on-first": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-3.0.0.tgz", + "integrity": "sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "license": "MIT", + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-string/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "license": "MIT", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "license": "Apache-2.0", + "dependencies": { + "frac": "~1.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "license": "MIT", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stat-mode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", + "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/stream-meter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/stream-meter/-/stream-meter-1.0.4.tgz", + "integrity": "sha512-4sOEtrbgFotXwnEuzzsQBYEV1elAeFSO8rSGeTwabuX1RRn/kEq9JVH7I0MRBhKVRR0sJkr0M0QCH7yOLf9fhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^2.1.4" + } + }, + "node_modules/stream-meter/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/stream-meter/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/stream-meter/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-convert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", + "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==", + "license": "MIT", + "peer": true + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/stringstream": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz", + "integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==", + "license": "MIT" + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/style-to-js": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.16.tgz", + "integrity": "sha512-/Q6ld50hKYPH3d/r6nr117TZkHR0w0kGGIVfpG9N6D8NymRPM9RqCUv4pRpJ62E5DqOYx2AFpbZMyCPnjQCnOw==", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.8" + } + }, + "node_modules/style-to-object": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.8.tgz", + "integrity": "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.4" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, + "node_modules/sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.1.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/swr": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.3.tgz", + "integrity": "sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", + "license": "MIT" + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar-fs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz", + "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-fs/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true, + "license": "ISC" + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/temp-file": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", + "integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-exit-hook": "^2.0.1", + "fs-extra": "^10.0.0" + } + }, + "node_modules/temp-file/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/temp-file/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/temp-file/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/text-extensions": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", + "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/throttle-debounce": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz", + "integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12.22" + } + }, + "node_modules/throttleit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-2.1.0.tgz", + "integrity": "sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "node_modules/tiny-typed-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz", + "integrity": "sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA==", + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", + "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", + "license": "MIT" + }, + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/tmp-promise": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", + "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tmp": "^0.2.0" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==", + "license": "MIT", + "peer": true + }, + "node_modules/tough-cookie": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.2.2.tgz", + "integrity": "sha512-Knz9Yr0hlBoWQgUKzOIvRg5adinizAf49i2gHRhj6cLjlM304zRw7uyiY22ADniDxnPHXfIeyQD0EAkgpIz0ow==", + "deprecated": "ReDoS vulnerability parsing Set-Cookie https://nodesecurity.io/advisories/130", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", + "dev": true, + "license": "WTFPL", + "dependencies": { + "utf8-byte-length": "^1.0.1" + } + }, + "node_modules/ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "license": "MIT", + "engines": { + "node": ">=6.10" + } + }, + "node_modules/ts-md5": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/ts-md5/-/ts-md5-1.3.1.tgz", + "integrity": "sha512-DiwiXfwvcTeZ5wCE0z+2A9EseZsztaiZtGrtSaY5JOD7ekPnR/GoIVD5gXZAlK9Na9Kvpo9Waz5rW64WKAWApg==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/turndown": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/turndown/-/turndown-7.2.0.tgz", + "integrity": "sha512-eCZGBN4nNNqM9Owkv9HAtWRYfLA4h909E/WGAWWBpmB275ehNhZyk87/Tpvjbp0jjNl9XwCsbe6bm6CqFsgD+A==", + "license": "MIT", + "dependencies": { + "@mixmark-io/domino": "^2.2.0" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "license": "Unlicense" + }, + "node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", + "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", + "license": "MIT" + }, + "node_modules/underscore": { + "version": "1.13.7", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", + "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "license": "MIT" + }, + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-find-after": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", + "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", + "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpdf": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/unpdf/-/unpdf-0.12.2.tgz", + "integrity": "sha512-3eyDFfayk+Sf5+inJ4OyhecR2BtRFEeZqUfGPdq2O8aBLau9MYL9lAP+GEcSAaVd2JWqde8Dnz38z0x7KRglaA==", + "license": "MIT", + "optionalDependencies": { + "canvas": "^2.11.2" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ==", + "license": "MIT", + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url-join": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz", + "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", + "license": "MIT" + }, + "node_modules/use-isomorphic-layout-effect": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz", + "integrity": "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-merge-value": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-merge-value/-/use-merge-value-1.2.0.tgz", + "integrity": "sha512-DXgG0kkgJN45TcyoXL49vJnn55LehnrmoHc7MbKi+QDBvr8dsesqws8UlyIWGHMR+JXgxc1nvY+jDGMlycsUcw==", + "license": "MIT", + "peerDependencies": { + "react": ">= 16.x" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/utf8-byte-length": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", + "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", + "dev": true, + "license": "(WTFPL OR MIT)" + }, + "node_modules/util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "license": "MIT", + "dependencies": { + "inherits": "2.0.3" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/util/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "license": "ISC" + }, + "node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8n": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/v8n/-/v8n-1.5.1.tgz", + "integrity": "sha512-LdabyT4OffkyXFCe9UT+uMkxNBs5rcTVuZClvxQr08D5TUgo1OFKkoT65qYRCsiKBl/usHjpXvP4hHMzzDRj3A==", + "license": "MIT" + }, + "node_modules/verror": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", + "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/victory-vendor": { + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz", + "integrity": "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "license": "MIT", + "dependencies": { + "vscode-languageserver-protocol": "3.17.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "license": "MIT" + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "license": "MIT" + }, + "node_modules/vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", + "license": "MIT" + }, + "node_modules/wait-on": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-7.2.0.tgz", + "integrity": "sha512-wCQcHkRazgjG5XoAq9jbTMLpNIjoSlZslrJ2+N9MxDsGEv1HnFoVjOCexL0ESva7Y9cu350j+DWADdk54s4AFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "axios": "^1.6.1", + "joi": "^17.11.0", + "lodash": "^4.17.21", + "minimist": "^1.2.8", + "rxjs": "^7.8.1" + }, + "bin": { + "wait-on": "bin/wait-on" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "license": "ISC", + "optional": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/xlsx": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", + "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + }, + "bin": { + "xlsx": "bin/xlsx.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, + "node_modules/xmldom": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.6.0.tgz", + "integrity": "sha512-iAcin401y58LckRZ0TkI4k0VSM1Qg0KGSc3i8rU+xrxe19A/BN1zHyVSJY7uoutVlaTSzYyk/v5AmkewAP7jtg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "devOptional": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", + "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yocto-queue": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz", + "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zhipu-ai-provider": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/zhipu-ai-provider/-/zhipu-ai-provider-0.1.1.tgz", + "integrity": "sha512-cVwvvGtPiQqgsGdBzHCHC5oQ7z6slEQTbXJ5+42gQGX4N5uRUvYj+YYLp7Cr1HPQGF3zR2p8vNbT5etPHD4NbA==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "1.0.9", + "@ai-sdk/provider-utils": "2.1.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.0.0" + } + }, + "node_modules/zhipu-ai-provider/node_modules/@ai-sdk/provider": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-1.0.9.tgz", + "integrity": "sha512-jie6ZJT2ZR0uVOVCDc9R2xCX5I/Dum/wEK28lx21PJx6ZnFAN9EzD2WsPhcDWfCgGx3OAZZ0GyM3CEobXpa9LA==", + "license": "Apache-2.0", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/zhipu-ai-provider/node_modules/@ai-sdk/provider-utils": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-2.1.10.tgz", + "integrity": "sha512-4GZ8GHjOFxePFzkl3q42AU0DQOtTQ5w09vmaWUf/pKFXJPizlnzKSUkF0f+VkapIUfDugyMqPMT1ge8XQzVI7Q==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "1.0.9", + "eventsource-parser": "^3.0.0", + "nanoid": "^3.3.8", + "secure-json-parse": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/zhipu-ai-provider/node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/zip-stream": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", + "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "archiver-utils": "^3.0.4", + "compress-commons": "^4.1.2", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zip-stream/node_modules/archiver-utils": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", + "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "glob": "^7.2.3", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.24.5", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz", + "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.24.1" + } + }, + "node_modules/zustand": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz", + "integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==", + "license": "MIT", + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/easy-dataset-main/package.json b/easy-dataset-main/package.json new file mode 100644 index 0000000..595a55a --- /dev/null +++ b/easy-dataset-main/package.json @@ -0,0 +1,184 @@ +{ + "name": "easy-dataset", + "version": "1.7.2", + "private": true, + "author": { + "name": "ConardLi", + "email": "1009903985@qq.com", + "url": "https://github.com/ConardLi" + }, + "homepage": "https://github.com/ConardLi/easy-dataset", + "scripts": { + "db:studio": "prisma studio", + "db:push": "prisma db push", + "db:template": "node prisma/generate-template.js", + "dev": "prisma db push && next dev -p 1717", + "build": "prisma db push && next build", + "start": "next start -p 1717", + "lint": "next lint", + "electron": "electron .", + "electron-dev": "concurrently \"pnpm dev\" \"wait-on http://localhost:1717 && electron .\"", + "electron-pack": "electron-builder --dir", + "electron-dist": "electron-builder", + "clean-dist": "rm -rf dist", + "electron-build": "pnpm clean-dist && pnpm db:template && prisma db push && next build && electron-builder -mwl", + "electron-build-mac": "pnpm clean-dist && pnpm db:template && prisma db push && next build && electron-builder --mac", + "electron-build-win": "pnpm clean-dist && pnpm db:template && prisma db push && next build && electron-builder --win", + "electron-build-linux": "pnpm clean-dist && pnpm db:template && prisma db push && next build && electron-builder --linux", + "docker": "docker build -t easy-dataset .", + "prettier": "npx prettier --write ." + }, + "bin": "desktop/server.js", + "pkg": { + "assets": [ + ".next/**/*", + "public/**/*", + "locales/**/*", + "package.json", + "node_modules/next/**/*" + ], + "targets": [ + "node18-macos-arm64", + "node18-macos-x64", + "node18-win-x64", + "node18-linux-x64" + ], + "outputPath": "dist" + }, + "dependencies": { + "@ai-sdk/openai": "^1.3.9", + "@ai-sdk/openai-compatible": "^1.0.22", + "@emotion/react": "^11.11.3", + "@emotion/styled": "^11.11.0", + "@fontsource/inter": "^5.0.16", + "@fontsource/jetbrains-mono": "^5.0.18", + "@huggingface/hub": "^2.0.2", + "@lobehub/icons": "^1.96.0", + "@mui/icons-material": "5.16.14", + "@mui/lab": "5.0.0-alpha.175", + "@mui/material": "5.16.14", + "@opendocsg/pdf2md": "^0.2.1", + "@openrouter/ai-sdk-provider": "^0.4.5", + "@prisma/client": "^6.6.0", + "adm-zip": "^0.5.16", + "ai": "^4.3.4", + "axios": "^1.8.4", + "electron-build": "^0.0.3", + "electron-updater": "^6.3.9", + "formidable": "^3.5.2", + "framer-motion": "^12.4.10", + "github-markdown-css": "^5.8.1", + "i18next": "^24.2.2", + "i18next-browser-languagedetector": "^8.0.4", + "image-size": "^2.0.2", + "jotai": "^2.12.3", + "jsonrepair": "^3.13.1", + "jszip": "^3.10.1", + "langchain": "^0.3.24", + "mammoth": "^1.9.0", + "nanoid": "^5.1.5", + "next": "^14.2.29", + "next-themes": "^0.2.1", + "ollama-ai-provider": "^1.2.0", + "opener": "^1.5.2", + "pdf2md-js": "1.0.8", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-i18next": "^15.4.1", + "react-markdown": "^10.0.1", + "recharts": "^3.6.0", + "sharp": "^0.33.1", + "sonner": "^2.0.3", + "turndown": "^7.2.0", + "xlsx": "^0.18.5", + "xmldom": "^0.6.0", + "zhipu-ai-provider": "^0.1.1", + "zod": "^3.25.76" + }, + "license": "AGPL 3.0", + "devDependencies": { + "@commitlint/cli": "^19.8.0", + "@commitlint/config-conventional": "^19.8.0", + "concurrently": "^8.2.2", + "electron": "^35.0.0", + "electron-builder": "^24.13.3", + "husky": "^9.1.7", + "lint-staged": "15.5.2", + "pkg": "^5.8.1", + "prisma": "^6.6.0", + "wait-on": "^7.2.0" + }, + "lint-staged": { + "*.{js,jsx,ts,tsx,json,md}": "npm run prettier" + }, + "main": "electron/main.js", + "description": "一个用于创建大模型微调数据集的应用程序", + "build": { + "appId": "com.easydataset.app", + "productName": "Easy Dataset", + "files": [ + ".next/**/*", + "!.next/cache/**/*", + "public/**/*", + "locales/**/*", + "package.json", + "electron/**/*", + "node_modules/**/*", + "!node_modules/.cache/**/*", + "!node_modules/.bin/**/*", + "!node_modules/.vite/**/*", + "!**/*.{md,d.ts,map}", + "!**/node_modules/*/{CHANGELOG.md,README.md,README,readme.md,readme}" + ], + "extraResources": [ + "prisma/schema.prisma", + "prisma/template.sqlite", + "prisma/sql.json", + "node_modules/.prisma/**/*", + "node_modules/@prisma/client/**/*" + ], + "directories": { + "buildResources": "public", + "output": "dist" + }, + "asar": true, + "asarUnpack": [ + "**/node_modules/sharp/**/*", + "**/node_modules/@img/**/*" + ], + "compression": "maximum", + "mac": { + "icon": "public/imgs/logo.icns", + "category": "public.app-category.developer-tools", + "target": [ + { + "target": "dmg", + "arch": [ + "arm64", + "x64" + ] + } + ], + "electronLanguages": [ + "zh_CN", + "en" + ] + }, + "win": { + "icon": "public/imgs/logo.ico", + "target": [ + { + "target": "nsis", + "arch": [ + "x64" + ] + } + ] + }, + "nsis": { + "oneClick": false, + "allowToChangeInstallationDirectory": true, + "perMachine": false + } + } +} diff --git a/easy-dataset-main/pnpm-lock.yaml b/easy-dataset-main/pnpm-lock.yaml new file mode 100644 index 0000000..ed78411 --- /dev/null +++ b/easy-dataset-main/pnpm-lock.yaml @@ -0,0 +1,13492 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + .: + dependencies: + '@ai-sdk/openai': + specifier: ^1.3.9 + version: 1.3.22(zod@3.25.76) + '@ai-sdk/openai-compatible': + specifier: ^1.0.22 + version: 1.0.22(zod@3.25.76) + '@emotion/react': + specifier: ^11.11.3 + version: 11.14.0(@types/react@19.1.8)(react@18.3.1) + '@emotion/styled': + specifier: ^11.11.0 + version: 11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react@18.3.1) + '@fontsource/inter': + specifier: ^5.0.16 + version: 5.2.6 + '@fontsource/jetbrains-mono': + specifier: ^5.0.18 + version: 5.2.6 + '@huggingface/hub': + specifier: ^2.0.2 + version: 2.2.0 + '@lobehub/icons': + specifier: ^1.96.0 + version: 1.98.0(@babel/core@7.27.4)(@types/react@19.1.8)(acorn@8.15.0)(antd@5.26.0(date-fns@2.30.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(framer-motion@12.17.0(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/icons-material': + specifier: 5.16.14 + version: 5.16.14(@mui/material@5.16.14(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@19.1.8)(react@18.3.1) + '@mui/lab': + specifier: 5.0.0-alpha.175 + version: 5.0.0-alpha.175(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react@18.3.1))(@mui/material@5.16.14(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@19.1.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/material': + specifier: 5.16.14 + version: 5.16.14(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@opendocsg/pdf2md': + specifier: ^0.2.1 + version: 0.2.1 + '@openrouter/ai-sdk-provider': + specifier: ^0.4.5 + version: 0.4.6(zod@3.25.76) + '@prisma/client': + specifier: ^6.6.0 + version: 6.9.0(prisma@6.9.0(typescript@5.8.3))(typescript@5.8.3) + adm-zip: + specifier: ^0.5.16 + version: 0.5.16 + ai: + specifier: ^4.3.4 + version: 4.3.16(react@18.3.1)(zod@3.25.76) + axios: + specifier: ^1.8.4 + version: 1.9.0 + electron-build: + specifier: ^0.0.3 + version: 0.0.3 + electron-updater: + specifier: ^6.3.9 + version: 6.6.2 + formidable: + specifier: ^3.5.2 + version: 3.5.4 + framer-motion: + specifier: ^12.4.10 + version: 12.17.0(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + github-markdown-css: + specifier: ^5.8.1 + version: 5.8.1 + i18next: + specifier: ^24.2.2 + version: 24.2.3(typescript@5.8.3) + i18next-browser-languagedetector: + specifier: ^8.0.4 + version: 8.2.0 + image-size: + specifier: ^2.0.2 + version: 2.0.2 + jotai: + specifier: ^2.12.3 + version: 2.12.5(@types/react@19.1.8)(react@18.3.1) + jsonrepair: + specifier: ^3.13.1 + version: 3.13.1 + jszip: + specifier: ^3.10.1 + version: 3.10.1 + langchain: + specifier: ^0.3.24 + version: 0.3.28(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.76)))(axios@1.9.0)(openai@4.104.0(zod@3.25.76)) + mammoth: + specifier: ^1.9.0 + version: 1.9.1 + nanoid: + specifier: ^5.1.5 + version: 5.1.5 + next: + specifier: ^14.2.29 + version: 14.2.29(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next-themes: + specifier: ^0.2.1 + version: 0.2.1(next@14.2.29(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + ollama-ai-provider: + specifier: ^1.2.0 + version: 1.2.0(zod@3.25.76) + opener: + specifier: ^1.5.2 + version: 1.5.2 + pdf2md-js: + specifier: 1.0.8 + version: 1.0.8 + react: + specifier: ^18.2.0 + version: 18.3.1 + react-dom: + specifier: ^18.2.0 + version: 18.3.1(react@18.3.1) + react-i18next: + specifier: ^15.4.1 + version: 15.5.2(i18next@24.2.3(typescript@5.8.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3) + react-markdown: + specifier: ^10.0.1 + version: 10.1.0(@types/react@19.1.8)(react@18.3.1) + recharts: + specifier: ^3.6.0 + version: 3.6.0(@types/react@19.1.8)(react-dom@18.3.1(react@18.3.1))(react-is@19.1.0)(react@18.3.1)(redux@5.0.1) + sharp: + specifier: ^0.33.1 + version: 0.33.5 + sonner: + specifier: ^2.0.3 + version: 2.0.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + turndown: + specifier: ^7.2.0 + version: 7.2.0 + xlsx: + specifier: ^0.18.5 + version: 0.18.5 + xmldom: + specifier: ^0.6.0 + version: 0.6.0 + zhipu-ai-provider: + specifier: ^0.1.1 + version: 0.1.1(zod@3.25.76) + zod: + specifier: ^3.25.76 + version: 3.25.76 + devDependencies: + '@commitlint/cli': + specifier: ^19.8.0 + version: 19.8.1(@types/node@24.0.1)(typescript@5.8.3) + '@commitlint/config-conventional': + specifier: ^19.8.0 + version: 19.8.1 + concurrently: + specifier: ^8.2.2 + version: 8.2.2 + electron: + specifier: ^35.0.0 + version: 35.5.1 + electron-builder: + specifier: ^24.13.3 + version: 24.13.3(electron-builder-squirrel-windows@24.13.3) + husky: + specifier: ^9.1.7 + version: 9.1.7 + lint-staged: + specifier: 15.5.2 + version: 15.5.2 + pkg: + specifier: ^5.8.1 + version: 5.8.1 + prisma: + specifier: ^6.6.0 + version: 6.9.0(typescript@5.8.3) + wait-on: + specifier: ^7.2.0 + version: 7.2.0 + +packages: + 7zip-bin@5.2.0: + resolution: + { integrity: sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A== } + + '@ai-sdk/openai-compatible@1.0.22': + resolution: + { integrity: sha512-Q+lwBIeMprc/iM+vg1yGjvzRrp74l316wDpqWdbmd4VXXlllblzGsUgBLTeKvcEapFTgqk0FRETvSb58Y6dsfA== } + engines: { node: '>=18' } + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/openai@1.3.22': + resolution: + { integrity: sha512-QwA+2EkG0QyjVR+7h6FE7iOu2ivNqAVMm9UJZkVxxTk5OIq5fFJDTEI/zICEMuHImTTXR2JjsL6EirJ28Jc4cw== } + engines: { node: '>=18' } + peerDependencies: + zod: ^3.0.0 + + '@ai-sdk/provider-utils@2.1.10': + resolution: + { integrity: sha512-4GZ8GHjOFxePFzkl3q42AU0DQOtTQ5w09vmaWUf/pKFXJPizlnzKSUkF0f+VkapIUfDugyMqPMT1ge8XQzVI7Q== } + engines: { node: '>=18' } + peerDependencies: + zod: ^3.0.0 + peerDependenciesMeta: + zod: + optional: true + + '@ai-sdk/provider-utils@2.2.8': + resolution: + { integrity: sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA== } + engines: { node: '>=18' } + peerDependencies: + zod: ^3.23.8 + + '@ai-sdk/provider-utils@3.0.12': + resolution: + { integrity: sha512-ZtbdvYxdMoria+2SlNarEk6Hlgyf+zzcznlD55EAl+7VZvJaSg2sqPvwArY7L6TfDEDJsnCq0fdhBSkYo0Xqdg== } + engines: { node: '>=18' } + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/provider@1.0.9': + resolution: + { integrity: sha512-jie6ZJT2ZR0uVOVCDc9R2xCX5I/Dum/wEK28lx21PJx6ZnFAN9EzD2WsPhcDWfCgGx3OAZZ0GyM3CEobXpa9LA== } + engines: { node: '>=18' } + + '@ai-sdk/provider@1.1.3': + resolution: + { integrity: sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg== } + engines: { node: '>=18' } + + '@ai-sdk/provider@2.0.0': + resolution: + { integrity: sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA== } + engines: { node: '>=18' } + + '@ai-sdk/react@1.2.12': + resolution: + { integrity: sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g== } + engines: { node: '>=18' } + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + zod: ^3.23.8 + peerDependenciesMeta: + zod: + optional: true + + '@ai-sdk/ui-utils@1.2.11': + resolution: + { integrity: sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w== } + engines: { node: '>=18' } + peerDependencies: + zod: ^3.23.8 + + '@ampproject/remapping@2.3.0': + resolution: + { integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== } + engines: { node: '>=6.0.0' } + + '@ant-design/colors@7.2.1': + resolution: + { integrity: sha512-lCHDcEzieu4GA3n8ELeZ5VQ8pKQAWcGGLRTQ50aQM2iqPpq2evTxER84jfdPvsPAtEcZ7m44NI45edFMo8oOYQ== } + + '@ant-design/cssinjs-utils@1.1.3': + resolution: + { integrity: sha512-nOoQMLW1l+xR1Co8NFVYiP8pZp3VjIIzqV6D6ShYF2ljtdwWJn5WSsH+7kvCktXL/yhEtWURKOfH5Xz/gzlwsg== } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@ant-design/cssinjs@1.23.0': + resolution: + { integrity: sha512-7GAg9bD/iC9ikWatU9ym+P9ugJhi/WbsTWzcKN6T4gU0aehsprtke1UAaaSxxkjjmkJb3llet/rbUSLPgwlY4w== } + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + '@ant-design/fast-color@2.0.6': + resolution: + { integrity: sha512-y2217gk4NqL35giHl72o6Zzqji9O7vHh9YmhUVkPtAOpoTCH4uWxo/pr4VE8t0+ChEPs0qo4eJRC5Q1eXWo3vA== } + engines: { node: '>=8.x' } + + '@ant-design/icons-svg@4.4.2': + resolution: + { integrity: sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA== } + + '@ant-design/icons@5.6.1': + resolution: + { integrity: sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg== } + engines: { node: '>=8' } + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + '@ant-design/react-slick@1.1.2': + resolution: + { integrity: sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA== } + peerDependencies: + react: '>=16.9.0' + + '@antfu/install-pkg@1.1.0': + resolution: + { integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ== } + + '@antfu/utils@8.1.1': + resolution: + { integrity: sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ== } + + '@babel/code-frame@7.27.1': + resolution: + { integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== } + engines: { node: '>=6.9.0' } + + '@babel/compat-data@7.27.5': + resolution: + { integrity: sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg== } + engines: { node: '>=6.9.0' } + + '@babel/core@7.27.4': + resolution: + { integrity: sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g== } + engines: { node: '>=6.9.0' } + + '@babel/generator@7.18.2': + resolution: + { integrity: sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw== } + engines: { node: '>=6.9.0' } + + '@babel/generator@7.27.5': + resolution: + { integrity: sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw== } + engines: { node: '>=6.9.0' } + + '@babel/helper-compilation-targets@7.27.2': + resolution: + { integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ== } + engines: { node: '>=6.9.0' } + + '@babel/helper-define-polyfill-provider@0.6.4': + resolution: + { integrity: sha512-jljfR1rGnXXNWnmQg2K3+bvhkxB51Rl32QRaOTuwwjviGrHzIbSc8+x9CpraDtbT7mfyjXObULP4w/adunNwAw== } + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + '@babel/helper-module-imports@7.27.1': + resolution: + { integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w== } + engines: { node: '>=6.9.0' } + + '@babel/helper-module-transforms@7.27.3': + resolution: + { integrity: sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.27.1': + resolution: + { integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw== } + engines: { node: '>=6.9.0' } + + '@babel/helper-string-parser@7.27.1': + resolution: + { integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== } + engines: { node: '>=6.9.0' } + + '@babel/helper-validator-identifier@7.27.1': + resolution: + { integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow== } + engines: { node: '>=6.9.0' } + + '@babel/helper-validator-option@7.27.1': + resolution: + { integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== } + engines: { node: '>=6.9.0' } + + '@babel/helpers@7.27.6': + resolution: + { integrity: sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug== } + engines: { node: '>=6.9.0' } + + '@babel/parser@7.18.4': + resolution: + { integrity: sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow== } + engines: { node: '>=6.0.0' } + hasBin: true + + '@babel/parser@7.27.5': + resolution: + { integrity: sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg== } + engines: { node: '>=6.0.0' } + hasBin: true + + '@babel/plugin-transform-runtime@7.27.4': + resolution: + { integrity: sha512-D68nR5zxU64EUzV8i7T3R5XP0Xhrou/amNnddsRQssx6GrTLdZl1rLxyjtVZBd+v/NVX4AbTPOB5aU8thAZV1A== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.27.6': + resolution: + { integrity: sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q== } + engines: { node: '>=6.9.0' } + + '@babel/template@7.27.2': + resolution: + { integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw== } + engines: { node: '>=6.9.0' } + + '@babel/traverse@7.27.4': + resolution: + { integrity: sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA== } + engines: { node: '>=6.9.0' } + + '@babel/types@7.19.0': + resolution: + { integrity: sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA== } + engines: { node: '>=6.9.0' } + + '@babel/types@7.27.6': + resolution: + { integrity: sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q== } + engines: { node: '>=6.9.0' } + + '@braintree/sanitize-url@7.1.1': + resolution: + { integrity: sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw== } + + '@cfworker/json-schema@4.1.1': + resolution: + { integrity: sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og== } + + '@chevrotain/cst-dts-gen@11.0.3': + resolution: + { integrity: sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ== } + + '@chevrotain/gast@11.0.3': + resolution: + { integrity: sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q== } + + '@chevrotain/regexp-to-ast@11.0.3': + resolution: + { integrity: sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA== } + + '@chevrotain/types@11.0.3': + resolution: + { integrity: sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ== } + + '@chevrotain/utils@11.0.3': + resolution: + { integrity: sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ== } + + '@commitlint/cli@19.8.1': + resolution: + { integrity: sha512-LXUdNIkspyxrlV6VDHWBmCZRtkEVRpBKxi2Gtw3J54cGWhLCTouVD/Q6ZSaSvd2YaDObWK8mDjrz3TIKtaQMAA== } + engines: { node: '>=v18' } + hasBin: true + + '@commitlint/config-conventional@19.8.1': + resolution: + { integrity: sha512-/AZHJL6F6B/G959CsMAzrPKKZjeEiAVifRyEwXxcT6qtqbPwGw+iQxmNS+Bu+i09OCtdNRW6pNpBvgPrtMr9EQ== } + engines: { node: '>=v18' } + + '@commitlint/config-validator@19.8.1': + resolution: + { integrity: sha512-0jvJ4u+eqGPBIzzSdqKNX1rvdbSU1lPNYlfQQRIFnBgLy26BtC0cFnr7c/AyuzExMxWsMOte6MkTi9I3SQ3iGQ== } + engines: { node: '>=v18' } + + '@commitlint/ensure@19.8.1': + resolution: + { integrity: sha512-mXDnlJdvDzSObafjYrOSvZBwkD01cqB4gbnnFuVyNpGUM5ijwU/r/6uqUmBXAAOKRfyEjpkGVZxaDsCVnHAgyw== } + engines: { node: '>=v18' } + + '@commitlint/execute-rule@19.8.1': + resolution: + { integrity: sha512-YfJyIqIKWI64Mgvn/sE7FXvVMQER/Cd+s3hZke6cI1xgNT/f6ZAz5heND0QtffH+KbcqAwXDEE1/5niYayYaQA== } + engines: { node: '>=v18' } + + '@commitlint/format@19.8.1': + resolution: + { integrity: sha512-kSJj34Rp10ItP+Eh9oCItiuN/HwGQMXBnIRk69jdOwEW9llW9FlyqcWYbHPSGofmjsqeoxa38UaEA5tsbm2JWw== } + engines: { node: '>=v18' } + + '@commitlint/is-ignored@19.8.1': + resolution: + { integrity: sha512-AceOhEhekBUQ5dzrVhDDsbMaY5LqtN8s1mqSnT2Kz1ERvVZkNihrs3Sfk1Je/rxRNbXYFzKZSHaPsEJJDJV8dg== } + engines: { node: '>=v18' } + + '@commitlint/lint@19.8.1': + resolution: + { integrity: sha512-52PFbsl+1EvMuokZXLRlOsdcLHf10isTPlWwoY1FQIidTsTvjKXVXYb7AvtpWkDzRO2ZsqIgPK7bI98x8LRUEw== } + engines: { node: '>=v18' } + + '@commitlint/load@19.8.1': + resolution: + { integrity: sha512-9V99EKG3u7z+FEoe4ikgq7YGRCSukAcvmKQuTtUyiYPnOd9a2/H9Ak1J9nJA1HChRQp9OA/sIKPugGS+FK/k1A== } + engines: { node: '>=v18' } + + '@commitlint/message@19.8.1': + resolution: + { integrity: sha512-+PMLQvjRXiU+Ae0Wc+p99EoGEutzSXFVwQfa3jRNUZLNW5odZAyseb92OSBTKCu+9gGZiJASt76Cj3dLTtcTdg== } + engines: { node: '>=v18' } + + '@commitlint/parse@19.8.1': + resolution: + { integrity: sha512-mmAHYcMBmAgJDKWdkjIGq50X4yB0pSGpxyOODwYmoexxxiUCy5JJT99t1+PEMK7KtsCtzuWYIAXYAiKR+k+/Jw== } + engines: { node: '>=v18' } + + '@commitlint/read@19.8.1': + resolution: + { integrity: sha512-03Jbjb1MqluaVXKHKRuGhcKWtSgh3Jizqy2lJCRbRrnWpcM06MYm8th59Xcns8EqBYvo0Xqb+2DoZFlga97uXQ== } + engines: { node: '>=v18' } + + '@commitlint/resolve-extends@19.8.1': + resolution: + { integrity: sha512-GM0mAhFk49I+T/5UCYns5ayGStkTt4XFFrjjf0L4S26xoMTSkdCf9ZRO8en1kuopC4isDFuEm7ZOm/WRVeElVg== } + engines: { node: '>=v18' } + + '@commitlint/rules@19.8.1': + resolution: + { integrity: sha512-Hnlhd9DyvGiGwjfjfToMi1dsnw1EXKGJNLTcsuGORHz6SS9swRgkBsou33MQ2n51/boIDrbsg4tIBbRpEWK2kw== } + engines: { node: '>=v18' } + + '@commitlint/to-lines@19.8.1': + resolution: + { integrity: sha512-98Mm5inzbWTKuZQr2aW4SReY6WUukdWXuZhrqf1QdKPZBCCsXuG87c+iP0bwtD6DBnmVVQjgp4whoHRVixyPBg== } + engines: { node: '>=v18' } + + '@commitlint/top-level@19.8.1': + resolution: + { integrity: sha512-Ph8IN1IOHPSDhURCSXBz44+CIu+60duFwRsg6HqaISFHQHbmBtxVw4ZrFNIYUzEP7WwrNPxa2/5qJ//NK1FGcw== } + engines: { node: '>=v18' } + + '@commitlint/types@19.8.1': + resolution: + { integrity: sha512-/yCrWGCoA1SVKOks25EGadP9Pnj0oAIHGpl2wH2M2Y46dPM2ueb8wyCVOD7O3WCTkaJ0IkKvzhl1JY7+uCT2Dw== } + engines: { node: '>=v18' } + + '@develar/schema-utils@2.6.5': + resolution: + { integrity: sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig== } + engines: { node: '>= 8.9.0' } + + '@dnd-kit/accessibility@3.1.1': + resolution: + { integrity: sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw== } + peerDependencies: + react: '>=16.8.0' + + '@dnd-kit/core@6.3.1': + resolution: + { integrity: sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ== } + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@dnd-kit/modifiers@9.0.0': + resolution: + { integrity: sha512-ybiLc66qRGuZoC20wdSSG6pDXFikui/dCNGthxv4Ndy8ylErY0N3KVxY2bgo7AWwIbxDmXDg3ylAFmnrjcbVvw== } + peerDependencies: + '@dnd-kit/core': ^6.3.0 + react: '>=16.8.0' + + '@dnd-kit/sortable@10.0.0': + resolution: + { integrity: sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg== } + peerDependencies: + '@dnd-kit/core': ^6.3.0 + react: '>=16.8.0' + + '@dnd-kit/utilities@3.2.2': + resolution: + { integrity: sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg== } + peerDependencies: + react: '>=16.8.0' + + '@electron/asar@3.4.1': + resolution: + { integrity: sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA== } + engines: { node: '>=10.12.0' } + hasBin: true + + '@electron/get@2.0.3': + resolution: + { integrity: sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ== } + engines: { node: '>=12' } + + '@electron/notarize@2.2.1': + resolution: + { integrity: sha512-aL+bFMIkpR0cmmj5Zgy0LMKEpgy43/hw5zadEArgmAMWWlKc5buwFvFT9G/o/YJkvXAJm5q3iuTuLaiaXW39sg== } + engines: { node: '>= 10.0.0' } + + '@electron/osx-sign@1.0.5': + resolution: + { integrity: sha512-k9ZzUQtamSoweGQDV2jILiRIHUu7lYlJ3c6IEmjv1hC17rclE+eb9U+f6UFlOOETo0JzY1HNlXy4YOlCvl+Lww== } + engines: { node: '>=12.0.0' } + hasBin: true + + '@electron/universal@1.5.1': + resolution: + { integrity: sha512-kbgXxyEauPJiQQUNG2VgUeyfQNFk6hBF11ISN2PNI6agUgPl55pv4eQmaqHzTAzchBvqZ2tQuRVaPStGf0mxGw== } + engines: { node: '>=8.6' } + + '@emnapi/runtime@1.4.3': + resolution: + { integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ== } + + '@emoji-mart/data@1.2.1': + resolution: + { integrity: sha512-no2pQMWiBy6gpBEiqGeU77/bFejDqUTRY7KX+0+iur13op3bqUsXdnwoZs6Xb1zbv0gAj5VvS1PWoUUckSr5Dw== } + + '@emoji-mart/react@1.1.1': + resolution: + { integrity: sha512-NMlFNeWgv1//uPsvLxvGQoIerPuVdXwK/EUek8OOkJ6wVOWPUizRBJU0hDqWZCOROVpfBgCemaC3m6jDOXi03g== } + peerDependencies: + emoji-mart: ^5.2 + react: ^16.8 || ^17 || ^18 + + '@emotion/babel-plugin@11.13.5': + resolution: + { integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ== } + + '@emotion/cache@11.14.0': + resolution: + { integrity: sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA== } + + '@emotion/css@11.13.5': + resolution: + { integrity: sha512-wQdD0Xhkn3Qy2VNcIzbLP9MR8TafI0MJb7BEAXKp+w4+XqErksWR4OXomuDzPsN4InLdGhVe6EYcn2ZIUCpB8w== } + + '@emotion/hash@0.8.0': + resolution: + { integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== } + + '@emotion/hash@0.9.2': + resolution: + { integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g== } + + '@emotion/is-prop-valid@1.3.1': + resolution: + { integrity: sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw== } + + '@emotion/memoize@0.9.0': + resolution: + { integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ== } + + '@emotion/react@11.14.0': + resolution: + { integrity: sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA== } + peerDependencies: + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/serialize@1.3.3': + resolution: + { integrity: sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA== } + + '@emotion/sheet@1.4.0': + resolution: + { integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg== } + + '@emotion/styled@11.14.0': + resolution: + { integrity: sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA== } + peerDependencies: + '@emotion/react': ^11.0.0-rc.0 + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/unitless@0.10.0': + resolution: + { integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg== } + + '@emotion/unitless@0.7.5': + resolution: + { integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== } + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0': + resolution: + { integrity: sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg== } + peerDependencies: + react: '>=16.8.0' + + '@emotion/utils@1.4.2': + resolution: + { integrity: sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA== } + + '@emotion/weak-memoize@0.4.0': + resolution: + { integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg== } + + '@floating-ui/core@0.7.3': + resolution: + { integrity: sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg== } + + '@floating-ui/core@1.7.1': + resolution: + { integrity: sha512-azI0DrjMMfIug/ExbBaeDVJXcY0a7EPvPjb2xAJPa4HeimBX+Z18HK8QQR3jb6356SnDDdxx+hinMLcJEDdOjw== } + + '@floating-ui/dom@0.5.4': + resolution: + { integrity: sha512-419BMceRLq0RrmTSDxn8hf9R3VCJv2K9PUfugh5JyEFmdjzDo+e8U5EdR8nzKq8Yj1htzLm3b6eQEEam3/rrtg== } + + '@floating-ui/dom@1.7.1': + resolution: + { integrity: sha512-cwsmW/zyw5ltYTUeeYJ60CnQuPqmGwuGVhG9w0PRaRKkAyi38BT5CKrpIbb+jtahSwUl04cWzSx9ZOIxeS6RsQ== } + + '@floating-ui/react-dom@0.7.2': + resolution: + { integrity: sha512-1T0sJcpHgX/u4I1OzIEhlcrvkUN8ln39nz7fMoE/2HDHrPiMFoOGR7++GYyfUmIQHkkrTinaeQsO3XWubjSvGg== } + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/react-dom@2.1.3': + resolution: + { integrity: sha512-huMBfiU9UnQ2oBwIhgzyIiSpVgvlDstU8CX0AF+wS+KzmYMs0J2a3GwuFHV1Lz+jlrQGeC1fF+Nv0QoumyV0bA== } + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/react@0.27.12': + resolution: + { integrity: sha512-kKlWNrpIQxF1B/a2MZvE0/uyKby4960yjO91W7nVyNKmmfNi62xU9HCjL1M1eWzx/LFj/VPSwJVbwQk9Pq/68A== } + peerDependencies: + react: '>=17.0.0' + react-dom: '>=17.0.0' + + '@floating-ui/utils@0.2.9': + resolution: + { integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg== } + + '@fontsource/inter@5.2.6': + resolution: + { integrity: sha512-CZs9S1CrjD0jPwsNy9W6j0BhsmRSQrgwlTNkgQXTsAeDRM42LBRLo3eo9gCzfH4GvV7zpyf78Ozfl773826csw== } + + '@fontsource/jetbrains-mono@5.2.6': + resolution: + { integrity: sha512-nz//dBr99hXZmHp10wgNI00qThWImkzRR5PQjvRM+rpmuHO5rYBJCqPPWufidCvmkkryXx/GOP/lgqsM3R3Org== } + + '@giscus/react@3.1.0': + resolution: + { integrity: sha512-0TCO2TvL43+oOdyVVGHDItwxD1UMKP2ZYpT6gXmhFOqfAJtZxTzJ9hkn34iAF/b6YzyJ4Um89QIt9z/ajmAEeg== } + peerDependencies: + react: ^16 || ^17 || ^18 || ^19 + react-dom: ^16 || ^17 || ^18 || ^19 + + '@hapi/hoek@9.3.0': + resolution: + { integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ== } + + '@hapi/topo@5.1.0': + resolution: + { integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== } + + '@huggingface/hub@2.2.0': + resolution: + { integrity: sha512-G+VS1eMp80KovIHBlsiEigS6I6qmI4j+VQ1UZ8CaXT+pw2A7tj6e/crfxFdKNE2uOK5oQkRFiCBJykMwrWQ8OA== } + engines: { node: '>=18' } + hasBin: true + + '@huggingface/tasks@0.19.15': + resolution: + { integrity: sha512-L4wB/iolKtsErke5yniXXNsGrSuaFmyREpcD4hL/wJox2UKtSEV5gE5gNrlvNaRLBOY41yN7/QmBF4y9byTM6Q== } + + '@hyzyla/pdfium@2.1.7': + resolution: + { integrity: sha512-GQ0mxuVY15RHAyxVRC8IUREAeuhZ+Efab8czhvwiw3mveNmBQAtaV3x2O70NaQlu5juzaP0DXK3Jq8bh6Jgjjw== } + + '@iconify/types@2.0.0': + resolution: + { integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg== } + + '@iconify/utils@2.3.0': + resolution: + { integrity: sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA== } + + '@img/sharp-darwin-arm64@0.33.5': + resolution: + { integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ== } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.33.5': + resolution: + { integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q== } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.0.4': + resolution: + { integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg== } + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.0.4': + resolution: + { integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ== } + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.0.4': + resolution: + { integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA== } + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.0.5': + resolution: + { integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g== } + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.0.4': + resolution: + { integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA== } + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.0.4': + resolution: + { integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw== } + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.0.4': + resolution: + { integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA== } + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.0.4': + resolution: + { integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw== } + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.33.5': + resolution: + { integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA== } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.33.5': + resolution: + { integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ== } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [arm] + os: [linux] + + '@img/sharp-linux-s390x@0.33.5': + resolution: + { integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q== } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.33.5': + resolution: + { integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA== } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.33.5': + resolution: + { integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g== } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.33.5': + resolution: + { integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw== } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.33.5': + resolution: + { integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg== } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [wasm32] + + '@img/sharp-win32-ia32@0.33.5': + resolution: + { integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ== } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.33.5': + resolution: + { integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg== } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [x64] + os: [win32] + + '@isaacs/cliui@8.0.2': + resolution: + { integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== } + engines: { node: '>=12' } + + '@jridgewell/gen-mapping@0.3.8': + resolution: + { integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA== } + engines: { node: '>=6.0.0' } + + '@jridgewell/resolve-uri@3.1.2': + resolution: + { integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== } + engines: { node: '>=6.0.0' } + + '@jridgewell/set-array@1.2.1': + resolution: + { integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== } + engines: { node: '>=6.0.0' } + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: + { integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== } + + '@jridgewell/trace-mapping@0.3.25': + resolution: + { integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== } + + '@langchain/core@0.3.58': + resolution: + { integrity: sha512-HLkOtVofgBHefaUae/+2fLNkpMLzEjHSavTmUF0YC7bDa5NPIZGlP80CGrSFXAeJ+WCPd8rIK8K/p6AW94inUQ== } + engines: { node: '>=18' } + + '@langchain/openai@0.5.13': + resolution: + { integrity: sha512-t5UsO7XYE+DBQlXQ21QK74Y+LH4It20wnENrmueNvxIWTn0nHDIGVmO6wo4rJxbmOOPRQ4l/oAxGRnYU8B8v6w== } + engines: { node: '>=18' } + peerDependencies: + '@langchain/core': '>=0.3.58 <0.4.0' + + '@langchain/textsplitters@0.1.0': + resolution: + { integrity: sha512-djI4uw9rlkAb5iMhtLED+xJebDdAG935AdP4eRTB02R7OB/act55Bj9wsskhZsvuyQRpO4O1wQOp85s6T6GWmw== } + engines: { node: '>=18' } + peerDependencies: + '@langchain/core': '>=0.2.21 <0.4.0' + + '@lit-labs/ssr-dom-shim@1.3.0': + resolution: + { integrity: sha512-nQIWonJ6eFAvUUrSlwyHDm/aE8PBDu5kRpL0vHMg6K8fK3Diq1xdPjTnsJSwxABhaZ+5eBi1btQB5ShUTKo4nQ== } + + '@lit/reactive-element@2.1.0': + resolution: + { integrity: sha512-L2qyoZSQClcBmq0qajBVbhYEcG6iK0XfLn66ifLe/RfC0/ihpc+pl0Wdn8bJ8o+hj38cG0fGXRgSS20MuXn7qA== } + + '@lobehub/emojilib@1.0.0': + resolution: + { integrity: sha512-s9KnjaPjsEefaNv150G3aifvB+J3P4eEKG+epY9zDPS2BeB6+V2jELWqAZll+nkogMaVovjEE813z3V751QwGw== } + + '@lobehub/fluent-emoji@2.0.0': + resolution: + { integrity: sha512-bKjU3sf0+7NppvcdqD/raWvKGJIw8HDJVporNQ7oR8pIPoLeb9IUu/vqIYClOlwfu9qntji7FFySfbdNqXSiJw== } + peerDependencies: + antd: ^5.23.0 + react: ^19.0.0 + react-dom: ^19.0.0 + + '@lobehub/icons@1.98.0': + resolution: + { integrity: sha512-2+t3wX1PdHQnWgHN5ZNrK+X2GBLQkrDGsrvJgks50JyvKYQ8j10yY+qg+epB9uAt2zXzGiAy4A3tNMe7bLoOPQ== } + deprecated: deprecate + peerDependencies: + antd: ^5.23.0 + react: ^19.0.0 + react-dom: ^19.0.0 + + '@lobehub/icons@2.4.0': + resolution: + { integrity: sha512-/qzGIu1lIQftP3vZCrlbF2D7rQEPlSISGdmDFJ6yQquzE8W8RNX5sy+4I1jhqC2uZ+b5YDFYg8evyWrLxUJuNA== } + peerDependencies: + antd: ^5.23.0 + react: ^19.0.0 + react-dom: ^19.0.0 + + '@lobehub/ui@2.4.0': + resolution: + { integrity: sha512-x6VYO4LOKPouIpJ1vvcNj1JrXJ+ov11i+dTceX44b4kQ6EcxafJQSatrghROgnqHvu9wFH+AGrPXyYtSMPJ4tw== } + peerDependencies: + antd: ^5.25.0 + framer-motion: ^12.0.0 + react: ^19.0.0 + react-dom: ^19.0.0 + + '@malept/cross-spawn-promise@1.1.1': + resolution: + { integrity: sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ== } + engines: { node: '>= 10' } + + '@malept/flatpak-bundler@0.4.0': + resolution: + { integrity: sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q== } + engines: { node: '>= 10.0.0' } + + '@mapbox/node-pre-gyp@1.0.11': + resolution: + { integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ== } + hasBin: true + + '@mdx-js/mdx@3.1.0': + resolution: + { integrity: sha512-/QxEhPAvGwbQmy1Px8F899L5Uc2KZ6JtXwlCgJmjSTBedwOZkByYcBG4GceIGPXRDsmfxhHazuS+hlOShRLeDw== } + + '@mdx-js/react@3.1.0': + resolution: + { integrity: sha512-QjHtSaoameoalGnKDT3FoIl4+9RwyTmo9ZJGBdLOks/YOiWHoRDI3PUwEzOE7kEmGcV3AFcp9K6dYu9rEuKLAQ== } + peerDependencies: + '@types/react': '>=16' + react: '>=16' + + '@mermaid-js/parser@0.4.0': + resolution: + { integrity: sha512-wla8XOWvQAwuqy+gxiZqY+c7FokraOTHRWMsbB4AgRx9Sy7zKslNyejy7E+a77qHfey5GXw/ik3IXv/NHMJgaA== } + + '@mixmark-io/domino@2.2.0': + resolution: + { integrity: sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw== } + + '@mui/base@5.0.0-beta.40-0': + resolution: + { integrity: sha512-hG3atoDUxlvEy+0mqdMpWd04wca8HKr2IHjW/fAjlkCHQolSLazhZM46vnHjOf15M4ESu25mV/3PgjczyjVM4w== } + engines: { node: '>=12.0.0' } + deprecated: This package has been replaced by @base-ui-components/react + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/core-downloads-tracker@5.17.1': + resolution: + { integrity: sha512-OcZj+cs6EfUD39IoPBOgN61zf1XFVY+imsGoBDwXeSq2UHJZE3N59zzBOVjclck91Ne3e9gudONOeILvHCIhUA== } + + '@mui/icons-material@5.16.14': + resolution: + { integrity: sha512-heL4S+EawrP61xMXBm59QH6HODsu0gxtZi5JtnXF2r+rghzyU/3Uftlt1ij8rmJh+cFdKTQug1L9KkZB5JgpMQ== } + engines: { node: '>=12.0.0' } + peerDependencies: + '@mui/material': ^5.0.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/lab@5.0.0-alpha.175': + resolution: + { integrity: sha512-AvM0Nvnnj7vHc9+pkkQkoE1i+dEbr6gsMdnSfy7X4w3Ljgcj1yrjZhIt3jGTCLzyKVLa6uve5eLluOcGkvMqUA== } + engines: { node: '>=12.0.0' } + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@mui/material': '>=5.15.0' + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@types/react': + optional: true + + '@mui/material@5.16.14': + resolution: + { integrity: sha512-eSXQVCMKU2xc7EcTxe/X/rC9QsV2jUe8eLM3MUCPYbo6V52eCE436akRIvELq/AqZpxx2bwkq7HC0cRhLB+yaw== } + engines: { node: '>=12.0.0' } + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@types/react': + optional: true + + '@mui/private-theming@5.17.1': + resolution: + { integrity: sha512-XMxU0NTYcKqdsG8LRmSoxERPXwMbp16sIXPcLVgLGII/bVNagX0xaheWAwFv8+zDK7tI3ajllkuD3GZZE++ICQ== } + engines: { node: '>=12.0.0' } + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/styled-engine@5.16.14': + resolution: + { integrity: sha512-UAiMPZABZ7p8mUW4akDV6O7N3+4DatStpXMZwPlt+H/dA0lt67qawN021MNND+4QTpjaiMYxbhKZeQcyWCbuKw== } + engines: { node: '>=12.0.0' } + peerDependencies: + '@emotion/react': ^11.4.1 + '@emotion/styled': ^11.3.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + + '@mui/system@5.17.1': + resolution: + { integrity: sha512-aJrmGfQpyF0U4D4xYwA6ueVtQcEMebET43CUmKMP7e7iFh3sMIF3sBR0l8Urb4pqx1CBjHAaWgB0ojpND4Q3Jg== } + engines: { node: '>=12.0.0' } + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@types/react': + optional: true + + '@mui/types@7.2.24': + resolution: + { integrity: sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw== } + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/types@7.4.3': + resolution: + { integrity: sha512-2UCEiK29vtiZTeLdS2d4GndBKacVyxGvReznGXGr+CzW/YhjIX+OHUdCIczZjzcRAgKBGmE9zCIgoV9FleuyRQ== } + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/utils@5.17.1': + resolution: + { integrity: sha512-jEZ8FTqInt2WzxDV8bhImWBqeQRD99c/id/fq83H0ER9tFl+sfZlaAoCdznGvbSQQ9ividMxqSV2c7cC1vBcQg== } + engines: { node: '>=12.0.0' } + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@next/env@14.2.29': + resolution: + { integrity: sha512-UzgLR2eBfhKIQt0aJ7PWH7XRPYw7SXz0Fpzdl5THjUnvxy4kfBk9OU4RNPNiETewEEtaBcExNFNn1QWH8wQTjg== } + + '@next/swc-darwin-arm64@14.2.29': + resolution: + { integrity: sha512-wWtrAaxCVMejxPHFb1SK/PVV1WDIrXGs9ki0C/kUM8ubKHQm+3hU9MouUywCw8Wbhj3pewfHT2wjunLEr/TaLA== } + engines: { node: '>= 10' } + cpu: [arm64] + os: [darwin] + + '@next/swc-darwin-x64@14.2.29': + resolution: + { integrity: sha512-7Z/jk+6EVBj4pNLw/JQrvZVrAh9Bv8q81zCFSfvTMZ51WySyEHWVpwCEaJY910LyBftv2F37kuDPQm0w9CEXyg== } + engines: { node: '>= 10' } + cpu: [x64] + os: [darwin] + + '@next/swc-linux-arm64-gnu@14.2.29': + resolution: + { integrity: sha512-o6hrz5xRBwi+G7JFTHc+RUsXo2lVXEfwh4/qsuWBMQq6aut+0w98WEnoNwAwt7hkEqegzvazf81dNiwo7KjITw== } + engines: { node: '>= 10' } + cpu: [arm64] + os: [linux] + + '@next/swc-linux-arm64-musl@14.2.29': + resolution: + { integrity: sha512-9i+JEHBOVgqxQ92HHRFlSW1EQXqa/89IVjtHgOqsShCcB/ZBjTtkWGi+SGCJaYyWkr/lzu51NTMCfKuBf7ULNw== } + engines: { node: '>= 10' } + cpu: [arm64] + os: [linux] + + '@next/swc-linux-x64-gnu@14.2.29': + resolution: + { integrity: sha512-B7JtMbkUwHijrGBOhgSQu2ncbCYq9E7PZ7MX58kxheiEOwdkM+jGx0cBb+rN5AeqF96JypEppK6i/bEL9T13lA== } + engines: { node: '>= 10' } + cpu: [x64] + os: [linux] + + '@next/swc-linux-x64-musl@14.2.29': + resolution: + { integrity: sha512-yCcZo1OrO3aQ38B5zctqKU1Z3klOohIxug6qdiKO3Q3qNye/1n6XIs01YJ+Uf+TdpZQ0fNrOQI2HrTLF3Zprnw== } + engines: { node: '>= 10' } + cpu: [x64] + os: [linux] + + '@next/swc-win32-arm64-msvc@14.2.29': + resolution: + { integrity: sha512-WnrfeOEtTVidI9Z6jDLy+gxrpDcEJtZva54LYC0bSKQqmyuHzl0ego+v0F/v2aXq0am67BRqo/ybmmt45Tzo4A== } + engines: { node: '>= 10' } + cpu: [arm64] + os: [win32] + + '@next/swc-win32-ia32-msvc@14.2.29': + resolution: + { integrity: sha512-vkcriFROT4wsTdSeIzbxaZjTNTFKjSYmLd8q/GVH3Dn8JmYjUKOuKXHK8n+lovW/kdcpIvydO5GtN+It2CvKWA== } + engines: { node: '>= 10' } + cpu: [ia32] + os: [win32] + + '@next/swc-win32-x64-msvc@14.2.29': + resolution: + { integrity: sha512-iPPwUEKnVs7pwR0EBLJlwxLD7TTHWS/AoVZx1l9ZQzfQciqaFEr5AlYzA2uB6Fyby1IF18t4PL0nTpB+k4Tzlw== } + engines: { node: '>= 10' } + cpu: [x64] + os: [win32] + + '@noble/hashes@1.8.0': + resolution: + { integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A== } + engines: { node: ^14.21.3 || >=16 } + + '@nodelib/fs.scandir@2.1.5': + resolution: + { integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== } + engines: { node: '>= 8' } + + '@nodelib/fs.stat@2.0.5': + resolution: + { integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== } + engines: { node: '>= 8' } + + '@nodelib/fs.walk@1.2.8': + resolution: + { integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== } + engines: { node: '>= 8' } + + '@opendocsg/pdf2md@0.2.1': + resolution: + { integrity: sha512-k/yvfrTb+GPTIIm/bMm5IsenTqAFl+IqvkBgFwFlmflS5TT7FOfyRLp8MypVWLAG4G9AnT7AZFbdQYgN/CR5BA== } + hasBin: true + + '@openrouter/ai-sdk-provider@0.4.6': + resolution: + { integrity: sha512-oUa8xtssyUhiKEU/aW662lsZ0HUvIUTRk8vVIF3Ha3KI/DnqX54zmVIuzYnaDpermqhy18CHqblAY4dDt1JW3g== } + engines: { node: '>=18' } + peerDependencies: + zod: ^3.0.0 + + '@opentelemetry/api@1.9.0': + resolution: + { integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg== } + engines: { node: '>=8.0.0' } + + '@paralleldrive/cuid2@2.2.2': + resolution: + { integrity: sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA== } + + '@pkgjs/parseargs@0.11.0': + resolution: + { integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== } + engines: { node: '>=14' } + + '@popperjs/core@2.11.8': + resolution: + { integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== } + + '@prisma/client@6.9.0': + resolution: + { integrity: sha512-Gg7j1hwy3SgF1KHrh0PZsYvAaykeR0PaxusnLXydehS96voYCGt1U5zVR31NIouYc63hWzidcrir1a7AIyCsNQ== } + engines: { node: '>=18.18' } + peerDependencies: + prisma: '*' + typescript: '>=5.1.0' + peerDependenciesMeta: + prisma: + optional: true + typescript: + optional: true + + '@prisma/config@6.9.0': + resolution: + { integrity: sha512-Wcfk8/lN3WRJd5w4jmNQkUwhUw0eksaU/+BlAJwPQKW10k0h0LC9PD/6TQFmqKVbHQL0vG2z266r0S1MPzzhbA== } + + '@prisma/debug@6.9.0': + resolution: + { integrity: sha512-bFeur/qi/Q+Mqk4JdQ3R38upSYPebv5aOyD1RKywVD+rAMLtRkmTFn28ZuTtVOnZHEdtxnNOCH+bPIeSGz1+Fg== } + + '@prisma/engines-version@6.9.0-10.81e4af48011447c3cc503a190e86995b66d2a28e': + resolution: + { integrity: sha512-Qp9gMoBHgqhKlrvumZWujmuD7q4DV/gooEyPCLtbkc13EZdSz2RsGUJ5mHb3RJgAbk+dm6XenqG7obJEhXcJ6Q== } + + '@prisma/engines@6.9.0': + resolution: + { integrity: sha512-im0X0bwDLA0244CDf8fuvnLuCQcBBdAGgr+ByvGfQY9wWl6EA+kRGwVk8ZIpG65rnlOwtaWIr/ZcEU5pNVvq9g== } + + '@prisma/fetch-engine@6.9.0': + resolution: + { integrity: sha512-PMKhJdl4fOdeE3J3NkcWZ+tf3W6rx3ht/rLU8w4SXFRcLhd5+3VcqY4Kslpdm8osca4ej3gTfB3+cSk5pGxgFg== } + + '@prisma/get-platform@6.9.0': + resolution: + { integrity: sha512-/B4n+5V1LI/1JQcHp+sUpyRT1bBgZVPHbsC4lt4/19Xp4jvNIVcq5KYNtQDk5e/ukTSjo9PZVAxxy9ieFtlpTQ== } + + '@radix-ui/primitive@1.0.0': + resolution: + { integrity: sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA== } + + '@radix-ui/react-arrow@1.0.2': + resolution: + { integrity: sha512-fqYwhhI9IarZ0ll2cUSfKuXHlJK0qE4AfnRrPBbRwEH/4mGQn04/QFGomLi8TXWIdv9WJk//KgGm+aDxVIr1wA== } + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-compose-refs@1.0.0': + resolution: + { integrity: sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA== } + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-compose-refs@1.1.2': + resolution: + { integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg== } + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context@1.0.0': + resolution: + { integrity: sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg== } + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-dismissable-layer@1.0.3': + resolution: + { integrity: sha512-nXZOvFjOuHS1ovumntGV7NNoLaEp9JEvTht3MBjP44NSW5hUKj/8OnfN3+8WmB+CEhN44XaGhpHoSsUIEl5P7Q== } + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-id@1.0.0': + resolution: + { integrity: sha512-Q6iAB/U7Tq3NTolBBQbHTgclPmGWE3OlktGGqrClPozSw4vkQ1DfQAOtzgRPecKsMdJINE05iaoDUG8tRzCBjw== } + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-popper@1.1.1': + resolution: + { integrity: sha512-keYDcdMPNMjSC8zTsZ8wezUMiWM9Yj14wtF3s0PTIs9srnEPC9Kt2Gny1T3T81mmSeyDjZxsD9N5WCwNNb712w== } + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-portal@1.0.2': + resolution: + { integrity: sha512-swu32idoCW7KA2VEiUZGBSu9nB6qwGdV6k6HYhUoOo3M1FFpD+VgLzUqtt3mwL1ssz7r2x8MggpLSQach2Xy/Q== } + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-presence@1.0.0': + resolution: + { integrity: sha512-A+6XEvN01NfVWiKu38ybawfHsBjWum42MRPnEuqPsBZ4eV7e/7K321B5VgYMPv3Xx5An6o1/l9ZuDBgmcmWK3w== } + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-primitive@1.0.2': + resolution: + { integrity: sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw== } + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-slot@1.0.1': + resolution: + { integrity: sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw== } + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-slot@1.2.3': + resolution: + { integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A== } + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-tooltip@1.0.5': + resolution: + { integrity: sha512-cDKVcfzyO6PpckZekODJZDe5ZxZ2fCZlzKzTmPhe4mX9qTHRfLcKgqb0OKf22xLwDequ2tVleim+ZYx3rabD5w== } + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-use-callback-ref@1.0.0': + resolution: + { integrity: sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg== } + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-use-controllable-state@1.0.0': + resolution: + { integrity: sha512-FohDoZvk3mEXh9AWAVyRTYR4Sq7/gavuofglmiXB2g1aKyboUD4YtgWxKj8O5n+Uak52gXQ4wKz5IFST4vtJHg== } + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-use-escape-keydown@1.0.2': + resolution: + { integrity: sha512-DXGim3x74WgUv+iMNCF+cAo8xUHHeqvjx8zs7trKf+FkQKPQXLk2sX7Gx1ysH7Q76xCpZuxIJE7HLPxRE+Q+GA== } + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-use-layout-effect@1.0.0': + resolution: + { integrity: sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ== } + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-use-rect@1.0.0': + resolution: + { integrity: sha512-TB7pID8NRMEHxb/qQJpvSt3hQU4sqNPM1VCTjTRjEOa7cEop/QMuq8S6fb/5Tsz64kqSvB9WnwsDHtjnrM9qew== } + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-use-size@1.0.0': + resolution: + { integrity: sha512-imZ3aYcoYCKhhgNpkNDh/aTiU05qw9hX+HHI1QDBTyIlcFjgeFlKKySNGMwTp7nYFLQg/j0VA2FmCY4WPDDHMg== } + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-visually-hidden@1.0.2': + resolution: + { integrity: sha512-qirnJxtYn73HEk1rXL12/mXnu2rwsNHDID10th2JGtdK25T9wX+mxRmGt7iPSahw512GbZOc0syZX1nLQGoEOg== } + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/rect@1.0.0': + resolution: + { integrity: sha512-d0O68AYy/9oeEy1DdC07bz1/ZXX+DqCskRd3i4JzLSTXwefzaepQrKjXC7aNM8lTHjFLDO0pDgaEiQ7jEk+HVg== } + + '@rc-component/async-validator@5.0.4': + resolution: + { integrity: sha512-qgGdcVIF604M9EqjNF0hbUTz42bz/RDtxWdWuU5EQe3hi7M8ob54B6B35rOsvX5eSvIHIzT9iH1R3n+hk3CGfg== } + engines: { node: '>=14.x' } + + '@rc-component/color-picker@2.0.1': + resolution: + { integrity: sha512-WcZYwAThV/b2GISQ8F+7650r5ZZJ043E57aVBFkQ+kSY4C6wdofXgB0hBx+GPGpIU0Z81eETNoDUJMr7oy/P8Q== } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/context@1.4.0': + resolution: + { integrity: sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w== } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/mini-decimal@1.1.0': + resolution: + { integrity: sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ== } + engines: { node: '>=8.x' } + + '@rc-component/mutate-observer@1.1.0': + resolution: + { integrity: sha512-QjrOsDXQusNwGZPf4/qRQasg7UFEj06XiCJ8iuiq/Io7CrHrgVi6Uuetw60WAMG1799v+aM8kyc+1L/GBbHSlw== } + engines: { node: '>=8.x' } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/portal@1.1.2': + resolution: + { integrity: sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg== } + engines: { node: '>=8.x' } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/qrcode@1.0.0': + resolution: + { integrity: sha512-L+rZ4HXP2sJ1gHMGHjsg9jlYBX/SLN2D6OxP9Zn3qgtpMWtO2vUfxVFwiogHpAIqs54FnALxraUy/BCO1yRIgg== } + engines: { node: '>=8.x' } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/tour@1.15.1': + resolution: + { integrity: sha512-Tr2t7J1DKZUpfJuDZWHxyxWpfmj8EZrqSgyMZ+BCdvKZ6r1UDsfU46M/iWAAFBy961Ssfom2kv5f3UcjIL2CmQ== } + engines: { node: '>=8.x' } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/trigger@2.2.6': + resolution: + { integrity: sha512-/9zuTnWwhQ3S3WT1T8BubuFTT46kvnXgaERR9f4BTKyn61/wpf/BvbImzYBubzJibU707FxwbKszLlHjcLiv1Q== } + engines: { node: '>=8.x' } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@reduxjs/toolkit@2.11.2': + resolution: + { integrity: sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ== } + peerDependencies: + react: ^16.9.0 || ^17.0.0 || ^18 || ^19 + react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 + peerDependenciesMeta: + react: + optional: true + react-redux: + optional: true + + '@shikijs/core@3.6.0': + resolution: + { integrity: sha512-9By7Xb3olEX0o6UeJyPLI1PE1scC4d3wcVepvtv2xbuN9/IThYN4Wcwh24rcFeASzPam11MCq8yQpwwzCgSBRw== } + + '@shikijs/engine-javascript@3.6.0': + resolution: + { integrity: sha512-7YnLhZG/TU05IHMG14QaLvTW/9WiK8SEYafceccHUSXs2Qr5vJibUwsDfXDLmRi0zHdzsxrGKpSX6hnqe0k8nA== } + + '@shikijs/engine-oniguruma@3.6.0': + resolution: + { integrity: sha512-nmOhIZ9yT3Grd+2plmW/d8+vZ2pcQmo/UnVwXMUXAKTXdi+LK0S08Ancrz5tQQPkxvjBalpMW2aKvwXfelauvA== } + + '@shikijs/langs@3.6.0': + resolution: + { integrity: sha512-IdZkQJaLBu1LCYCwkr30hNuSDfllOT8RWYVZK1tD2J03DkiagYKRxj/pDSl8Didml3xxuyzUjgtioInwEQM/TA== } + + '@shikijs/themes@3.6.0': + resolution: + { integrity: sha512-Fq2j4nWr1DF4drvmhqKq8x5vVQ27VncF8XZMBuHuQMZvUSS3NBgpqfwz/FoGe36+W6PvniZ1yDlg2d4kmYDU6w== } + + '@shikijs/transformers@3.6.0': + resolution: + { integrity: sha512-PYkU54lYV0RCaUG8n2FNTF+YWiU3uPhcjLGq2x/C8lIrUX9GVnRb3bK+R5xtdFHbuctntATKm7ondp/H/dux9Q== } + + '@shikijs/types@3.6.0': + resolution: + { integrity: sha512-cLWFiToxYu0aAzJqhXTQsFiJRTFDAGl93IrMSBNaGSzs7ixkLfdG6pH11HipuWFGW5vyx4X47W8HDQ7eSrmBUg== } + + '@shikijs/vscode-textmate@10.0.2': + resolution: + { integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg== } + + '@sideway/address@4.1.5': + resolution: + { integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q== } + + '@sideway/formula@3.0.1': + resolution: + { integrity: sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== } + + '@sideway/pinpoint@2.0.0': + resolution: + { integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== } + + '@sindresorhus/is@4.6.0': + resolution: + { integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== } + engines: { node: '>=10' } + + '@splinetool/runtime@0.9.526': + resolution: + { integrity: sha512-qznHbXA5aKwDbCgESAothCNm1IeEZcmNWG145p5aXj4w5uoqR1TZ9qkTHTKLTsUbHeitCwdhzmRqan1kxboLgQ== } + + '@standard-schema/spec@1.0.0': + resolution: + { integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA== } + + '@standard-schema/utils@0.3.0': + resolution: + { integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g== } + + '@stitches/react@1.2.8': + resolution: + { integrity: sha512-9g9dWI4gsSVe8bNLlb+lMkBYsnIKCZTmvqvDG+Avnn69XfmHZKiaMrx7cgTaddq7aTPPmXiTsbFcUy0xgI4+wA== } + peerDependencies: + react: '>= 16.3.0' + + '@swc/counter@0.1.3': + resolution: + { integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ== } + + '@swc/helpers@0.5.5': + resolution: + { integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A== } + + '@szmarczak/http-timer@4.0.6': + resolution: + { integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== } + engines: { node: '>=10' } + + '@tootallnate/once@2.0.0': + resolution: + { integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== } + engines: { node: '>= 10' } + + '@types/cacheable-request@6.0.3': + resolution: + { integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw== } + + '@types/conventional-commits-parser@5.0.1': + resolution: + { integrity: sha512-7uz5EHdzz2TqoMfV7ee61Egf5y6NkcO4FB/1iCCQnbeiI1F3xzv3vK5dBCXUCLQgGYS+mUeigK1iKQzvED+QnQ== } + + '@types/d3-array@3.2.1': + resolution: + { integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg== } + + '@types/d3-axis@3.0.6': + resolution: + { integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw== } + + '@types/d3-brush@3.0.6': + resolution: + { integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A== } + + '@types/d3-chord@3.0.6': + resolution: + { integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg== } + + '@types/d3-color@3.1.3': + resolution: + { integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A== } + + '@types/d3-contour@3.0.6': + resolution: + { integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg== } + + '@types/d3-delaunay@6.0.4': + resolution: + { integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw== } + + '@types/d3-dispatch@3.0.6': + resolution: + { integrity: sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ== } + + '@types/d3-drag@3.0.7': + resolution: + { integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ== } + + '@types/d3-dsv@3.0.7': + resolution: + { integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g== } + + '@types/d3-ease@3.0.2': + resolution: + { integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA== } + + '@types/d3-fetch@3.0.7': + resolution: + { integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA== } + + '@types/d3-force@3.0.10': + resolution: + { integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw== } + + '@types/d3-format@3.0.4': + resolution: + { integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g== } + + '@types/d3-geo@3.1.0': + resolution: + { integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ== } + + '@types/d3-hierarchy@3.1.7': + resolution: + { integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg== } + + '@types/d3-interpolate@3.0.4': + resolution: + { integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA== } + + '@types/d3-path@3.1.1': + resolution: + { integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg== } + + '@types/d3-polygon@3.0.2': + resolution: + { integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA== } + + '@types/d3-quadtree@3.0.6': + resolution: + { integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg== } + + '@types/d3-random@3.0.3': + resolution: + { integrity: sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ== } + + '@types/d3-scale-chromatic@3.1.0': + resolution: + { integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ== } + + '@types/d3-scale@4.0.9': + resolution: + { integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw== } + + '@types/d3-selection@3.0.11': + resolution: + { integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w== } + + '@types/d3-shape@3.1.7': + resolution: + { integrity: sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg== } + + '@types/d3-time-format@4.0.3': + resolution: + { integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg== } + + '@types/d3-time@3.0.4': + resolution: + { integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g== } + + '@types/d3-timer@3.0.2': + resolution: + { integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw== } + + '@types/d3-transition@3.0.9': + resolution: + { integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg== } + + '@types/d3-zoom@3.0.8': + resolution: + { integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw== } + + '@types/d3@7.4.3': + resolution: + { integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww== } + + '@types/debug@4.1.12': + resolution: + { integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ== } + + '@types/diff-match-patch@1.0.36': + resolution: + { integrity: sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg== } + + '@types/estree-jsx@1.0.5': + resolution: + { integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg== } + + '@types/estree@1.0.8': + resolution: + { integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== } + + '@types/fs-extra@9.0.13': + resolution: + { integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA== } + + '@types/geojson@7946.0.16': + resolution: + { integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg== } + + '@types/hast@3.0.4': + resolution: + { integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ== } + + '@types/http-cache-semantics@4.0.4': + resolution: + { integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA== } + + '@types/katex@0.16.7': + resolution: + { integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ== } + + '@types/keyv@3.1.4': + resolution: + { integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg== } + + '@types/mdast@4.0.4': + resolution: + { integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA== } + + '@types/mdx@2.0.13': + resolution: + { integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw== } + + '@types/ms@2.1.0': + resolution: + { integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA== } + + '@types/node-fetch@2.6.12': + resolution: + { integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA== } + + '@types/node@18.19.111': + resolution: + { integrity: sha512-90sGdgA+QLJr1F9X79tQuEut0gEYIfkX9pydI4XGRgvFo9g2JWswefI+WUSUHPYVBHYSEfTEqBxA5hQvAZB3Mw== } + + '@types/node@22.15.31': + resolution: + { integrity: sha512-jnVe5ULKl6tijxUhvQeNbQG/84fHfg+yMak02cT8QVhBx/F05rAVxCGBYYTh2EKz22D6JF5ktXuNwdx7b9iEGw== } + + '@types/node@24.0.1': + resolution: + { integrity: sha512-MX4Zioh39chHlDJbKmEgydJDS3tspMP/lnQC67G3SWsTnb9NeYVWOjkxpOSy4oMfPs4StcWHwBrvUb4ybfnuaw== } + + '@types/parse-json@4.0.2': + resolution: + { integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== } + + '@types/plist@3.0.5': + resolution: + { integrity: sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA== } + + '@types/prop-types@15.7.15': + resolution: + { integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw== } + + '@types/react-transition-group@4.4.12': + resolution: + { integrity: sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w== } + peerDependencies: + '@types/react': '*' + + '@types/react@19.1.8': + resolution: + { integrity: sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g== } + + '@types/responselike@1.0.3': + resolution: + { integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw== } + + '@types/retry@0.12.0': + resolution: + { integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== } + + '@types/trusted-types@2.0.7': + resolution: + { integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== } + + '@types/unist@2.0.11': + resolution: + { integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA== } + + '@types/unist@3.0.3': + resolution: + { integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q== } + + '@types/use-sync-external-store@0.0.6': + resolution: + { integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg== } + + '@types/uuid@10.0.0': + resolution: + { integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ== } + + '@types/verror@1.10.11': + resolution: + { integrity: sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg== } + + '@types/yauzl@2.10.3': + resolution: + { integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q== } + + '@ungap/structured-clone@1.3.0': + resolution: + { integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== } + + '@use-gesture/core@10.3.1': + resolution: + { integrity: sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw== } + + '@use-gesture/react@10.3.1': + resolution: + { integrity: sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g== } + peerDependencies: + react: '>= 16.8.0' + + '@xmldom/xmldom@0.8.10': + resolution: + { integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw== } + engines: { node: '>=10.0.0' } + + JSONStream@1.3.5: + resolution: + { integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== } + hasBin: true + + abbrev@1.1.1: + resolution: + { integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== } + + abort-controller@3.0.0: + resolution: + { integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== } + engines: { node: '>=6.5' } + + acorn-jsx@5.3.2: + resolution: + { integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== } + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.15.0: + resolution: + { integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== } + engines: { node: '>=0.4.0' } + hasBin: true + + adler-32@1.3.1: + resolution: + { integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A== } + engines: { node: '>=0.8' } + + adm-zip@0.5.16: + resolution: + { integrity: sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ== } + engines: { node: '>=12.0' } + + agent-base@6.0.2: + resolution: + { integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== } + engines: { node: '>= 6.0.0' } + + agentkeepalive@4.6.0: + resolution: + { integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ== } + engines: { node: '>= 8.0.0' } + + ahooks@3.8.5: + resolution: + { integrity: sha512-Y+MLoJpBXVdjsnnBjE5rOSPkQ4DK+8i5aPDzLJdIOsCpo/fiAeXcBY1Y7oWgtOK0TpOz0gFa/XcyO1UGdoqLcw== } + engines: { node: '>=8.0.0' } + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + ai@4.3.16: + resolution: + { integrity: sha512-KUDwlThJ5tr2Vw0A1ZkbDKNME3wzWhuVfAOwIvFUzl1TPVDFAXDFTXio3p+jaKneB+dKNCvFFlolYmmgHttG1g== } + engines: { node: '>=18' } + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + zod: ^3.23.8 + peerDependenciesMeta: + react: + optional: true + + ajv-keywords@3.5.2: + resolution: + { integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== } + peerDependencies: + ajv: ^6.9.1 + + ajv@6.12.6: + resolution: + { integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== } + + ajv@8.17.1: + resolution: + { integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== } + + ansi-escapes@7.0.0: + resolution: + { integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw== } + engines: { node: '>=18' } + + ansi-regex@2.1.1: + resolution: + { integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== } + engines: { node: '>=0.10.0' } + + ansi-regex@5.0.1: + resolution: + { integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== } + engines: { node: '>=8' } + + ansi-regex@6.1.0: + resolution: + { integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== } + engines: { node: '>=12' } + + ansi-styles@2.2.1: + resolution: + { integrity: sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA== } + engines: { node: '>=0.10.0' } + + ansi-styles@4.3.0: + resolution: + { integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== } + engines: { node: '>=8' } + + ansi-styles@5.2.0: + resolution: + { integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== } + engines: { node: '>=10' } + + ansi-styles@6.2.1: + resolution: + { integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== } + engines: { node: '>=12' } + + antd-style@3.7.1: + resolution: + { integrity: sha512-CQOfddVp4aOvBfCepa+Kj2e7ap+2XBINg1Kn2osdE3oQvrD7KJu/K0sfnLcFLkgCJygbxmuazYdWLKb+drPDYA== } + peerDependencies: + antd: '>=5.8.1' + react: '>=18' + + antd@5.26.0: + resolution: + { integrity: sha512-iMPYKFTo2HvIRGutUOuN5AG+Uf+B2QaqcGQbdPp/100fqV3FAil6vFZLVuV3C4XEUOlDNkkUlJKhLR9V5rzIEg== } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + app-builder-bin@4.0.0: + resolution: + { integrity: sha512-xwdG0FJPQMe0M0UA4Tz0zEB8rBJTRA5a476ZawAqiBkMv16GRK5xpXThOjMaEOFnZ6zabejjG4J3da0SXG63KA== } + + app-builder-lib@24.13.3: + resolution: + { integrity: sha512-FAzX6IBit2POXYGnTCT8YHFO/lr5AapAII6zzhQO3Rw4cEDOgK+t1xhLc5tNcKlicTHlo9zxIwnYCX9X2DLkig== } + engines: { node: '>=14.0.0' } + peerDependencies: + dmg-builder: 24.13.3 + electron-builder-squirrel-windows: 24.13.3 + + aproba@2.0.0: + resolution: + { integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== } + + archiver-utils@2.1.0: + resolution: + { integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw== } + engines: { node: '>= 6' } + + archiver-utils@3.0.4: + resolution: + { integrity: sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw== } + engines: { node: '>= 10' } + + archiver@5.3.2: + resolution: + { integrity: sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw== } + engines: { node: '>= 10' } + + are-we-there-yet@2.0.0: + resolution: + { integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== } + engines: { node: '>=10' } + deprecated: This package is no longer supported. + + argparse@1.0.10: + resolution: + { integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== } + + argparse@2.0.1: + resolution: + { integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== } + + array-ify@1.0.0: + resolution: + { integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng== } + + array-union@2.1.0: + resolution: + { integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== } + engines: { node: '>=8' } + + asap@2.0.6: + resolution: + { integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== } + + asn1@0.2.6: + resolution: + { integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== } + + assert-plus@0.2.0: + resolution: + { integrity: sha512-u1L0ZLywRziOVjUhRxI0Qg9G+4RnFB9H/Rq40YWn0dieDgO7vAYeJz6jKAO6t/aruzlDFLAPkQTT87e+f8Imaw== } + engines: { node: '>=0.8' } + + assert-plus@1.0.0: + resolution: + { integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== } + engines: { node: '>=0.8' } + + assign-symbols@1.0.0: + resolution: + { integrity: sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw== } + engines: { node: '>=0.10.0' } + + astral-regex@2.0.0: + resolution: + { integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== } + engines: { node: '>=8' } + + astring@1.9.0: + resolution: + { integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg== } + hasBin: true + + async-exit-hook@2.0.1: + resolution: + { integrity: sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw== } + engines: { node: '>=0.12.0' } + + async@2.6.4: + resolution: + { integrity: sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== } + + async@3.2.6: + resolution: + { integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== } + + asynckit@0.4.0: + resolution: + { integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== } + + at-least-node@1.0.0: + resolution: + { integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== } + engines: { node: '>= 4.0.0' } + + attr-accept@2.2.5: + resolution: + { integrity: sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ== } + engines: { node: '>=4' } + + aws-sign2@0.6.0: + resolution: + { integrity: sha512-JnJpAS0p9RmixkOvW2XwDxxzs1bd4/VAGIl6Q0EC5YOo+p+hqIhtDhn/nmFnB/xUNXbLkpE2mOjgVIBRKD4xYw== } + + aws4@1.13.2: + resolution: + { integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw== } + + axios@1.9.0: + resolution: + { integrity: sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg== } + + babel-plugin-macros@3.1.0: + resolution: + { integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg== } + engines: { node: '>=10', npm: '>=6' } + + babel-plugin-polyfill-corejs2@0.4.13: + resolution: + { integrity: sha512-3sX/eOms8kd3q2KZ6DAhKPc0dgm525Gqq5NtWKZ7QYYZEv57OQ54KtblzJzH1lQF/eQxO8KjWGIK9IPUJNus5g== } + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-corejs3@0.11.1: + resolution: + { integrity: sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ== } + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-regenerator@0.6.4: + resolution: + { integrity: sha512-7gD3pRadPrbjhjLyxebmx/WrFYcuSjZ0XbdUujQMZ/fcE9oeewk2U/7PCvez84UeuK3oSjmPZ0Ch0dlupQvGzw== } + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + bail@2.0.2: + resolution: + { integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw== } + + balanced-match@1.0.2: + resolution: + { integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== } + + base64-js@1.5.1: + resolution: + { integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== } + + bcrypt-pbkdf@1.0.2: + resolution: + { integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== } + + bl@1.1.2: + resolution: + { integrity: sha512-uVVYHEQk+OuWvCi5U+iquVXvvGCWXKawjwELIR2XMLsqfV/e2sGDClVBs8OlGIgGsStPRY/Es311YKYIlYCWAg== } + + bl@4.1.0: + resolution: + { integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== } + + bluebird-lst@1.0.9: + resolution: + { integrity: sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw== } + + bluebird@3.4.7: + resolution: + { integrity: sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA== } + + bluebird@3.7.2: + resolution: + { integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== } + + boolean@3.2.0: + resolution: + { integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw== } + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + + boom@2.10.1: + resolution: + { integrity: sha512-KbiZEa9/vofNcVJXGwdWWn25reQ3V3dHBWbS07FTF3/TOehLnm9GEhJV4T6ZvGPkShRpmUqYwnaCrkj0mRnP6Q== } + engines: { node: '>=0.10.40' } + deprecated: This version has been deprecated in accordance with the hapi support policy (hapi.im/support). Please upgrade to the latest version to get the best features, bug fixes, and security patches. If you are unable to upgrade at this time, paid support is available for older versions (hapi.im/commercial). + + brace-expansion@1.1.12: + resolution: + { integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg== } + + brace-expansion@2.0.2: + resolution: + { integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== } + + braces@3.0.3: + resolution: + { integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== } + engines: { node: '>=8' } + + browserslist@4.25.0: + resolution: + { integrity: sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA== } + engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 } + hasBin: true + + buffer-crc32@0.2.13: + resolution: + { integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== } + + buffer-equal@1.0.1: + resolution: + { integrity: sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg== } + engines: { node: '>=0.4' } + + buffer-from@1.1.2: + resolution: + { integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== } + + buffer@5.7.1: + resolution: + { integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== } + + builder-util-runtime@9.2.4: + resolution: + { integrity: sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA== } + engines: { node: '>=12.0.0' } + + builder-util-runtime@9.3.1: + resolution: + { integrity: sha512-2/egrNDDnRaxVwK3A+cJq6UOlqOdedGA7JPqCeJjN2Zjk1/QB/6QUi3b714ScIGS7HafFXTyzJEOr5b44I3kvQ== } + engines: { node: '>=12.0.0' } + + builder-util@24.13.1: + resolution: + { integrity: sha512-NhbCSIntruNDTOVI9fdXz0dihaqX2YuE1D6zZMrwiErzH4ELZHE6mdiB40wEgZNprDia+FghRFgKoAqMZRRjSA== } + + busboy@1.6.0: + resolution: + { integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== } + engines: { node: '>=10.16.0' } + + cacheable-lookup@5.0.4: + resolution: + { integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== } + engines: { node: '>=10.6.0' } + + cacheable-request@7.0.4: + resolution: + { integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg== } + engines: { node: '>=8' } + + call-bind-apply-helpers@1.0.2: + resolution: + { integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== } + engines: { node: '>= 0.4' } + + callsites@3.1.0: + resolution: + { integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== } + engines: { node: '>=6' } + + camelcase@6.3.0: + resolution: + { integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== } + engines: { node: '>=10' } + + caniuse-lite@1.0.30001722: + resolution: + { integrity: sha512-DCQHBBZtiK6JVkAGw7drvAMK0Q0POD/xZvEmDp6baiMMP6QXXk9HpD6mNYBZWhOPG6LvIDb82ITqtWjhDckHCA== } + + canvas@2.11.2: + resolution: + { integrity: sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw== } + engines: { node: '>=6' } + + caseless@0.11.0: + resolution: + { integrity: sha512-ODLXH644w9C2fMPAm7bMDQ3GRvipZWZfKc+8As6hIadRIelE0n0xZuN38NS6kiK3KPEVrpymmQD8bvncAHWQkQ== } + + ccount@2.0.1: + resolution: + { integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== } + + cfb@1.2.2: + resolution: + { integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA== } + engines: { node: '>=0.8' } + + chalk@1.1.3: + resolution: + { integrity: sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A== } + engines: { node: '>=0.10.0' } + + chalk@4.1.2: + resolution: + { integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== } + engines: { node: '>=10' } + + chalk@5.4.1: + resolution: + { integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w== } + engines: { node: ^12.17.0 || ^14.13 || >=16.0.0 } + + character-entities-html4@2.1.0: + resolution: + { integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA== } + + character-entities-legacy@3.0.0: + resolution: + { integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ== } + + character-entities@2.0.2: + resolution: + { integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ== } + + character-reference-invalid@2.0.1: + resolution: + { integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw== } + + chevrotain-allstar@0.3.1: + resolution: + { integrity: sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw== } + peerDependencies: + chevrotain: ^11.0.0 + + chevrotain@11.0.3: + resolution: + { integrity: sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw== } + + chownr@1.1.4: + resolution: + { integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== } + + chownr@2.0.0: + resolution: + { integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== } + engines: { node: '>=10' } + + chroma-js@3.1.2: + resolution: + { integrity: sha512-IJnETTalXbsLx1eKEgx19d5L6SRM7cH4vINw/99p/M11HCuXGRWL+6YmCm7FWFGIo6dtWuQoQi1dc5yQ7ESIHg== } + + chromium-pickle-js@0.2.0: + resolution: + { integrity: sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw== } + + ci-info@3.9.0: + resolution: + { integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== } + engines: { node: '>=8' } + + class-variance-authority@0.7.1: + resolution: + { integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg== } + + classnames@2.5.1: + resolution: + { integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== } + + cli-cursor@5.0.0: + resolution: + { integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw== } + engines: { node: '>=18' } + + cli-truncate@2.1.0: + resolution: + { integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== } + engines: { node: '>=8' } + + cli-truncate@4.0.0: + resolution: + { integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA== } + engines: { node: '>=18' } + + client-only@0.0.1: + resolution: + { integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== } + + cliui@7.0.4: + resolution: + { integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== } + + cliui@8.0.1: + resolution: + { integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== } + engines: { node: '>=12' } + + clone-response@1.0.3: + resolution: + { integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA== } + + clsx@1.2.1: + resolution: + { integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== } + engines: { node: '>=6' } + + clsx@2.1.1: + resolution: + { integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== } + engines: { node: '>=6' } + + co-prompt@1.0.0: + resolution: + { integrity: sha512-uKmEbjDnL9SJTb+TNfIFsATe1F3IsNsR7KDGUG1hq7ColkMV0MSn7dg3eKVS+3wwtyvVqrgfIwi39NOJiknO7Q== } + + co@4.6.0: + resolution: + { integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== } + engines: { iojs: '>= 1.0.0', node: '>= 0.12.0' } + + codepage@1.15.0: + resolution: + { integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA== } + engines: { node: '>=0.8' } + + collapse-white-space@2.1.0: + resolution: + { integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw== } + + color-convert@2.0.1: + resolution: + { integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== } + engines: { node: '>=7.0.0' } + + color-name@1.1.4: + resolution: + { integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== } + + color-string@1.9.1: + resolution: + { integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== } + + color-support@1.1.3: + resolution: + { integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== } + hasBin: true + + color@4.2.3: + resolution: + { integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A== } + engines: { node: '>=12.5.0' } + + colord@2.9.3: + resolution: + { integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== } + + colorette@2.0.20: + resolution: + { integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== } + + combined-stream@1.0.8: + resolution: + { integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== } + engines: { node: '>= 0.8' } + + comma-separated-tokens@2.0.3: + resolution: + { integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg== } + + commander@13.1.0: + resolution: + { integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw== } + engines: { node: '>=18' } + + commander@2.9.0: + resolution: + { integrity: sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A== } + engines: { node: '>= 0.6.x' } + + commander@5.1.0: + resolution: + { integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== } + engines: { node: '>= 6' } + + commander@7.2.0: + resolution: + { integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== } + engines: { node: '>= 10' } + + commander@8.3.0: + resolution: + { integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== } + engines: { node: '>= 12' } + + compare-func@2.0.0: + resolution: + { integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA== } + + compare-version@0.1.2: + resolution: + { integrity: sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A== } + engines: { node: '>=0.10.0' } + + compress-commons@4.1.2: + resolution: + { integrity: sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg== } + engines: { node: '>= 10' } + + compute-scroll-into-view@3.1.1: + resolution: + { integrity: sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw== } + + concat-map@0.0.1: + resolution: + { integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== } + + concurrently@8.2.2: + resolution: + { integrity: sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg== } + engines: { node: ^14.13.0 || >=16.0.0 } + hasBin: true + + confbox@0.1.8: + resolution: + { integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w== } + + confbox@0.2.2: + resolution: + { integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ== } + + config-file-ts@0.2.6: + resolution: + { integrity: sha512-6boGVaglwblBgJqGyxm4+xCmEGcWgnWHSWHY5jad58awQhB6gftq0G8HbzU39YqCIYHMLAiL1yjwiZ36m/CL8w== } + + console-control-strings@1.1.0: + resolution: + { integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== } + + console-table-printer@2.14.3: + resolution: + { integrity: sha512-X5OCFnjYlXzRuC8ac5hPA2QflRjJvNKJocMhlnqK/Ap7q3DHXr0NJ0TGzwmEKOiOdJrjsSwEd0m+a32JAYPrKQ== } + + conventional-changelog-angular@7.0.0: + resolution: + { integrity: sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ== } + engines: { node: '>=16' } + + conventional-changelog-conventionalcommits@7.0.2: + resolution: + { integrity: sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w== } + engines: { node: '>=16' } + + conventional-commits-parser@5.0.0: + resolution: + { integrity: sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA== } + engines: { node: '>=16' } + hasBin: true + + convert-source-map@1.9.0: + resolution: + { integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== } + + convert-source-map@2.0.0: + resolution: + { integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== } + + copy-to-clipboard@3.3.3: + resolution: + { integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA== } + + core-js-compat@3.43.0: + resolution: + { integrity: sha512-2GML2ZsCc5LR7hZYz4AXmjQw8zuy2T//2QntwdnpuYI7jteT6GVYJL7F6C2C57R7gSYrcqVW3lAALefdbhBLDA== } + + core-util-is@1.0.2: + resolution: + { integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== } + + core-util-is@1.0.3: + resolution: + { integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== } + + cose-base@1.0.3: + resolution: + { integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg== } + + cose-base@2.2.0: + resolution: + { integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g== } + + cosmiconfig-typescript-loader@6.1.0: + resolution: + { integrity: sha512-tJ1w35ZRUiM5FeTzT7DtYWAFFv37ZLqSRkGi2oeCK1gPhvaWjkAtfXvLmvE1pRfxxp9aQo6ba/Pvg1dKj05D4g== } + engines: { node: '>=v18' } + peerDependencies: + '@types/node': '*' + cosmiconfig: '>=9' + typescript: '>=5' + + cosmiconfig@7.1.0: + resolution: + { integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== } + engines: { node: '>=10' } + + cosmiconfig@9.0.0: + resolution: + { integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg== } + engines: { node: '>=14' } + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + crc-32@1.2.2: + resolution: + { integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== } + engines: { node: '>=0.8' } + hasBin: true + + crc32-stream@4.0.3: + resolution: + { integrity: sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw== } + engines: { node: '>= 10' } + + crc@3.8.0: + resolution: + { integrity: sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ== } + + cross-spawn@7.0.6: + resolution: + { integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== } + engines: { node: '>= 8' } + + cryptiles@2.0.5: + resolution: + { integrity: sha512-FFN5KwpvvQTTS5hWPxrU8/QE4kQUc6uwZcrnlMBN82t1MgAtq8mnoDwINBly9Tdr02seeIIhtdF+UH1feBYGog== } + engines: { node: '>=0.10.40' } + deprecated: This version has been deprecated in accordance with the hapi support policy (hapi.im/support). Please upgrade to the latest version to get the best features, bug fixes, and security patches. If you are unable to upgrade at this time, paid support is available for older versions (hapi.im/commercial). + + csstype@3.1.3: + resolution: + { integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== } + + cytoscape-cose-bilkent@4.1.0: + resolution: + { integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ== } + peerDependencies: + cytoscape: ^3.2.0 + + cytoscape-fcose@2.2.0: + resolution: + { integrity: sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ== } + peerDependencies: + cytoscape: ^3.2.0 + + cytoscape@3.32.0: + resolution: + { integrity: sha512-5JHBC9n75kz5851jeklCPmZWcg3hUe6sjqJvyk3+hVqFaKcHwHgxsjeN1yLmggoUc6STbtm9/NQyabQehfjvWQ== } + engines: { node: '>=0.10' } + + d3-array@2.12.1: + resolution: + { integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ== } + + d3-array@3.2.4: + resolution: + { integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg== } + engines: { node: '>=12' } + + d3-axis@3.0.0: + resolution: + { integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw== } + engines: { node: '>=12' } + + d3-brush@3.0.0: + resolution: + { integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ== } + engines: { node: '>=12' } + + d3-chord@3.0.1: + resolution: + { integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g== } + engines: { node: '>=12' } + + d3-color@3.1.0: + resolution: + { integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA== } + engines: { node: '>=12' } + + d3-contour@4.0.2: + resolution: + { integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA== } + engines: { node: '>=12' } + + d3-delaunay@6.0.4: + resolution: + { integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A== } + engines: { node: '>=12' } + + d3-dispatch@3.0.1: + resolution: + { integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg== } + engines: { node: '>=12' } + + d3-drag@3.0.0: + resolution: + { integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg== } + engines: { node: '>=12' } + + d3-dsv@3.0.1: + resolution: + { integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q== } + engines: { node: '>=12' } + hasBin: true + + d3-ease@3.0.1: + resolution: + { integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w== } + engines: { node: '>=12' } + + d3-fetch@3.0.1: + resolution: + { integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw== } + engines: { node: '>=12' } + + d3-force@3.0.0: + resolution: + { integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg== } + engines: { node: '>=12' } + + d3-format@3.1.0: + resolution: + { integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA== } + engines: { node: '>=12' } + + d3-geo@3.1.1: + resolution: + { integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q== } + engines: { node: '>=12' } + + d3-hierarchy@3.1.2: + resolution: + { integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA== } + engines: { node: '>=12' } + + d3-interpolate@3.0.1: + resolution: + { integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g== } + engines: { node: '>=12' } + + d3-path@1.0.9: + resolution: + { integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg== } + + d3-path@3.1.0: + resolution: + { integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ== } + engines: { node: '>=12' } + + d3-polygon@3.0.1: + resolution: + { integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg== } + engines: { node: '>=12' } + + d3-quadtree@3.0.1: + resolution: + { integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw== } + engines: { node: '>=12' } + + d3-random@3.0.1: + resolution: + { integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ== } + engines: { node: '>=12' } + + d3-sankey@0.12.3: + resolution: + { integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ== } + + d3-scale-chromatic@3.1.0: + resolution: + { integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ== } + engines: { node: '>=12' } + + d3-scale@4.0.2: + resolution: + { integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ== } + engines: { node: '>=12' } + + d3-selection@3.0.0: + resolution: + { integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ== } + engines: { node: '>=12' } + + d3-shape@1.3.7: + resolution: + { integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw== } + + d3-shape@3.2.0: + resolution: + { integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA== } + engines: { node: '>=12' } + + d3-time-format@4.1.0: + resolution: + { integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg== } + engines: { node: '>=12' } + + d3-time@3.1.0: + resolution: + { integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q== } + engines: { node: '>=12' } + + d3-timer@3.0.1: + resolution: + { integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA== } + engines: { node: '>=12' } + + d3-transition@3.0.1: + resolution: + { integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w== } + engines: { node: '>=12' } + peerDependencies: + d3-selection: 2 - 3 + + d3-zoom@3.0.0: + resolution: + { integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw== } + engines: { node: '>=12' } + + d3@7.9.0: + resolution: + { integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA== } + engines: { node: '>=12' } + + dagre-d3-es@7.0.11: + resolution: + { integrity: sha512-tvlJLyQf834SylNKax8Wkzco/1ias1OPw8DcUMDE7oUIoSEW25riQVuiu/0OWEFqT0cxHT3Pa9/D82Jr47IONw== } + + dargs@8.1.0: + resolution: + { integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw== } + engines: { node: '>=12' } + + dashdash@1.14.1: + resolution: + { integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== } + engines: { node: '>=0.10' } + + date-fns@2.30.0: + resolution: + { integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw== } + engines: { node: '>=0.11' } + + dayjs@1.11.13: + resolution: + { integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg== } + + debug@4.4.1: + resolution: + { integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== } + engines: { node: '>=6.0' } + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decamelize@1.2.0: + resolution: + { integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== } + engines: { node: '>=0.10.0' } + + decimal.js-light@2.5.1: + resolution: + { integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg== } + + decode-named-character-reference@1.1.0: + resolution: + { integrity: sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w== } + + decode-uri-component@0.4.1: + resolution: + { integrity: sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ== } + engines: { node: '>=14.16' } + + decompress-response@4.2.1: + resolution: + { integrity: sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw== } + engines: { node: '>=8' } + + decompress-response@6.0.0: + resolution: + { integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== } + engines: { node: '>=10' } + + deep-extend@0.6.0: + resolution: + { integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== } + engines: { node: '>=4.0.0' } + + defer-to-connect@2.0.1: + resolution: + { integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== } + engines: { node: '>=10' } + + define-data-property@1.1.4: + resolution: + { integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== } + engines: { node: '>= 0.4' } + + define-properties@1.2.1: + resolution: + { integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== } + engines: { node: '>= 0.4' } + + delaunator@5.0.1: + resolution: + { integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw== } + + delayed-stream@1.0.0: + resolution: + { integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== } + engines: { node: '>=0.4.0' } + + delegates@1.0.0: + resolution: + { integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== } + + dequal@2.0.3: + resolution: + { integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== } + engines: { node: '>=6' } + + detect-libc@2.0.4: + resolution: + { integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA== } + engines: { node: '>=8' } + + detect-node@2.1.0: + resolution: + { integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== } + + devlop@1.1.0: + resolution: + { integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA== } + + dezalgo@1.0.4: + resolution: + { integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig== } + + diff-match-patch@1.0.5: + resolution: + { integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw== } + + dingbat-to-unicode@1.0.1: + resolution: + { integrity: sha512-98l0sW87ZT58pU4i61wa2OHwxbiYSbuxsCBozaVnYX2iCnr3bLM3fIes1/ej7h1YdOKuKt/MLs706TVnALA65w== } + + dir-compare@3.3.0: + resolution: + { integrity: sha512-J7/et3WlGUCxjdnD3HAAzQ6nsnc0WL6DD7WcwJb7c39iH1+AWfg+9OqzJNaI6PkBwBvm1mhZNL9iY/nRiZXlPg== } + + dir-glob@3.0.1: + resolution: + { integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== } + engines: { node: '>=8' } + + dmg-builder@24.13.3: + resolution: + { integrity: sha512-rcJUkMfnJpfCboZoOOPf4L29TRtEieHNOeAbYPWPxlaBw/Z1RKrRA86dOI9rwaI4tQSc/RD82zTNHprfUHXsoQ== } + + dmg-license@1.0.11: + resolution: + { integrity: sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q== } + engines: { node: '>=8' } + os: [darwin] + hasBin: true + + dom-helpers@5.2.1: + resolution: + { integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA== } + + dompurify@3.2.6: + resolution: + { integrity: sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ== } + + dot-prop@5.3.0: + resolution: + { integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== } + engines: { node: '>=8' } + + dotenv-expand@5.1.0: + resolution: + { integrity: sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA== } + + dotenv@9.0.2: + resolution: + { integrity: sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg== } + engines: { node: '>=10' } + + duck@0.1.12: + resolution: + { integrity: sha512-wkctla1O6VfP89gQ+J/yDesM0S7B7XLXjKGzXxMDVFg7uEn706niAtyYovKbyq1oT9YwDcly721/iUWoc8MVRg== } + + dunder-proto@1.0.1: + resolution: + { integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== } + engines: { node: '>= 0.4' } + + eastasianwidth@0.2.0: + resolution: + { integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== } + + ecc-jsbn@0.1.2: + resolution: + { integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== } + + ejs@3.1.10: + resolution: + { integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA== } + engines: { node: '>=0.10.0' } + hasBin: true + + electron-build@0.0.3: + resolution: + { integrity: sha512-gJ+Civsa9MfR4TssR1+XiJczYylYGoSD2mFMHcOimRV0JxeHA2bbqYJ1Usb9b61F+QuG6dN+l+wLGh7680zH+A== } + hasBin: true + + electron-builder-squirrel-windows@24.13.3: + resolution: + { integrity: sha512-oHkV0iogWfyK+ah9ZIvMDpei1m9ZRpdXcvde1wTpra2U8AFDNNpqJdnin5z+PM1GbQ5BoaKCWas2HSjtR0HwMg== } + + electron-builder@24.13.3: + resolution: + { integrity: sha512-yZSgVHft5dNVlo31qmJAe4BVKQfFdwpRw7sFp1iQglDRCDD6r22zfRJuZlhtB5gp9FHUxCMEoWGq10SkCnMAIg== } + engines: { node: '>=14.0.0' } + hasBin: true + + electron-publish@24.13.1: + resolution: + { integrity: sha512-2ZgdEqJ8e9D17Hwp5LEq5mLQPjqU3lv/IALvgp+4W8VeNhryfGhYEQC/PgDPMrnWUp+l60Ou5SJLsu+k4mhQ8A== } + + electron-to-chromium@1.5.166: + resolution: + { integrity: sha512-QPWqHL0BglzPYyJJ1zSSmwFFL6MFXhbACOCcsCdUMCkzPdS9/OIBVxg516X/Ado2qwAq8k0nJJ7phQPCqiaFAw== } + + electron-updater@6.6.2: + resolution: + { integrity: sha512-Cr4GDOkbAUqRHP5/oeOmH/L2Bn6+FQPxVLZtPbcmKZC63a1F3uu5EefYOssgZXG3u/zBlubbJ5PJdITdMVggbw== } + + electron@35.5.1: + resolution: + { integrity: sha512-kkbGXz56safvXcxqAZyMS2nJGYK9NFG/iKOJsAO5e0HPH8y3EnV4Fi87tvbfFpeLiaruFS+eN0YFy6f1StpSgQ== } + engines: { node: '>= 12.20.55' } + hasBin: true + + emoji-mart@5.6.0: + resolution: + { integrity: sha512-eJp3QRe79pjwa+duv+n7+5YsNhRcMl812EcFVwrnRvYKoNPoQb5qxU8DG6Bgwji0akHdp6D4Ln6tYLG58MFSow== } + + emoji-regex@10.4.0: + resolution: + { integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw== } + + emoji-regex@8.0.0: + resolution: + { integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== } + + emoji-regex@9.2.2: + resolution: + { integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== } + + end-of-stream@1.4.4: + resolution: + { integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== } + + entities@6.0.1: + resolution: + { integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g== } + engines: { node: '>=0.12' } + + enumify@1.0.4: + resolution: + { integrity: sha512-5mwWXaVzJaqyUdOW/PDH5QySRgmQ8VvujmxmvXoXj9w0n+6omhVuyD56eI37FMqy/LxueJzsQ4DrHVQzuT/TXg== } + + env-paths@2.2.1: + resolution: + { integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== } + engines: { node: '>=6' } + + environment@1.1.0: + resolution: + { integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q== } + engines: { node: '>=18' } + + err-code@2.0.3: + resolution: + { integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== } + + error-ex@1.3.2: + resolution: + { integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== } + + es-define-property@1.0.1: + resolution: + { integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== } + engines: { node: '>= 0.4' } + + es-errors@1.3.0: + resolution: + { integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== } + engines: { node: '>= 0.4' } + + es-object-atoms@1.1.1: + resolution: + { integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== } + engines: { node: '>= 0.4' } + + es-set-tostringtag@2.1.0: + resolution: + { integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== } + engines: { node: '>= 0.4' } + + es-toolkit@1.43.0: + resolution: + { integrity: sha512-SKCT8AsWvYzBBuUqMk4NPwFlSdqLpJwmy6AP322ERn8W2YLIB6JBXnwMI2Qsh2gfphT3q7EKAxKb23cvFHFwKA== } + + es6-error@4.1.1: + resolution: + { integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== } + + esast-util-from-estree@2.0.0: + resolution: + { integrity: sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ== } + + esast-util-from-js@2.0.1: + resolution: + { integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw== } + + escalade@3.2.0: + resolution: + { integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== } + engines: { node: '>=6' } + + escape-string-regexp@1.0.5: + resolution: + { integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== } + engines: { node: '>=0.8.0' } + + escape-string-regexp@4.0.0: + resolution: + { integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== } + engines: { node: '>=10' } + + escape-string-regexp@5.0.0: + resolution: + { integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== } + engines: { node: '>=12' } + + estree-util-attach-comments@3.0.0: + resolution: + { integrity: sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw== } + + estree-util-build-jsx@3.0.1: + resolution: + { integrity: sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ== } + + estree-util-is-identifier-name@3.0.0: + resolution: + { integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg== } + + estree-util-scope@1.0.0: + resolution: + { integrity: sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ== } + + estree-util-to-js@2.0.0: + resolution: + { integrity: sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg== } + + estree-util-visit@2.0.0: + resolution: + { integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww== } + + estree-walker@3.0.3: + resolution: + { integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== } + + event-target-shim@5.0.1: + resolution: + { integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== } + engines: { node: '>=6' } + + eventemitter3@4.0.7: + resolution: + { integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== } + + eventemitter3@5.0.1: + resolution: + { integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== } + + eventsource-parser@3.0.2: + resolution: + { integrity: sha512-6RxOBZ/cYgd8usLwsEl+EC09Au/9BcmCKYF2/xbml6DNczf7nv0MQb+7BA2F+li6//I+28VNlQR37XfQtcAJuA== } + engines: { node: '>=18.0.0' } + + eventsource-parser@3.0.6: + resolution: + { integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg== } + engines: { node: '>=18.0.0' } + + execa@8.0.1: + resolution: + { integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg== } + engines: { node: '>=16.17' } + + expand-template@2.0.3: + resolution: + { integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== } + engines: { node: '>=6' } + + exsolve@1.0.5: + resolution: + { integrity: sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg== } + + extend-shallow@2.0.1: + resolution: + { integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== } + engines: { node: '>=0.10.0' } + + extend-shallow@3.0.2: + resolution: + { integrity: sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q== } + engines: { node: '>=0.10.0' } + + extend@3.0.2: + resolution: + { integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== } + + extract-zip@2.0.1: + resolution: + { integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== } + engines: { node: '>= 10.17.0' } + hasBin: true + + extsprintf@1.3.0: + resolution: + { integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== } + engines: { '0': node >=0.6.0 } + + extsprintf@1.4.1: + resolution: + { integrity: sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== } + engines: { '0': node >=0.6.0 } + + fast-deep-equal@3.1.3: + resolution: + { integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== } + + fast-glob@3.3.3: + resolution: + { integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== } + engines: { node: '>=8.6.0' } + + fast-json-stable-stringify@2.1.0: + resolution: + { integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== } + + fast-uri@3.0.6: + resolution: + { integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw== } + + fastq@1.19.1: + resolution: + { integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ== } + + fd-slicer@1.1.0: + resolution: + { integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== } + + file-selector@0.5.0: + resolution: + { integrity: sha512-s8KNnmIDTBoD0p9uJ9uD0XY38SCeBOtj0UMXyQSLg1Ypfrfj8+dAvwsLjYQkQ2GjhVtp2HrnF5cJzMhBjfD8HA== } + engines: { node: '>= 10' } + + filelist@1.0.4: + resolution: + { integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q== } + + fill-range@7.1.1: + resolution: + { integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== } + engines: { node: '>=8' } + + filter-obj@5.1.0: + resolution: + { integrity: sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng== } + engines: { node: '>=14.16' } + + find-root@1.1.0: + resolution: + { integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== } + + find-up@7.0.0: + resolution: + { integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g== } + engines: { node: '>=18' } + + follow-redirects@1.15.9: + resolution: + { integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== } + engines: { node: '>=4.0' } + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + for-in@1.0.2: + resolution: + { integrity: sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ== } + engines: { node: '>=0.10.0' } + + foreground-child@3.3.1: + resolution: + { integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== } + engines: { node: '>=14' } + + forever-agent@0.6.1: + resolution: + { integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== } + + form-data-encoder@1.7.2: + resolution: + { integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A== } + + form-data@1.0.1: + resolution: + { integrity: sha512-M4Yhq2mLogpCtpUmfopFlTTuIe6mSCTgKvnlMhDj3NcgVhA1uS20jT0n+xunKPzpmL5w2erSVtp+SKiJf1TlWg== } + engines: { node: '>= 0.10' } + + form-data@4.0.3: + resolution: + { integrity: sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA== } + engines: { node: '>= 6' } + + formdata-node@4.4.1: + resolution: + { integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ== } + engines: { node: '>= 12.20' } + + formidable@3.5.4: + resolution: + { integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug== } + engines: { node: '>=14.0.0' } + + frac@1.1.2: + resolution: + { integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA== } + engines: { node: '>=0.8' } + + framer-motion@12.17.0: + resolution: + { integrity: sha512-2hISKgDk49yCLStwG1wf4Kdy/D6eBw9/eRNaWFIYoI9vMQ/Mqd1Fz+gzVlEtxJmtQ9y4IWnXm19/+UXD3dAYAA== } + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true + + from2@2.3.0: + resolution: + { integrity: sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g== } + + fs-constants@1.0.0: + resolution: + { integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== } + + fs-extra@10.1.0: + resolution: + { integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== } + engines: { node: '>=12' } + + fs-extra@11.3.0: + resolution: + { integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew== } + engines: { node: '>=14.14' } + + fs-extra@8.1.0: + resolution: + { integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== } + engines: { node: '>=6 <7 || >=8' } + + fs-extra@9.1.0: + resolution: + { integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== } + engines: { node: '>=10' } + + fs-minipass@2.1.0: + resolution: + { integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== } + engines: { node: '>= 8' } + + fs.realpath@1.0.0: + resolution: + { integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== } + + function-bind@1.1.2: + resolution: + { integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== } + + gauge@3.0.2: + resolution: + { integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== } + engines: { node: '>=10' } + deprecated: This package is no longer supported. + + generate-function@2.3.1: + resolution: + { integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ== } + + generate-object-property@1.2.0: + resolution: + { integrity: sha512-TuOwZWgJ2VAMEGJvAyPWvpqxSANF0LDpmyHauMjFYzaACvn+QTT/AZomvPCzVBV7yDN3OmwHQ5OvHaeLKre3JQ== } + + gensync@1.0.0-beta.2: + resolution: + { integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== } + engines: { node: '>=6.9.0' } + + get-caller-file@2.0.5: + resolution: + { integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== } + engines: { node: 6.* || 8.* || >= 10.* } + + get-east-asian-width@1.3.0: + resolution: + { integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ== } + engines: { node: '>=18' } + + get-intrinsic@1.3.0: + resolution: + { integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== } + engines: { node: '>= 0.4' } + + get-proto@1.0.1: + resolution: + { integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== } + engines: { node: '>= 0.4' } + + get-stream@5.2.0: + resolution: + { integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== } + engines: { node: '>=8' } + + get-stream@8.0.1: + resolution: + { integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== } + engines: { node: '>=16' } + + get-value@2.0.6: + resolution: + { integrity: sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA== } + engines: { node: '>=0.10.0' } + + getpass@0.1.7: + resolution: + { integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== } + + giscus@1.6.0: + resolution: + { integrity: sha512-Zrsi8r4t1LVW950keaWcsURuZUQwUaMKjvJgTCY125vkW6OiEBkatE7ScJDbpqKHdZwb///7FVC21SE3iFK3PQ== } + + git-raw-commits@4.0.0: + resolution: + { integrity: sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ== } + engines: { node: '>=16' } + hasBin: true + + github-from-package@0.0.0: + resolution: + { integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw== } + + github-markdown-css@5.8.1: + resolution: + { integrity: sha512-8G+PFvqigBQSWLQjyzgpa2ThD9bo7+kDsriUIidGcRhXgmcaAWUIpCZf8DavJgc+xifjbCG+GvMyWr0XMXmc7g== } + engines: { node: '>=10' } + + glob-parent@5.1.2: + resolution: + { integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== } + engines: { node: '>= 6' } + + glob@10.4.5: + resolution: + { integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== } + hasBin: true + + glob@7.2.3: + resolution: + { integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== } + deprecated: Glob versions prior to v9 are no longer supported + + global-agent@3.0.0: + resolution: + { integrity: sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q== } + engines: { node: '>=10.0' } + + global-directory@4.0.1: + resolution: + { integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q== } + engines: { node: '>=18' } + + globals@11.12.0: + resolution: + { integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== } + engines: { node: '>=4' } + + globals@15.15.0: + resolution: + { integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg== } + engines: { node: '>=18' } + + globalthis@1.0.4: + resolution: + { integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== } + engines: { node: '>= 0.4' } + + globby@11.1.0: + resolution: + { integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== } + engines: { node: '>=10' } + + gopd@1.2.0: + resolution: + { integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== } + engines: { node: '>= 0.4' } + + got@11.8.6: + resolution: + { integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g== } + engines: { node: '>=10.19.0' } + + graceful-fs@4.2.11: + resolution: + { integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== } + + graceful-readlink@1.0.1: + resolution: + { integrity: sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w== } + + hachure-fill@0.5.2: + resolution: + { integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg== } + + har-validator@2.0.6: + resolution: + { integrity: sha512-P6tFV+wCcUL3nbyTDAvveDySfbhy0XkDtAIfZP6HITjM2WUsiPna/Eg1Yy93SFXvahqoX+kt0n+6xlXKDXYowA== } + engines: { node: '>=0.10' } + deprecated: this library is no longer supported + hasBin: true + + has-ansi@2.0.0: + resolution: + { integrity: sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg== } + engines: { node: '>=0.10.0' } + + has-flag@4.0.0: + resolution: + { integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== } + engines: { node: '>=8' } + + has-property-descriptors@1.0.2: + resolution: + { integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== } + + has-symbols@1.1.0: + resolution: + { integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== } + engines: { node: '>= 0.4' } + + has-tostringtag@1.0.2: + resolution: + { integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== } + engines: { node: '>= 0.4' } + + has-unicode@2.0.1: + resolution: + { integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== } + + has@1.0.4: + resolution: + { integrity: sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ== } + engines: { node: '>= 0.4.0' } + + hasown@2.0.2: + resolution: + { integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== } + engines: { node: '>= 0.4' } + + hast-util-from-dom@5.0.1: + resolution: + { integrity: sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q== } + + hast-util-from-html-isomorphic@2.0.0: + resolution: + { integrity: sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw== } + + hast-util-from-html@2.0.3: + resolution: + { integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw== } + + hast-util-from-parse5@8.0.3: + resolution: + { integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg== } + + hast-util-is-element@3.0.0: + resolution: + { integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g== } + + hast-util-parse-selector@4.0.0: + resolution: + { integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A== } + + hast-util-raw@9.1.0: + resolution: + { integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw== } + + hast-util-to-estree@3.1.3: + resolution: + { integrity: sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w== } + + hast-util-to-html@9.0.5: + resolution: + { integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw== } + + hast-util-to-jsx-runtime@2.3.6: + resolution: + { integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg== } + + hast-util-to-parse5@8.0.0: + resolution: + { integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw== } + + hast-util-to-text@4.0.2: + resolution: + { integrity: sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A== } + + hast-util-whitespace@3.0.0: + resolution: + { integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw== } + + hastscript@9.0.1: + resolution: + { integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w== } + + hawk@3.1.3: + resolution: + { integrity: sha512-X8xbmTc1cbPXcQV4WkLcRMALuyoxhfpFATmyuCxJPOAvrDS4DNnsTAOmKUxMTOWU6TzrTOkxPKwIx5ZOpJVSrg== } + engines: { node: '>=0.10.32' } + deprecated: This module moved to @hapi/hawk. Please make sure to switch over as this distribution is no longer supported and may contain bugs and critical security issues. + + hoek@2.16.3: + resolution: + { integrity: sha512-V6Yw1rIcYV/4JsnggjBU0l4Kr+EXhpwqXRusENU1Xx6ro00IHPHYNynCuBTOZAPlr3AAmLvchH9I7N/VUdvOwQ== } + engines: { node: '>=0.10.40' } + deprecated: This version has been deprecated in accordance with the hapi support policy (hapi.im/support). Please upgrade to the latest version to get the best features, bug fixes, and security patches. If you are unable to upgrade at this time, paid support is available for older versions (hapi.im/commercial). + + hoist-non-react-statics@3.3.2: + resolution: + { integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== } + + hosted-git-info@4.1.0: + resolution: + { integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA== } + engines: { node: '>=10' } + + html-parse-stringify@3.0.1: + resolution: + { integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg== } + + html-url-attributes@3.0.1: + resolution: + { integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ== } + + html-void-elements@3.0.0: + resolution: + { integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg== } + + http-cache-semantics@4.2.0: + resolution: + { integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ== } + + http-proxy-agent@5.0.0: + resolution: + { integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== } + engines: { node: '>= 6' } + + http-signature@1.1.1: + resolution: + { integrity: sha512-iUn0NcRULlDGtqNLN1Jxmzayk8ogm7NToldASyZBpM2qggbphjXzNOiw3piN8tgz+e/DRs6X5gAzFwTI6BCRcg== } + engines: { node: '>=0.8', npm: '>=1.3.7' } + + http2-wrapper@1.0.3: + resolution: + { integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== } + engines: { node: '>=10.19.0' } + + https-proxy-agent@5.0.1: + resolution: + { integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== } + engines: { node: '>= 6' } + + human-signals@5.0.0: + resolution: + { integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== } + engines: { node: '>=16.17.0' } + + humanize-ms@1.2.1: + resolution: + { integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ== } + + husky@9.1.7: + resolution: + { integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA== } + engines: { node: '>=18' } + hasBin: true + + i18next-browser-languagedetector@8.2.0: + resolution: + { integrity: sha512-P+3zEKLnOF0qmiesW383vsLdtQVyKtCNA9cjSoKCppTKPQVfKd2W8hbVo5ZhNJKDqeM7BOcvNoKJOjpHh4Js9g== } + + i18next@24.2.3: + resolution: + { integrity: sha512-lfbf80OzkocvX7nmZtu7nSTNbrTYR52sLWxPtlXX1zAhVw8WEnFk4puUkCR4B1dNQwbSpEHHHemcZu//7EcB7A== } + peerDependencies: + typescript: ^5 + peerDependenciesMeta: + typescript: + optional: true + + iconv-corefoundation@1.1.7: + resolution: + { integrity: sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ== } + engines: { node: ^8.11.2 || >=10 } + os: [darwin] + + iconv-lite@0.6.3: + resolution: + { integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== } + engines: { node: '>=0.10.0' } + + ieee754@1.2.1: + resolution: + { integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== } + + ignore@5.3.2: + resolution: + { integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== } + engines: { node: '>= 4' } + + image-size@2.0.2: + resolution: + { integrity: sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w== } + engines: { node: '>=16.x' } + hasBin: true + + immediate@3.0.6: + resolution: + { integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== } + + immer@10.1.1: + resolution: + { integrity: sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw== } + + immer@11.1.0: + resolution: + { integrity: sha512-dlzb07f5LDY+tzs+iLCSXV2yuhaYfezqyZQc+n6baLECWkOMEWxkECAOnXL0ba7lsA25fM9b2jtzpu/uxo1a7g== } + + import-fresh@3.3.1: + resolution: + { integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== } + engines: { node: '>=6' } + + import-meta-resolve@4.1.0: + resolution: + { integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw== } + + inflight@1.0.6: + resolution: + { integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== } + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.3: + resolution: + { integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== } + + inherits@2.0.4: + resolution: + { integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== } + + ini@1.3.8: + resolution: + { integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== } + + ini@4.1.1: + resolution: + { integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g== } + engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + + inline-style-parser@0.2.4: + resolution: + { integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q== } + + internmap@1.0.1: + resolution: + { integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw== } + + internmap@2.0.3: + resolution: + { integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg== } + engines: { node: '>=12' } + + intersection-observer@0.12.2: + resolution: + { integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg== } + + into-stream@6.0.0: + resolution: + { integrity: sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA== } + engines: { node: '>=10' } + + is-alphabetical@2.0.1: + resolution: + { integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ== } + + is-alphanumerical@2.0.1: + resolution: + { integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw== } + + is-arrayish@0.2.1: + resolution: + { integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== } + + is-arrayish@0.3.2: + resolution: + { integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== } + + is-ci@3.0.1: + resolution: + { integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ== } + hasBin: true + + is-core-module@2.16.1: + resolution: + { integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== } + engines: { node: '>= 0.4' } + + is-core-module@2.9.0: + resolution: + { integrity: sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== } + + is-decimal@2.0.1: + resolution: + { integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A== } + + is-extendable@0.1.1: + resolution: + { integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== } + engines: { node: '>=0.10.0' } + + is-extendable@1.0.1: + resolution: + { integrity: sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== } + engines: { node: '>=0.10.0' } + + is-extglob@2.1.1: + resolution: + { integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== } + engines: { node: '>=0.10.0' } + + is-fullwidth-code-point@3.0.0: + resolution: + { integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== } + engines: { node: '>=8' } + + is-fullwidth-code-point@4.0.0: + resolution: + { integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ== } + engines: { node: '>=12' } + + is-fullwidth-code-point@5.0.0: + resolution: + { integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA== } + engines: { node: '>=18' } + + is-glob@4.0.3: + resolution: + { integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== } + engines: { node: '>=0.10.0' } + + is-hexadecimal@2.0.1: + resolution: + { integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg== } + + is-my-ip-valid@1.0.1: + resolution: + { integrity: sha512-jxc8cBcOWbNK2i2aTkCZP6i7wkHF1bqKFrwEHuN5Jtg5BSaZHUZQ/JTOJwoV41YvHnOaRyWWh72T/KvfNz9DJg== } + + is-my-json-valid@2.20.6: + resolution: + { integrity: sha512-1JQwulVNjx8UqkPE/bqDaxtH4PXCe/2VRh/y3p99heOV87HG4Id5/VfDswd+YiAfHcRTfDlWgISycnHuhZq1aw== } + + is-number@7.0.0: + resolution: + { integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== } + engines: { node: '>=0.12.0' } + + is-obj@2.0.0: + resolution: + { integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== } + engines: { node: '>=8' } + + is-plain-obj@4.1.0: + resolution: + { integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== } + engines: { node: '>=12' } + + is-plain-object@2.0.4: + resolution: + { integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== } + engines: { node: '>=0.10.0' } + + is-property@1.0.2: + resolution: + { integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g== } + + is-stream@3.0.0: + resolution: + { integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + + is-text-path@2.0.0: + resolution: + { integrity: sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw== } + engines: { node: '>=8' } + + is-typedarray@1.0.0: + resolution: + { integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== } + + isarray@1.0.0: + resolution: + { integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== } + + isbinaryfile@4.0.10: + resolution: + { integrity: sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw== } + engines: { node: '>= 8.0.0' } + + isbinaryfile@5.0.4: + resolution: + { integrity: sha512-YKBKVkKhty7s8rxddb40oOkuP0NbaeXrQvLin6QMHL7Ypiy2RW9LwOVrVgZRyOrhQlayMd9t+D8yDy8MKFTSDQ== } + engines: { node: '>= 18.0.0' } + + isexe@2.0.0: + resolution: + { integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== } + + isobject@3.0.1: + resolution: + { integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== } + engines: { node: '>=0.10.0' } + + isstream@0.1.2: + resolution: + { integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== } + + jackspeak@3.4.3: + resolution: + { integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== } + + jake@10.9.2: + resolution: + { integrity: sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA== } + engines: { node: '>=10' } + hasBin: true + + jiti@2.4.2: + resolution: + { integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A== } + hasBin: true + + joi@17.13.3: + resolution: + { integrity: sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA== } + + jotai@2.12.5: + resolution: + { integrity: sha512-G8m32HW3lSmcz/4mbqx0hgJIQ0ekndKWiYP7kWVKi0p6saLXdSoye+FZiOFyonnd7Q482LCzm8sMDl7Ar1NWDw== } + engines: { node: '>=12.20.0' } + peerDependencies: + '@types/react': '>=17.0.0' + react: '>=17.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + + js-cookie@3.0.5: + resolution: + { integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw== } + engines: { node: '>=14' } + + js-tiktoken@1.0.20: + resolution: + { integrity: sha512-Xlaqhhs8VfCd6Sh7a1cFkZHQbYTLCwVJJWiHVxBYzLPxW0XsoxBy1hitmjkdIjD3Aon5BXLHFwU5O8WUx6HH+A== } + + js-tokens@4.0.0: + resolution: + { integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== } + + js-yaml@4.1.0: + resolution: + { integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== } + hasBin: true + + jsbn@0.1.1: + resolution: + { integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== } + + jsesc@2.5.2: + resolution: + { integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== } + engines: { node: '>=4' } + hasBin: true + + jsesc@3.1.0: + resolution: + { integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== } + engines: { node: '>=6' } + hasBin: true + + json-buffer@3.0.1: + resolution: + { integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== } + + json-parse-even-better-errors@2.3.1: + resolution: + { integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== } + + json-schema-traverse@0.4.1: + resolution: + { integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== } + + json-schema-traverse@1.0.0: + resolution: + { integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== } + + json-schema@0.4.0: + resolution: + { integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== } + + json-stringify-safe@5.0.1: + resolution: + { integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== } + + json2mq@0.2.0: + resolution: + { integrity: sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA== } + + json5@2.2.3: + resolution: + { integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== } + engines: { node: '>=6' } + hasBin: true + + jsondiffpatch@0.6.0: + resolution: + { integrity: sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ== } + engines: { node: ^18.0.0 || >=20.0.0 } + hasBin: true + + jsonfile@4.0.0: + resolution: + { integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== } + + jsonfile@6.1.0: + resolution: + { integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== } + + jsonparse@1.3.1: + resolution: + { integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== } + engines: { '0': node >= 0.2.0 } + + jsonpointer@5.0.1: + resolution: + { integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ== } + engines: { node: '>=0.10.0' } + + jsonrepair@3.13.1: + resolution: + { integrity: sha512-WJeiE0jGfxYmtLwBTEk8+y/mYcaleyLXWaqp5bJu0/ZTSeG0KQq/wWQ8pmnkKenEdN6pdnn6QtcoSUkbqDHWNw== } + hasBin: true + + jsprim@1.4.2: + resolution: + { integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw== } + engines: { node: '>=0.6.0' } + + jszip@2.5.0: + resolution: + { integrity: sha512-IRoyf8JSYY3nx+uyh5xPc0qdy8pUDTp2UkHOWYNF/IO/3D8nx7899UlSAjD8rf8wUgOmm0lACWx/GbW3EaxIXQ== } + + jszip@3.10.1: + resolution: + { integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g== } + + katex@0.16.22: + resolution: + { integrity: sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg== } + hasBin: true + + keypress@0.2.1: + resolution: + { integrity: sha512-HjorDJFNhnM4SicvaUXac0X77NiskggxJdesG72+O5zBKpSqKFCrqmndKVqpu3pFqkla0St6uGk8Ju0sCurrmg== } + + keyv@4.5.4: + resolution: + { integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== } + + khroma@2.1.0: + resolution: + { integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw== } + + kolorist@1.8.0: + resolution: + { integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ== } + + langchain@0.3.28: + resolution: + { integrity: sha512-h4GGlBJNGU/Sj2PipW9kL+ewj7To3c+SnnNKH3HZaVHEqGPMHVB96T1lLjtCLcZCyUfabMr/zFIkLNI4War+Xg== } + engines: { node: '>=18' } + peerDependencies: + '@langchain/anthropic': '*' + '@langchain/aws': '*' + '@langchain/cerebras': '*' + '@langchain/cohere': '*' + '@langchain/core': '>=0.3.58 <0.4.0' + '@langchain/deepseek': '*' + '@langchain/google-genai': '*' + '@langchain/google-vertexai': '*' + '@langchain/google-vertexai-web': '*' + '@langchain/groq': '*' + '@langchain/mistralai': '*' + '@langchain/ollama': '*' + '@langchain/xai': '*' + axios: '*' + cheerio: '*' + handlebars: ^4.7.8 + peggy: ^3.0.2 + typeorm: '*' + peerDependenciesMeta: + '@langchain/anthropic': + optional: true + '@langchain/aws': + optional: true + '@langchain/cerebras': + optional: true + '@langchain/cohere': + optional: true + '@langchain/deepseek': + optional: true + '@langchain/google-genai': + optional: true + '@langchain/google-vertexai': + optional: true + '@langchain/google-vertexai-web': + optional: true + '@langchain/groq': + optional: true + '@langchain/mistralai': + optional: true + '@langchain/ollama': + optional: true + '@langchain/xai': + optional: true + axios: + optional: true + cheerio: + optional: true + handlebars: + optional: true + peggy: + optional: true + typeorm: + optional: true + + langium@3.3.1: + resolution: + { integrity: sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w== } + engines: { node: '>=16.0.0' } + + langsmith@0.3.31: + resolution: + { integrity: sha512-9lwuLZuN3tXFYQ6eMg0rmbBw7oxQo4bu1NYeylbjz27bOdG1XB9XNoxaiIArkK4ciLdOIOhPMBXP4bkvZOgHRw== } + peerDependencies: + openai: '*' + peerDependenciesMeta: + openai: + optional: true + + layout-base@1.0.2: + resolution: + { integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg== } + + layout-base@2.0.1: + resolution: + { integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg== } + + lazy-val@1.0.5: + resolution: + { integrity: sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q== } + + lazystream@1.0.1: + resolution: + { integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw== } + engines: { node: '>= 0.6.3' } + + leva@0.10.0: + resolution: + { integrity: sha512-RiNJWmeqQdKIeHuVXgshmxIHu144a2AMYtLxKf8Nm1j93pisDPexuQDHKNdQlbo37wdyDQibLjY9JKGIiD7gaw== } + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + + lie@3.3.0: + resolution: + { integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ== } + + lilconfig@3.1.3: + resolution: + { integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw== } + engines: { node: '>=14' } + + lines-and-columns@1.2.4: + resolution: + { integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== } + + lint-staged@15.5.2: + resolution: + { integrity: sha512-YUSOLq9VeRNAo/CTaVmhGDKG+LBtA8KF1X4K5+ykMSwWST1vDxJRB2kv2COgLb1fvpCo+A/y9A0G0znNVmdx4w== } + engines: { node: '>=18.12.0' } + hasBin: true + + listr2@8.3.3: + resolution: + { integrity: sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ== } + engines: { node: '>=18.0.0' } + + lit-element@4.2.0: + resolution: + { integrity: sha512-MGrXJVAI5x+Bfth/pU9Kst1iWID6GHDLEzFEnyULB/sFiRLgkd8NPK/PeeXxktA3T6EIIaq8U3KcbTU5XFcP2Q== } + + lit-html@3.3.0: + resolution: + { integrity: sha512-RHoswrFAxY2d8Cf2mm4OZ1DgzCoBKUKSPvA1fhtSELxUERq2aQQ2h05pO9j81gS1o7RIRJ+CePLogfyahwmynw== } + + lit@3.3.0: + resolution: + { integrity: sha512-DGVsqsOIHBww2DqnuZzW7QsuCdahp50ojuDaBPC7jUDRpYoH0z7kHBBYZewRzer75FwtrkmkKk7iOAwSaWdBmw== } + + local-pkg@1.1.1: + resolution: + { integrity: sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg== } + engines: { node: '>=14' } + + locate-path@7.2.0: + resolution: + { integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA== } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + + lodash-es@4.17.21: + resolution: + { integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== } + + lodash.camelcase@4.3.0: + resolution: + { integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== } + + lodash.debounce@4.0.8: + resolution: + { integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== } + + lodash.defaults@4.2.0: + resolution: + { integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ== } + + lodash.difference@4.5.0: + resolution: + { integrity: sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA== } + + lodash.escaperegexp@4.1.2: + resolution: + { integrity: sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw== } + + lodash.flatten@4.4.0: + resolution: + { integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g== } + + lodash.isequal@4.5.0: + resolution: + { integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== } + deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. + + lodash.isplainobject@4.0.6: + resolution: + { integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== } + + lodash.kebabcase@4.1.1: + resolution: + { integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g== } + + lodash.merge@4.6.2: + resolution: + { integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== } + + lodash.mergewith@4.6.2: + resolution: + { integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== } + + lodash.snakecase@4.1.1: + resolution: + { integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw== } + + lodash.startcase@4.4.0: + resolution: + { integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg== } + + lodash.union@4.6.0: + resolution: + { integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw== } + + lodash.uniq@4.5.0: + resolution: + { integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== } + + lodash.upperfirst@4.3.1: + resolution: + { integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg== } + + lodash@4.17.21: + resolution: + { integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== } + + log-update@6.1.0: + resolution: + { integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w== } + engines: { node: '>=18' } + + longest-streak@3.1.0: + resolution: + { integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g== } + + loose-envify@1.4.0: + resolution: + { integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== } + hasBin: true + + lop@0.4.2: + resolution: + { integrity: sha512-RefILVDQ4DKoRZsJ4Pj22TxE3omDO47yFpkIBoDKzkqPRISs5U1cnAdg/5583YPkWPaLIYHOKRMQSvjFsO26cw== } + + lowercase-keys@2.0.0: + resolution: + { integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== } + engines: { node: '>=8' } + + lru-cache@10.4.3: + resolution: + { integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== } + + lru-cache@5.1.1: + resolution: + { integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== } + + lru-cache@6.0.0: + resolution: + { integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== } + engines: { node: '>=10' } + + lucide-react@0.469.0: + resolution: + { integrity: sha512-28vvUnnKQ/dBwiCQtwJw7QauYnE7yd2Cyp4tTTJpvglX4EMpbflcdBgrgToX2j71B3YvugK/NH3BGUk+E/p/Fw== } + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + lucide-react@0.484.0: + resolution: + { integrity: sha512-oZy8coK9kZzvqhSgfbGkPtTgyjpBvs3ukLgDPv14dSOZtBtboryWF5o8i3qen7QbGg7JhiJBz5mK1p8YoMZTLQ== } + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + make-dir@3.1.0: + resolution: + { integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== } + engines: { node: '>=8' } + + mammoth@1.9.1: + resolution: + { integrity: sha512-4S2v1eP4Yo4so0zGNicJKcP93su3wDPcUk+xvkjSG75nlNjSkDJu8BhWQ+e54BROM0HfA6nPzJn12S6bq2Ko6w== } + engines: { node: '>=12.0.0' } + hasBin: true + + markdown-extensions@2.0.0: + resolution: + { integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q== } + engines: { node: '>=16' } + + markdown-table@3.0.4: + resolution: + { integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw== } + + marked@15.0.12: + resolution: + { integrity: sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA== } + engines: { node: '>= 18' } + hasBin: true + + matcher@3.0.0: + resolution: + { integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng== } + engines: { node: '>=10' } + + math-intrinsics@1.1.0: + resolution: + { integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== } + engines: { node: '>= 0.4' } + + mdast-util-find-and-replace@3.0.2: + resolution: + { integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg== } + + mdast-util-from-markdown@2.0.2: + resolution: + { integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA== } + + mdast-util-gfm-autolink-literal@2.0.1: + resolution: + { integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ== } + + mdast-util-gfm-footnote@2.1.0: + resolution: + { integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ== } + + mdast-util-gfm-strikethrough@2.0.0: + resolution: + { integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg== } + + mdast-util-gfm-table@2.0.0: + resolution: + { integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg== } + + mdast-util-gfm-task-list-item@2.0.0: + resolution: + { integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ== } + + mdast-util-gfm@3.1.0: + resolution: + { integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ== } + + mdast-util-math@3.0.0: + resolution: + { integrity: sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w== } + + mdast-util-mdx-expression@2.0.1: + resolution: + { integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ== } + + mdast-util-mdx-jsx@3.2.0: + resolution: + { integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q== } + + mdast-util-mdx@3.0.0: + resolution: + { integrity: sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w== } + + mdast-util-mdxjs-esm@2.0.1: + resolution: + { integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg== } + + mdast-util-newline-to-break@2.0.0: + resolution: + { integrity: sha512-MbgeFca0hLYIEx/2zGsszCSEJJ1JSCdiY5xQxRcLDDGa8EPvlLPupJ4DSajbMPAnC0je8jfb9TiUATnxxrHUog== } + + mdast-util-phrasing@4.1.0: + resolution: + { integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w== } + + mdast-util-to-hast@13.2.0: + resolution: + { integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA== } + + mdast-util-to-markdown@2.1.2: + resolution: + { integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA== } + + mdast-util-to-string@4.0.0: + resolution: + { integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg== } + + meow@12.1.1: + resolution: + { integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw== } + engines: { node: '>=16.10' } + + merge-stream@2.0.0: + resolution: + { integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== } + + merge-value@1.0.0: + resolution: + { integrity: sha512-fJMmvat4NeKz63Uv9iHWcPDjCWcCkoiRoajRTEO8hlhUC6rwaHg0QCF9hBOTjZmm4JuglPckPSTtcuJL5kp0TQ== } + engines: { node: '>=0.10.0' } + + merge2@1.4.1: + resolution: + { integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== } + engines: { node: '>= 8' } + + mermaid@11.6.0: + resolution: + { integrity: sha512-PE8hGUy1LDlWIHWBP05SFdqUHGmRcCcK4IzpOKPE35eOw+G9zZgcnMpyunJVUEOgb//KBORPjysKndw8bFLuRg== } + + micromark-core-commonmark@2.0.3: + resolution: + { integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg== } + + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: + { integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw== } + + micromark-extension-gfm-footnote@2.1.0: + resolution: + { integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw== } + + micromark-extension-gfm-strikethrough@2.1.0: + resolution: + { integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw== } + + micromark-extension-gfm-table@2.1.1: + resolution: + { integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg== } + + micromark-extension-gfm-tagfilter@2.0.0: + resolution: + { integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg== } + + micromark-extension-gfm-task-list-item@2.1.0: + resolution: + { integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw== } + + micromark-extension-gfm@3.0.0: + resolution: + { integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w== } + + micromark-extension-math@3.1.0: + resolution: + { integrity: sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg== } + + micromark-extension-mdx-expression@3.0.1: + resolution: + { integrity: sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q== } + + micromark-extension-mdx-jsx@3.0.2: + resolution: + { integrity: sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ== } + + micromark-extension-mdx-md@2.0.0: + resolution: + { integrity: sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ== } + + micromark-extension-mdxjs-esm@3.0.0: + resolution: + { integrity: sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A== } + + micromark-extension-mdxjs@3.0.0: + resolution: + { integrity: sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ== } + + micromark-factory-destination@2.0.1: + resolution: + { integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA== } + + micromark-factory-label@2.0.1: + resolution: + { integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg== } + + micromark-factory-mdx-expression@2.0.3: + resolution: + { integrity: sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ== } + + micromark-factory-space@2.0.1: + resolution: + { integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg== } + + micromark-factory-title@2.0.1: + resolution: + { integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw== } + + micromark-factory-whitespace@2.0.1: + resolution: + { integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ== } + + micromark-util-character@2.1.1: + resolution: + { integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q== } + + micromark-util-chunked@2.0.1: + resolution: + { integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA== } + + micromark-util-classify-character@2.0.1: + resolution: + { integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q== } + + micromark-util-combine-extensions@2.0.1: + resolution: + { integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg== } + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: + { integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw== } + + micromark-util-decode-string@2.0.1: + resolution: + { integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ== } + + micromark-util-encode@2.0.1: + resolution: + { integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw== } + + micromark-util-events-to-acorn@2.0.3: + resolution: + { integrity: sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg== } + + micromark-util-html-tag-name@2.0.1: + resolution: + { integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA== } + + micromark-util-normalize-identifier@2.0.1: + resolution: + { integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q== } + + micromark-util-resolve-all@2.0.1: + resolution: + { integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg== } + + micromark-util-sanitize-uri@2.0.1: + resolution: + { integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ== } + + micromark-util-subtokenize@2.1.0: + resolution: + { integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA== } + + micromark-util-symbol@2.0.1: + resolution: + { integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q== } + + micromark-util-types@2.0.2: + resolution: + { integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA== } + + micromark@4.0.2: + resolution: + { integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA== } + + micromatch@4.0.8: + resolution: + { integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== } + engines: { node: '>=8.6' } + + mime-db@1.52.0: + resolution: + { integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== } + engines: { node: '>= 0.6' } + + mime-types@2.1.35: + resolution: + { integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== } + engines: { node: '>= 0.6' } + + mime@2.6.0: + resolution: + { integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== } + engines: { node: '>=4.0.0' } + hasBin: true + + mimic-fn@4.0.0: + resolution: + { integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== } + engines: { node: '>=12' } + + mimic-function@5.0.1: + resolution: + { integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== } + engines: { node: '>=18' } + + mimic-response@1.0.1: + resolution: + { integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== } + engines: { node: '>=4' } + + mimic-response@2.1.0: + resolution: + { integrity: sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA== } + engines: { node: '>=8' } + + mimic-response@3.1.0: + resolution: + { integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== } + engines: { node: '>=10' } + + minimatch@3.1.2: + resolution: + { integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== } + + minimatch@5.1.6: + resolution: + { integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== } + engines: { node: '>=10' } + + minimatch@9.0.5: + resolution: + { integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== } + engines: { node: '>=16 || 14 >=14.17' } + + minimist@1.2.8: + resolution: + { integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== } + + minipass@3.3.6: + resolution: + { integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== } + engines: { node: '>=8' } + + minipass@5.0.0: + resolution: + { integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== } + engines: { node: '>=8' } + + minipass@7.1.2: + resolution: + { integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== } + engines: { node: '>=16 || 14 >=14.17' } + + minizlib@2.1.2: + resolution: + { integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== } + engines: { node: '>= 8' } + + mixin-deep@1.3.2: + resolution: + { integrity: sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== } + engines: { node: '>=0.10.0' } + + mkdirp-classic@0.5.3: + resolution: + { integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== } + + mkdirp@1.0.4: + resolution: + { integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== } + engines: { node: '>=10' } + hasBin: true + + mlly@1.7.4: + resolution: + { integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw== } + + motion-dom@12.17.0: + resolution: + { integrity: sha512-FA6/c70R9NKs3g41XDVONzmUUrEmyaifLVGCWtAmHP0usDnX9W+RN/tmbC4EUl0w6yLGvMTOwnWCFVgA5luhRg== } + + motion-utils@12.12.1: + resolution: + { integrity: sha512-f9qiqUHm7hWSLlNW8gS9pisnsN7CRFRD58vNjptKdsqFLpkVnX00TNeD6Q0d27V9KzT7ySFyK1TZ/DShfVOv6w== } + + ms@2.1.3: + resolution: + { integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== } + + multistream@4.1.0: + resolution: + { integrity: sha512-J1XDiAmmNpRCBfIWJv+n0ymC4ABcf/Pl+5YvC5B/D2f/2+8PtHvCNxMPKiQcZyi922Hq69J2YOpb1pTywfifyw== } + + mustache@4.2.0: + resolution: + { integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ== } + hasBin: true + + nan@2.22.2: + resolution: + { integrity: sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ== } + + nanoid@3.3.11: + resolution: + { integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== } + engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 } + hasBin: true + + nanoid@5.1.5: + resolution: + { integrity: sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw== } + engines: { node: ^18 || >=20 } + hasBin: true + + napi-build-utils@1.0.2: + resolution: + { integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== } + + next-themes@0.2.1: + resolution: + { integrity: sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A== } + peerDependencies: + next: '*' + react: '*' + react-dom: '*' + + next@14.2.29: + resolution: + { integrity: sha512-s98mCOMOWLGGpGOfgKSnleXLuegvvH415qtRZXpSp00HeEgdmrxmwL9cgKU+h4XrhB16zEI5d/7BnkS3ATInsA== } + engines: { node: '>=18.17.0' } + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.41.2 + react: ^18.2.0 + react-dom: ^18.2.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + sass: + optional: true + + node-abi@3.75.0: + resolution: + { integrity: sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg== } + engines: { node: '>=10' } + + node-addon-api@1.7.2: + resolution: + { integrity: sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg== } + + node-domexception@1.0.0: + resolution: + { integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== } + engines: { node: '>=10.5.0' } + deprecated: Use your platform's native DOMException instead + + node-fetch@2.7.0: + resolution: + { integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== } + engines: { node: 4.x || >=6.0.0 } + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-releases@2.0.19: + resolution: + { integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== } + + node-uuid@1.4.8: + resolution: + { integrity: sha512-TkCET/3rr9mUuRp+CpO7qfgT++aAxfDRaalQhwPFzI9BY/2rCDn6OfpZOVggi1AXfTPpfkTrg5f5WQx5G1uLxA== } + deprecated: Use uuid module instead + hasBin: true + + nopt@5.0.0: + resolution: + { integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== } + engines: { node: '>=6' } + hasBin: true + + normalize-path@3.0.0: + resolution: + { integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== } + engines: { node: '>=0.10.0' } + + normalize-url@6.1.0: + resolution: + { integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== } + engines: { node: '>=10' } + + npm-run-path@5.3.0: + resolution: + { integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ== } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + + npmlog@5.0.1: + resolution: + { integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== } + deprecated: This package is no longer supported. + + numeral@2.0.6: + resolution: + { integrity: sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA== } + + oauth-sign@0.8.2: + resolution: + { integrity: sha512-VlF07iu3VV3+BTXj43Nmp6Irt/G7j/NgEctUS6IweH1RGhURjjCc2NWtzXFPXXWWfc7hgbXQdtiQu2LGp6MxUg== } + + object-assign@4.1.1: + resolution: + { integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== } + engines: { node: '>=0.10.0' } + + object-keys@1.1.1: + resolution: + { integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== } + engines: { node: '>= 0.4' } + + ollama-ai-provider@1.2.0: + resolution: + { integrity: sha512-jTNFruwe3O/ruJeppI/quoOUxG7NA6blG3ZyQj3lei4+NnJo7bi3eIRWqlVpRlu/mbzbFXeJSBuYQWF6pzGKww== } + engines: { node: '>=18' } + peerDependencies: + zod: ^3.0.0 + peerDependenciesMeta: + zod: + optional: true + + on-change@4.0.2: + resolution: + { integrity: sha512-cMtCyuJmTx/bg2HCpHo3ZLeF7FZnBOapLqZHr2AlLeJ5Ul0Zu2mUJJz051Fdwu/Et2YW04ZD+TtU+gVy0ACNCA== } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + + once@1.4.0: + resolution: + { integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== } + + onetime@6.0.0: + resolution: + { integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== } + engines: { node: '>=12' } + + onetime@7.0.0: + resolution: + { integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ== } + engines: { node: '>=18' } + + oniguruma-parser@0.12.1: + resolution: + { integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w== } + + oniguruma-to-es@4.3.3: + resolution: + { integrity: sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg== } + + openai@4.104.0: + resolution: + { integrity: sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA== } + hasBin: true + peerDependencies: + ws: ^8.18.0 + zod: ^3.23.8 + peerDependenciesMeta: + ws: + optional: true + zod: + optional: true + + openapi-types@12.1.3: + resolution: + { integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw== } + + opener@1.5.2: + resolution: + { integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== } + hasBin: true + + option@0.2.4: + resolution: + { integrity: sha512-pkEqbDyl8ou5cpq+VsnQbe/WlEy5qS7xPzMS1U55OCG9KPvwFD46zDbxQIj3egJSFc3D+XhYOPUzz49zQAVy7A== } + + p-cancelable@2.1.1: + resolution: + { integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== } + engines: { node: '>=8' } + + p-finally@1.0.0: + resolution: + { integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== } + engines: { node: '>=4' } + + p-is-promise@3.0.0: + resolution: + { integrity: sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ== } + engines: { node: '>=8' } + + p-limit@4.0.0: + resolution: + { integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ== } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + + p-locate@6.0.0: + resolution: + { integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw== } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + + p-queue@6.6.2: + resolution: + { integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ== } + engines: { node: '>=8' } + + p-retry@4.6.2: + resolution: + { integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ== } + engines: { node: '>=8' } + + p-timeout@3.2.0: + resolution: + { integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== } + engines: { node: '>=8' } + + package-json-from-dist@1.0.1: + resolution: + { integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== } + + package-manager-detector@1.3.0: + resolution: + { integrity: sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ== } + + pako@0.2.9: + resolution: + { integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA== } + + pako@1.0.11: + resolution: + { integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== } + + parent-module@1.0.1: + resolution: + { integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== } + engines: { node: '>=6' } + + parse-entities@4.0.2: + resolution: + { integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw== } + + parse-json@5.2.0: + resolution: + { integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== } + engines: { node: '>=8' } + + parse5@7.3.0: + resolution: + { integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw== } + + partial-json@0.1.7: + resolution: + { integrity: sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA== } + + path-data-parser@0.1.0: + resolution: + { integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w== } + + path-exists@5.0.0: + resolution: + { integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ== } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + + path-is-absolute@1.0.1: + resolution: + { integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== } + engines: { node: '>=0.10.0' } + + path-key@3.1.1: + resolution: + { integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== } + engines: { node: '>=8' } + + path-key@4.0.0: + resolution: + { integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== } + engines: { node: '>=12' } + + path-parse@1.0.7: + resolution: + { integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== } + + path-scurry@1.11.1: + resolution: + { integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== } + engines: { node: '>=16 || 14 >=14.18' } + + path-type@4.0.0: + resolution: + { integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== } + engines: { node: '>=8' } + + path@0.12.7: + resolution: + { integrity: sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q== } + + pathe@2.0.3: + resolution: + { integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== } + + pdf2md-js@1.0.8: + resolution: + { integrity: sha512-cjKv46RzWmUNCt0mOgr/HGOJJl1fJmNOaldx+tdwlxTiZojZVBkGwI27HLPbms5zH+XRdJiCwjynnZZuKQygvQ== } + engines: { node: '>=18' } + + pend@1.2.0: + resolution: + { integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== } + + picocolors@1.1.1: + resolution: + { integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== } + + picomatch@2.3.1: + resolution: + { integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== } + engines: { node: '>=8.6' } + + pidtree@0.6.0: + resolution: + { integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g== } + engines: { node: '>=0.10' } + hasBin: true + + pinkie-promise@2.0.1: + resolution: + { integrity: sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw== } + engines: { node: '>=0.10.0' } + + pinkie@2.0.4: + resolution: + { integrity: sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg== } + engines: { node: '>=0.10.0' } + + pkg-fetch@3.4.2: + resolution: + { integrity: sha512-0+uijmzYcnhC0hStDjm/cl2VYdrmVVBpe7Q8k9YBojxmR5tG8mvR9/nooQq3QSXiQqORDVOTY3XqMEqJVIzkHA== } + hasBin: true + + pkg-types@1.3.1: + resolution: + { integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ== } + + pkg-types@2.1.0: + resolution: + { integrity: sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A== } + + pkg@5.8.1: + resolution: + { integrity: sha512-CjBWtFStCfIiT4Bde9QpJy0KeH19jCfwZRJqHFDFXfhUklCx8JoFmMj3wgnEYIwGmZVNkhsStPHEOnrtrQhEXA== } + hasBin: true + peerDependencies: + node-notifier: '>=9.0.1' + peerDependenciesMeta: + node-notifier: + optional: true + + plist@3.1.0: + resolution: + { integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ== } + engines: { node: '>=10.4.0' } + + points-on-curve@0.2.0: + resolution: + { integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A== } + + points-on-path@0.2.1: + resolution: + { integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g== } + + polished@4.3.1: + resolution: + { integrity: sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA== } + engines: { node: '>=10' } + + postcss@8.4.31: + resolution: + { integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== } + engines: { node: ^10 || ^12 || >=14 } + + prebuild-install@7.1.1: + resolution: + { integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw== } + engines: { node: '>=10' } + hasBin: true + + prisma@6.9.0: + resolution: + { integrity: sha512-resJAwMyZREC/I40LF6FZ6rZTnlrlrYrb63oW37Gq+U+9xHwbyMSPJjKtM7VZf3gTO86t/Oyz+YeSXr3CmAY1Q== } + engines: { node: '>=18.18' } + hasBin: true + peerDependencies: + typescript: '>=5.1.0' + peerDependenciesMeta: + typescript: + optional: true + + process-nextick-args@1.0.7: + resolution: + { integrity: sha512-yN0WQmuCX63LP/TMvAg31nvT6m4vDqJEiiv2CAZqWOGNWutc9DfDk1NPYYmKUFmaVM2UwDowH4u5AHWYP/jxKw== } + + process-nextick-args@2.0.1: + resolution: + { integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== } + + process@0.11.10: + resolution: + { integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== } + engines: { node: '>= 0.6.0' } + + progress@1.1.8: + resolution: + { integrity: sha512-UdA8mJ4weIkUBO224tIarHzuHs4HuYiJvsuGT7j/SPQiUJVjYvNDBIPa0hAorduOfjGohB/qHWRa/lrrWX/mXw== } + engines: { node: '>=0.4.0' } + + progress@2.0.3: + resolution: + { integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== } + engines: { node: '>=0.4.0' } + + promise-retry@2.0.1: + resolution: + { integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== } + engines: { node: '>=10' } + + prop-types@15.8.1: + resolution: + { integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== } + + property-information@6.5.0: + resolution: + { integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig== } + + property-information@7.1.0: + resolution: + { integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ== } + + proxy-from-env@1.1.0: + resolution: + { integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== } + + pump@3.0.2: + resolution: + { integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw== } + + punycode@1.3.2: + resolution: + { integrity: sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw== } + + punycode@2.3.1: + resolution: + { integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== } + engines: { node: '>=6' } + + q@1.4.1: + resolution: + { integrity: sha512-/CdEdaw49VZVmyIDGUQKDDT53c7qBkO6g5CefWz91Ae+l4+cRtcDYwMTXh6me4O8TMldeGHG3N2Bl84V78Ywbg== } + engines: { node: '>=0.6.0', teleport: '>=0.2.0' } + deprecated: |- + You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other. + + (For a CapTP with native promises, see @endo/eventual-send and @endo/captp) + + qs@6.1.2: + resolution: + { integrity: sha512-vkyEo9cSlcgr1xj5n14ykoPWKE36R8wkxK2fQkbGACZNv4zDGFw/juEwFFUs9/APU7DaTMRlRNTYISLPD0Z4Qw== } + engines: { node: '>=0.6' } + + quansync@0.2.10: + resolution: + { integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A== } + + query-string@9.2.0: + resolution: + { integrity: sha512-YIRhrHujoQxhexwRLxfy3VSjOXmvZRd2nyw1PwL1UUqZ/ys1dEZd1+NSgXkne2l/4X/7OXkigEAuhTX0g/ivJQ== } + engines: { node: '>=18' } + + querystring@0.2.0: + resolution: + { integrity: sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g== } + engines: { node: '>=0.4.x' } + deprecated: The querystring API is considered Legacy. new code should use the URLSearchParams API instead. + + queue-microtask@1.2.3: + resolution: + { integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== } + + quick-lru@5.1.1: + resolution: + { integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== } + engines: { node: '>=10' } + + rc-cascader@3.34.0: + resolution: + { integrity: sha512-KpXypcvju9ptjW9FaN2NFcA2QH9E9LHKq169Y0eWtH4e/wHQ5Wh5qZakAgvb8EKZ736WZ3B0zLLOBsrsja5Dag== } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-checkbox@3.5.0: + resolution: + { integrity: sha512-aOAQc3E98HteIIsSqm6Xk2FPKIER6+5vyEFMZfo73TqM+VVAIqOkHoPjgKLqSNtVLWScoaM7vY2ZrGEheI79yg== } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-collapse@3.9.0: + resolution: + { integrity: sha512-swDdz4QZ4dFTo4RAUMLL50qP0EY62N2kvmk2We5xYdRwcRn8WcYtuetCJpwpaCbUfUt5+huLpVxhvmnK+PHrkA== } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-collapse@4.0.0: + resolution: + { integrity: sha512-SwoOByE39/3oIokDs/BnkqI+ltwirZbP8HZdq1/3SkPSBi7xDdvWHTp7cpNI9ullozkR6mwTWQi6/E/9huQVrA== } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-dialog@9.6.0: + resolution: + { integrity: sha512-ApoVi9Z8PaCQg6FsUzS8yvBEQy0ZL2PkuvAgrmohPkN3okps5WZ5WQWPc1RNuiOKaAYv8B97ACdsFU5LizzCqg== } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-drawer@7.3.0: + resolution: + { integrity: sha512-DX6CIgiBWNpJIMGFO8BAISFkxiuKitoizooj4BDyee8/SnBn0zwO2FHrNDpqqepj0E/TFTDpmEBCyFuTgC7MOg== } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-dropdown@4.2.1: + resolution: + { integrity: sha512-YDAlXsPv3I1n42dv1JpdM7wJ+gSUBfeyPK59ZpBD9jQhK9jVuxpjj3NmWQHOBceA1zEPVX84T2wbdb2SD0UjmA== } + peerDependencies: + react: '>=16.11.0' + react-dom: '>=16.11.0' + + rc-field-form@2.7.0: + resolution: + { integrity: sha512-hgKsCay2taxzVnBPZl+1n4ZondsV78G++XVsMIJCAoioMjlMQR9YwAp7JZDIECzIu2Z66R+f4SFIRrO2DjDNAA== } + engines: { node: '>=8.x' } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-footer@0.6.8: + resolution: + { integrity: sha512-JBZ+xcb6kkex8XnBd4VHw1ZxjV6kmcwUumSHaIFdka2qzMCo7Klcy4sI6G0XtUpG/vtpislQCc+S9Bc+NLHYMg== } + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + rc-image@7.12.0: + resolution: + { integrity: sha512-cZ3HTyyckPnNnUb9/DRqduqzLfrQRyi+CdHjdqgsyDpI3Ln5UX1kXnAhPBSJj9pVRzwRFgqkN7p9b6HBDjmu/Q== } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-input-number@9.5.0: + resolution: + { integrity: sha512-bKaEvB5tHebUURAEXw35LDcnRZLq3x1k7GxfAqBMzmpHkDGzjAtnUL8y4y5N15rIFIg5IJgwr211jInl3cipag== } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-input@1.8.0: + resolution: + { integrity: sha512-KXvaTbX+7ha8a/k+eg6SYRVERK0NddX8QX7a7AnRvUa/rEH0CNMlpcBzBkhI0wp2C8C4HlMoYl8TImSN+fuHKA== } + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + rc-mentions@2.20.0: + resolution: + { integrity: sha512-w8HCMZEh3f0nR8ZEd466ATqmXFCMGMN5UFCzEUL0bM/nGw/wOS2GgRzKBcm19K++jDyuWCOJOdgcKGXU3fXfbQ== } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-menu@9.16.1: + resolution: + { integrity: sha512-ghHx6/6Dvp+fw8CJhDUHFHDJ84hJE3BXNCzSgLdmNiFErWSOaZNsihDAsKq9ByTALo/xkNIwtDFGIl6r+RPXBg== } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-motion@2.9.5: + resolution: + { integrity: sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA== } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-notification@5.6.4: + resolution: + { integrity: sha512-KcS4O6B4qzM3KH7lkwOB7ooLPZ4b6J+VMmQgT51VZCeEcmghdeR4IrMcFq0LG+RPdnbe/ArT086tGM8Snimgiw== } + engines: { node: '>=8.x' } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-overflow@1.4.1: + resolution: + { integrity: sha512-3MoPQQPV1uKyOMVNd6SZfONi+f3st0r8PksexIdBTeIYbMX0Jr+k7pHEDvsXtR4BpCv90/Pv2MovVNhktKrwvw== } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-pagination@5.1.0: + resolution: + { integrity: sha512-8416Yip/+eclTFdHXLKTxZvn70duYVGTvUUWbckCCZoIl3jagqke3GLsFrMs0bsQBikiYpZLD9206Ej4SOdOXQ== } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-picker@4.11.3: + resolution: + { integrity: sha512-MJ5teb7FlNE0NFHTncxXQ62Y5lytq6sh5nUw0iH8OkHL/TjARSEvSHpr940pWgjGANpjCwyMdvsEV55l5tYNSg== } + engines: { node: '>=8.x' } + peerDependencies: + date-fns: '>= 2.x' + dayjs: '>= 1.x' + luxon: '>= 3.x' + moment: '>= 2.x' + react: '>=16.9.0' + react-dom: '>=16.9.0' + peerDependenciesMeta: + date-fns: + optional: true + dayjs: + optional: true + luxon: + optional: true + moment: + optional: true + + rc-progress@4.0.0: + resolution: + { integrity: sha512-oofVMMafOCokIUIBnZLNcOZFsABaUw8PPrf1/y0ZBvKZNpOiu5h4AO9vv11Sw0p4Hb3D0yGWuEattcQGtNJ/aw== } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-rate@2.13.1: + resolution: + { integrity: sha512-QUhQ9ivQ8Gy7mtMZPAjLbxBt5y9GRp65VcUyGUMF3N3fhiftivPHdpuDIaWIMOTEprAjZPC08bls1dQB+I1F2Q== } + engines: { node: '>=8.x' } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-resize-observer@1.4.3: + resolution: + { integrity: sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ== } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-segmented@2.7.0: + resolution: + { integrity: sha512-liijAjXz+KnTRVnxxXG2sYDGd6iLL7VpGGdR8gwoxAXy2KglviKCxLWZdjKYJzYzGSUwKDSTdYk8brj54Bn5BA== } + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + rc-select@14.16.8: + resolution: + { integrity: sha512-NOV5BZa1wZrsdkKaiK7LHRuo5ZjZYMDxPP6/1+09+FB4KoNi8jcG1ZqLE3AVCxEsYMBe65OBx71wFoHRTP3LRg== } + engines: { node: '>=8.x' } + peerDependencies: + react: '*' + react-dom: '*' + + rc-slider@11.1.8: + resolution: + { integrity: sha512-2gg/72YFSpKP+Ja5AjC5DPL1YnV8DEITDQrcc1eASrUYjl0esptaBVJBh5nLTXCCp15eD8EuGjwezVGSHhs9tQ== } + engines: { node: '>=8.x' } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-steps@6.0.1: + resolution: + { integrity: sha512-lKHL+Sny0SeHkQKKDJlAjV5oZ8DwCdS2hFhAkIjuQt1/pB81M0cA0ErVFdHq9+jmPmFw1vJB2F5NBzFXLJxV+g== } + engines: { node: '>=8.x' } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-switch@4.1.0: + resolution: + { integrity: sha512-TI8ufP2Az9oEbvyCeVE4+90PDSljGyuwix3fV58p7HV2o4wBnVToEyomJRVyTaZeqNPAp+vqeo4Wnj5u0ZZQBg== } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-table@7.51.0: + resolution: + { integrity: sha512-7ZlvW6lB0IDKaSFInD6OfJsCepSJJtfsQv2PZLtzEeZd/PLzQnKliXPaoZqkqDdLdJ3jxE2x4sane4DjxcAg+g== } + engines: { node: '>=8.x' } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-tabs@15.6.1: + resolution: + { integrity: sha512-/HzDV1VqOsUWyuC0c6AkxVYFjvx9+rFPKZ32ejxX0Uc7QCzcEjTA9/xMgv4HemPKwzBNX8KhGVbbumDjnj92aA== } + engines: { node: '>=8.x' } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-textarea@1.10.0: + resolution: + { integrity: sha512-ai9IkanNuyBS4x6sOL8qu/Ld40e6cEs6pgk93R+XLYg0mDSjNBGey6/ZpDs5+gNLD7urQ14po3V6Ck2dJLt9SA== } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-tooltip@6.4.0: + resolution: + { integrity: sha512-kqyivim5cp8I5RkHmpsp1Nn/Wk+1oeloMv9c7LXNgDxUpGm+RbXJGL+OPvDlcRnx9DBeOe4wyOIl4OKUERyH1g== } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-tree-select@5.27.0: + resolution: + { integrity: sha512-2qTBTzwIT7LRI1o7zLyrCzmo5tQanmyGbSaGTIf7sYimCklAToVVfpMC6OAldSKolcnjorBYPNSKQqJmN3TCww== } + peerDependencies: + react: '*' + react-dom: '*' + + rc-tree@5.13.1: + resolution: + { integrity: sha512-FNhIefhftobCdUJshO7M8uZTA9F4OPGVXqGfZkkD/5soDeOhwO06T/aKTrg0WD8gRg/pyfq+ql3aMymLHCTC4A== } + engines: { node: '>=10.x' } + peerDependencies: + react: '*' + react-dom: '*' + + rc-upload@4.9.2: + resolution: + { integrity: sha512-nHx+9rbd1FKMiMRYsqQ3NkXUv7COHPBo3X1Obwq9SWS6/diF/A0aJ5OHubvwUAIDs+4RMleljV0pcrNUc823GQ== } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-util@5.44.4: + resolution: + { integrity: sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w== } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-virtual-list@3.18.6: + resolution: + { integrity: sha512-TQ5SsutL3McvWmmxqQtMIbfeoE3dGjJrRSfKekgby7WQMpPIFvv4ghytp5Z0s3D8Nik9i9YNOCqHBfk86AwgAA== } + engines: { node: '>=8.x' } + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc@1.2.8: + resolution: + { integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== } + hasBin: true + + re-resizable@6.11.2: + resolution: + { integrity: sha512-2xI2P3OHs5qw7K0Ud1aLILK6MQxW50TcO+DetD9eIV58j84TqYeHoZcL9H4GXFXXIh7afhH8mv5iUCXII7OW7A== } + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + react-avatar-editor@13.0.2: + resolution: + { integrity: sha512-a4ajbi7lwDh98kgEtSEeKMu0vs0CHTczkq4Xcxr1EiwMFH1GlgHCEtwGU8q/H5W8SeLnH4KPK8LUjEEaZXklxQ== } + peerDependencies: + react: ^0.14.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^0.14.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + + react-colorful@5.6.1: + resolution: + { integrity: sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw== } + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + react-dom@18.3.1: + resolution: + { integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== } + peerDependencies: + react: ^18.3.1 + + react-draggable@4.4.6: + resolution: + { integrity: sha512-LtY5Xw1zTPqHkVmtM3X8MUOxNDOUhv/khTgBgrUvwaS064bwVvxT+q5El0uUFNx5IEPKXuRejr7UqLwBIg5pdw== } + peerDependencies: + react: '>= 16.3.0' + react-dom: '>= 16.3.0' + + react-dropzone@12.1.0: + resolution: + { integrity: sha512-iBYHA1rbopIvtzokEX4QubO6qk5IF/x3BtKGu74rF2JkQDXnwC4uO/lHKpaw4PJIV6iIAYOlwLv2FpiGyqHNog== } + engines: { node: '>= 10.13' } + peerDependencies: + react: '>= 16.8' + + react-error-boundary@5.0.0: + resolution: + { integrity: sha512-tnjAxG+IkpLephNcePNA7v6F/QpWLH8He65+DmedchDwg162JZqx4NmbXj0mlAYVVEd81OW7aFhmbsScYfiAFQ== } + peerDependencies: + react: '>=16.13.1' + + react-fast-compare@3.2.2: + resolution: + { integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ== } + + react-hotkeys-hook@5.1.0: + resolution: + { integrity: sha512-GCNGXjBzV9buOS3REoQFmSmE4WTvBhYQ0YrAeeMZI83bhXg3dRWsLHXDutcVDdEjwJqJCxk5iewWYX5LtFUd7g== } + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + react-i18next@15.5.2: + resolution: + { integrity: sha512-ePODyXgmZQAOYTbZXQn5rRsSBu3Gszo69jxW6aKmlSgxKAI1fOhDwSu6bT4EKHciWPKQ7v7lPrjeiadR6Gi+1A== } + peerDependencies: + i18next: '>= 23.2.3' + react: '>= 16.8.0' + react-dom: '*' + react-native: '*' + typescript: ^5 + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + typescript: + optional: true + + react-is@16.13.1: + resolution: + { integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== } + + react-is@18.3.1: + resolution: + { integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== } + + react-is@19.1.0: + resolution: + { integrity: sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg== } + + react-layout-kit@1.9.1: + resolution: + { integrity: sha512-tQO5J+Ajppu2JCdhgFaFbWCg01WJXXaQ5vg8cxzsv8vVeogJKGFgoJm9OI2saDFchfKP3RABd+aRY5vB++poqw== } + peerDependencies: + react: '>=18' + + react-markdown@10.1.0: + resolution: + { integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ== } + peerDependencies: + '@types/react': '>=18' + react: '>=18' + + react-merge-refs@3.0.2: + resolution: + { integrity: sha512-MSZAfwFfdbEvwkKWP5EI5chuLYnNUxNS7vyS0i1Jp+wtd8J4Ga2ddzhaE68aMol2Z4vCnRM/oGOo1a3V75UPlw== } + peerDependencies: + react: '>=16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0' + peerDependenciesMeta: + react: + optional: true + + react-redux@9.2.0: + resolution: + { integrity: sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g== } + peerDependencies: + '@types/react': ^18.2.25 || ^19 + react: ^18.0 || ^19 + redux: ^5.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + redux: + optional: true + + react-rnd@10.5.2: + resolution: + { integrity: sha512-0Tm4x7k7pfHf2snewJA8x7Nwgt3LV+58MVEWOVsFjk51eYruFEa6Wy7BNdxt4/lH0wIRsu7Gm3KjSXY2w7YaNw== } + peerDependencies: + react: '>=16.3.0' + react-dom: '>=16.3.0' + + react-transition-group@4.4.5: + resolution: + { integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g== } + peerDependencies: + react: '>=16.6.0' + react-dom: '>=16.6.0' + + react-zoom-pan-pinch@3.7.0: + resolution: + { integrity: sha512-UmReVZ0TxlKzxSbYiAj+LeGRW8s8LraAFTXRAxzMYnNRgGPsxCudwZKVkjvGmjtx7SW/hZamt69NUmGf4xrkXA== } + engines: { node: '>=8', npm: '>=5' } + peerDependencies: + react: '*' + react-dom: '*' + + react@18.3.1: + resolution: + { integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== } + engines: { node: '>=0.10.0' } + + read-config-file@6.3.2: + resolution: + { integrity: sha512-M80lpCjnE6Wt6zb98DoW8WHR09nzMSpu8XHtPkiTHrJ5Az9CybfeQhTJ8D7saeBHpGhLPIVyA8lcL6ZmdKwY6Q== } + engines: { node: '>=12.0.0' } + + readable-stream@2.0.6: + resolution: + { integrity: sha512-TXcFfb63BQe1+ySzsHZI/5v1aJPCShfqvWJ64ayNImXMsN1Cd0YGk/wm8KB7/OeessgPc9QvS9Zou8QTkFzsLw== } + + readable-stream@2.3.8: + resolution: + { integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== } + + readable-stream@3.6.2: + resolution: + { integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== } + engines: { node: '>= 6' } + + readdir-glob@1.1.3: + resolution: + { integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA== } + + recharts@3.6.0: + resolution: + { integrity: sha512-L5bjxvQRAe26RlToBAziKUB7whaGKEwD3znoM6fz3DrTowCIC/FnJYnuq1GEzB8Zv2kdTfaxQfi5GoH0tBinyg== } + engines: { node: '>=18' } + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-is: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + recma-build-jsx@1.0.0: + resolution: + { integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew== } + + recma-jsx@1.0.0: + resolution: + { integrity: sha512-5vwkv65qWwYxg+Atz95acp8DMu1JDSqdGkA2Of1j6rCreyFUE/gp15fC8MnGEuG1W68UKjM6x6+YTWIh7hZM/Q== } + + recma-parse@1.0.0: + resolution: + { integrity: sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ== } + + recma-stringify@1.0.0: + resolution: + { integrity: sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g== } + + redux-thunk@3.1.0: + resolution: + { integrity: sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw== } + peerDependencies: + redux: ^5.0.0 + + redux@5.0.1: + resolution: + { integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w== } + + regex-recursion@6.0.2: + resolution: + { integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg== } + + regex-utilities@2.3.0: + resolution: + { integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng== } + + regex@6.0.1: + resolution: + { integrity: sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA== } + + rehype-katex@7.0.1: + resolution: + { integrity: sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA== } + + rehype-raw@7.0.0: + resolution: + { integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww== } + + rehype-recma@1.0.0: + resolution: + { integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw== } + + remark-breaks@4.0.0: + resolution: + { integrity: sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ== } + + remark-gfm@4.0.1: + resolution: + { integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg== } + + remark-math@6.0.0: + resolution: + { integrity: sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA== } + + remark-mdx@3.1.0: + resolution: + { integrity: sha512-Ngl/H3YXyBV9RcRNdlYsZujAmhsxwzxpDzpDEhFBVAGthS4GDgnctpDjgFl/ULx5UEDzqtW1cyBSNKqYYrqLBA== } + + remark-parse@11.0.0: + resolution: + { integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA== } + + remark-rehype@11.1.2: + resolution: + { integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw== } + + remark-stringify@11.0.0: + resolution: + { integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw== } + + request@2.72.0: + resolution: + { integrity: sha512-rQiQ3Eza3HNC+gBlzKxXaPwG1rQIcO0/7TKGIgA9D/obvFK//H+pzkCS4CctQ7aFk6LboTvyFXHMEdf8P4pSxg== } + engines: { node: '>=0.8.0' } + deprecated: request has been deprecated, see https://github.com/request/request/issues/3142 + + require-directory@2.1.1: + resolution: + { integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== } + engines: { node: '>=0.10.0' } + + require-from-string@2.0.2: + resolution: + { integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== } + engines: { node: '>=0.10.0' } + + reselect@5.1.1: + resolution: + { integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w== } + + resize-observer-polyfill@1.5.1: + resolution: + { integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== } + + resolve-alpn@1.2.1: + resolution: + { integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== } + + resolve-from@4.0.0: + resolution: + { integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== } + engines: { node: '>=4' } + + resolve-from@5.0.0: + resolution: + { integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== } + engines: { node: '>=8' } + + resolve@1.22.10: + resolution: + { integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== } + engines: { node: '>= 0.4' } + hasBin: true + + responselike@2.0.1: + resolution: + { integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw== } + + restore-cursor@5.1.0: + resolution: + { integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA== } + engines: { node: '>=18' } + + retry@0.12.0: + resolution: + { integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== } + engines: { node: '>= 4' } + + retry@0.13.1: + resolution: + { integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== } + engines: { node: '>= 4' } + + reusify@1.1.0: + resolution: + { integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw== } + engines: { iojs: '>=1.0.0', node: '>=0.10.0' } + + rfdc@1.4.1: + resolution: + { integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== } + + rimraf@3.0.2: + resolution: + { integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== } + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + roarr@2.15.4: + resolution: + { integrity: sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A== } + engines: { node: '>=8.0' } + + robust-predicates@3.0.2: + resolution: + { integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg== } + + roughjs@4.6.6: + resolution: + { integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ== } + + run-parallel@1.2.0: + resolution: + { integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== } + + rw@1.3.3: + resolution: + { integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ== } + + rxjs@7.8.2: + resolution: + { integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA== } + + safe-buffer@5.1.2: + resolution: + { integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== } + + safe-buffer@5.2.1: + resolution: + { integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== } + + safer-buffer@2.1.2: + resolution: + { integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== } + + sanitize-filename@1.6.3: + resolution: + { integrity: sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg== } + + sax@1.4.1: + resolution: + { integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== } + + scheduler@0.23.2: + resolution: + { integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ== } + + screenfull@5.2.0: + resolution: + { integrity: sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA== } + engines: { node: '>=0.10.0' } + + scroll-into-view-if-needed@3.1.0: + resolution: + { integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ== } + + secure-json-parse@2.7.0: + resolution: + { integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw== } + + semver-compare@1.0.0: + resolution: + { integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow== } + + semver@6.3.1: + resolution: + { integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== } + hasBin: true + + semver@7.7.2: + resolution: + { integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== } + engines: { node: '>=10' } + hasBin: true + + serialize-error@7.0.1: + resolution: + { integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw== } + engines: { node: '>=10' } + + set-blocking@2.0.0: + resolution: + { integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== } + + set-value@2.0.1: + resolution: + { integrity: sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== } + engines: { node: '>=0.10.0' } + + setimmediate@1.0.5: + resolution: + { integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== } + + sharp@0.33.5: + resolution: + { integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw== } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + + shebang-command@2.0.0: + resolution: + { integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== } + engines: { node: '>=8' } + + shebang-regex@3.0.0: + resolution: + { integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== } + engines: { node: '>=8' } + + shell-quote@1.8.3: + resolution: + { integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw== } + engines: { node: '>= 0.4' } + + shiki@3.6.0: + resolution: + { integrity: sha512-tKn/Y0MGBTffQoklaATXmTqDU02zx8NYBGQ+F6gy87/YjKbizcLd+Cybh/0ZtOBX9r1NEnAy/GTRDKtOsc1L9w== } + + signal-exit@3.0.7: + resolution: + { integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== } + + signal-exit@4.1.0: + resolution: + { integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== } + engines: { node: '>=14' } + + simple-concat@1.0.1: + resolution: + { integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== } + + simple-get@3.1.1: + resolution: + { integrity: sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA== } + + simple-get@4.0.1: + resolution: + { integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== } + + simple-swizzle@0.2.2: + resolution: + { integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== } + + simple-update-notifier@2.0.0: + resolution: + { integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w== } + engines: { node: '>=10' } + + simple-wcswidth@1.0.1: + resolution: + { integrity: sha512-xMO/8eNREtaROt7tJvWJqHBDTMFN4eiQ5I4JRMuilwfnFcV5W9u7RUkueNkdw0jPqGMX36iCywelS5yilTuOxg== } + + slash@3.0.0: + resolution: + { integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== } + engines: { node: '>=8' } + + slice-ansi@3.0.0: + resolution: + { integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== } + engines: { node: '>=8' } + + slice-ansi@5.0.0: + resolution: + { integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ== } + engines: { node: '>=12' } + + slice-ansi@7.1.0: + resolution: + { integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg== } + engines: { node: '>=18' } + + smart-buffer@4.2.0: + resolution: + { integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== } + engines: { node: '>= 6.0.0', npm: '>= 3.0.0' } + + sntp@1.0.9: + resolution: + { integrity: sha512-7bgVOAnPj3XjrKY577S+puCKGCRlUrcrEdsMeRXlg9Ghf5df/xNi6sONUa43WrHUd3TjJBF7O04jYoiY0FVa0A== } + engines: { node: '>=0.8.0' } + deprecated: This module moved to @hapi/sntp. Please make sure to switch over as this distribution is no longer supported and may contain bugs and critical security issues. + + sonner@2.0.5: + resolution: + { integrity: sha512-YwbHQO6cSso3HBXlbCkgrgzDNIhws14r4MO87Ofy+cV2X7ES4pOoAK3+veSmVTvqNx1BWUxlhPmZzP00Crk2aQ== } + peerDependencies: + react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + + source-map-js@1.2.1: + resolution: + { integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== } + engines: { node: '>=0.10.0' } + + source-map-support@0.5.21: + resolution: + { integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== } + + source-map@0.5.7: + resolution: + { integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== } + engines: { node: '>=0.10.0' } + + source-map@0.6.1: + resolution: + { integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== } + engines: { node: '>=0.10.0' } + + source-map@0.7.4: + resolution: + { integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== } + engines: { node: '>= 8' } + + space-separated-tokens@2.0.2: + resolution: + { integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q== } + + spawn-command@0.0.2: + resolution: + { integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ== } + + split-on-first@3.0.0: + resolution: + { integrity: sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA== } + engines: { node: '>=12' } + + split-string@3.1.0: + resolution: + { integrity: sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== } + engines: { node: '>=0.10.0' } + + split2@4.2.0: + resolution: + { integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg== } + engines: { node: '>= 10.x' } + + sprintf-js@1.0.3: + resolution: + { integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== } + + sprintf-js@1.1.3: + resolution: + { integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== } + + ssf@0.11.2: + resolution: + { integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g== } + engines: { node: '>=0.8' } + + sshpk@1.18.0: + resolution: + { integrity: sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ== } + engines: { node: '>=0.10.0' } + hasBin: true + + stat-mode@1.0.0: + resolution: + { integrity: sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg== } + engines: { node: '>= 6' } + + stream-meter@1.0.4: + resolution: + { integrity: sha512-4sOEtrbgFotXwnEuzzsQBYEV1elAeFSO8rSGeTwabuX1RRn/kEq9JVH7I0MRBhKVRR0sJkr0M0QCH7yOLf9fhQ== } + + streamsearch@1.1.0: + resolution: + { integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== } + engines: { node: '>=10.0.0' } + + string-argv@0.3.2: + resolution: + { integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== } + engines: { node: '>=0.6.19' } + + string-convert@0.2.1: + resolution: + { integrity: sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A== } + + string-width@4.2.3: + resolution: + { integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== } + engines: { node: '>=8' } + + string-width@5.1.2: + resolution: + { integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== } + engines: { node: '>=12' } + + string-width@7.2.0: + resolution: + { integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ== } + engines: { node: '>=18' } + + string_decoder@0.10.31: + resolution: + { integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ== } + + string_decoder@1.1.1: + resolution: + { integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== } + + string_decoder@1.3.0: + resolution: + { integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== } + + stringify-entities@4.0.4: + resolution: + { integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg== } + + stringstream@0.0.6: + resolution: + { integrity: sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA== } + + strip-ansi@3.0.1: + resolution: + { integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg== } + engines: { node: '>=0.10.0' } + + strip-ansi@6.0.1: + resolution: + { integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== } + engines: { node: '>=8' } + + strip-ansi@7.1.0: + resolution: + { integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== } + engines: { node: '>=12' } + + strip-final-newline@3.0.0: + resolution: + { integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== } + engines: { node: '>=12' } + + strip-json-comments@2.0.1: + resolution: + { integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== } + engines: { node: '>=0.10.0' } + + style-to-js@1.1.16: + resolution: + { integrity: sha512-/Q6ld50hKYPH3d/r6nr117TZkHR0w0kGGIVfpG9N6D8NymRPM9RqCUv4pRpJ62E5DqOYx2AFpbZMyCPnjQCnOw== } + + style-to-object@1.0.8: + resolution: + { integrity: sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g== } + + styled-jsx@5.1.1: + resolution: + { integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw== } + engines: { node: '>= 12.0.0' } + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + + stylis@4.2.0: + resolution: + { integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw== } + + stylis@4.3.6: + resolution: + { integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ== } + + sumchecker@3.0.1: + resolution: + { integrity: sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg== } + engines: { node: '>= 8.0' } + + supports-color@2.0.0: + resolution: + { integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g== } + engines: { node: '>=0.8.0' } + + supports-color@7.2.0: + resolution: + { integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== } + engines: { node: '>=8' } + + supports-color@8.1.1: + resolution: + { integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== } + engines: { node: '>=10' } + + supports-preserve-symlinks-flag@1.0.0: + resolution: + { integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== } + engines: { node: '>= 0.4' } + + swr@2.3.3: + resolution: + { integrity: sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A== } + peerDependencies: + react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + tabbable@6.2.0: + resolution: + { integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew== } + + tar-fs@2.1.3: + resolution: + { integrity: sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg== } + + tar-stream@2.2.0: + resolution: + { integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== } + engines: { node: '>=6' } + + tar@6.2.1: + resolution: + { integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== } + engines: { node: '>=10' } + + temp-file@3.4.0: + resolution: + { integrity: sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg== } + + text-extensions@2.4.0: + resolution: + { integrity: sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g== } + engines: { node: '>=8' } + + throttle-debounce@5.0.2: + resolution: + { integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A== } + engines: { node: '>=12.22' } + + throttleit@2.1.0: + resolution: + { integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw== } + engines: { node: '>=18' } + + through@2.3.8: + resolution: + { integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== } + + tiny-invariant@1.3.3: + resolution: + { integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg== } + + tiny-typed-emitter@2.1.0: + resolution: + { integrity: sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA== } + + tinyexec@1.0.1: + resolution: + { integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw== } + + tmp-promise@3.0.3: + resolution: + { integrity: sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ== } + + tmp@0.2.3: + resolution: + { integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w== } + engines: { node: '>=14.14' } + + to-fast-properties@2.0.0: + resolution: + { integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== } + engines: { node: '>=4' } + + to-regex-range@5.0.1: + resolution: + { integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== } + engines: { node: '>=8.0' } + + toggle-selection@1.0.6: + resolution: + { integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ== } + + tough-cookie@2.2.2: + resolution: + { integrity: sha512-Knz9Yr0hlBoWQgUKzOIvRg5adinizAf49i2gHRhj6cLjlM304zRw7uyiY22ADniDxnPHXfIeyQD0EAkgpIz0ow== } + engines: { node: '>=0.10.0' } + deprecated: ReDoS vulnerability parsing Set-Cookie https://nodesecurity.io/advisories/130 + + tr46@0.0.3: + resolution: + { integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== } + + tree-kill@1.2.2: + resolution: + { integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== } + hasBin: true + + trim-lines@3.0.1: + resolution: + { integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg== } + + trough@2.2.0: + resolution: + { integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw== } + + truncate-utf8-bytes@1.0.2: + resolution: + { integrity: sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ== } + + ts-dedent@2.2.0: + resolution: + { integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ== } + engines: { node: '>=6.10' } + + ts-md5@1.3.1: + resolution: + { integrity: sha512-DiwiXfwvcTeZ5wCE0z+2A9EseZsztaiZtGrtSaY5JOD7ekPnR/GoIVD5gXZAlK9Na9Kvpo9Waz5rW64WKAWApg== } + engines: { node: '>=12' } + + tslib@2.6.2: + resolution: + { integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== } + + tslib@2.8.1: + resolution: + { integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== } + + tunnel-agent@0.4.3: + resolution: + { integrity: sha512-e0IoVDWx8SDHc/hwFTqJDQ7CCDTEeGhmcT9jkWJjoGQSpgBz20nAMr80E3Tpk7PatJ1b37DQDgJR3CNSzcMOZQ== } + + tunnel-agent@0.6.0: + resolution: + { integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== } + + turndown@7.2.0: + resolution: + { integrity: sha512-eCZGBN4nNNqM9Owkv9HAtWRYfLA4h909E/WGAWWBpmB275ehNhZyk87/Tpvjbp0jjNl9XwCsbe6bm6CqFsgD+A== } + + tweetnacl@0.14.5: + resolution: + { integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== } + + type-fest@0.13.1: + resolution: + { integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== } + engines: { node: '>=10' } + + typescript@5.8.3: + resolution: + { integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== } + engines: { node: '>=14.17' } + hasBin: true + + ufo@1.6.1: + resolution: + { integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA== } + + underscore@1.13.7: + resolution: + { integrity: sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g== } + + undici-types@5.26.5: + resolution: + { integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== } + + undici-types@6.21.0: + resolution: + { integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== } + + undici-types@7.8.0: + resolution: + { integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw== } + + unicorn-magic@0.1.0: + resolution: + { integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ== } + engines: { node: '>=18' } + + unified@11.0.5: + resolution: + { integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA== } + + unist-util-find-after@5.0.0: + resolution: + { integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ== } + + unist-util-is@6.0.0: + resolution: + { integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw== } + + unist-util-position-from-estree@2.0.0: + resolution: + { integrity: sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ== } + + unist-util-position@5.0.0: + resolution: + { integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA== } + + unist-util-remove-position@5.0.0: + resolution: + { integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q== } + + unist-util-stringify-position@4.0.0: + resolution: + { integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ== } + + unist-util-visit-parents@6.0.1: + resolution: + { integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw== } + + unist-util-visit@5.0.0: + resolution: + { integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg== } + + universalify@0.1.2: + resolution: + { integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== } + engines: { node: '>= 4.0.0' } + + universalify@2.0.1: + resolution: + { integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== } + engines: { node: '>= 10.0.0' } + + unpdf@0.12.2: + resolution: + { integrity: sha512-3eyDFfayk+Sf5+inJ4OyhecR2BtRFEeZqUfGPdq2O8aBLau9MYL9lAP+GEcSAaVd2JWqde8Dnz38z0x7KRglaA== } + + update-browserslist-db@1.1.3: + resolution: + { integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw== } + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: + { integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== } + + url-join@5.0.0: + resolution: + { integrity: sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA== } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + + url@0.11.0: + resolution: + { integrity: sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ== } + + use-isomorphic-layout-effect@1.2.1: + resolution: + { integrity: sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA== } + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + use-merge-value@1.2.0: + resolution: + { integrity: sha512-DXgG0kkgJN45TcyoXL49vJnn55LehnrmoHc7MbKi+QDBvr8dsesqws8UlyIWGHMR+JXgxc1nvY+jDGMlycsUcw== } + peerDependencies: + react: '>= 16.x' + + use-sync-external-store@1.5.0: + resolution: + { integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A== } + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + utf8-byte-length@1.0.5: + resolution: + { integrity: sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA== } + + util-deprecate@1.0.2: + resolution: + { integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== } + + util@0.10.4: + resolution: + { integrity: sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A== } + + uuid@10.0.0: + resolution: + { integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ== } + hasBin: true + + uuid@11.1.0: + resolution: + { integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A== } + hasBin: true + + v8n@1.5.1: + resolution: + { integrity: sha512-LdabyT4OffkyXFCe9UT+uMkxNBs5rcTVuZClvxQr08D5TUgo1OFKkoT65qYRCsiKBl/usHjpXvP4hHMzzDRj3A== } + + verror@1.10.0: + resolution: + { integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== } + engines: { '0': node >=0.6.0 } + + verror@1.10.1: + resolution: + { integrity: sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg== } + engines: { node: '>=0.6.0' } + + vfile-location@5.0.3: + resolution: + { integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg== } + + vfile-message@4.0.2: + resolution: + { integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw== } + + vfile@6.0.3: + resolution: + { integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q== } + + victory-vendor@37.3.6: + resolution: + { integrity: sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ== } + + void-elements@3.1.0: + resolution: + { integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w== } + engines: { node: '>=0.10.0' } + + vscode-jsonrpc@8.2.0: + resolution: + { integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA== } + engines: { node: '>=14.0.0' } + + vscode-languageserver-protocol@3.17.5: + resolution: + { integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg== } + + vscode-languageserver-textdocument@1.0.12: + resolution: + { integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA== } + + vscode-languageserver-types@3.17.5: + resolution: + { integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg== } + + vscode-languageserver@9.0.1: + resolution: + { integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g== } + hasBin: true + + vscode-uri@3.0.8: + resolution: + { integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw== } + + wait-on@7.2.0: + resolution: + { integrity: sha512-wCQcHkRazgjG5XoAq9jbTMLpNIjoSlZslrJ2+N9MxDsGEv1HnFoVjOCexL0ESva7Y9cu350j+DWADdk54s4AFQ== } + engines: { node: '>=12.0.0' } + hasBin: true + + web-namespaces@2.0.1: + resolution: + { integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ== } + + web-streams-polyfill@4.0.0-beta.3: + resolution: + { integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug== } + engines: { node: '>= 14' } + + webidl-conversions@3.0.1: + resolution: + { integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== } + + whatwg-url@5.0.0: + resolution: + { integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== } + + which@2.0.2: + resolution: + { integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== } + engines: { node: '>= 8' } + hasBin: true + + wide-align@1.1.5: + resolution: + { integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== } + + wmf@1.0.2: + resolution: + { integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw== } + engines: { node: '>=0.8' } + + word@0.3.0: + resolution: + { integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA== } + engines: { node: '>=0.8' } + + wrap-ansi@7.0.0: + resolution: + { integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== } + engines: { node: '>=10' } + + wrap-ansi@8.1.0: + resolution: + { integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== } + engines: { node: '>=12' } + + wrap-ansi@9.0.0: + resolution: + { integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q== } + engines: { node: '>=18' } + + wrappy@1.0.2: + resolution: + { integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== } + + xlsx@0.18.5: + resolution: + { integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ== } + engines: { node: '>=0.8' } + hasBin: true + + xmlbuilder@10.1.1: + resolution: + { integrity: sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg== } + engines: { node: '>=4.0' } + + xmlbuilder@15.1.1: + resolution: + { integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg== } + engines: { node: '>=8.0' } + + xmldom@0.6.0: + resolution: + { integrity: sha512-iAcin401y58LckRZ0TkI4k0VSM1Qg0KGSc3i8rU+xrxe19A/BN1zHyVSJY7uoutVlaTSzYyk/v5AmkewAP7jtg== } + engines: { node: '>=10.0.0' } + + xtend@4.0.2: + resolution: + { integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== } + engines: { node: '>=0.4' } + + y18n@5.0.8: + resolution: + { integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== } + engines: { node: '>=10' } + + yallist@3.1.1: + resolution: + { integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== } + + yallist@4.0.0: + resolution: + { integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== } + + yaml@1.10.2: + resolution: + { integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== } + engines: { node: '>= 6' } + + yaml@2.8.0: + resolution: + { integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ== } + engines: { node: '>= 14.6' } + hasBin: true + + yargs-parser@20.2.9: + resolution: + { integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== } + engines: { node: '>=10' } + + yargs-parser@21.1.1: + resolution: + { integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== } + engines: { node: '>=12' } + + yargs@16.2.0: + resolution: + { integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== } + engines: { node: '>=10' } + + yargs@17.7.2: + resolution: + { integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== } + engines: { node: '>=12' } + + yauzl@2.10.0: + resolution: + { integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== } + + yocto-queue@1.2.1: + resolution: + { integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg== } + engines: { node: '>=12.20' } + + zhipu-ai-provider@0.1.1: + resolution: + { integrity: sha512-cVwvvGtPiQqgsGdBzHCHC5oQ7z6slEQTbXJ5+42gQGX4N5uRUvYj+YYLp7Cr1HPQGF3zR2p8vNbT5etPHD4NbA== } + engines: { node: '>=18' } + peerDependencies: + zod: ^3.0.0 + + zip-stream@4.1.1: + resolution: + { integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ== } + engines: { node: '>= 10' } + + zod-to-json-schema@3.24.5: + resolution: + { integrity: sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g== } + peerDependencies: + zod: ^3.24.1 + + zod@3.25.32: + resolution: + { integrity: sha512-OSm2xTIRfW8CV5/QKgngwmQW/8aPfGdaQFlrGoErlgg/Epm7cjb6K6VEyExfe65a3VybUOnu381edLb0dfJl0g== } + + zod@3.25.76: + resolution: + { integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ== } + + zustand@3.7.2: + resolution: + { integrity: sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA== } + engines: { node: '>=12.7.0' } + peerDependencies: + react: '>=16.8' + peerDependenciesMeta: + react: + optional: true + + zwitch@2.0.4: + resolution: + { integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A== } + +snapshots: + 7zip-bin@5.2.0: {} + + '@ai-sdk/openai-compatible@1.0.22(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.12(zod@3.25.76) + zod: 3.25.76 + + '@ai-sdk/openai@1.3.22(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) + zod: 3.25.76 + + '@ai-sdk/provider-utils@2.1.10(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 1.0.9 + eventsource-parser: 3.0.2 + nanoid: 3.3.11 + secure-json-parse: 2.7.0 + optionalDependencies: + zod: 3.25.76 + + '@ai-sdk/provider-utils@2.2.8(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 1.1.3 + nanoid: 3.3.11 + secure-json-parse: 2.7.0 + zod: 3.25.76 + + '@ai-sdk/provider-utils@3.0.12(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.0 + '@standard-schema/spec': 1.0.0 + eventsource-parser: 3.0.6 + zod: 3.25.76 + + '@ai-sdk/provider@1.0.9': + dependencies: + json-schema: 0.4.0 + + '@ai-sdk/provider@1.1.3': + dependencies: + json-schema: 0.4.0 + + '@ai-sdk/provider@2.0.0': + dependencies: + json-schema: 0.4.0 + + '@ai-sdk/react@1.2.12(react@18.3.1)(zod@3.25.76)': + dependencies: + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) + '@ai-sdk/ui-utils': 1.2.11(zod@3.25.76) + react: 18.3.1 + swr: 2.3.3(react@18.3.1) + throttleit: 2.1.0 + optionalDependencies: + zod: 3.25.76 + + '@ai-sdk/ui-utils@1.2.11(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) + zod: 3.25.76 + zod-to-json-schema: 3.24.5(zod@3.25.76) + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + + '@ant-design/colors@7.2.1': + dependencies: + '@ant-design/fast-color': 2.0.6 + + '@ant-design/cssinjs-utils@1.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@ant-design/cssinjs': 1.23.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@babel/runtime': 7.27.6 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@ant-design/cssinjs@1.23.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + '@emotion/hash': 0.8.0 + '@emotion/unitless': 0.7.5 + classnames: 2.5.1 + csstype: 3.1.3 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + stylis: 4.3.6 + + '@ant-design/fast-color@2.0.6': + dependencies: + '@babel/runtime': 7.27.6 + + '@ant-design/icons-svg@4.4.2': {} + + '@ant-design/icons@5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@ant-design/colors': 7.2.1 + '@ant-design/icons-svg': 4.4.2 + '@babel/runtime': 7.27.6 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@ant-design/react-slick@1.1.2(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + classnames: 2.5.1 + json2mq: 0.2.0 + react: 18.3.1 + resize-observer-polyfill: 1.5.1 + throttle-debounce: 5.0.2 + + '@antfu/install-pkg@1.1.0': + dependencies: + package-manager-detector: 1.3.0 + tinyexec: 1.0.1 + + '@antfu/utils@8.1.1': {} + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.27.5': {} + + '@babel/core@7.27.4': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.27.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.4) + '@babel/helpers': 7.27.6 + '@babel/parser': 7.27.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.27.4 + '@babel/types': 7.27.6 + convert-source-map: 2.0.0 + debug: 4.4.1 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.18.2': + dependencies: + '@babel/types': 7.19.0 + '@jridgewell/gen-mapping': 0.3.8 + jsesc: 2.5.2 + + '@babel/generator@7.27.5': + dependencies: + '@babel/parser': 7.27.5 + '@babel/types': 7.27.6 + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.27.5 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.25.0 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-define-polyfill-provider@0.6.4(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + debug: 4.4.1 + lodash.debounce: 4.0.8 + resolve: 1.22.10 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.27.4 + '@babel/types': 7.27.6 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.27.3(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.27.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.27.1': {} + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.27.6': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.27.6 + + '@babel/parser@7.18.4': + dependencies: + '@babel/types': 7.19.0 + + '@babel/parser@7.27.5': + dependencies: + '@babel/types': 7.27.6 + + '@babel/plugin-transform-runtime@7.27.4(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + babel-plugin-polyfill-corejs2: 0.4.13(@babel/core@7.27.4) + babel-plugin-polyfill-corejs3: 0.11.1(@babel/core@7.27.4) + babel-plugin-polyfill-regenerator: 0.6.4(@babel/core@7.27.4) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/runtime@7.27.6': {} + + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.27.5 + '@babel/types': 7.27.6 + + '@babel/traverse@7.27.4': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.27.5 + '@babel/parser': 7.27.5 + '@babel/template': 7.27.2 + '@babel/types': 7.27.6 + debug: 4.4.1 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.19.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + to-fast-properties: 2.0.0 + + '@babel/types@7.27.6': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@braintree/sanitize-url@7.1.1': {} + + '@cfworker/json-schema@4.1.1': {} + + '@chevrotain/cst-dts-gen@11.0.3': + dependencies: + '@chevrotain/gast': 11.0.3 + '@chevrotain/types': 11.0.3 + lodash-es: 4.17.21 + + '@chevrotain/gast@11.0.3': + dependencies: + '@chevrotain/types': 11.0.3 + lodash-es: 4.17.21 + + '@chevrotain/regexp-to-ast@11.0.3': {} + + '@chevrotain/types@11.0.3': {} + + '@chevrotain/utils@11.0.3': {} + + '@commitlint/cli@19.8.1(@types/node@24.0.1)(typescript@5.8.3)': + dependencies: + '@commitlint/format': 19.8.1 + '@commitlint/lint': 19.8.1 + '@commitlint/load': 19.8.1(@types/node@24.0.1)(typescript@5.8.3) + '@commitlint/read': 19.8.1 + '@commitlint/types': 19.8.1 + tinyexec: 1.0.1 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - typescript + + '@commitlint/config-conventional@19.8.1': + dependencies: + '@commitlint/types': 19.8.1 + conventional-changelog-conventionalcommits: 7.0.2 + + '@commitlint/config-validator@19.8.1': + dependencies: + '@commitlint/types': 19.8.1 + ajv: 8.17.1 + + '@commitlint/ensure@19.8.1': + dependencies: + '@commitlint/types': 19.8.1 + lodash.camelcase: 4.3.0 + lodash.kebabcase: 4.1.1 + lodash.snakecase: 4.1.1 + lodash.startcase: 4.4.0 + lodash.upperfirst: 4.3.1 + + '@commitlint/execute-rule@19.8.1': {} + + '@commitlint/format@19.8.1': + dependencies: + '@commitlint/types': 19.8.1 + chalk: 5.4.1 + + '@commitlint/is-ignored@19.8.1': + dependencies: + '@commitlint/types': 19.8.1 + semver: 7.7.2 + + '@commitlint/lint@19.8.1': + dependencies: + '@commitlint/is-ignored': 19.8.1 + '@commitlint/parse': 19.8.1 + '@commitlint/rules': 19.8.1 + '@commitlint/types': 19.8.1 + + '@commitlint/load@19.8.1(@types/node@24.0.1)(typescript@5.8.3)': + dependencies: + '@commitlint/config-validator': 19.8.1 + '@commitlint/execute-rule': 19.8.1 + '@commitlint/resolve-extends': 19.8.1 + '@commitlint/types': 19.8.1 + chalk: 5.4.1 + cosmiconfig: 9.0.0(typescript@5.8.3) + cosmiconfig-typescript-loader: 6.1.0(@types/node@24.0.1)(cosmiconfig@9.0.0(typescript@5.8.3))(typescript@5.8.3) + lodash.isplainobject: 4.0.6 + lodash.merge: 4.6.2 + lodash.uniq: 4.5.0 + transitivePeerDependencies: + - '@types/node' + - typescript + + '@commitlint/message@19.8.1': {} + + '@commitlint/parse@19.8.1': + dependencies: + '@commitlint/types': 19.8.1 + conventional-changelog-angular: 7.0.0 + conventional-commits-parser: 5.0.0 + + '@commitlint/read@19.8.1': + dependencies: + '@commitlint/top-level': 19.8.1 + '@commitlint/types': 19.8.1 + git-raw-commits: 4.0.0 + minimist: 1.2.8 + tinyexec: 1.0.1 + + '@commitlint/resolve-extends@19.8.1': + dependencies: + '@commitlint/config-validator': 19.8.1 + '@commitlint/types': 19.8.1 + global-directory: 4.0.1 + import-meta-resolve: 4.1.0 + lodash.mergewith: 4.6.2 + resolve-from: 5.0.0 + + '@commitlint/rules@19.8.1': + dependencies: + '@commitlint/ensure': 19.8.1 + '@commitlint/message': 19.8.1 + '@commitlint/to-lines': 19.8.1 + '@commitlint/types': 19.8.1 + + '@commitlint/to-lines@19.8.1': {} + + '@commitlint/top-level@19.8.1': + dependencies: + find-up: 7.0.0 + + '@commitlint/types@19.8.1': + dependencies: + '@types/conventional-commits-parser': 5.0.1 + chalk: 5.4.1 + + '@develar/schema-utils@2.6.5': + dependencies: + ajv: 6.12.6 + ajv-keywords: 3.5.2(ajv@6.12.6) + + '@dnd-kit/accessibility@3.1.1(react@18.3.1)': + dependencies: + react: 18.3.1 + tslib: 2.8.1 + + '@dnd-kit/core@6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@dnd-kit/accessibility': 3.1.1(react@18.3.1) + '@dnd-kit/utilities': 3.2.2(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + tslib: 2.8.1 + + '@dnd-kit/modifiers@9.0.0(@dnd-kit/core@6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': + dependencies: + '@dnd-kit/core': 6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@dnd-kit/utilities': 3.2.2(react@18.3.1) + react: 18.3.1 + tslib: 2.8.1 + + '@dnd-kit/sortable@10.0.0(@dnd-kit/core@6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': + dependencies: + '@dnd-kit/core': 6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@dnd-kit/utilities': 3.2.2(react@18.3.1) + react: 18.3.1 + tslib: 2.8.1 + + '@dnd-kit/utilities@3.2.2(react@18.3.1)': + dependencies: + react: 18.3.1 + tslib: 2.8.1 + + '@electron/asar@3.4.1': + dependencies: + commander: 5.1.0 + glob: 7.2.3 + minimatch: 3.1.2 + + '@electron/get@2.0.3': + dependencies: + debug: 4.4.1 + env-paths: 2.2.1 + fs-extra: 8.1.0 + got: 11.8.6 + progress: 2.0.3 + semver: 6.3.1 + sumchecker: 3.0.1 + optionalDependencies: + global-agent: 3.0.0 + transitivePeerDependencies: + - supports-color + + '@electron/notarize@2.2.1': + dependencies: + debug: 4.4.1 + fs-extra: 9.1.0 + promise-retry: 2.0.1 + transitivePeerDependencies: + - supports-color + + '@electron/osx-sign@1.0.5': + dependencies: + compare-version: 0.1.2 + debug: 4.4.1 + fs-extra: 10.1.0 + isbinaryfile: 4.0.10 + minimist: 1.2.8 + plist: 3.1.0 + transitivePeerDependencies: + - supports-color + + '@electron/universal@1.5.1': + dependencies: + '@electron/asar': 3.4.1 + '@malept/cross-spawn-promise': 1.1.1 + debug: 4.4.1 + dir-compare: 3.3.0 + fs-extra: 9.1.0 + minimatch: 3.1.2 + plist: 3.1.0 + transitivePeerDependencies: + - supports-color + + '@emnapi/runtime@1.4.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@emoji-mart/data@1.2.1': {} + + '@emoji-mart/react@1.1.1(emoji-mart@5.6.0)(react@18.3.1)': + dependencies: + emoji-mart: 5.6.0 + react: 18.3.1 + + '@emotion/babel-plugin@11.13.5': + dependencies: + '@babel/helper-module-imports': 7.27.1 + '@babel/runtime': 7.27.6 + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/serialize': 1.3.3 + babel-plugin-macros: 3.1.0 + convert-source-map: 1.9.0 + escape-string-regexp: 4.0.0 + find-root: 1.1.0 + source-map: 0.5.7 + stylis: 4.2.0 + transitivePeerDependencies: + - supports-color + + '@emotion/cache@11.14.0': + dependencies: + '@emotion/memoize': 0.9.0 + '@emotion/sheet': 1.4.0 + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + stylis: 4.2.0 + + '@emotion/css@11.13.5': + dependencies: + '@emotion/babel-plugin': 11.13.5 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + '@emotion/sheet': 1.4.0 + '@emotion/utils': 1.4.2 + transitivePeerDependencies: + - supports-color + + '@emotion/hash@0.8.0': {} + + '@emotion/hash@0.9.2': {} + + '@emotion/is-prop-valid@1.3.1': + dependencies: + '@emotion/memoize': 0.9.0 + + '@emotion/memoize@0.9.0': {} + + '@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + '@emotion/babel-plugin': 11.13.5 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@18.3.1) + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + hoist-non-react-statics: 3.3.2 + react: 18.3.1 + optionalDependencies: + '@types/react': 19.1.8 + transitivePeerDependencies: + - supports-color + + '@emotion/serialize@1.3.3': + dependencies: + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/unitless': 0.10.0 + '@emotion/utils': 1.4.2 + csstype: 3.1.3 + + '@emotion/sheet@1.4.0': {} + + '@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + '@emotion/babel-plugin': 11.13.5 + '@emotion/is-prop-valid': 1.3.1 + '@emotion/react': 11.14.0(@types/react@19.1.8)(react@18.3.1) + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@18.3.1) + '@emotion/utils': 1.4.2 + react: 18.3.1 + optionalDependencies: + '@types/react': 19.1.8 + transitivePeerDependencies: + - supports-color + + '@emotion/unitless@0.10.0': {} + + '@emotion/unitless@0.7.5': {} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@emotion/utils@1.4.2': {} + + '@emotion/weak-memoize@0.4.0': {} + + '@floating-ui/core@0.7.3': {} + + '@floating-ui/core@1.7.1': + dependencies: + '@floating-ui/utils': 0.2.9 + + '@floating-ui/dom@0.5.4': + dependencies: + '@floating-ui/core': 0.7.3 + + '@floating-ui/dom@1.7.1': + dependencies: + '@floating-ui/core': 1.7.1 + '@floating-ui/utils': 0.2.9 + + '@floating-ui/react-dom@0.7.2(@types/react@19.1.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/dom': 0.5.4 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + use-isomorphic-layout-effect: 1.2.1(@types/react@19.1.8)(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + + '@floating-ui/react-dom@2.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/dom': 1.7.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@floating-ui/react@0.27.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/react-dom': 2.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@floating-ui/utils': 0.2.9 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + tabbable: 6.2.0 + + '@floating-ui/utils@0.2.9': {} + + '@fontsource/inter@5.2.6': {} + + '@fontsource/jetbrains-mono@5.2.6': {} + + '@giscus/react@3.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + giscus: 1.6.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@hapi/hoek@9.3.0': {} + + '@hapi/topo@5.1.0': + dependencies: + '@hapi/hoek': 9.3.0 + + '@huggingface/hub@2.2.0': + dependencies: + '@huggingface/tasks': 0.19.15 + + '@huggingface/tasks@0.19.15': {} + + '@hyzyla/pdfium@2.1.7': {} + + '@iconify/types@2.0.0': {} + + '@iconify/utils@2.3.0': + dependencies: + '@antfu/install-pkg': 1.1.0 + '@antfu/utils': 8.1.1 + '@iconify/types': 2.0.0 + debug: 4.4.1 + globals: 15.15.0 + kolorist: 1.8.0 + local-pkg: 1.1.1 + mlly: 1.7.4 + transitivePeerDependencies: + - supports-color + + '@img/sharp-darwin-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.0.4 + optional: true + + '@img/sharp-darwin-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.0.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.0.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.0.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.0.4': + optional: true + + '@img/sharp-libvips-linux-arm@1.0.5': + optional: true + + '@img/sharp-libvips-linux-s390x@1.0.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.0.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.0.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.0.4': + optional: true + + '@img/sharp-linux-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.0.4 + optional: true + + '@img/sharp-linux-arm@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.0.5 + optional: true + + '@img/sharp-linux-s390x@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.0.4 + optional: true + + '@img/sharp-linux-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.0.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.0.4 + optional: true + + '@img/sharp-wasm32@0.33.5': + dependencies: + '@emnapi/runtime': 1.4.3 + optional: true + + '@img/sharp-win32-ia32@0.33.5': + optional: true + + '@img/sharp-win32-x64@0.33.5': + optional: true + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@jridgewell/gen-mapping@0.3.8': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@langchain/core@0.3.58(openai@4.104.0(zod@3.25.76))': + dependencies: + '@cfworker/json-schema': 4.1.1 + ansi-styles: 5.2.0 + camelcase: 6.3.0 + decamelize: 1.2.0 + js-tiktoken: 1.0.20 + langsmith: 0.3.31(openai@4.104.0(zod@3.25.76)) + mustache: 4.2.0 + p-queue: 6.6.2 + p-retry: 4.6.2 + uuid: 10.0.0 + zod: 3.25.76 + zod-to-json-schema: 3.24.5(zod@3.25.76) + transitivePeerDependencies: + - openai + + '@langchain/openai@0.5.13(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.76)))': + dependencies: + '@langchain/core': 0.3.58(openai@4.104.0(zod@3.25.76)) + js-tiktoken: 1.0.20 + openai: 4.104.0(zod@3.25.32) + zod: 3.25.32 + transitivePeerDependencies: + - encoding + - ws + + '@langchain/textsplitters@0.1.0(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.76)))': + dependencies: + '@langchain/core': 0.3.58(openai@4.104.0(zod@3.25.76)) + js-tiktoken: 1.0.20 + + '@lit-labs/ssr-dom-shim@1.3.0': {} + + '@lit/reactive-element@2.1.0': + dependencies: + '@lit-labs/ssr-dom-shim': 1.3.0 + + '@lobehub/emojilib@1.0.0': {} + + '@lobehub/fluent-emoji@2.0.0(@babel/core@7.27.4)(@types/react@19.1.8)(acorn@8.15.0)(antd@5.26.0(date-fns@2.30.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(framer-motion@12.17.0(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@lobehub/emojilib': 1.0.0 + '@lobehub/ui': 2.4.0(@babel/core@7.27.4)(@types/react@19.1.8)(acorn@8.15.0)(antd@5.26.0(date-fns@2.30.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(framer-motion@12.17.0(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + antd: 5.26.0(date-fns@2.30.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + antd-style: 3.7.1(@types/react@19.1.8)(antd@5.26.0(date-fns@2.30.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + emoji-regex: 10.4.0 + lodash-es: 4.17.21 + lucide-react: 0.469.0(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-layout-kit: 1.9.1(react@18.3.1) + url-join: 5.0.0 + transitivePeerDependencies: + - '@babel/core' + - '@types/react' + - acorn + - framer-motion + - supports-color + + '@lobehub/icons@1.98.0(@babel/core@7.27.4)(@types/react@19.1.8)(acorn@8.15.0)(antd@5.26.0(date-fns@2.30.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(framer-motion@12.17.0(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@lobehub/ui': 2.4.0(@babel/core@7.27.4)(@types/react@19.1.8)(acorn@8.15.0)(antd@5.26.0(date-fns@2.30.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(framer-motion@12.17.0(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + antd: 5.26.0(date-fns@2.30.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + antd-style: 3.7.1(@types/react@19.1.8)(antd@5.26.0(date-fns@2.30.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + lucide-react: 0.469.0(react@18.3.1) + polished: 4.3.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-layout-kit: 1.9.1(react@18.3.1) + transitivePeerDependencies: + - '@babel/core' + - '@types/react' + - acorn + - framer-motion + - supports-color + + '@lobehub/icons@2.4.0(@babel/core@7.27.4)(@types/react@19.1.8)(acorn@8.15.0)(antd@5.26.0(date-fns@2.30.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(framer-motion@12.17.0(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@lobehub/ui': 2.4.0(@babel/core@7.27.4)(@types/react@19.1.8)(acorn@8.15.0)(antd@5.26.0(date-fns@2.30.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(framer-motion@12.17.0(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + antd: 5.26.0(date-fns@2.30.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + antd-style: 3.7.1(@types/react@19.1.8)(antd@5.26.0(date-fns@2.30.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + lucide-react: 0.469.0(react@18.3.1) + polished: 4.3.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-layout-kit: 1.9.1(react@18.3.1) + transitivePeerDependencies: + - '@babel/core' + - '@types/react' + - acorn + - framer-motion + - supports-color + + '@lobehub/ui@2.4.0(@babel/core@7.27.4)(@types/react@19.1.8)(acorn@8.15.0)(antd@5.26.0(date-fns@2.30.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(framer-motion@12.17.0(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@ant-design/cssinjs': 1.23.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@dnd-kit/core': 6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@dnd-kit/modifiers': 9.0.0(@dnd-kit/core@6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + '@dnd-kit/sortable': 10.0.0(@dnd-kit/core@6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + '@dnd-kit/utilities': 3.2.2(react@18.3.1) + '@emoji-mart/data': 1.2.1 + '@emoji-mart/react': 1.1.1(emoji-mart@5.6.0)(react@18.3.1) + '@floating-ui/react': 0.27.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@giscus/react': 3.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@lobehub/fluent-emoji': 2.0.0(@babel/core@7.27.4)(@types/react@19.1.8)(acorn@8.15.0)(antd@5.26.0(date-fns@2.30.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(framer-motion@12.17.0(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@lobehub/icons': 2.4.0(@babel/core@7.27.4)(@types/react@19.1.8)(acorn@8.15.0)(antd@5.26.0(date-fns@2.30.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(framer-motion@12.17.0(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mdx-js/mdx': 3.1.0(acorn@8.15.0) + '@mdx-js/react': 3.1.0(@types/react@19.1.8)(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.8)(react@18.3.1) + '@shikijs/transformers': 3.6.0 + '@splinetool/runtime': 0.9.526 + ahooks: 3.8.5(react@18.3.1) + antd: 5.26.0(date-fns@2.30.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + antd-style: 3.7.1(@types/react@19.1.8)(antd@5.26.0(date-fns@2.30.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + chroma-js: 3.1.2 + class-variance-authority: 0.7.1 + dayjs: 1.11.13 + emoji-mart: 5.6.0 + fast-deep-equal: 3.1.3 + framer-motion: 12.17.0(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + immer: 10.1.1 + katex: 0.16.22 + leva: 0.10.0(@types/react@19.1.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + lodash-es: 4.17.21 + lucide-react: 0.484.0(react@18.3.1) + mermaid: 11.6.0 + numeral: 2.0.6 + polished: 4.3.1 + query-string: 9.2.0 + rc-collapse: 4.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-footer: 0.6.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-image: 7.12.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-menu: 9.16.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + re-resizable: 6.11.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-avatar-editor: 13.0.2(@babel/core@7.27.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-dom: 18.3.1(react@18.3.1) + react-error-boundary: 5.0.0(react@18.3.1) + react-hotkeys-hook: 5.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-layout-kit: 1.9.1(react@18.3.1) + react-markdown: 10.1.0(@types/react@19.1.8)(react@18.3.1) + react-merge-refs: 3.0.2(react@18.3.1) + react-rnd: 10.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-zoom-pan-pinch: 3.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rehype-katex: 7.0.1 + rehype-raw: 7.0.0 + remark-breaks: 4.0.0 + remark-gfm: 4.0.1 + remark-math: 6.0.0 + shiki: 3.6.0 + swr: 2.3.3(react@18.3.1) + ts-md5: 1.3.1 + unified: 11.0.5 + url-join: 5.0.0 + use-merge-value: 1.2.0(react@18.3.1) + uuid: 11.1.0 + transitivePeerDependencies: + - '@babel/core' + - '@types/react' + - acorn + - supports-color + + '@malept/cross-spawn-promise@1.1.1': + dependencies: + cross-spawn: 7.0.6 + + '@malept/flatpak-bundler@0.4.0': + dependencies: + debug: 4.4.1 + fs-extra: 9.1.0 + lodash: 4.17.21 + tmp-promise: 3.0.3 + transitivePeerDependencies: + - supports-color + + '@mapbox/node-pre-gyp@1.0.11': + dependencies: + detect-libc: 2.0.4 + https-proxy-agent: 5.0.1 + make-dir: 3.1.0 + node-fetch: 2.7.0 + nopt: 5.0.0 + npmlog: 5.0.1 + rimraf: 3.0.2 + semver: 7.7.2 + tar: 6.2.1 + transitivePeerDependencies: + - encoding + - supports-color + optional: true + + '@mdx-js/mdx@3.1.0(acorn@8.15.0)': + dependencies: + '@types/estree': 1.0.8 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdx': 2.0.13 + collapse-white-space: 2.1.0 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + estree-util-scope: 1.0.0 + estree-walker: 3.0.3 + hast-util-to-jsx-runtime: 2.3.6 + markdown-extensions: 2.0.0 + recma-build-jsx: 1.0.0 + recma-jsx: 1.0.0(acorn@8.15.0) + recma-stringify: 1.0.0 + rehype-recma: 1.0.0 + remark-mdx: 3.1.0 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + source-map: 0.7.4 + unified: 11.0.5 + unist-util-position-from-estree: 2.0.0 + unist-util-stringify-position: 4.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + transitivePeerDependencies: + - acorn + - supports-color + + '@mdx-js/react@3.1.0(@types/react@19.1.8)(react@18.3.1)': + dependencies: + '@types/mdx': 2.0.13 + '@types/react': 19.1.8 + react: 18.3.1 + + '@mermaid-js/parser@0.4.0': + dependencies: + langium: 3.3.1 + + '@mixmark-io/domino@2.2.0': {} + + '@mui/base@5.0.0-beta.40-0(@types/react@19.1.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + '@floating-ui/react-dom': 2.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/types': 7.4.3(@types/react@19.1.8) + '@mui/utils': 5.17.1(@types/react@19.1.8)(react@18.3.1) + '@popperjs/core': 2.11.8 + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.1.8 + + '@mui/core-downloads-tracker@5.17.1': {} + + '@mui/icons-material@5.16.14(@mui/material@5.16.14(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@19.1.8)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + '@mui/material': 5.16.14(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 19.1.8 + + '@mui/lab@5.0.0-alpha.175(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react@18.3.1))(@mui/material@5.16.14(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@19.1.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + '@mui/base': 5.0.0-beta.40-0(@types/react@19.1.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/material': 5.16.14(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/system': 5.17.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react@18.3.1) + '@mui/types': 7.4.3(@types/react@19.1.8) + '@mui/utils': 5.17.1(@types/react@19.1.8)(react@18.3.1) + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@19.1.8)(react@18.3.1) + '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react@18.3.1) + '@types/react': 19.1.8 + + '@mui/material@5.16.14(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + '@mui/core-downloads-tracker': 5.17.1 + '@mui/system': 5.17.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react@18.3.1) + '@mui/types': 7.4.3(@types/react@19.1.8) + '@mui/utils': 5.17.1(@types/react@19.1.8)(react@18.3.1) + '@popperjs/core': 2.11.8 + '@types/react-transition-group': 4.4.12(@types/react@19.1.8) + clsx: 2.1.1 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-is: 19.1.0 + react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@19.1.8)(react@18.3.1) + '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react@18.3.1) + '@types/react': 19.1.8 + + '@mui/private-theming@5.17.1(@types/react@19.1.8)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + '@mui/utils': 5.17.1(@types/react@19.1.8)(react@18.3.1) + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@types/react': 19.1.8 + + '@mui/styled-engine@5.16.14(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + '@emotion/cache': 11.14.0 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@19.1.8)(react@18.3.1) + '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react@18.3.1) + + '@mui/system@5.17.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + '@mui/private-theming': 5.17.1(@types/react@19.1.8)(react@18.3.1) + '@mui/styled-engine': 5.16.14(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react@18.3.1))(react@18.3.1) + '@mui/types': 7.2.24(@types/react@19.1.8) + '@mui/utils': 5.17.1(@types/react@19.1.8)(react@18.3.1) + clsx: 2.1.1 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@19.1.8)(react@18.3.1) + '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@18.3.1))(@types/react@19.1.8)(react@18.3.1) + '@types/react': 19.1.8 + + '@mui/types@7.2.24(@types/react@19.1.8)': + optionalDependencies: + '@types/react': 19.1.8 + + '@mui/types@7.4.3(@types/react@19.1.8)': + dependencies: + '@babel/runtime': 7.27.6 + optionalDependencies: + '@types/react': 19.1.8 + + '@mui/utils@5.17.1(@types/react@19.1.8)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + '@mui/types': 7.2.24(@types/react@19.1.8) + '@types/prop-types': 15.7.15 + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-is: 19.1.0 + optionalDependencies: + '@types/react': 19.1.8 + + '@next/env@14.2.29': {} + + '@next/swc-darwin-arm64@14.2.29': + optional: true + + '@next/swc-darwin-x64@14.2.29': + optional: true + + '@next/swc-linux-arm64-gnu@14.2.29': + optional: true + + '@next/swc-linux-arm64-musl@14.2.29': + optional: true + + '@next/swc-linux-x64-gnu@14.2.29': + optional: true + + '@next/swc-linux-x64-musl@14.2.29': + optional: true + + '@next/swc-win32-arm64-msvc@14.2.29': + optional: true + + '@next/swc-win32-ia32-msvc@14.2.29': + optional: true + + '@next/swc-win32-x64-msvc@14.2.29': + optional: true + + '@noble/hashes@1.8.0': {} + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@opendocsg/pdf2md@0.2.1': + dependencies: + enumify: 1.0.4 + minimist: 1.2.8 + unpdf: 0.12.2 + transitivePeerDependencies: + - encoding + - supports-color + + '@openrouter/ai-sdk-provider@0.4.6(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 1.0.9 + '@ai-sdk/provider-utils': 2.1.10(zod@3.25.76) + zod: 3.25.76 + + '@opentelemetry/api@1.9.0': {} + + '@paralleldrive/cuid2@2.2.2': + dependencies: + '@noble/hashes': 1.8.0 + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@popperjs/core@2.11.8': {} + + '@prisma/client@6.9.0(prisma@6.9.0(typescript@5.8.3))(typescript@5.8.3)': + optionalDependencies: + prisma: 6.9.0(typescript@5.8.3) + typescript: 5.8.3 + + '@prisma/config@6.9.0': + dependencies: + jiti: 2.4.2 + + '@prisma/debug@6.9.0': {} + + '@prisma/engines-version@6.9.0-10.81e4af48011447c3cc503a190e86995b66d2a28e': {} + + '@prisma/engines@6.9.0': + dependencies: + '@prisma/debug': 6.9.0 + '@prisma/engines-version': 6.9.0-10.81e4af48011447c3cc503a190e86995b66d2a28e + '@prisma/fetch-engine': 6.9.0 + '@prisma/get-platform': 6.9.0 + + '@prisma/fetch-engine@6.9.0': + dependencies: + '@prisma/debug': 6.9.0 + '@prisma/engines-version': 6.9.0-10.81e4af48011447c3cc503a190e86995b66d2a28e + '@prisma/get-platform': 6.9.0 + + '@prisma/get-platform@6.9.0': + dependencies: + '@prisma/debug': 6.9.0 + + '@radix-ui/primitive@1.0.0': + dependencies: + '@babel/runtime': 7.27.6 + + '@radix-ui/react-arrow@1.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + '@radix-ui/react-primitive': 1.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@radix-ui/react-compose-refs@1.0.0(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + react: 18.3.1 + + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.1.8)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 19.1.8 + + '@radix-ui/react-context@1.0.0(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + react: 18.3.1 + + '@radix-ui/react-dismissable-layer@1.0.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-compose-refs': 1.0.0(react@18.3.1) + '@radix-ui/react-primitive': 1.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.0.0(react@18.3.1) + '@radix-ui/react-use-escape-keydown': 1.0.2(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@radix-ui/react-id@1.0.0(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + '@radix-ui/react-use-layout-effect': 1.0.0(react@18.3.1) + react: 18.3.1 + + '@radix-ui/react-popper@1.1.1(@types/react@19.1.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + '@floating-ui/react-dom': 0.7.2(@types/react@19.1.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-arrow': 1.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.0.0(react@18.3.1) + '@radix-ui/react-context': 1.0.0(react@18.3.1) + '@radix-ui/react-primitive': 1.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.0.0(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.0.0(react@18.3.1) + '@radix-ui/react-use-rect': 1.0.0(react@18.3.1) + '@radix-ui/react-use-size': 1.0.0(react@18.3.1) + '@radix-ui/rect': 1.0.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + + '@radix-ui/react-portal@1.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + '@radix-ui/react-primitive': 1.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@radix-ui/react-presence@1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + '@radix-ui/react-compose-refs': 1.0.0(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.0.0(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@radix-ui/react-primitive@1.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + '@radix-ui/react-slot': 1.0.1(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@radix-ui/react-slot@1.0.1(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + '@radix-ui/react-compose-refs': 1.0.0(react@18.3.1) + react: 18.3.1 + + '@radix-ui/react-slot@1.2.3(@types/react@19.1.8)(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 19.1.8 + + '@radix-ui/react-tooltip@1.0.5(@types/react@19.1.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-compose-refs': 1.0.0(react@18.3.1) + '@radix-ui/react-context': 1.0.0(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.0.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.0.0(react@18.3.1) + '@radix-ui/react-popper': 1.1.1(@types/react@19.1.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 1.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.0.1(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.0.0(react@18.3.1) + '@radix-ui/react-visually-hidden': 1.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + + '@radix-ui/react-use-callback-ref@1.0.0(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + react: 18.3.1 + + '@radix-ui/react-use-controllable-state@1.0.0(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + '@radix-ui/react-use-callback-ref': 1.0.0(react@18.3.1) + react: 18.3.1 + + '@radix-ui/react-use-escape-keydown@1.0.2(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + '@radix-ui/react-use-callback-ref': 1.0.0(react@18.3.1) + react: 18.3.1 + + '@radix-ui/react-use-layout-effect@1.0.0(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + react: 18.3.1 + + '@radix-ui/react-use-rect@1.0.0(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + '@radix-ui/rect': 1.0.0 + react: 18.3.1 + + '@radix-ui/react-use-size@1.0.0(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + '@radix-ui/react-use-layout-effect': 1.0.0(react@18.3.1) + react: 18.3.1 + + '@radix-ui/react-visually-hidden@1.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + '@radix-ui/react-primitive': 1.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@radix-ui/rect@1.0.0': + dependencies: + '@babel/runtime': 7.27.6 + + '@rc-component/async-validator@5.0.4': + dependencies: + '@babel/runtime': 7.27.6 + + '@rc-component/color-picker@2.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@ant-design/fast-color': 2.0.6 + '@babel/runtime': 7.27.6 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@rc-component/context@1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@rc-component/mini-decimal@1.1.0': + dependencies: + '@babel/runtime': 7.27.6 + + '@rc-component/mutate-observer@1.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@rc-component/portal@1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@rc-component/qrcode@1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@rc-component/tour@1.15.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + '@rc-component/portal': 1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@rc-component/trigger': 2.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@rc-component/trigger@2.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + '@rc-component/portal': 1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-resize-observer: 1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@reduxjs/toolkit@2.11.2(react-redux@9.2.0(@types/react@19.1.8)(react@18.3.1)(redux@5.0.1))(react@18.3.1)': + dependencies: + '@standard-schema/spec': 1.0.0 + '@standard-schema/utils': 0.3.0 + immer: 11.1.0 + redux: 5.0.1 + redux-thunk: 3.1.0(redux@5.0.1) + reselect: 5.1.1 + optionalDependencies: + react: 18.3.1 + react-redux: 9.2.0(@types/react@19.1.8)(react@18.3.1)(redux@5.0.1) + + '@shikijs/core@3.6.0': + dependencies: + '@shikijs/types': 3.6.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@3.6.0': + dependencies: + '@shikijs/types': 3.6.0 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 4.3.3 + + '@shikijs/engine-oniguruma@3.6.0': + dependencies: + '@shikijs/types': 3.6.0 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@3.6.0': + dependencies: + '@shikijs/types': 3.6.0 + + '@shikijs/themes@3.6.0': + dependencies: + '@shikijs/types': 3.6.0 + + '@shikijs/transformers@3.6.0': + dependencies: + '@shikijs/core': 3.6.0 + '@shikijs/types': 3.6.0 + + '@shikijs/types@3.6.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.2': {} + + '@sideway/address@4.1.5': + dependencies: + '@hapi/hoek': 9.3.0 + + '@sideway/formula@3.0.1': {} + + '@sideway/pinpoint@2.0.0': {} + + '@sindresorhus/is@4.6.0': {} + + '@splinetool/runtime@0.9.526': + dependencies: + on-change: 4.0.2 + semver-compare: 1.0.0 + + '@standard-schema/spec@1.0.0': {} + + '@standard-schema/utils@0.3.0': {} + + '@stitches/react@1.2.8(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@swc/counter@0.1.3': {} + + '@swc/helpers@0.5.5': + dependencies: + '@swc/counter': 0.1.3 + tslib: 2.8.1 + + '@szmarczak/http-timer@4.0.6': + dependencies: + defer-to-connect: 2.0.1 + + '@tootallnate/once@2.0.0': {} + + '@types/cacheable-request@6.0.3': + dependencies: + '@types/http-cache-semantics': 4.0.4 + '@types/keyv': 3.1.4 + '@types/node': 22.15.31 + '@types/responselike': 1.0.3 + + '@types/conventional-commits-parser@5.0.1': + dependencies: + '@types/node': 24.0.1 + + '@types/d3-array@3.2.1': {} + + '@types/d3-axis@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-brush@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-chord@3.0.6': {} + + '@types/d3-color@3.1.3': {} + + '@types/d3-contour@3.0.6': + dependencies: + '@types/d3-array': 3.2.1 + '@types/geojson': 7946.0.16 + + '@types/d3-delaunay@6.0.4': {} + + '@types/d3-dispatch@3.0.6': {} + + '@types/d3-drag@3.0.7': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-dsv@3.0.7': {} + + '@types/d3-ease@3.0.2': {} + + '@types/d3-fetch@3.0.7': + dependencies: + '@types/d3-dsv': 3.0.7 + + '@types/d3-force@3.0.10': {} + + '@types/d3-format@3.0.4': {} + + '@types/d3-geo@3.1.0': + dependencies: + '@types/geojson': 7946.0.16 + + '@types/d3-hierarchy@3.1.7': {} + + '@types/d3-interpolate@3.0.4': + dependencies: + '@types/d3-color': 3.1.3 + + '@types/d3-path@3.1.1': {} + + '@types/d3-polygon@3.0.2': {} + + '@types/d3-quadtree@3.0.6': {} + + '@types/d3-random@3.0.3': {} + + '@types/d3-scale-chromatic@3.1.0': {} + + '@types/d3-scale@4.0.9': + dependencies: + '@types/d3-time': 3.0.4 + + '@types/d3-selection@3.0.11': {} + + '@types/d3-shape@3.1.7': + dependencies: + '@types/d3-path': 3.1.1 + + '@types/d3-time-format@4.0.3': {} + + '@types/d3-time@3.0.4': {} + + '@types/d3-timer@3.0.2': {} + + '@types/d3-transition@3.0.9': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-zoom@3.0.8': + dependencies: + '@types/d3-interpolate': 3.0.4 + '@types/d3-selection': 3.0.11 + + '@types/d3@7.4.3': + dependencies: + '@types/d3-array': 3.2.1 + '@types/d3-axis': 3.0.6 + '@types/d3-brush': 3.0.6 + '@types/d3-chord': 3.0.6 + '@types/d3-color': 3.1.3 + '@types/d3-contour': 3.0.6 + '@types/d3-delaunay': 6.0.4 + '@types/d3-dispatch': 3.0.6 + '@types/d3-drag': 3.0.7 + '@types/d3-dsv': 3.0.7 + '@types/d3-ease': 3.0.2 + '@types/d3-fetch': 3.0.7 + '@types/d3-force': 3.0.10 + '@types/d3-format': 3.0.4 + '@types/d3-geo': 3.1.0 + '@types/d3-hierarchy': 3.1.7 + '@types/d3-interpolate': 3.0.4 + '@types/d3-path': 3.1.1 + '@types/d3-polygon': 3.0.2 + '@types/d3-quadtree': 3.0.6 + '@types/d3-random': 3.0.3 + '@types/d3-scale': 4.0.9 + '@types/d3-scale-chromatic': 3.1.0 + '@types/d3-selection': 3.0.11 + '@types/d3-shape': 3.1.7 + '@types/d3-time': 3.0.4 + '@types/d3-time-format': 4.0.3 + '@types/d3-timer': 3.0.2 + '@types/d3-transition': 3.0.9 + '@types/d3-zoom': 3.0.8 + + '@types/debug@4.1.12': + dependencies: + '@types/ms': 2.1.0 + + '@types/diff-match-patch@1.0.36': {} + + '@types/estree-jsx@1.0.5': + dependencies: + '@types/estree': 1.0.8 + + '@types/estree@1.0.8': {} + + '@types/fs-extra@9.0.13': + dependencies: + '@types/node': 24.0.1 + + '@types/geojson@7946.0.16': {} + + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/http-cache-semantics@4.0.4': {} + + '@types/katex@0.16.7': {} + + '@types/keyv@3.1.4': + dependencies: + '@types/node': 22.15.31 + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/mdx@2.0.13': {} + + '@types/ms@2.1.0': {} + + '@types/node-fetch@2.6.12': + dependencies: + '@types/node': 18.19.111 + form-data: 4.0.3 + + '@types/node@18.19.111': + dependencies: + undici-types: 5.26.5 + + '@types/node@22.15.31': + dependencies: + undici-types: 6.21.0 + + '@types/node@24.0.1': + dependencies: + undici-types: 7.8.0 + + '@types/parse-json@4.0.2': {} + + '@types/plist@3.0.5': + dependencies: + '@types/node': 24.0.1 + xmlbuilder: 15.1.1 + optional: true + + '@types/prop-types@15.7.15': {} + + '@types/react-transition-group@4.4.12(@types/react@19.1.8)': + dependencies: + '@types/react': 19.1.8 + + '@types/react@19.1.8': + dependencies: + csstype: 3.1.3 + + '@types/responselike@1.0.3': + dependencies: + '@types/node': 22.15.31 + + '@types/retry@0.12.0': {} + + '@types/trusted-types@2.0.7': {} + + '@types/unist@2.0.11': {} + + '@types/unist@3.0.3': {} + + '@types/use-sync-external-store@0.0.6': {} + + '@types/uuid@10.0.0': {} + + '@types/verror@1.10.11': + optional: true + + '@types/yauzl@2.10.3': + dependencies: + '@types/node': 22.15.31 + optional: true + + '@ungap/structured-clone@1.3.0': {} + + '@use-gesture/core@10.3.1': {} + + '@use-gesture/react@10.3.1(react@18.3.1)': + dependencies: + '@use-gesture/core': 10.3.1 + react: 18.3.1 + + '@xmldom/xmldom@0.8.10': {} + + JSONStream@1.3.5: + dependencies: + jsonparse: 1.3.1 + through: 2.3.8 + + abbrev@1.1.1: + optional: true + + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + adler-32@1.3.1: {} + + adm-zip@0.5.16: {} + + agent-base@6.0.2: + dependencies: + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + + agentkeepalive@4.6.0: + dependencies: + humanize-ms: 1.2.1 + + ahooks@3.8.5(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + dayjs: 1.11.13 + intersection-observer: 0.12.2 + js-cookie: 3.0.5 + lodash: 4.17.21 + react: 18.3.1 + react-fast-compare: 3.2.2 + resize-observer-polyfill: 1.5.1 + screenfull: 5.2.0 + tslib: 2.8.1 + + ai@4.3.16(react@18.3.1)(zod@3.25.76): + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) + '@ai-sdk/react': 1.2.12(react@18.3.1)(zod@3.25.76) + '@ai-sdk/ui-utils': 1.2.11(zod@3.25.76) + '@opentelemetry/api': 1.9.0 + jsondiffpatch: 0.6.0 + zod: 3.25.76 + optionalDependencies: + react: 18.3.1 + + ajv-keywords@3.5.2(ajv@6.12.6): + dependencies: + ajv: 6.12.6 + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.0.6 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-escapes@7.0.0: + dependencies: + environment: 1.1.0 + + ansi-regex@2.1.1: {} + + ansi-regex@5.0.1: {} + + ansi-regex@6.1.0: {} + + ansi-styles@2.2.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + ansi-styles@6.2.1: {} + + antd-style@3.7.1(@types/react@19.1.8)(antd@5.26.0(date-fns@2.30.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@ant-design/cssinjs': 1.23.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@babel/runtime': 7.27.6 + '@emotion/cache': 11.14.0 + '@emotion/css': 11.13.5 + '@emotion/react': 11.14.0(@types/react@19.1.8)(react@18.3.1) + '@emotion/serialize': 1.3.3 + '@emotion/utils': 1.4.2 + antd: 5.26.0(date-fns@2.30.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + use-merge-value: 1.2.0(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - react-dom + - supports-color + + antd@5.26.0(date-fns@2.30.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@ant-design/colors': 7.2.1 + '@ant-design/cssinjs': 1.23.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@ant-design/cssinjs-utils': 1.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@ant-design/fast-color': 2.0.6 + '@ant-design/icons': 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@ant-design/react-slick': 1.1.2(react@18.3.1) + '@babel/runtime': 7.27.6 + '@rc-component/color-picker': 2.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@rc-component/mutate-observer': 1.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@rc-component/qrcode': 1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@rc-component/tour': 1.15.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@rc-component/trigger': 2.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + copy-to-clipboard: 3.3.3 + dayjs: 1.11.13 + rc-cascader: 3.34.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-checkbox: 3.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-collapse: 3.9.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-dialog: 9.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-drawer: 7.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-dropdown: 4.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-field-form: 2.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-image: 7.12.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-input: 1.8.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-input-number: 9.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-mentions: 2.20.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-menu: 9.16.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-notification: 5.6.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-pagination: 5.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-picker: 4.11.3(date-fns@2.30.0)(dayjs@1.11.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-progress: 4.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-rate: 2.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-resize-observer: 1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-segmented: 2.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-select: 14.16.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-slider: 11.1.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-steps: 6.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-switch: 4.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-table: 7.51.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-tabs: 15.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-textarea: 1.10.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-tooltip: 6.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-tree: 5.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-tree-select: 5.27.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-upload: 4.9.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + scroll-into-view-if-needed: 3.1.0 + throttle-debounce: 5.0.2 + transitivePeerDependencies: + - date-fns + - luxon + - moment + + app-builder-bin@4.0.0: {} + + app-builder-lib@24.13.3(dmg-builder@24.13.3)(electron-builder-squirrel-windows@24.13.3): + dependencies: + '@develar/schema-utils': 2.6.5 + '@electron/notarize': 2.2.1 + '@electron/osx-sign': 1.0.5 + '@electron/universal': 1.5.1 + '@malept/flatpak-bundler': 0.4.0 + '@types/fs-extra': 9.0.13 + async-exit-hook: 2.0.1 + bluebird-lst: 1.0.9 + builder-util: 24.13.1 + builder-util-runtime: 9.2.4 + chromium-pickle-js: 0.2.0 + debug: 4.4.1 + dmg-builder: 24.13.3(electron-builder-squirrel-windows@24.13.3) + ejs: 3.1.10 + electron-builder-squirrel-windows: 24.13.3(dmg-builder@24.13.3) + electron-publish: 24.13.1 + form-data: 4.0.3 + fs-extra: 10.1.0 + hosted-git-info: 4.1.0 + is-ci: 3.0.1 + isbinaryfile: 5.0.4 + js-yaml: 4.1.0 + lazy-val: 1.0.5 + minimatch: 5.1.6 + read-config-file: 6.3.2 + sanitize-filename: 1.6.3 + semver: 7.7.2 + tar: 6.2.1 + temp-file: 3.4.0 + transitivePeerDependencies: + - supports-color + + aproba@2.0.0: + optional: true + + archiver-utils@2.1.0: + dependencies: + glob: 7.2.3 + graceful-fs: 4.2.11 + lazystream: 1.0.1 + lodash.defaults: 4.2.0 + lodash.difference: 4.5.0 + lodash.flatten: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.union: 4.6.0 + normalize-path: 3.0.0 + readable-stream: 2.3.8 + + archiver-utils@3.0.4: + dependencies: + glob: 7.2.3 + graceful-fs: 4.2.11 + lazystream: 1.0.1 + lodash.defaults: 4.2.0 + lodash.difference: 4.5.0 + lodash.flatten: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.union: 4.6.0 + normalize-path: 3.0.0 + readable-stream: 3.6.2 + + archiver@5.3.2: + dependencies: + archiver-utils: 2.1.0 + async: 3.2.6 + buffer-crc32: 0.2.13 + readable-stream: 3.6.2 + readdir-glob: 1.1.3 + tar-stream: 2.2.0 + zip-stream: 4.1.1 + + are-we-there-yet@2.0.0: + dependencies: + delegates: 1.0.0 + readable-stream: 3.6.2 + optional: true + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + + array-ify@1.0.0: {} + + array-union@2.1.0: {} + + asap@2.0.6: {} + + asn1@0.2.6: + dependencies: + safer-buffer: 2.1.2 + + assert-plus@0.2.0: {} + + assert-plus@1.0.0: {} + + assign-symbols@1.0.0: {} + + astral-regex@2.0.0: + optional: true + + astring@1.9.0: {} + + async-exit-hook@2.0.1: {} + + async@2.6.4: + dependencies: + lodash: 4.17.21 + + async@3.2.6: {} + + asynckit@0.4.0: {} + + at-least-node@1.0.0: {} + + attr-accept@2.2.5: {} + + aws-sign2@0.6.0: {} + + aws4@1.13.2: {} + + axios@1.9.0: + dependencies: + follow-redirects: 1.15.9 + form-data: 4.0.3 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + babel-plugin-macros@3.1.0: + dependencies: + '@babel/runtime': 7.27.6 + cosmiconfig: 7.1.0 + resolve: 1.22.10 + + babel-plugin-polyfill-corejs2@0.4.13(@babel/core@7.27.4): + dependencies: + '@babel/compat-data': 7.27.5 + '@babel/core': 7.27.4 + '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.27.4) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.11.1(@babel/core@7.27.4): + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.27.4) + core-js-compat: 3.43.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.6.4(@babel/core@7.27.4): + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.27.4) + transitivePeerDependencies: + - supports-color + + bail@2.0.2: {} + + balanced-match@1.0.2: {} + + base64-js@1.5.1: {} + + bcrypt-pbkdf@1.0.2: + dependencies: + tweetnacl: 0.14.5 + + bl@1.1.2: + dependencies: + readable-stream: 2.0.6 + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + + bluebird-lst@1.0.9: + dependencies: + bluebird: 3.7.2 + + bluebird@3.4.7: {} + + bluebird@3.7.2: {} + + boolean@3.2.0: + optional: true + + boom@2.10.1: + dependencies: + hoek: 2.16.3 + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.25.0: + dependencies: + caniuse-lite: 1.0.30001722 + electron-to-chromium: 1.5.166 + node-releases: 2.0.19 + update-browserslist-db: 1.1.3(browserslist@4.25.0) + + buffer-crc32@0.2.13: {} + + buffer-equal@1.0.1: {} + + buffer-from@1.1.2: {} + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + builder-util-runtime@9.2.4: + dependencies: + debug: 4.4.1 + sax: 1.4.1 + transitivePeerDependencies: + - supports-color + + builder-util-runtime@9.3.1: + dependencies: + debug: 4.4.1 + sax: 1.4.1 + transitivePeerDependencies: + - supports-color + + builder-util@24.13.1: + dependencies: + 7zip-bin: 5.2.0 + '@types/debug': 4.1.12 + app-builder-bin: 4.0.0 + bluebird-lst: 1.0.9 + builder-util-runtime: 9.2.4 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.1 + fs-extra: 10.1.0 + http-proxy-agent: 5.0.0 + https-proxy-agent: 5.0.1 + is-ci: 3.0.1 + js-yaml: 4.1.0 + source-map-support: 0.5.21 + stat-mode: 1.0.0 + temp-file: 3.4.0 + transitivePeerDependencies: + - supports-color + + busboy@1.6.0: + dependencies: + streamsearch: 1.1.0 + + cacheable-lookup@5.0.4: {} + + cacheable-request@7.0.4: + dependencies: + clone-response: 1.0.3 + get-stream: 5.2.0 + http-cache-semantics: 4.2.0 + keyv: 4.5.4 + lowercase-keys: 2.0.0 + normalize-url: 6.1.0 + responselike: 2.0.1 + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + callsites@3.1.0: {} + + camelcase@6.3.0: {} + + caniuse-lite@1.0.30001722: {} + + canvas@2.11.2: + dependencies: + '@mapbox/node-pre-gyp': 1.0.11 + nan: 2.22.2 + simple-get: 3.1.1 + transitivePeerDependencies: + - encoding + - supports-color + optional: true + + caseless@0.11.0: {} + + ccount@2.0.1: {} + + cfb@1.2.2: + dependencies: + adler-32: 1.3.1 + crc-32: 1.2.2 + + chalk@1.1.3: + dependencies: + ansi-styles: 2.2.1 + escape-string-regexp: 1.0.5 + has-ansi: 2.0.0 + strip-ansi: 3.0.1 + supports-color: 2.0.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.4.1: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + + character-reference-invalid@2.0.1: {} + + chevrotain-allstar@0.3.1(chevrotain@11.0.3): + dependencies: + chevrotain: 11.0.3 + lodash-es: 4.17.21 + + chevrotain@11.0.3: + dependencies: + '@chevrotain/cst-dts-gen': 11.0.3 + '@chevrotain/gast': 11.0.3 + '@chevrotain/regexp-to-ast': 11.0.3 + '@chevrotain/types': 11.0.3 + '@chevrotain/utils': 11.0.3 + lodash-es: 4.17.21 + + chownr@1.1.4: {} + + chownr@2.0.0: {} + + chroma-js@3.1.2: {} + + chromium-pickle-js@0.2.0: {} + + ci-info@3.9.0: {} + + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + + classnames@2.5.1: {} + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-truncate@2.1.0: + dependencies: + slice-ansi: 3.0.0 + string-width: 4.2.3 + optional: true + + cli-truncate@4.0.0: + dependencies: + slice-ansi: 5.0.0 + string-width: 7.2.0 + + client-only@0.0.1: {} + + cliui@7.0.4: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + clone-response@1.0.3: + dependencies: + mimic-response: 1.0.1 + + clsx@1.2.1: {} + + clsx@2.1.1: {} + + co-prompt@1.0.0: + dependencies: + keypress: 0.2.1 + + co@4.6.0: {} + + codepage@1.15.0: {} + + collapse-white-space@2.1.0: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + color-string@1.9.1: + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + + color-support@1.1.3: + optional: true + + color@4.2.3: + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + + colord@2.9.3: {} + + colorette@2.0.20: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + comma-separated-tokens@2.0.3: {} + + commander@13.1.0: {} + + commander@2.9.0: + dependencies: + graceful-readlink: 1.0.1 + + commander@5.1.0: {} + + commander@7.2.0: {} + + commander@8.3.0: {} + + compare-func@2.0.0: + dependencies: + array-ify: 1.0.0 + dot-prop: 5.3.0 + + compare-version@0.1.2: {} + + compress-commons@4.1.2: + dependencies: + buffer-crc32: 0.2.13 + crc32-stream: 4.0.3 + normalize-path: 3.0.0 + readable-stream: 3.6.2 + + compute-scroll-into-view@3.1.1: {} + + concat-map@0.0.1: {} + + concurrently@8.2.2: + dependencies: + chalk: 4.1.2 + date-fns: 2.30.0 + lodash: 4.17.21 + rxjs: 7.8.2 + shell-quote: 1.8.3 + spawn-command: 0.0.2 + supports-color: 8.1.1 + tree-kill: 1.2.2 + yargs: 17.7.2 + + confbox@0.1.8: {} + + confbox@0.2.2: {} + + config-file-ts@0.2.6: + dependencies: + glob: 10.4.5 + typescript: 5.8.3 + + console-control-strings@1.1.0: + optional: true + + console-table-printer@2.14.3: + dependencies: + simple-wcswidth: 1.0.1 + + conventional-changelog-angular@7.0.0: + dependencies: + compare-func: 2.0.0 + + conventional-changelog-conventionalcommits@7.0.2: + dependencies: + compare-func: 2.0.0 + + conventional-commits-parser@5.0.0: + dependencies: + JSONStream: 1.3.5 + is-text-path: 2.0.0 + meow: 12.1.1 + split2: 4.2.0 + + convert-source-map@1.9.0: {} + + convert-source-map@2.0.0: {} + + copy-to-clipboard@3.3.3: + dependencies: + toggle-selection: 1.0.6 + + core-js-compat@3.43.0: + dependencies: + browserslist: 4.25.0 + + core-util-is@1.0.2: {} + + core-util-is@1.0.3: {} + + cose-base@1.0.3: + dependencies: + layout-base: 1.0.2 + + cose-base@2.2.0: + dependencies: + layout-base: 2.0.1 + + cosmiconfig-typescript-loader@6.1.0(@types/node@24.0.1)(cosmiconfig@9.0.0(typescript@5.8.3))(typescript@5.8.3): + dependencies: + '@types/node': 24.0.1 + cosmiconfig: 9.0.0(typescript@5.8.3) + jiti: 2.4.2 + typescript: 5.8.3 + + cosmiconfig@7.1.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.1 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + + cosmiconfig@9.0.0(typescript@5.8.3): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + parse-json: 5.2.0 + optionalDependencies: + typescript: 5.8.3 + + crc-32@1.2.2: {} + + crc32-stream@4.0.3: + dependencies: + crc-32: 1.2.2 + readable-stream: 3.6.2 + + crc@3.8.0: + dependencies: + buffer: 5.7.1 + optional: true + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + cryptiles@2.0.5: + dependencies: + boom: 2.10.1 + + csstype@3.1.3: {} + + cytoscape-cose-bilkent@4.1.0(cytoscape@3.32.0): + dependencies: + cose-base: 1.0.3 + cytoscape: 3.32.0 + + cytoscape-fcose@2.2.0(cytoscape@3.32.0): + dependencies: + cose-base: 2.2.0 + cytoscape: 3.32.0 + + cytoscape@3.32.0: {} + + d3-array@2.12.1: + dependencies: + internmap: 1.0.1 + + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-axis@3.0.0: {} + + d3-brush@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3-chord@3.0.1: + dependencies: + d3-path: 3.1.0 + + d3-color@3.1.0: {} + + d3-contour@4.0.2: + dependencies: + d3-array: 3.2.4 + + d3-delaunay@6.0.4: + dependencies: + delaunator: 5.0.1 + + d3-dispatch@3.0.1: {} + + d3-drag@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-selection: 3.0.0 + + d3-dsv@3.0.1: + dependencies: + commander: 7.2.0 + iconv-lite: 0.6.3 + rw: 1.3.3 + + d3-ease@3.0.1: {} + + d3-fetch@3.0.1: + dependencies: + d3-dsv: 3.0.1 + + d3-force@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-quadtree: 3.0.1 + d3-timer: 3.0.1 + + d3-format@3.1.0: {} + + d3-geo@3.1.1: + dependencies: + d3-array: 3.2.4 + + d3-hierarchy@3.1.2: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@1.0.9: {} + + d3-path@3.1.0: {} + + d3-polygon@3.0.1: {} + + d3-quadtree@3.0.1: {} + + d3-random@3.0.1: {} + + d3-sankey@0.12.3: + dependencies: + d3-array: 2.12.1 + d3-shape: 1.3.7 + + d3-scale-chromatic@3.1.0: + dependencies: + d3-color: 3.1.0 + d3-interpolate: 3.0.1 + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.0 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-selection@3.0.0: {} + + d3-shape@1.3.7: + dependencies: + d3-path: 1.0.9 + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@3.0.1: {} + + d3-transition@3.0.1(d3-selection@3.0.0): + dependencies: + d3-color: 3.1.0 + d3-dispatch: 3.0.1 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-timer: 3.0.1 + + d3-zoom@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3@7.9.0: + dependencies: + d3-array: 3.2.4 + d3-axis: 3.0.0 + d3-brush: 3.0.0 + d3-chord: 3.0.1 + d3-color: 3.1.0 + d3-contour: 4.0.2 + d3-delaunay: 6.0.4 + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-dsv: 3.0.1 + d3-ease: 3.0.1 + d3-fetch: 3.0.1 + d3-force: 3.0.0 + d3-format: 3.1.0 + d3-geo: 3.1.1 + d3-hierarchy: 3.1.2 + d3-interpolate: 3.0.1 + d3-path: 3.1.0 + d3-polygon: 3.0.1 + d3-quadtree: 3.0.1 + d3-random: 3.0.1 + d3-scale: 4.0.2 + d3-scale-chromatic: 3.1.0 + d3-selection: 3.0.0 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + d3-timer: 3.0.1 + d3-transition: 3.0.1(d3-selection@3.0.0) + d3-zoom: 3.0.0 + + dagre-d3-es@7.0.11: + dependencies: + d3: 7.9.0 + lodash-es: 4.17.21 + + dargs@8.1.0: {} + + dashdash@1.14.1: + dependencies: + assert-plus: 1.0.0 + + date-fns@2.30.0: + dependencies: + '@babel/runtime': 7.27.6 + + dayjs@1.11.13: {} + + debug@4.4.1: + dependencies: + ms: 2.1.3 + + decamelize@1.2.0: {} + + decimal.js-light@2.5.1: {} + + decode-named-character-reference@1.1.0: + dependencies: + character-entities: 2.0.2 + + decode-uri-component@0.4.1: {} + + decompress-response@4.2.1: + dependencies: + mimic-response: 2.1.0 + optional: true + + decompress-response@6.0.0: + dependencies: + mimic-response: 3.1.0 + + deep-extend@0.6.0: {} + + defer-to-connect@2.0.1: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + optional: true + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + optional: true + + delaunator@5.0.1: + dependencies: + robust-predicates: 3.0.2 + + delayed-stream@1.0.0: {} + + delegates@1.0.0: + optional: true + + dequal@2.0.3: {} + + detect-libc@2.0.4: {} + + detect-node@2.1.0: + optional: true + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + dezalgo@1.0.4: + dependencies: + asap: 2.0.6 + wrappy: 1.0.2 + + diff-match-patch@1.0.5: {} + + dingbat-to-unicode@1.0.1: {} + + dir-compare@3.3.0: + dependencies: + buffer-equal: 1.0.1 + minimatch: 3.1.2 + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + dmg-builder@24.13.3(electron-builder-squirrel-windows@24.13.3): + dependencies: + app-builder-lib: 24.13.3(dmg-builder@24.13.3)(electron-builder-squirrel-windows@24.13.3) + builder-util: 24.13.1 + builder-util-runtime: 9.2.4 + fs-extra: 10.1.0 + iconv-lite: 0.6.3 + js-yaml: 4.1.0 + optionalDependencies: + dmg-license: 1.0.11 + transitivePeerDependencies: + - electron-builder-squirrel-windows + - supports-color + + dmg-license@1.0.11: + dependencies: + '@types/plist': 3.0.5 + '@types/verror': 1.10.11 + ajv: 6.12.6 + crc: 3.8.0 + iconv-corefoundation: 1.1.7 + plist: 3.1.0 + smart-buffer: 4.2.0 + verror: 1.10.1 + optional: true + + dom-helpers@5.2.1: + dependencies: + '@babel/runtime': 7.27.6 + csstype: 3.1.3 + + dompurify@3.2.6: + optionalDependencies: + '@types/trusted-types': 2.0.7 + + dot-prop@5.3.0: + dependencies: + is-obj: 2.0.0 + + dotenv-expand@5.1.0: {} + + dotenv@9.0.2: {} + + duck@0.1.12: + dependencies: + underscore: 1.13.7 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + eastasianwidth@0.2.0: {} + + ecc-jsbn@0.1.2: + dependencies: + jsbn: 0.1.1 + safer-buffer: 2.1.2 + + ejs@3.1.10: + dependencies: + jake: 10.9.2 + + electron-build@0.0.3: + dependencies: + co: 4.6.0 + co-prompt: 1.0.0 + commander: 2.9.0 + jszip: 2.5.0 + path: 0.12.7 + progress: 1.1.8 + q: 1.4.1 + request: 2.72.0 + url: 0.11.0 + + electron-builder-squirrel-windows@24.13.3(dmg-builder@24.13.3): + dependencies: + app-builder-lib: 24.13.3(dmg-builder@24.13.3)(electron-builder-squirrel-windows@24.13.3) + archiver: 5.3.2 + builder-util: 24.13.1 + fs-extra: 10.1.0 + transitivePeerDependencies: + - dmg-builder + - supports-color + + electron-builder@24.13.3(electron-builder-squirrel-windows@24.13.3): + dependencies: + app-builder-lib: 24.13.3(dmg-builder@24.13.3)(electron-builder-squirrel-windows@24.13.3) + builder-util: 24.13.1 + builder-util-runtime: 9.2.4 + chalk: 4.1.2 + dmg-builder: 24.13.3(electron-builder-squirrel-windows@24.13.3) + fs-extra: 10.1.0 + is-ci: 3.0.1 + lazy-val: 1.0.5 + read-config-file: 6.3.2 + simple-update-notifier: 2.0.0 + yargs: 17.7.2 + transitivePeerDependencies: + - electron-builder-squirrel-windows + - supports-color + + electron-publish@24.13.1: + dependencies: + '@types/fs-extra': 9.0.13 + builder-util: 24.13.1 + builder-util-runtime: 9.2.4 + chalk: 4.1.2 + fs-extra: 10.1.0 + lazy-val: 1.0.5 + mime: 2.6.0 + transitivePeerDependencies: + - supports-color + + electron-to-chromium@1.5.166: {} + + electron-updater@6.6.2: + dependencies: + builder-util-runtime: 9.3.1 + fs-extra: 10.1.0 + js-yaml: 4.1.0 + lazy-val: 1.0.5 + lodash.escaperegexp: 4.1.2 + lodash.isequal: 4.5.0 + semver: 7.7.2 + tiny-typed-emitter: 2.1.0 + transitivePeerDependencies: + - supports-color + + electron@35.5.1: + dependencies: + '@electron/get': 2.0.3 + '@types/node': 22.15.31 + extract-zip: 2.0.1 + transitivePeerDependencies: + - supports-color + + emoji-mart@5.6.0: {} + + emoji-regex@10.4.0: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + end-of-stream@1.4.4: + dependencies: + once: 1.4.0 + + entities@6.0.1: {} + + enumify@1.0.4: {} + + env-paths@2.2.1: {} + + environment@1.1.0: {} + + err-code@2.0.3: {} + + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-toolkit@1.43.0: {} + + es6-error@4.1.1: + optional: true + + esast-util-from-estree@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + devlop: 1.1.0 + estree-util-visit: 2.0.0 + unist-util-position-from-estree: 2.0.0 + + esast-util-from-js@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + acorn: 8.15.0 + esast-util-from-estree: 2.0.0 + vfile-message: 4.0.2 + + escalade@3.2.0: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@4.0.0: {} + + escape-string-regexp@5.0.0: {} + + estree-util-attach-comments@3.0.0: + dependencies: + '@types/estree': 1.0.8 + + estree-util-build-jsx@3.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + estree-walker: 3.0.3 + + estree-util-is-identifier-name@3.0.0: {} + + estree-util-scope@1.0.0: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + + estree-util-to-js@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + astring: 1.9.0 + source-map: 0.7.4 + + estree-util-visit@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/unist': 3.0.3 + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + event-target-shim@5.0.1: {} + + eventemitter3@4.0.7: {} + + eventemitter3@5.0.1: {} + + eventsource-parser@3.0.2: {} + + eventsource-parser@3.0.6: {} + + execa@8.0.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.3.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + + expand-template@2.0.3: {} + + exsolve@1.0.5: {} + + extend-shallow@2.0.1: + dependencies: + is-extendable: 0.1.1 + + extend-shallow@3.0.2: + dependencies: + assign-symbols: 1.0.0 + is-extendable: 1.0.1 + + extend@3.0.2: {} + + extract-zip@2.0.1: + dependencies: + debug: 4.4.1 + get-stream: 5.2.0 + yauzl: 2.10.0 + optionalDependencies: + '@types/yauzl': 2.10.3 + transitivePeerDependencies: + - supports-color + + extsprintf@1.3.0: {} + + extsprintf@1.4.1: {} + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-uri@3.0.6: {} + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + fd-slicer@1.1.0: + dependencies: + pend: 1.2.0 + + file-selector@0.5.0: + dependencies: + tslib: 2.8.1 + + filelist@1.0.4: + dependencies: + minimatch: 5.1.6 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + filter-obj@5.1.0: {} + + find-root@1.1.0: {} + + find-up@7.0.0: + dependencies: + locate-path: 7.2.0 + path-exists: 5.0.0 + unicorn-magic: 0.1.0 + + follow-redirects@1.15.9: {} + + for-in@1.0.2: {} + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + forever-agent@0.6.1: {} + + form-data-encoder@1.7.2: {} + + form-data@1.0.1: + dependencies: + async: 2.6.4 + combined-stream: 1.0.8 + mime-types: 2.1.35 + + form-data@4.0.3: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + + formdata-node@4.4.1: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 4.0.0-beta.3 + + formidable@3.5.4: + dependencies: + '@paralleldrive/cuid2': 2.2.2 + dezalgo: 1.0.4 + once: 1.4.0 + + frac@1.1.2: {} + + framer-motion@12.17.0(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + motion-dom: 12.17.0 + motion-utils: 12.12.1 + tslib: 2.8.1 + optionalDependencies: + '@emotion/is-prop-valid': 1.3.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + from2@2.3.0: + dependencies: + inherits: 2.0.4 + readable-stream: 2.3.8 + + fs-constants@1.0.0: {} + + fs-extra@10.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + + fs-extra@11.3.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + + fs-extra@8.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + + fs-extra@9.1.0: + dependencies: + at-least-node: 1.0.0 + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + + fs-minipass@2.1.0: + dependencies: + minipass: 3.3.6 + + fs.realpath@1.0.0: {} + + function-bind@1.1.2: {} + + gauge@3.0.2: + dependencies: + aproba: 2.0.0 + color-support: 1.1.3 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + object-assign: 4.1.1 + signal-exit: 3.0.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wide-align: 1.1.5 + optional: true + + generate-function@2.3.1: + dependencies: + is-property: 1.0.2 + + generate-object-property@1.2.0: + dependencies: + is-property: 1.0.2 + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-east-asian-width@1.3.0: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-stream@5.2.0: + dependencies: + pump: 3.0.2 + + get-stream@8.0.1: {} + + get-value@2.0.6: {} + + getpass@0.1.7: + dependencies: + assert-plus: 1.0.0 + + giscus@1.6.0: + dependencies: + lit: 3.3.0 + + git-raw-commits@4.0.0: + dependencies: + dargs: 8.1.0 + meow: 12.1.1 + split2: 4.2.0 + + github-from-package@0.0.0: {} + + github-markdown-css@5.8.1: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob@10.4.5: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + global-agent@3.0.0: + dependencies: + boolean: 3.2.0 + es6-error: 4.1.1 + matcher: 3.0.0 + roarr: 2.15.4 + semver: 7.7.2 + serialize-error: 7.0.1 + optional: true + + global-directory@4.0.1: + dependencies: + ini: 4.1.1 + + globals@11.12.0: {} + + globals@15.15.0: {} + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + optional: true + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + gopd@1.2.0: {} + + got@11.8.6: + dependencies: + '@sindresorhus/is': 4.6.0 + '@szmarczak/http-timer': 4.0.6 + '@types/cacheable-request': 6.0.3 + '@types/responselike': 1.0.3 + cacheable-lookup: 5.0.4 + cacheable-request: 7.0.4 + decompress-response: 6.0.0 + http2-wrapper: 1.0.3 + lowercase-keys: 2.0.0 + p-cancelable: 2.1.1 + responselike: 2.0.1 + + graceful-fs@4.2.11: {} + + graceful-readlink@1.0.1: {} + + hachure-fill@0.5.2: {} + + har-validator@2.0.6: + dependencies: + chalk: 1.1.3 + commander: 2.9.0 + is-my-json-valid: 2.20.6 + pinkie-promise: 2.0.1 + + has-ansi@2.0.0: + dependencies: + ansi-regex: 2.1.1 + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + optional: true + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + has-unicode@2.0.1: + optional: true + + has@1.0.4: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hast-util-from-dom@5.0.1: + dependencies: + '@types/hast': 3.0.4 + hastscript: 9.0.1 + web-namespaces: 2.0.1 + + hast-util-from-html-isomorphic@2.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-from-dom: 5.0.1 + hast-util-from-html: 2.0.3 + unist-util-remove-position: 5.0.0 + + hast-util-from-html@2.0.3: + dependencies: + '@types/hast': 3.0.4 + devlop: 1.1.0 + hast-util-from-parse5: 8.0.3 + parse5: 7.3.0 + vfile: 6.0.3 + vfile-message: 4.0.2 + + hast-util-from-parse5@8.0.3: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + devlop: 1.1.0 + hastscript: 9.0.1 + property-information: 7.1.0 + vfile: 6.0.3 + vfile-location: 5.0.3 + web-namespaces: 2.0.1 + + hast-util-is-element@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-parse-selector@4.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-raw@9.1.0: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + '@ungap/structured-clone': 1.3.0 + hast-util-from-parse5: 8.0.3 + hast-util-to-parse5: 8.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.0 + parse5: 7.3.0 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-to-estree@3.1.3: + dependencies: + '@types/estree': 1.0.8 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-attach-comments: 3.0.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.16 + unist-util-position: 5.0.0 + zwitch: 2.0.4 + transitivePeerDependencies: + - supports-color + + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.0 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-to-jsx-runtime@2.3.6: + dependencies: + '@types/estree': 1.0.8 + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.16 + unist-util-position: 5.0.0 + vfile-message: 4.0.2 + transitivePeerDependencies: + - supports-color + + hast-util-to-parse5@8.0.0: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + property-information: 6.5.0 + space-separated-tokens: 2.0.2 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-to-text@4.0.2: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + hast-util-is-element: 3.0.0 + unist-util-find-after: 5.0.0 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hastscript@9.0.1: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + hast-util-parse-selector: 4.0.0 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + + hawk@3.1.3: + dependencies: + boom: 2.10.1 + cryptiles: 2.0.5 + hoek: 2.16.3 + sntp: 1.0.9 + + hoek@2.16.3: {} + + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + + hosted-git-info@4.1.0: + dependencies: + lru-cache: 6.0.0 + + html-parse-stringify@3.0.1: + dependencies: + void-elements: 3.1.0 + + html-url-attributes@3.0.1: {} + + html-void-elements@3.0.0: {} + + http-cache-semantics@4.2.0: {} + + http-proxy-agent@5.0.0: + dependencies: + '@tootallnate/once': 2.0.0 + agent-base: 6.0.2 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + + http-signature@1.1.1: + dependencies: + assert-plus: 0.2.0 + jsprim: 1.4.2 + sshpk: 1.18.0 + + http2-wrapper@1.0.3: + dependencies: + quick-lru: 5.1.1 + resolve-alpn: 1.2.1 + + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + + human-signals@5.0.0: {} + + humanize-ms@1.2.1: + dependencies: + ms: 2.1.3 + + husky@9.1.7: {} + + i18next-browser-languagedetector@8.2.0: + dependencies: + '@babel/runtime': 7.27.6 + + i18next@24.2.3(typescript@5.8.3): + dependencies: + '@babel/runtime': 7.27.6 + optionalDependencies: + typescript: 5.8.3 + + iconv-corefoundation@1.1.7: + dependencies: + cli-truncate: 2.1.0 + node-addon-api: 1.7.2 + optional: true + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + ieee754@1.2.1: {} + + ignore@5.3.2: {} + + image-size@2.0.2: {} + + immediate@3.0.6: {} + + immer@10.1.1: {} + + immer@11.1.0: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-meta-resolve@4.1.0: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.3: {} + + inherits@2.0.4: {} + + ini@1.3.8: {} + + ini@4.1.1: {} + + inline-style-parser@0.2.4: {} + + internmap@1.0.1: {} + + internmap@2.0.3: {} + + intersection-observer@0.12.2: {} + + into-stream@6.0.0: + dependencies: + from2: 2.3.0 + p-is-promise: 3.0.0 + + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + + is-arrayish@0.2.1: {} + + is-arrayish@0.3.2: {} + + is-ci@3.0.1: + dependencies: + ci-info: 3.9.0 + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-core-module@2.9.0: + dependencies: + has: 1.0.4 + + is-decimal@2.0.1: {} + + is-extendable@0.1.1: {} + + is-extendable@1.0.1: + dependencies: + is-plain-object: 2.0.4 + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-fullwidth-code-point@4.0.0: {} + + is-fullwidth-code-point@5.0.0: + dependencies: + get-east-asian-width: 1.3.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-hexadecimal@2.0.1: {} + + is-my-ip-valid@1.0.1: {} + + is-my-json-valid@2.20.6: + dependencies: + generate-function: 2.3.1 + generate-object-property: 1.2.0 + is-my-ip-valid: 1.0.1 + jsonpointer: 5.0.1 + xtend: 4.0.2 + + is-number@7.0.0: {} + + is-obj@2.0.0: {} + + is-plain-obj@4.1.0: {} + + is-plain-object@2.0.4: + dependencies: + isobject: 3.0.1 + + is-property@1.0.2: {} + + is-stream@3.0.0: {} + + is-text-path@2.0.0: + dependencies: + text-extensions: 2.4.0 + + is-typedarray@1.0.0: {} + + isarray@1.0.0: {} + + isbinaryfile@4.0.10: {} + + isbinaryfile@5.0.4: {} + + isexe@2.0.0: {} + + isobject@3.0.1: {} + + isstream@0.1.2: {} + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jake@10.9.2: + dependencies: + async: 3.2.6 + chalk: 4.1.2 + filelist: 1.0.4 + minimatch: 3.1.2 + + jiti@2.4.2: {} + + joi@17.13.3: + dependencies: + '@hapi/hoek': 9.3.0 + '@hapi/topo': 5.1.0 + '@sideway/address': 4.1.5 + '@sideway/formula': 3.0.1 + '@sideway/pinpoint': 2.0.0 + + jotai@2.12.5(@types/react@19.1.8)(react@18.3.1): + optionalDependencies: + '@types/react': 19.1.8 + react: 18.3.1 + + js-cookie@3.0.5: {} + + js-tiktoken@1.0.20: + dependencies: + base64-js: 1.5.1 + + js-tokens@4.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsbn@0.1.1: {} + + jsesc@2.5.2: {} + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-schema@0.4.0: {} + + json-stringify-safe@5.0.1: {} + + json2mq@0.2.0: + dependencies: + string-convert: 0.2.1 + + json5@2.2.3: {} + + jsondiffpatch@0.6.0: + dependencies: + '@types/diff-match-patch': 1.0.36 + chalk: 5.4.1 + diff-match-patch: 1.0.5 + + jsonfile@4.0.0: + optionalDependencies: + graceful-fs: 4.2.11 + + jsonfile@6.1.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + jsonparse@1.3.1: {} + + jsonpointer@5.0.1: {} + + jsonrepair@3.13.1: {} + + jsprim@1.4.2: + dependencies: + assert-plus: 1.0.0 + extsprintf: 1.3.0 + json-schema: 0.4.0 + verror: 1.10.0 + + jszip@2.5.0: + dependencies: + pako: 0.2.9 + + jszip@3.10.1: + dependencies: + lie: 3.3.0 + pako: 1.0.11 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + + katex@0.16.22: + dependencies: + commander: 8.3.0 + + keypress@0.2.1: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + khroma@2.1.0: {} + + kolorist@1.8.0: {} + + langchain@0.3.28(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.76)))(axios@1.9.0)(openai@4.104.0(zod@3.25.76)): + dependencies: + '@langchain/core': 0.3.58(openai@4.104.0(zod@3.25.76)) + '@langchain/openai': 0.5.13(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.76))) + '@langchain/textsplitters': 0.1.0(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.76))) + js-tiktoken: 1.0.20 + js-yaml: 4.1.0 + jsonpointer: 5.0.1 + langsmith: 0.3.31(openai@4.104.0(zod@3.25.76)) + openapi-types: 12.1.3 + p-retry: 4.6.2 + uuid: 10.0.0 + yaml: 2.8.0 + zod: 3.25.76 + optionalDependencies: + axios: 1.9.0 + transitivePeerDependencies: + - encoding + - openai + - ws + + langium@3.3.1: + dependencies: + chevrotain: 11.0.3 + chevrotain-allstar: 0.3.1(chevrotain@11.0.3) + vscode-languageserver: 9.0.1 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.0.8 + + langsmith@0.3.31(openai@4.104.0(zod@3.25.76)): + dependencies: + '@types/uuid': 10.0.0 + chalk: 4.1.2 + console-table-printer: 2.14.3 + p-queue: 6.6.2 + p-retry: 4.6.2 + semver: 7.7.2 + uuid: 10.0.0 + optionalDependencies: + openai: 4.104.0(zod@3.25.76) + + layout-base@1.0.2: {} + + layout-base@2.0.1: {} + + lazy-val@1.0.5: {} + + lazystream@1.0.1: + dependencies: + readable-stream: 2.3.8 + + leva@0.10.0(@types/react@19.1.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@radix-ui/react-portal': 1.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-tooltip': 1.0.5(@types/react@19.1.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@stitches/react': 1.2.8(react@18.3.1) + '@use-gesture/react': 10.3.1(react@18.3.1) + colord: 2.9.3 + dequal: 2.0.3 + merge-value: 1.0.0 + react: 18.3.1 + react-colorful: 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-dom: 18.3.1(react@18.3.1) + react-dropzone: 12.1.0(react@18.3.1) + v8n: 1.5.1 + zustand: 3.7.2(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + + lie@3.3.0: + dependencies: + immediate: 3.0.6 + + lilconfig@3.1.3: {} + + lines-and-columns@1.2.4: {} + + lint-staged@15.5.2: + dependencies: + chalk: 5.4.1 + commander: 13.1.0 + debug: 4.4.1 + execa: 8.0.1 + lilconfig: 3.1.3 + listr2: 8.3.3 + micromatch: 4.0.8 + pidtree: 0.6.0 + string-argv: 0.3.2 + yaml: 2.8.0 + transitivePeerDependencies: + - supports-color + + listr2@8.3.3: + dependencies: + cli-truncate: 4.0.0 + colorette: 2.0.20 + eventemitter3: 5.0.1 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 9.0.0 + + lit-element@4.2.0: + dependencies: + '@lit-labs/ssr-dom-shim': 1.3.0 + '@lit/reactive-element': 2.1.0 + lit-html: 3.3.0 + + lit-html@3.3.0: + dependencies: + '@types/trusted-types': 2.0.7 + + lit@3.3.0: + dependencies: + '@lit/reactive-element': 2.1.0 + lit-element: 4.2.0 + lit-html: 3.3.0 + + local-pkg@1.1.1: + dependencies: + mlly: 1.7.4 + pkg-types: 2.1.0 + quansync: 0.2.10 + + locate-path@7.2.0: + dependencies: + p-locate: 6.0.0 + + lodash-es@4.17.21: {} + + lodash.camelcase@4.3.0: {} + + lodash.debounce@4.0.8: {} + + lodash.defaults@4.2.0: {} + + lodash.difference@4.5.0: {} + + lodash.escaperegexp@4.1.2: {} + + lodash.flatten@4.4.0: {} + + lodash.isequal@4.5.0: {} + + lodash.isplainobject@4.0.6: {} + + lodash.kebabcase@4.1.1: {} + + lodash.merge@4.6.2: {} + + lodash.mergewith@4.6.2: {} + + lodash.snakecase@4.1.1: {} + + lodash.startcase@4.4.0: {} + + lodash.union@4.6.0: {} + + lodash.uniq@4.5.0: {} + + lodash.upperfirst@4.3.1: {} + + lodash@4.17.21: {} + + log-update@6.1.0: + dependencies: + ansi-escapes: 7.0.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.0 + strip-ansi: 7.1.0 + wrap-ansi: 9.0.0 + + longest-streak@3.1.0: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lop@0.4.2: + dependencies: + duck: 0.1.12 + option: 0.2.4 + underscore: 1.13.7 + + lowercase-keys@2.0.0: {} + + lru-cache@10.4.3: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + + lucide-react@0.469.0(react@18.3.1): + dependencies: + react: 18.3.1 + + lucide-react@0.484.0(react@18.3.1): + dependencies: + react: 18.3.1 + + make-dir@3.1.0: + dependencies: + semver: 6.3.1 + optional: true + + mammoth@1.9.1: + dependencies: + '@xmldom/xmldom': 0.8.10 + argparse: 1.0.10 + base64-js: 1.5.1 + bluebird: 3.4.7 + dingbat-to-unicode: 1.0.1 + jszip: 3.10.1 + lop: 0.4.2 + path-is-absolute: 1.0.1 + underscore: 1.13.7 + xmlbuilder: 10.1.1 + + markdown-extensions@2.0.0: {} + + markdown-table@3.0.4: {} + + marked@15.0.12: {} + + matcher@3.0.0: + dependencies: + escape-string-regexp: 4.0.0 + optional: true + + math-intrinsics@1.1.0: {} + + mdast-util-find-and-replace@3.0.2: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + + mdast-util-from-markdown@2.0.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.1.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.1.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.1.0: + dependencies: + mdast-util-from-markdown: 2.0.2 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.1.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-math@3.0.0: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + longest-streak: 3.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + unist-util-remove-position: 5.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.2.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx@3.0.0: + dependencies: + mdast-util-from-markdown: 2.0.2 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-newline-to-break@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-find-and-replace: 3.0.2 + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.0 + + mdast-util-to-hast@13.2.0: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.0.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + + meow@12.1.1: {} + + merge-stream@2.0.0: {} + + merge-value@1.0.0: + dependencies: + get-value: 2.0.6 + is-extendable: 1.0.1 + mixin-deep: 1.3.2 + set-value: 2.0.1 + + merge2@1.4.1: {} + + mermaid@11.6.0: + dependencies: + '@braintree/sanitize-url': 7.1.1 + '@iconify/utils': 2.3.0 + '@mermaid-js/parser': 0.4.0 + '@types/d3': 7.4.3 + cytoscape: 3.32.0 + cytoscape-cose-bilkent: 4.1.0(cytoscape@3.32.0) + cytoscape-fcose: 2.2.0(cytoscape@3.32.0) + d3: 7.9.0 + d3-sankey: 0.12.3 + dagre-d3-es: 7.0.11 + dayjs: 1.11.13 + dompurify: 3.2.6 + katex: 0.16.22 + khroma: 2.1.0 + lodash-es: 4.17.21 + marked: 15.0.12 + roughjs: 4.6.6 + stylis: 4.3.6 + ts-dedent: 2.2.0 + uuid: 11.1.0 + transitivePeerDependencies: + - supports-color + + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.1.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-strikethrough@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-table@2.1.1: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-tagfilter@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-gfm-task-list-item@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.1 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-math@3.1.0: + dependencies: + '@types/katex': 0.16.7 + devlop: 1.1.0 + katex: 0.16.22 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-mdx-expression@3.0.1: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + micromark-factory-mdx-expression: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-mdx-jsx@3.0.2: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + micromark-factory-mdx-expression: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + vfile-message: 4.0.2 + + micromark-extension-mdx-md@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-mdxjs-esm@3.0.0: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-position-from-estree: 2.0.0 + vfile-message: 4.0.2 + + micromark-extension-mdxjs@3.0.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + micromark-extension-mdx-expression: 3.0.1 + micromark-extension-mdx-jsx: 3.0.2 + micromark-extension-mdx-md: 2.0.0 + micromark-extension-mdxjs-esm: 3.0.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-mdx-expression@2.0.3: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-position-from-estree: 2.0.0 + vfile-message: 4.0.2 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-events-to-acorn@2.0.3: + dependencies: + '@types/estree': 1.0.8 + '@types/unist': 3.0.3 + devlop: 1.1.0 + estree-util-visit: 2.0.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + vfile-message: 4.0.2 + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.12 + debug: 4.4.1 + decode-named-character-reference: 1.1.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime@2.6.0: {} + + mimic-fn@4.0.0: {} + + mimic-function@5.0.1: {} + + mimic-response@1.0.1: {} + + mimic-response@2.1.0: + optional: true + + mimic-response@3.1.0: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimatch@5.1.6: + dependencies: + brace-expansion: 2.0.2 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minimist@1.2.8: {} + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + + minipass@5.0.0: {} + + minipass@7.1.2: {} + + minizlib@2.1.2: + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + + mixin-deep@1.3.2: + dependencies: + for-in: 1.0.2 + is-extendable: 1.0.1 + + mkdirp-classic@0.5.3: {} + + mkdirp@1.0.4: {} + + mlly@1.7.4: + dependencies: + acorn: 8.15.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.1 + + motion-dom@12.17.0: + dependencies: + motion-utils: 12.12.1 + + motion-utils@12.12.1: {} + + ms@2.1.3: {} + + multistream@4.1.0: + dependencies: + once: 1.4.0 + readable-stream: 3.6.2 + + mustache@4.2.0: {} + + nan@2.22.2: + optional: true + + nanoid@3.3.11: {} + + nanoid@5.1.5: {} + + napi-build-utils@1.0.2: {} + + next-themes@0.2.1(next@14.2.29(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + next: 14.2.29(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + next@14.2.29(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@next/env': 14.2.29 + '@swc/helpers': 0.5.5 + busboy: 1.6.0 + caniuse-lite: 1.0.30001722 + graceful-fs: 4.2.11 + postcss: 8.4.31 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + styled-jsx: 5.1.1(@babel/core@7.27.4)(react@18.3.1) + optionalDependencies: + '@next/swc-darwin-arm64': 14.2.29 + '@next/swc-darwin-x64': 14.2.29 + '@next/swc-linux-arm64-gnu': 14.2.29 + '@next/swc-linux-arm64-musl': 14.2.29 + '@next/swc-linux-x64-gnu': 14.2.29 + '@next/swc-linux-x64-musl': 14.2.29 + '@next/swc-win32-arm64-msvc': 14.2.29 + '@next/swc-win32-ia32-msvc': 14.2.29 + '@next/swc-win32-x64-msvc': 14.2.29 + '@opentelemetry/api': 1.9.0 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + + node-abi@3.75.0: + dependencies: + semver: 7.7.2 + + node-addon-api@1.7.2: + optional: true + + node-domexception@1.0.0: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-releases@2.0.19: {} + + node-uuid@1.4.8: {} + + nopt@5.0.0: + dependencies: + abbrev: 1.1.1 + optional: true + + normalize-path@3.0.0: {} + + normalize-url@6.1.0: {} + + npm-run-path@5.3.0: + dependencies: + path-key: 4.0.0 + + npmlog@5.0.1: + dependencies: + are-we-there-yet: 2.0.0 + console-control-strings: 1.1.0 + gauge: 3.0.2 + set-blocking: 2.0.0 + optional: true + + numeral@2.0.6: {} + + oauth-sign@0.8.2: {} + + object-assign@4.1.1: {} + + object-keys@1.1.1: + optional: true + + ollama-ai-provider@1.2.0(zod@3.25.76): + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) + partial-json: 0.1.7 + optionalDependencies: + zod: 3.25.76 + + on-change@4.0.2: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@6.0.0: + dependencies: + mimic-fn: 4.0.0 + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + oniguruma-parser@0.12.1: {} + + oniguruma-to-es@4.3.3: + dependencies: + oniguruma-parser: 0.12.1 + regex: 6.0.1 + regex-recursion: 6.0.2 + + openai@4.104.0(zod@3.25.32): + dependencies: + '@types/node': 18.19.111 + '@types/node-fetch': 2.6.12 + abort-controller: 3.0.0 + agentkeepalive: 4.6.0 + form-data-encoder: 1.7.2 + formdata-node: 4.4.1 + node-fetch: 2.7.0 + optionalDependencies: + zod: 3.25.32 + transitivePeerDependencies: + - encoding + + openai@4.104.0(zod@3.25.76): + dependencies: + '@types/node': 18.19.111 + '@types/node-fetch': 2.6.12 + abort-controller: 3.0.0 + agentkeepalive: 4.6.0 + form-data-encoder: 1.7.2 + formdata-node: 4.4.1 + node-fetch: 2.7.0 + optionalDependencies: + zod: 3.25.76 + transitivePeerDependencies: + - encoding + optional: true + + openapi-types@12.1.3: {} + + opener@1.5.2: {} + + option@0.2.4: {} + + p-cancelable@2.1.1: {} + + p-finally@1.0.0: {} + + p-is-promise@3.0.0: {} + + p-limit@4.0.0: + dependencies: + yocto-queue: 1.2.1 + + p-locate@6.0.0: + dependencies: + p-limit: 4.0.0 + + p-queue@6.6.2: + dependencies: + eventemitter3: 4.0.7 + p-timeout: 3.2.0 + + p-retry@4.6.2: + dependencies: + '@types/retry': 0.12.0 + retry: 0.13.1 + + p-timeout@3.2.0: + dependencies: + p-finally: 1.0.0 + + package-json-from-dist@1.0.1: {} + + package-manager-detector@1.3.0: {} + + pako@0.2.9: {} + + pako@1.0.11: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.1.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.27.1 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + + partial-json@0.1.7: {} + + path-data-parser@0.1.0: {} + + path-exists@5.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-key@4.0.0: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + + path-type@4.0.0: {} + + path@0.12.7: + dependencies: + process: 0.11.10 + util: 0.10.4 + + pathe@2.0.3: {} + + pdf2md-js@1.0.8: + dependencies: + '@hyzyla/pdfium': 2.1.7 + fs-extra: 11.3.0 + sharp: 0.33.5 + + pend@1.2.0: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + pidtree@0.6.0: {} + + pinkie-promise@2.0.1: + dependencies: + pinkie: 2.0.4 + + pinkie@2.0.4: {} + + pkg-fetch@3.4.2: + dependencies: + chalk: 4.1.2 + fs-extra: 9.1.0 + https-proxy-agent: 5.0.1 + node-fetch: 2.7.0 + progress: 2.0.3 + semver: 7.7.2 + tar-fs: 2.1.3 + yargs: 16.2.0 + transitivePeerDependencies: + - encoding + - supports-color + + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.7.4 + pathe: 2.0.3 + + pkg-types@2.1.0: + dependencies: + confbox: 0.2.2 + exsolve: 1.0.5 + pathe: 2.0.3 + + pkg@5.8.1: + dependencies: + '@babel/generator': 7.18.2 + '@babel/parser': 7.18.4 + '@babel/types': 7.19.0 + chalk: 4.1.2 + fs-extra: 9.1.0 + globby: 11.1.0 + into-stream: 6.0.0 + is-core-module: 2.9.0 + minimist: 1.2.8 + multistream: 4.1.0 + pkg-fetch: 3.4.2 + prebuild-install: 7.1.1 + resolve: 1.22.10 + stream-meter: 1.0.4 + transitivePeerDependencies: + - encoding + - supports-color + + plist@3.1.0: + dependencies: + '@xmldom/xmldom': 0.8.10 + base64-js: 1.5.1 + xmlbuilder: 15.1.1 + + points-on-curve@0.2.0: {} + + points-on-path@0.2.1: + dependencies: + path-data-parser: 0.1.0 + points-on-curve: 0.2.0 + + polished@4.3.1: + dependencies: + '@babel/runtime': 7.27.6 + + postcss@8.4.31: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prebuild-install@7.1.1: + dependencies: + detect-libc: 2.0.4 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + napi-build-utils: 1.0.2 + node-abi: 3.75.0 + pump: 3.0.2 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.3 + tunnel-agent: 0.6.0 + + prisma@6.9.0(typescript@5.8.3): + dependencies: + '@prisma/config': 6.9.0 + '@prisma/engines': 6.9.0 + optionalDependencies: + typescript: 5.8.3 + + process-nextick-args@1.0.7: {} + + process-nextick-args@2.0.1: {} + + process@0.11.10: {} + + progress@1.1.8: {} + + progress@2.0.3: {} + + promise-retry@2.0.1: + dependencies: + err-code: 2.0.3 + retry: 0.12.0 + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + property-information@6.5.0: {} + + property-information@7.1.0: {} + + proxy-from-env@1.1.0: {} + + pump@3.0.2: + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + + punycode@1.3.2: {} + + punycode@2.3.1: {} + + q@1.4.1: {} + + qs@6.1.2: {} + + quansync@0.2.10: {} + + query-string@9.2.0: + dependencies: + decode-uri-component: 0.4.1 + filter-obj: 5.1.0 + split-on-first: 3.0.0 + + querystring@0.2.0: {} + + queue-microtask@1.2.3: {} + + quick-lru@5.1.1: {} + + rc-cascader@3.34.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + classnames: 2.5.1 + rc-select: 14.16.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-tree: 5.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-checkbox@3.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-collapse@3.9.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-collapse@4.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-dialog@9.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + '@rc-component/portal': 1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-drawer@7.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + '@rc-component/portal': 1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-dropdown@4.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + '@rc-component/trigger': 2.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-field-form@2.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + '@rc-component/async-validator': 5.0.4 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-footer@0.6.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + classnames: 2.5.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-image@7.12.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + '@rc-component/portal': 1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-dialog: 9.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-input-number@9.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + '@rc-component/mini-decimal': 1.1.0 + classnames: 2.5.1 + rc-input: 1.8.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-input@1.8.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-mentions@2.20.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + '@rc-component/trigger': 2.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-input: 1.8.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-menu: 9.16.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-textarea: 1.10.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-menu@9.16.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + '@rc-component/trigger': 2.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-overflow: 1.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-motion@2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-notification@5.6.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-overflow@1.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + classnames: 2.5.1 + rc-resize-observer: 1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-pagination@5.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-picker@4.11.3(date-fns@2.30.0)(dayjs@1.11.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + '@rc-component/trigger': 2.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-overflow: 1.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-resize-observer: 1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + date-fns: 2.30.0 + dayjs: 1.11.13 + + rc-progress@4.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-rate@2.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-resize-observer@1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + resize-observer-polyfill: 1.5.1 + + rc-segmented@2.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-select@14.16.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + '@rc-component/trigger': 2.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-overflow: 1.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-virtual-list: 3.18.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-slider@11.1.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-steps@6.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-switch@4.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-table@7.51.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + '@rc-component/context': 1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-resize-observer: 1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-virtual-list: 3.18.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-tabs@15.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + classnames: 2.5.1 + rc-dropdown: 4.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-menu: 9.16.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-resize-observer: 1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-textarea@1.10.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + classnames: 2.5.1 + rc-input: 1.8.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-resize-observer: 1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-tooltip@6.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + '@rc-component/trigger': 2.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-tree-select@5.27.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + classnames: 2.5.1 + rc-select: 14.16.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-tree: 5.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-tree@5.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-virtual-list: 3.18.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-upload@4.9.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-util@5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-is: 18.3.1 + + rc-virtual-list@3.18.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + classnames: 2.5.1 + rc-resize-observer: 1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + + re-resizable@6.11.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react-avatar-editor@13.0.2(@babel/core@7.27.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/plugin-transform-runtime': 7.27.4(@babel/core@7.27.4) + '@babel/runtime': 7.27.6 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + transitivePeerDependencies: + - '@babel/core' + - supports-color + + react-colorful@5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react-dom@18.3.1(react@18.3.1): + dependencies: + loose-envify: 1.4.0 + react: 18.3.1 + scheduler: 0.23.2 + + react-draggable@4.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + clsx: 1.2.1 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react-dropzone@12.1.0(react@18.3.1): + dependencies: + attr-accept: 2.2.5 + file-selector: 0.5.0 + prop-types: 15.8.1 + react: 18.3.1 + + react-error-boundary@5.0.0(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + react: 18.3.1 + + react-fast-compare@3.2.2: {} + + react-hotkeys-hook@5.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react-i18next@15.5.2(i18next@24.2.3(typescript@5.8.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3): + dependencies: + '@babel/runtime': 7.27.6 + html-parse-stringify: 3.0.1 + i18next: 24.2.3(typescript@5.8.3) + react: 18.3.1 + optionalDependencies: + react-dom: 18.3.1(react@18.3.1) + typescript: 5.8.3 + + react-is@16.13.1: {} + + react-is@18.3.1: {} + + react-is@19.1.0: {} + + react-layout-kit@1.9.1(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + '@emotion/css': 11.13.5 + react: 18.3.1 + transitivePeerDependencies: + - supports-color + + react-markdown@10.1.0(@types/react@19.1.8)(react@18.3.1): + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/react': 19.1.8 + devlop: 1.1.0 + hast-util-to-jsx-runtime: 2.3.6 + html-url-attributes: 3.0.1 + mdast-util-to-hast: 13.2.0 + react: 18.3.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + unified: 11.0.5 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + react-merge-refs@3.0.2(react@18.3.1): + optionalDependencies: + react: 18.3.1 + + react-redux@9.2.0(@types/react@19.1.8)(react@18.3.1)(redux@5.0.1): + dependencies: + '@types/use-sync-external-store': 0.0.6 + react: 18.3.1 + use-sync-external-store: 1.5.0(react@18.3.1) + optionalDependencies: + '@types/react': 19.1.8 + redux: 5.0.1 + + react-rnd@10.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + re-resizable: 6.11.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-draggable: 4.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + tslib: 2.6.2 + + react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.27.6 + dom-helpers: 5.2.1 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react-zoom-pan-pinch@3.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react@18.3.1: + dependencies: + loose-envify: 1.4.0 + + read-config-file@6.3.2: + dependencies: + config-file-ts: 0.2.6 + dotenv: 9.0.2 + dotenv-expand: 5.1.0 + js-yaml: 4.1.0 + json5: 2.2.3 + lazy-val: 1.0.5 + + readable-stream@2.0.6: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 1.0.7 + string_decoder: 0.10.31 + util-deprecate: 1.0.2 + + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readdir-glob@1.1.3: + dependencies: + minimatch: 5.1.6 + + recharts@3.6.0(@types/react@19.1.8)(react-dom@18.3.1(react@18.3.1))(react-is@19.1.0)(react@18.3.1)(redux@5.0.1): + dependencies: + '@reduxjs/toolkit': 2.11.2(react-redux@9.2.0(@types/react@19.1.8)(react@18.3.1)(redux@5.0.1))(react@18.3.1) + clsx: 2.1.1 + decimal.js-light: 2.5.1 + es-toolkit: 1.43.0 + eventemitter3: 5.0.1 + immer: 10.1.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-is: 19.1.0 + react-redux: 9.2.0(@types/react@19.1.8)(react@18.3.1)(redux@5.0.1) + reselect: 5.1.1 + tiny-invariant: 1.3.3 + use-sync-external-store: 1.5.0(react@18.3.1) + victory-vendor: 37.3.6 + transitivePeerDependencies: + - '@types/react' + - redux + + recma-build-jsx@1.0.0: + dependencies: + '@types/estree': 1.0.8 + estree-util-build-jsx: 3.0.1 + vfile: 6.0.3 + + recma-jsx@1.0.0(acorn@8.15.0): + dependencies: + acorn-jsx: 5.3.2(acorn@8.15.0) + estree-util-to-js: 2.0.0 + recma-parse: 1.0.0 + recma-stringify: 1.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - acorn + + recma-parse@1.0.0: + dependencies: + '@types/estree': 1.0.8 + esast-util-from-js: 2.0.1 + unified: 11.0.5 + vfile: 6.0.3 + + recma-stringify@1.0.0: + dependencies: + '@types/estree': 1.0.8 + estree-util-to-js: 2.0.0 + unified: 11.0.5 + vfile: 6.0.3 + + redux-thunk@3.1.0(redux@5.0.1): + dependencies: + redux: 5.0.1 + + redux@5.0.1: {} + + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.0.1: + dependencies: + regex-utilities: 2.3.0 + + rehype-katex@7.0.1: + dependencies: + '@types/hast': 3.0.4 + '@types/katex': 0.16.7 + hast-util-from-html-isomorphic: 2.0.0 + hast-util-to-text: 4.0.2 + katex: 0.16.22 + unist-util-visit-parents: 6.0.1 + vfile: 6.0.3 + + rehype-raw@7.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-raw: 9.1.0 + vfile: 6.0.3 + + rehype-recma@1.0.0: + dependencies: + '@types/estree': 1.0.8 + '@types/hast': 3.0.4 + hast-util-to-estree: 3.1.3 + transitivePeerDependencies: + - supports-color + + remark-breaks@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-newline-to-break: 2.0.0 + unified: 11.0.5 + + remark-gfm@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-gfm: 3.1.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-math@6.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-math: 3.0.0 + micromark-extension-math: 3.1.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-mdx@3.1.0: + dependencies: + mdast-util-mdx: 3.0.0 + micromark-extension-mdxjs: 3.0.0 + transitivePeerDependencies: + - supports-color + + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.2 + micromark-util-types: 2.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.2: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.0 + unified: 11.0.5 + vfile: 6.0.3 + + remark-stringify@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-to-markdown: 2.1.2 + unified: 11.0.5 + + request@2.72.0: + dependencies: + aws-sign2: 0.6.0 + aws4: 1.13.2 + bl: 1.1.2 + caseless: 0.11.0 + combined-stream: 1.0.8 + extend: 3.0.2 + forever-agent: 0.6.1 + form-data: 1.0.1 + har-validator: 2.0.6 + hawk: 3.1.3 + http-signature: 1.1.1 + is-typedarray: 1.0.0 + isstream: 0.1.2 + json-stringify-safe: 5.0.1 + mime-types: 2.1.35 + node-uuid: 1.4.8 + oauth-sign: 0.8.2 + qs: 6.1.2 + stringstream: 0.0.6 + tough-cookie: 2.2.2 + tunnel-agent: 0.4.3 + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + reselect@5.1.1: {} + + resize-observer-polyfill@1.5.1: {} + + resolve-alpn@1.2.1: {} + + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + + resolve@1.22.10: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + responselike@2.0.1: + dependencies: + lowercase-keys: 2.0.0 + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + retry@0.12.0: {} + + retry@0.13.1: {} + + reusify@1.1.0: {} + + rfdc@1.4.1: {} + + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + optional: true + + roarr@2.15.4: + dependencies: + boolean: 3.2.0 + detect-node: 2.1.0 + globalthis: 1.0.4 + json-stringify-safe: 5.0.1 + semver-compare: 1.0.0 + sprintf-js: 1.1.3 + optional: true + + robust-predicates@3.0.2: {} + + roughjs@4.6.6: + dependencies: + hachure-fill: 0.5.2 + path-data-parser: 0.1.0 + points-on-curve: 0.2.0 + points-on-path: 0.2.1 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + rw@1.3.3: {} + + rxjs@7.8.2: + dependencies: + tslib: 2.8.1 + + safe-buffer@5.1.2: {} + + safe-buffer@5.2.1: {} + + safer-buffer@2.1.2: {} + + sanitize-filename@1.6.3: + dependencies: + truncate-utf8-bytes: 1.0.2 + + sax@1.4.1: {} + + scheduler@0.23.2: + dependencies: + loose-envify: 1.4.0 + + screenfull@5.2.0: {} + + scroll-into-view-if-needed@3.1.0: + dependencies: + compute-scroll-into-view: 3.1.1 + + secure-json-parse@2.7.0: {} + + semver-compare@1.0.0: {} + + semver@6.3.1: {} + + semver@7.7.2: {} + + serialize-error@7.0.1: + dependencies: + type-fest: 0.13.1 + optional: true + + set-blocking@2.0.0: + optional: true + + set-value@2.0.1: + dependencies: + extend-shallow: 2.0.1 + is-extendable: 0.1.1 + is-plain-object: 2.0.4 + split-string: 3.1.0 + + setimmediate@1.0.5: {} + + sharp@0.33.5: + dependencies: + color: 4.2.3 + detect-libc: 2.0.4 + semver: 7.7.2 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.33.5 + '@img/sharp-darwin-x64': 0.33.5 + '@img/sharp-libvips-darwin-arm64': 1.0.4 + '@img/sharp-libvips-darwin-x64': 1.0.4 + '@img/sharp-libvips-linux-arm': 1.0.5 + '@img/sharp-libvips-linux-arm64': 1.0.4 + '@img/sharp-libvips-linux-s390x': 1.0.4 + '@img/sharp-libvips-linux-x64': 1.0.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 + '@img/sharp-libvips-linuxmusl-x64': 1.0.4 + '@img/sharp-linux-arm': 0.33.5 + '@img/sharp-linux-arm64': 0.33.5 + '@img/sharp-linux-s390x': 0.33.5 + '@img/sharp-linux-x64': 0.33.5 + '@img/sharp-linuxmusl-arm64': 0.33.5 + '@img/sharp-linuxmusl-x64': 0.33.5 + '@img/sharp-wasm32': 0.33.5 + '@img/sharp-win32-ia32': 0.33.5 + '@img/sharp-win32-x64': 0.33.5 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shell-quote@1.8.3: {} + + shiki@3.6.0: + dependencies: + '@shikijs/core': 3.6.0 + '@shikijs/engine-javascript': 3.6.0 + '@shikijs/engine-oniguruma': 3.6.0 + '@shikijs/langs': 3.6.0 + '@shikijs/themes': 3.6.0 + '@shikijs/types': 3.6.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + signal-exit@3.0.7: + optional: true + + signal-exit@4.1.0: {} + + simple-concat@1.0.1: {} + + simple-get@3.1.1: + dependencies: + decompress-response: 4.2.1 + once: 1.4.0 + simple-concat: 1.0.1 + optional: true + + simple-get@4.0.1: + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + + simple-swizzle@0.2.2: + dependencies: + is-arrayish: 0.3.2 + + simple-update-notifier@2.0.0: + dependencies: + semver: 7.7.2 + + simple-wcswidth@1.0.1: {} + + slash@3.0.0: {} + + slice-ansi@3.0.0: + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + optional: true + + slice-ansi@5.0.0: + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 4.0.0 + + slice-ansi@7.1.0: + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 5.0.0 + + smart-buffer@4.2.0: + optional: true + + sntp@1.0.9: + dependencies: + hoek: 2.16.3 + + sonner@2.0.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + source-map-js@1.2.1: {} + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.5.7: {} + + source-map@0.6.1: {} + + source-map@0.7.4: {} + + space-separated-tokens@2.0.2: {} + + spawn-command@0.0.2: {} + + split-on-first@3.0.0: {} + + split-string@3.1.0: + dependencies: + extend-shallow: 3.0.2 + + split2@4.2.0: {} + + sprintf-js@1.0.3: {} + + sprintf-js@1.1.3: + optional: true + + ssf@0.11.2: + dependencies: + frac: 1.1.2 + + sshpk@1.18.0: + dependencies: + asn1: 0.2.6 + assert-plus: 1.0.0 + bcrypt-pbkdf: 1.0.2 + dashdash: 1.14.1 + ecc-jsbn: 0.1.2 + getpass: 0.1.7 + jsbn: 0.1.1 + safer-buffer: 2.1.2 + tweetnacl: 0.14.5 + + stat-mode@1.0.0: {} + + stream-meter@1.0.4: + dependencies: + readable-stream: 2.3.8 + + streamsearch@1.1.0: {} + + string-argv@0.3.2: {} + + string-convert@0.2.1: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.4.0 + get-east-asian-width: 1.3.0 + strip-ansi: 7.1.0 + + string_decoder@0.10.31: {} + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + stringstream@0.0.6: {} + + strip-ansi@3.0.1: + dependencies: + ansi-regex: 2.1.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.1.0 + + strip-final-newline@3.0.0: {} + + strip-json-comments@2.0.1: {} + + style-to-js@1.1.16: + dependencies: + style-to-object: 1.0.8 + + style-to-object@1.0.8: + dependencies: + inline-style-parser: 0.2.4 + + styled-jsx@5.1.1(@babel/core@7.27.4)(react@18.3.1): + dependencies: + client-only: 0.0.1 + react: 18.3.1 + optionalDependencies: + '@babel/core': 7.27.4 + + stylis@4.2.0: {} + + stylis@4.3.6: {} + + sumchecker@3.0.1: + dependencies: + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + + supports-color@2.0.0: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + swr@2.3.3(react@18.3.1): + dependencies: + dequal: 2.0.3 + react: 18.3.1 + use-sync-external-store: 1.5.0(react@18.3.1) + + tabbable@6.2.0: {} + + tar-fs@2.1.3: + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.2 + tar-stream: 2.2.0 + + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.4 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + + tar@6.2.1: + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + + temp-file@3.4.0: + dependencies: + async-exit-hook: 2.0.1 + fs-extra: 10.1.0 + + text-extensions@2.4.0: {} + + throttle-debounce@5.0.2: {} + + throttleit@2.1.0: {} + + through@2.3.8: {} + + tiny-invariant@1.3.3: {} + + tiny-typed-emitter@2.1.0: {} + + tinyexec@1.0.1: {} + + tmp-promise@3.0.3: + dependencies: + tmp: 0.2.3 + + tmp@0.2.3: {} + + to-fast-properties@2.0.0: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toggle-selection@1.0.6: {} + + tough-cookie@2.2.2: {} + + tr46@0.0.3: {} + + tree-kill@1.2.2: {} + + trim-lines@3.0.1: {} + + trough@2.2.0: {} + + truncate-utf8-bytes@1.0.2: + dependencies: + utf8-byte-length: 1.0.5 + + ts-dedent@2.2.0: {} + + ts-md5@1.3.1: {} + + tslib@2.6.2: {} + + tslib@2.8.1: {} + + tunnel-agent@0.4.3: {} + + tunnel-agent@0.6.0: + dependencies: + safe-buffer: 5.2.1 + + turndown@7.2.0: + dependencies: + '@mixmark-io/domino': 2.2.0 + + tweetnacl@0.14.5: {} + + type-fest@0.13.1: + optional: true + + typescript@5.8.3: {} + + ufo@1.6.1: {} + + underscore@1.13.7: {} + + undici-types@5.26.5: {} + + undici-types@6.21.0: {} + + undici-types@7.8.0: {} + + unicorn-magic@0.1.0: {} + + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unist-util-find-after@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + + unist-util-is@6.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position-from-estree@2.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-remove-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-visit: 5.0.0 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.1: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + + unist-util-visit@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + + universalify@0.1.2: {} + + universalify@2.0.1: {} + + unpdf@0.12.2: + optionalDependencies: + canvas: 2.11.2 + transitivePeerDependencies: + - encoding + - supports-color + + update-browserslist-db@1.1.3(browserslist@4.25.0): + dependencies: + browserslist: 4.25.0 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + url-join@5.0.0: {} + + url@0.11.0: + dependencies: + punycode: 1.3.2 + querystring: 0.2.0 + + use-isomorphic-layout-effect@1.2.1(@types/react@19.1.8)(react@18.3.1): + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 19.1.8 + + use-merge-value@1.2.0(react@18.3.1): + dependencies: + react: 18.3.1 + + use-sync-external-store@1.5.0(react@18.3.1): + dependencies: + react: 18.3.1 + + utf8-byte-length@1.0.5: {} + + util-deprecate@1.0.2: {} + + util@0.10.4: + dependencies: + inherits: 2.0.3 + + uuid@10.0.0: {} + + uuid@11.1.0: {} + + v8n@1.5.1: {} + + verror@1.10.0: + dependencies: + assert-plus: 1.0.0 + core-util-is: 1.0.2 + extsprintf: 1.4.1 + + verror@1.10.1: + dependencies: + assert-plus: 1.0.0 + core-util-is: 1.0.2 + extsprintf: 1.4.1 + optional: true + + vfile-location@5.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile: 6.0.3 + + vfile-message@4.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.2 + + victory-vendor@37.3.6: + dependencies: + '@types/d3-array': 3.2.1 + '@types/d3-ease': 3.0.2 + '@types/d3-interpolate': 3.0.4 + '@types/d3-scale': 4.0.9 + '@types/d3-shape': 3.1.7 + '@types/d3-time': 3.0.4 + '@types/d3-timer': 3.0.2 + d3-array: 3.2.4 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-scale: 4.0.2 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-timer: 3.0.1 + + void-elements@3.1.0: {} + + vscode-jsonrpc@8.2.0: {} + + vscode-languageserver-protocol@3.17.5: + dependencies: + vscode-jsonrpc: 8.2.0 + vscode-languageserver-types: 3.17.5 + + vscode-languageserver-textdocument@1.0.12: {} + + vscode-languageserver-types@3.17.5: {} + + vscode-languageserver@9.0.1: + dependencies: + vscode-languageserver-protocol: 3.17.5 + + vscode-uri@3.0.8: {} + + wait-on@7.2.0: + dependencies: + axios: 1.9.0 + joi: 17.13.3 + lodash: 4.17.21 + minimist: 1.2.8 + rxjs: 7.8.2 + transitivePeerDependencies: + - debug + + web-namespaces@2.0.1: {} + + web-streams-polyfill@4.0.0-beta.3: {} + + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wide-align@1.1.5: + dependencies: + string-width: 4.2.3 + optional: true + + wmf@1.0.2: {} + + word@0.3.0: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + + wrap-ansi@9.0.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 7.2.0 + strip-ansi: 7.1.0 + + wrappy@1.0.2: {} + + xlsx@0.18.5: + dependencies: + adler-32: 1.3.1 + cfb: 1.2.2 + codepage: 1.15.0 + crc-32: 1.2.2 + ssf: 0.11.2 + wmf: 1.0.2 + word: 0.3.0 + + xmlbuilder@10.1.1: {} + + xmlbuilder@15.1.1: {} + + xmldom@0.6.0: {} + + xtend@4.0.2: {} + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yallist@4.0.0: {} + + yaml@1.10.2: {} + + yaml@2.8.0: {} + + yargs-parser@20.2.9: {} + + yargs-parser@21.1.1: {} + + yargs@16.2.0: + dependencies: + cliui: 7.0.4 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yauzl@2.10.0: + dependencies: + buffer-crc32: 0.2.13 + fd-slicer: 1.1.0 + + yocto-queue@1.2.1: {} + + zhipu-ai-provider@0.1.1(zod@3.25.76): + dependencies: + '@ai-sdk/provider': 1.0.9 + '@ai-sdk/provider-utils': 2.1.10(zod@3.25.76) + zod: 3.25.76 + + zip-stream@4.1.1: + dependencies: + archiver-utils: 3.0.4 + compress-commons: 4.1.2 + readable-stream: 3.6.2 + + zod-to-json-schema@3.24.5(zod@3.25.76): + dependencies: + zod: 3.25.76 + + zod@3.25.32: {} + + zod@3.25.76: {} + + zustand@3.7.2(react@18.3.1): + optionalDependencies: + react: 18.3.1 + + zwitch@2.0.4: {} diff --git a/easy-dataset-main/prisma/generate-template.js b/easy-dataset-main/prisma/generate-template.js new file mode 100644 index 0000000..573ca80 --- /dev/null +++ b/easy-dataset-main/prisma/generate-template.js @@ -0,0 +1,48 @@ +/** + * 此脚本用于生成空的模板数据库文件(template.sqlite) + * 该文件将在应用打包时被包含,并在用户首次启动应用时作为初始数据库 + */ + +const fs = require('fs'); +const path = require('path'); +const { execSync } = require('child_process'); + +const templatePath = path.join(__dirname, 'template.sqlite'); +const sqlitePath = path.join(__dirname, 'empty.db.sqlite'); + +// 如果存在旧的模板文件,先删除 +if (fs.existsSync(templatePath)) { + console.log('删除旧的模板数据库...'); + fs.unlinkSync(templatePath); +} + +// 如果存在临时数据库文件,先删除 +if (fs.existsSync(sqlitePath)) { + console.log('删除临时数据库文件...'); + fs.unlinkSync(sqlitePath); +} + +try { + console.log('设置临时数据库路径...'); + // 设置 DATABASE_URL 环境变量 + process.env.DATABASE_URL = `file:${sqlitePath}`; + + console.log('执行 prisma db push 创建新的数据库架构...'); + // 执行 prisma db push 创建数据库架构 + execSync('npx prisma db push', { stdio: 'inherit' }); + + console.log('将生成的数据库文件复制为模板...'); + // 复制生成的数据库文件为模板 + fs.copyFileSync(sqlitePath, templatePath); + + console.log(`✅ 模板数据库已成功生成: ${templatePath}`); +} catch (error) { + console.error('❌ 生成模板数据库失败:', error); + process.exit(1); +} finally { + // 清理: 删除临时数据库文件 + if (fs.existsSync(sqlitePath)) { + console.log('清理临时数据库文件...'); + fs.unlinkSync(sqlitePath); + } +} diff --git a/easy-dataset-main/prisma/schema.prisma b/easy-dataset-main/prisma/schema.prisma new file mode 100644 index 0000000..c7b77ff --- /dev/null +++ b/easy-dataset-main/prisma/schema.prisma @@ -0,0 +1,406 @@ +generator client { + provider = "prisma-client-js" + binaryTargets = ["native", "darwin-arm64", "darwin", "windows", "debian-openssl-3.0.x", "linux-arm64-openssl-3.0.x", "debian-openssl-1.1.x"] +} + +datasource db { + provider = "sqlite" + url = env("DATABASE_URL") +} + +model Projects { + id String @id @default(nanoid(12)) + name String + description String + globalPrompt String @default("") + questionPrompt String @default("") + answerPrompt String @default("") + labelPrompt String @default("") + domainTreePrompt String @default("") + cleanPrompt String @default("") + defaultModelConfigId String? + test String @default("") + createAt DateTime @default(now()) + updateAt DateTime @updatedAt + Questions Questions[] + Datasets Datasets[] + DatasetConversations DatasetConversations[] + Chunks Chunks[] + ModelConfig ModelConfig[] + UploadFiles UploadFiles[] + Tags Tags[] + Task Task[] + GaPairs GaPairs[] + CustomPrompts CustomPrompts[] + Images Images[] + ImageDatasets ImageDatasets[] + QuestionTemplates QuestionTemplates[] + EvalDatasets EvalDatasets[] +} + +model UploadFiles { + id String @id @default(nanoid()) + project Projects @relation(fields: [projectId], references: [id], onDelete: Cascade) + projectId String + fileName String + fileExt String + path String + size Int + md5 String + createAt DateTime @default(now()) + updateAt DateTime @updatedAt + GaPairs GaPairs[] +} + +model Chunks { + id String @id @default(nanoid()) + name String + project Projects @relation(fields: [projectId], references: [id], onDelete: Cascade) + projectId String + fileId String + fileName String + content String + summary String + size Int + createAt DateTime @default(now()) + updateAt DateTime @updatedAt + Questions Questions[] + EvalDatasets EvalDatasets[] + + @@index([projectId]) +} + +model Tags { + id String @id @default(nanoid()) + label String + project Projects @relation(fields: [projectId], references: [id], onDelete: Cascade) + projectId String + parentId String? + parent Tags? @relation("Tags", fields: [parentId], references: [id]) + children Tags[] @relation("Tags") + + @@index([projectId, label]) + @@index([projectId, parentId]) +} + +model Questions { + id String @id @default(nanoid()) + project Projects @relation(fields: [projectId], references: [id], onDelete: Cascade) + projectId String + chunk Chunks @relation(fields: [chunkId], references: [id]) + chunkId String + gaPair GaPairs? @relation(fields: [gaPairId], references: [id]) + gaPairId String? // Optional: links question to the GA pair that generated it + question String + label String + answered Boolean @default(false) + imageId String? // Optional: for image-based questions + imageName String? // Optional: for image-based questions + templateId String? // Optional: links to ImageQuestionTemplates + createAt DateTime @default(now()) + updateAt DateTime @updatedAt + + @@index([projectId]) + @@index([imageId]) + @@index([templateId]) + @@index([projectId, label]) +} + +model Datasets { + id String @id @default(nanoid()) + project Projects @relation(fields: [projectId], references: [id], onDelete: Cascade) + projectId String + questionId String + question String + answer String + answerType String? @default("text") // 'text' | 'label' | 'custom_format' + chunkName String + chunkContent String + model String + questionLabel String + cot String + confirmed Boolean @default(false) + score Float @default(0) + aiEvaluation String @default("") // AI评估结论 + tags String @default("") + note String @default("") + other String @default("") // 存储其他字段的JSON字符串 + createAt DateTime @default(now()) + updateAt DateTime @updatedAt + + @@index([projectId]) + @@index([projectId, confirmed, createAt, id], name: "idx_export_confirmed") + @@index([projectId, createAt], name: "idx_project_createAt") +} + +model DatasetConversations { + id String @id @default(nanoid()) + project Projects @relation(fields: [projectId], references: [id], onDelete: Cascade) + projectId String + questionId String // 第一个问题 Id(初始问题) + question String // 第一个问题(初始问题) + chunkId String // 基于哪个文本块生成 + model String + questionLabel String + score Float @default(0) + aiEvaluation String @default("") // AI评估结论 + tags String @default("") + note String @default("") + scenario String // 对话场景(教学/咨询/讨论等) + roleA String // 角色A设定 + roleB String // 角色B设定 + turnCount Int // 实际轮数 + maxTurns Int // 设置的最大轮数 + rawMessages String // JSON存储完整对话(和 ShareGPT 格式保持完全一致) + confirmed Boolean @default(false) + createAt DateTime @default(now()) + updateAt DateTime @updatedAt + + @@index([projectId]) +} + +model LlmProviders { + id String @id + name String + apiUrl String + createAt DateTime @default(now()) + updateAt DateTime @updatedAt + LlmModels LlmModels[] +} + +model LlmModels { + id String @id @default(nanoid()) + modelId String + modelName String + provider LlmProviders @relation(fields: [providerId], references: [id]) + providerId String + createAt DateTime @default(now()) + updateAt DateTime @updatedAt +} + +model ModelConfig { + id String @id @default(nanoid()) + project Projects @relation(fields: [projectId], references: [id], onDelete: Cascade) + projectId String + providerId String + providerName String + endpoint String + apiKey String + modelId String + modelName String + type String + temperature Float + maxTokens Int + topP Float + topK Float + status Int + createAt DateTime @default(now()) + updateAt DateTime @updatedAt +} + +model Task { + id String @id @default(nanoid()) + project Projects @relation(fields: [projectId], references: [id], onDelete: Cascade) + projectId String + taskType String // 任务类型: text-processing, question-generation, answer-generation, data-distillation + status Int // 任务状态: 0-处理中, 1-已完成, 2-失败, 3-已中断 + startTime DateTime @default(now()) + endTime DateTime? + completedCount Int @default(0) + totalCount Int @default(0) + modelInfo String // JSON格式存储,包含使用的模型信息 + language String @default("zh-CN") + detail String @default("") // 任务详情 + note String @default("") // 任务备注 + createAt DateTime @default(now()) + updateAt DateTime @updatedAt + + @@index([projectId]) +} + +model CustomPrompts { + id String @id @default(nanoid()) + project Projects @relation(fields: [projectId], references: [id], onDelete: Cascade) + projectId String + promptType String // 提示词类型,对应 lib/llm/prompts 下的文件名 + promptKey String // 提示词在模块中的键名,如 QUESTION_PROMPT, QUESTION_PROMPT_EN + language String // 语言: zh-CN, en + content String // 自定义的提示词内容 + isActive Boolean @default(true) // 是否启用 + createAt DateTime @default(now()) + updateAt DateTime @updatedAt + + @@unique([projectId, promptType, promptKey, language]) + @@index([projectId, promptType]) + @@index([projectId, language]) +} + +model GaPairs { + id String @id @default(nanoid()) + project Projects @relation(fields: [projectId], references: [id], onDelete: Cascade) + projectId String + uploadFile UploadFiles @relation(fields: [fileId], references: [id], onDelete: Cascade) + fileId String + pairNumber Int // 1-5, representing the 5 generated pairs + genreTitle String // Genre name/title + genreDesc String // Genre description + audienceTitle String // Audience name/title + audienceDesc String // Audience description + isActive Boolean @default(true) // Whether this pair is active for use + questions Questions[] // Questions generated by this GA pair + createAt DateTime @default(now()) + updateAt DateTime @updatedAt + + @@unique([fileId, pairNumber]) + @@index([projectId]) + @@index([fileId]) +} + +model Images { + id String @id @default(nanoid()) + project Projects @relation(fields: [projectId], references: [id], onDelete: Cascade) + projectId String + imageName String + path String // 图片存储路径 + size Int // 文件大小(字节) + width Int? // 图片宽度 + height Int? // 图片高度 + createAt DateTime @default(now()) + updateAt DateTime @updatedAt + ImageDatasets ImageDatasets[] + + @@unique([projectId, imageName]) + @@index([projectId]) +} + +model ImageDatasets { + id String @id @default(nanoid()) + project Projects @relation(fields: [projectId], references: [id], onDelete: Cascade) + projectId String + image Images @relation(fields: [imageId], references: [id], onDelete: Cascade) + imageId String + imageName String + questionId String? // Optional: links to Questions table + question String + answer String // Stores all answer types: text, JSON array for labels, or custom format JSON + answerType String @default("text") // 'text' | 'label' | 'custom_format' + model String + confirmed Boolean @default(false) + score Float @default(0) + tags String @default("") + note String @default("") + createAt DateTime @default(now()) + updateAt DateTime @updatedAt + + @@index([projectId]) + @@index([imageId]) + @@index([questionId]) +} + +model QuestionTemplates { + id String @id @default(nanoid()) + project Projects @relation(fields: [projectId], references: [id], onDelete: Cascade) + projectId String + question String // Question content + sourceType String // 'image' | 'text' - data source type + answerType String // 'text' | 'label' | 'custom_format' + description String @default("") // Question description + labels String @default("") // JSON array of label options (for answerType='label') + customFormat String @default("") // Custom format definition (for answerType='custom_format') + order Int @default(0) // Display order + createAt DateTime @default(now()) + updateAt DateTime @updatedAt + + @@index([projectId]) + @@index([projectId, sourceType]) +} + +model LlmUsageLogs { + id String @id @default(nanoid()) + projectId String + provider String // 提供商: openai, anthropic, google 等 + model String // 模型名称 + + // 核心指标 + inputTokens Int @default(0) + outputTokens Int @default(0) + totalTokens Int @default(0) + latency Int @default(0) // 响应耗时(毫秒) + + // 状态与追踪 + status String @default("SUCCESS") // 状态: "SUCCESS", "FAILED" + errorMessage String? // 失败原因,status="FAILED" 时填写 + + // 时间维度 + createAt DateTime @default(now()) + dateString String // 格式 "YYYY-MM-DD",用于快速按天聚合 + + @@index([projectId, dateString]) + @@index([dateString]) + @@index([provider]) + @@index([model]) +} + +model EvalDatasets { + id String @id @default(nanoid()) + project Projects @relation(fields: [projectId], references: [id], onDelete: Cascade) + projectId String + + // 题目内容 + question String // 题目内容 + questionType String // 题型: true_false, single_choice, multiple_choice, short_answer, open_ended + + // 上下文信息(关联到文本块) + chunkId String? // 关联到 Chunks 表 + chunks Chunks? @relation(fields: [chunkId], references: [id]) + + // 选项(仅选择题使用) + options String @default("") // JSON数组: ["选项A", "选项B", "选项C", "选项D"] + + // 标准答案 + correctAnswer String // 标准答案 + + tags String @default("") // 标签,逗号分隔 + note String @default("") // 备注 + + // 时间戳 + createAt DateTime @default(now()) + updateAt DateTime @updatedAt + + // 关联评估结果 + EvalResults EvalResults[] + + @@index([projectId]) + @@index([projectId, questionType]) + @@index([chunkId]) +} + +model EvalResults { + id String @id @default(nanoid()) + projectId String + taskId String // 关联到 Task 表 + + // 关联评估题目 + evalDataset EvalDatasets @relation(fields: [evalDatasetId], references: [id], onDelete: Cascade) + evalDatasetId String + + // 评估结果 + modelAnswer String // 模型的回答 + score Float @default(0) // 得分 (0-1 之间) + isCorrect Boolean @default(false) // 是否正确(用于客观题) + judgeResponse String @default("") // LLM 评分的响应(用于主观题) + + // 答题详情 + duration Int @default(0) // 答题耗时(毫秒) + status Int @default(0) // 答题状态:0-成功, 1-输出不符合规范, 2-LLM调用报错 + errorMessage String @default("") // 答题报错信息 + + // 时间戳 + createAt DateTime @default(now()) + updateAt DateTime @updatedAt + + @@unique([taskId, evalDatasetId]) // 每个任务对每道题只能有一个结果 + @@index([projectId]) + @@index([taskId]) + @@index([evalDatasetId]) +} diff --git a/easy-dataset-main/prisma/sql.json b/easy-dataset-main/prisma/sql.json new file mode 100644 index 0000000..1e8a6e0 --- /dev/null +++ b/easy-dataset-main/prisma/sql.json @@ -0,0 +1,88 @@ +[ + { + "version": "1.2.5", + "sql": "ALTER TABLE Projects ADD COLUMN test VARCHAR(255) DEFAULT '';" + }, + { + "version": "1.3.3", + "sql": "CREATE TABLE IF NOT EXISTS Task (\n id VARCHAR(255) NOT NULL,\n projectId VARCHAR(255) NOT NULL,\n taskType VARCHAR(255) NOT NULL,\n status INT NOT NULL,\n startTime TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n endTime TIMESTAMP NULL,\n completedCount INT DEFAULT 0,\n totalCount INT DEFAULT 0,\n modelInfo TEXT NOT NULL,\n language VARCHAR(20) DEFAULT 'zh-CN',\n detail TEXT DEFAULT '',\n note TEXT DEFAULT '',\n createAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updateAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (id),\n FOREIGN KEY (projectId) REFERENCES Projects(id) ON DELETE CASCADE\n);\n\nCREATE INDEX idx_task_projectId ON Task(projectId);" + }, + { + "version": "1.3.6", + "sql": "CREATE TABLE IF NOT EXISTS GaPairs (\n id VARCHAR(255) NOT NULL,\n projectId VARCHAR(255) NOT NULL,\n fileId VARCHAR(255) NOT NULL,\n pairNumber INT NOT NULL,\n genreTitle VARCHAR(255) NOT NULL,\n genreDesc TEXT NOT NULL,\n audienceTitle VARCHAR(255) NOT NULL,\n audienceDesc TEXT NOT NULL,\n isActive BOOLEAN DEFAULT 1 NOT NULL,\n createAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updateAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (id),\n FOREIGN KEY (projectId) REFERENCES Projects(id) ON DELETE CASCADE,\n FOREIGN KEY (fileId) REFERENCES UploadFiles(id) ON DELETE CASCADE,\n UNIQUE (fileId, pairNumber)\n);\n\nCREATE INDEX idx_gapairs_projectId ON GaPairs(projectId);\nCREATE INDEX idx_gapairs_fileId ON GaPairs(fileId);" + }, + { + "version": "1.3.6", + "sql": "ALTER TABLE Questions ADD COLUMN gaPairId VARCHAR(255) NULL;" + }, + { + "version": "1.3.6", + "sql": "ALTER TABLE Questions ADD FOREIGN KEY (gaPairId) REFERENCES GaPairs(id) ON DELETE SET NULL;\n\nCREATE INDEX idx_questions_gaPairId ON Questions(gaPairId);" + }, + { + "version": "1.4.0", + "sql": "ALTER TABLE Datasets ADD COLUMN score REAL DEFAULT 0 NOT NULL;\nALTER TABLE Datasets ADD COLUMN tags TEXT DEFAULT '[]' NOT NULL;\nALTER TABLE Datasets ADD COLUMN note TEXT DEFAULT '' NOT NULL;\nALTER TABLE Datasets ADD COLUMN other TEXT DEFAULT '' NOT NULL;\nALTER TABLE Projects ADD COLUMN cleanPrompt TEXT DEFAULT '' NOT NULL;" + }, + { + "version": "1.5.0", + "sql": "CREATE TABLE IF NOT EXISTS CustomPrompts (\n id VARCHAR(255) NOT NULL,\n projectId VARCHAR(255) NOT NULL,\n promptType VARCHAR(255) NOT NULL,\n promptKey VARCHAR(255) NOT NULL,\n language VARCHAR(10) NOT NULL,\n content TEXT NOT NULL,\n isActive BOOLEAN DEFAULT 1 NOT NULL,\n createAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updateAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (id),\n FOREIGN KEY (projectId) REFERENCES Projects(id) ON DELETE CASCADE,\n UNIQUE (projectId, promptType, promptKey, language)\n);\n\nCREATE INDEX idx_customprompts_projectId ON CustomPrompts(projectId);\nCREATE INDEX idx_customprompts_project_type ON CustomPrompts(projectId, promptType);\nCREATE INDEX idx_customprompts_project_language ON CustomPrompts(projectId, language);" + }, + { + "version": "1.5.0", + "sql": "ALTER TABLE Datasets ADD COLUMN aiEvaluation TEXT DEFAULT '' NOT NULL;" + }, + { + "version": "1.5.0", + "sql": "CREATE TABLE IF NOT EXISTS DatasetConversations (\n id VARCHAR(255) NOT NULL,\n projectId VARCHAR(255) NOT NULL,\n questionId VARCHAR(255) NOT NULL,\n question TEXT NOT NULL,\n chunkId VARCHAR(255) NOT NULL,\n model VARCHAR(255) NOT NULL,\n questionLabel VARCHAR(255) NOT NULL,\n score REAL DEFAULT 0 NOT NULL,\n aiEvaluation TEXT DEFAULT '' NOT NULL,\n tags TEXT DEFAULT '' NOT NULL,\n note TEXT DEFAULT '' NOT NULL,\n scenario TEXT NOT NULL,\n roleA TEXT NOT NULL,\n roleB TEXT NOT NULL,\n turnCount INT NOT NULL,\n maxTurns INT NOT NULL,\n rawMessages TEXT NOT NULL,\n confirmed BOOLEAN DEFAULT 0 NOT NULL,\n createAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updateAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (id),\n FOREIGN KEY (projectId) REFERENCES Projects(id) ON DELETE CASCADE\n);\n\nCREATE INDEX idx_datasetconversations_projectId ON DatasetConversations(projectId);" + }, + { + "version": "1.6.0", + "description": "为 Questions 表添加图片相关字段", + "sql": "ALTER TABLE Questions ADD COLUMN imageId VARCHAR(255) NULL;\nALTER TABLE Questions ADD COLUMN imageName VARCHAR(255) NULL;\nALTER TABLE Questions ADD COLUMN templateId VARCHAR(255) NULL;" + }, + { + "version": "1.6.0", + "description": "为 Questions 表添加图片相关索引", + "sql": "CREATE INDEX idx_questions_imageId ON Questions(imageId);\nCREATE INDEX idx_questions_templateId ON Questions(templateId);" + }, + { + "version": "1.6.0", + "description": "为 Datasets 表添加 answerType 字段", + "sql": "ALTER TABLE Datasets ADD COLUMN answerType VARCHAR(50) DEFAULT 'text';" + }, + { + "version": "1.6.0", + "description": "创建 Images 表", + "sql": "CREATE TABLE IF NOT EXISTS Images (\n id VARCHAR(255) NOT NULL,\n projectId VARCHAR(255) NOT NULL,\n imageName VARCHAR(255) NOT NULL,\n path TEXT NOT NULL,\n size INT NOT NULL,\n width INT NULL,\n height INT NULL,\n createAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updateAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (id),\n FOREIGN KEY (projectId) REFERENCES Projects(id) ON DELETE CASCADE,\n UNIQUE (projectId, imageName)\n);\n\nCREATE INDEX idx_images_projectId ON Images(projectId);" + }, + { + "version": "1.6.0", + "description": "创建 ImageDatasets 表", + "sql": "CREATE TABLE IF NOT EXISTS ImageDatasets (\n id VARCHAR(255) NOT NULL,\n projectId VARCHAR(255) NOT NULL,\n imageId VARCHAR(255) NOT NULL,\n imageName VARCHAR(255) NOT NULL,\n questionId VARCHAR(255) NULL,\n question TEXT NOT NULL,\n answer TEXT NOT NULL,\n answerType VARCHAR(50) DEFAULT 'text',\n model VARCHAR(255) NOT NULL,\n confirmed BOOLEAN DEFAULT 0 NOT NULL,\n score REAL DEFAULT 0 NOT NULL,\n tags TEXT DEFAULT '' NOT NULL,\n note TEXT DEFAULT '' NOT NULL,\n createAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updateAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (id),\n FOREIGN KEY (projectId) REFERENCES Projects(id) ON DELETE CASCADE,\n FOREIGN KEY (imageId) REFERENCES Images(id) ON DELETE CASCADE\n);\n\nCREATE INDEX idx_imagedatasets_projectId ON ImageDatasets(projectId);\nCREATE INDEX idx_imagedatasets_imageId ON ImageDatasets(imageId);\nCREATE INDEX idx_imagedatasets_questionId ON ImageDatasets(questionId);" + }, + { + "version": "1.6.0", + "description": "创建 QuestionTemplates 表", + "sql": "CREATE TABLE IF NOT EXISTS QuestionTemplates (\n id VARCHAR(255) NOT NULL,\n projectId VARCHAR(255) NOT NULL,\n question TEXT NOT NULL,\n sourceType VARCHAR(50) NOT NULL,\n answerType VARCHAR(50) NOT NULL,\n description TEXT DEFAULT '' NOT NULL,\n labels TEXT DEFAULT '' NOT NULL,\n customFormat TEXT DEFAULT '' NOT NULL,\n \"order\" INT DEFAULT 0 NOT NULL,\n createAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updateAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (id),\n FOREIGN KEY (projectId) REFERENCES Projects(id) ON DELETE CASCADE\n);\n\nCREATE INDEX idx_questiontemplates_projectId ON QuestionTemplates(projectId);\nCREATE INDEX idx_questiontemplates_project_source ON QuestionTemplates(projectId, sourceType);" + }, + { + "version": "1.6.2", + "description": "创建 LlmUsageLogs 表 - LLM 调用统计日志", + "sql": "CREATE TABLE IF NOT EXISTS LlmUsageLogs (\n id VARCHAR(255) NOT NULL,\n projectId VARCHAR(255) NOT NULL,\n provider VARCHAR(255) NOT NULL,\n model VARCHAR(255) NOT NULL,\n inputTokens INT DEFAULT 0 NOT NULL,\n outputTokens INT DEFAULT 0 NOT NULL,\n totalTokens INT DEFAULT 0 NOT NULL,\n latency INT DEFAULT 0 NOT NULL,\n status VARCHAR(50) DEFAULT 'SUCCESS' NOT NULL,\n errorMessage TEXT NULL,\n createAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n dateString VARCHAR(10) NOT NULL,\n PRIMARY KEY (id)\n);\n\nCREATE INDEX idx_llmusagelogs_project_date ON LlmUsageLogs(projectId, dateString);\nCREATE INDEX idx_llmusagelogs_dateString ON LlmUsageLogs(dateString);\nCREATE INDEX idx_llmusagelogs_provider ON LlmUsageLogs(provider);\nCREATE INDEX idx_llmusagelogs_model ON LlmUsageLogs(model);" + }, + { + "version": "1.6.2", + "description": "为 Tags 和 Questions 表添加索引", + "sql": "CREATE INDEX idx_tags_project_label ON Tags(projectId, label);\nCREATE INDEX idx_tags_project_parentId ON Tags(projectId, parentId);\nCREATE INDEX idx_questions_project_label ON Questions(projectId, label);" + }, + { + "version": "1.7.0", + "description": "创建 EvalDatasets 表", + "sql": "CREATE TABLE IF NOT EXISTS EvalDatasets (\n id VARCHAR(255) NOT NULL,\n projectId VARCHAR(255) NOT NULL,\n question TEXT NOT NULL,\n questionType VARCHAR(50) NOT NULL,\n chunkId VARCHAR(255) NULL,\n options TEXT DEFAULT '' NOT NULL,\n correctAnswer TEXT NOT NULL,\n tags TEXT DEFAULT '' NOT NULL,\n note TEXT DEFAULT '' NOT NULL,\n createAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updateAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (id),\n FOREIGN KEY (projectId) REFERENCES Projects(id) ON DELETE CASCADE,\n FOREIGN KEY (chunkId) REFERENCES Chunks(id) ON DELETE SET NULL\n);\n\nCREATE INDEX idx_evaldatasets_projectId ON EvalDatasets(projectId);\nCREATE INDEX idx_evaldatasets_project_type ON EvalDatasets(projectId, questionType);\nCREATE INDEX idx_evaldatasets_chunkId ON EvalDatasets(chunkId);" + }, + { + "version": "1.7.0", + "description": "创建 EvalResults 表", + "sql": "CREATE TABLE IF NOT EXISTS EvalResults (\n id VARCHAR(255) NOT NULL,\n projectId VARCHAR(255) NOT NULL,\n taskId VARCHAR(255) NOT NULL,\n evalDatasetId VARCHAR(255) NOT NULL,\n modelAnswer TEXT NOT NULL,\n score REAL DEFAULT 0 NOT NULL,\n isCorrect BOOLEAN DEFAULT 0 NOT NULL,\n judgeResponse TEXT DEFAULT '' NOT NULL,\n duration INT DEFAULT 0 NOT NULL,\n status INT DEFAULT 0 NOT NULL,\n errorMessage TEXT DEFAULT '' NOT NULL,\n createAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updateAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (id),\n FOREIGN KEY (projectId) REFERENCES Projects(id) ON DELETE CASCADE,\n FOREIGN KEY (evalDatasetId) REFERENCES EvalDatasets(id) ON DELETE CASCADE,\n UNIQUE (taskId, evalDatasetId)\n);\n\nCREATE INDEX idx_evalresults_projectId ON EvalResults(projectId);\nCREATE INDEX idx_evalresults_taskId ON EvalResults(taskId);\nCREATE INDEX idx_evalresults_evalDatasetId ON EvalResults(evalDatasetId);" + } +] diff --git a/easy-dataset-main/public/imgs/1.png b/easy-dataset-main/public/imgs/1.png new file mode 100644 index 0000000000000000000000000000000000000000..23e42fdf646c5220570ba18bdb55094b070b5e5e GIT binary patch literal 1786251 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
  • WhGXSuft^|Hrxhe;%v_Luz`ufAGvhoTO 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

    n(l%TP2&Xyg#Z&?@SfNsD_Vai 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

    Z@y&%f7lq2CkEXPaP_5+m8s>2b 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 z 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%0DUu&P*ACiW? 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%0AO11+b+wZd_nsE1u_GE*w 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

    K($J<#S<{+UBHMV;SkyG)CwB>K$H{;O>{x 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 z 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

      U^Q*?T@PoFr(rdZX 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

        {_!>Nk~XyWd+uEv2tp8e(_%F4_|8C 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

        &raQbfeGO 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�}q*J%8|gr2rGs(!&1K`e47{j6yW`g^0YiLW*T^XPwMa$&k5-DD*{ax)Th z#yH1|64vgm@k?D>R%XS}tAb0SV8y~My7SF0`e1JB#zVqSAbY!>lyQRw*URDK_ZpT? zdf?+Uwx*TQKOtu~)vHcF67|%pSC(d;zv%Ql_}6?F27eP2raiC{fj3-3Kgp?O!RuO< zLC@1NeW`5CBfaJU8Ba>;D`0p{_JyA&Ju z_X5xJK_rdq4cAIjykD+=hSLT}#F)T*Fd)DHmVI}+b$i7LIK%%<^9Jm-EHSa+2G%MY zEix;(1P~P0wmWwp%DY1d3FK!Aad}EE@L4-p?Mk_IShtA)rMBz>?*Q8tlj!67m3M7y z1xN^5uY7``=dhPvMq@8&0rMBoC!y{gE!+#3Gs$Sm-Z1k4@y;T1stnC9XkD+ jN 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

        mbD%QH zs!2jw@e0RwV(v#cvIqIy*^-P+Pieb~P3gC2D5Sj-nRk*TSR|MhkVfT)S&>HP;#dP# zj|#x2?{R`3ND^-gJ?IO1EUI4Yw}fWZw~pYXH}wZw(wpx)KX9QhN+{dz<_=eSyq<~a z#PcOv5}==A>XDP8p$joy;)a?*IZ-P)2#?bE_upV5bepX!d*LDn>L*$K<5hDk6@8&HQhc5i>p78R5 z6kmPL#p|Gs1dxW5usrJK<7-yXO1^;)gBePy?&cB5TOXFPD}ybCdTAA!OmoOwDl@C5 z`2nn8Z?3Lt@q_JS{iv~kd>jwplse`08e*|A7UA3DZ5Sp$kvDSwLkY<^g$J$ zwod?{`eJm}zgxBq7@3!L>w=_dt>%BraR58~lsWbK4tm{S&twh&wP5+$D?$I@>Tc2e z2WN|LT>A7bXL#yH`mN_{1h|{L?^8+QWqHBbhzAFc{{+o0bLa`Z;WQ6GxCt$rl#3$m}QO?Pr1wH~UC?zQLKkOTVbKuh2KMZg%N zz{-h#=@e6J^WIOZFBPLc@ti4$_0an{8)vO&IvH1&N#;3Y4OM3xskxl1zI?0%xQsOI zMEx5A&$7sGryLq3tOs??u(if;%M+ObD6_A5CcIEY*F=Cvv8ycz(?5c|h%@@T1u!Q| zTZ}@(k_Is=<1`&7-xJ^zpmmxo+^6DQ#0NUU&~w$LOpZ~+u~(&nhf#~|u&d~{A`b@) zYfOebuNnwg^hE&M%To@7;zOiB@@|JtSHE4MpQI8B`+QY0fLg40KD!2J!+Lkzl`0SdYiuEo#Rn?-^gG2&uoS2Fr-y_BWAioZe@##N zn*PH$zx#g}=UHl?M^rDm(!ahofYA@B|Dy3phiE)N z&$w#FLG@?PB11!5nINjr`c3XtYoYH8_xbh>5AZME86)E6wkIx8F{ND@-Q6vC%7wSZGZ7P{*a5Q8GeAoh8zw#K)F75OE?ftmzmfakew*9$(@1vQM~p9yV1|*Uw=K8;5)Zz zM2Xz~vPY{flN9)wYVG2<_!DG}Z{|DKV?6=>TfN5~gnceb*ZiT@osRXY`%`eFK{|O97xgQ=p_Cj95 z{T*t%x}hqrgRX}EdXbQ3WaeP1Pq_a((U5(Q(c^}jm5@(YhIw>VrAk2thxv7u==&4+ z!!biNXZ6NIc^w6g_UP(LduFNA$ntf+-DhQg5dHIV)uJV%71ORTkFMXd`;JSQDQ*Q< z8xK?_+xG&*1-d2iRV�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

        N*zcj-M(FLb|@Ic_y^ zp7*LDWk@c8({8ELA#QUncw$DRF4~?C^wAY#zRnK)bS2C^&YH*eEAXA`toSARaLoRd zB5L-EP;4azP$XahqV zs^w@pV){p!t23x&%L!4*N2vO|EV^}%p5DK_CGqelS8UpxQF@0|JjefNubnG1?p&Xb4SvDU~YLSW4!#8W%3uR|AN}H zKLzdS!F~ANj&*XJ40@wiUmY5Y8mVW4`C8KtNq!Th_#r7MYuhQnEO80Cx8rtEe=M6~#b$=nE5`V_i9 zekaA&g=7kd(iN{|rUajD&ZMW8-f3bf(RVsqqZKMBZ|q9ZS_(R<&tZRU2(RLRwCY^B zpK>kB*Ip*L{&(C7VS`McTA0>k8$Td7X3rh)m=zvAl6rqUqBWLy8tPUE4$S*3eP@k6 zDNW7v@aoWV%XX2qc@>-U4qZA>(okSwc6e>NF1T{_U*PE&>%>hi$zckY3a^e7k^Unc z>valtC>Z-MNd#b?=$?%E0c}}1^)QUE5X8Zb8NJ}XqMiq7o5rR@(<5O zOK|r%PFtb3_b0#PsH1KHXt($_;wUTx@xWNhFO^oQoKfPR#|_K+>M>`G%Aff*zZy2j zi>)PH3L=vY$o4o=fD(reg4AuiGw;>-0H4xP=}n7B*l)$I;tj3kZ&o}yE9hD4coyQd zpip$Ga7MvaEM#P$0=prE{-is&ds*jDk%H=1j(*(ikejn&_=3hK^ye41LwMTX30$~E z?)6@O*I~Q;*-2S{&FtBzLqJ=10ihsbbVJiy@$0wI-1cw5eYotk^PzCRp46|@p&-Ff zD-jk}bn4=WAhOtT1N~8lUe?bj+RXMyrqU5;mr&$#neyk>VCE11!Dp=;Bf5#*g3%(_AxLSM8t=2pSJA^w< zvUN&y?->@hL1o-^NFO*8^>v=&D;5^poV$?*u1OXge3tDA)rsslfP!FN{Bb?Q;-H%I z7UWD=k+p2m#kL)p;f{>8YZkRhmdjC4mr~8_YPH_L9f@D1<7-FS5#HI7642(1?Q}=d z^(;_z_;I`RF!!w4w34xQ(2pGCg5&^A=kBQw;2;Su`>Chxkyoit&s(gnz;G!sVasVz z??J0aKWDsjQ_pZxKRx7k^So!JPAJ*_s;Nov&eY;uAa{k^D)K9}^3eUuQ@7i#qGM+- zdA7Hcteb{$J$A~RCXW<0B?2*Q%3+cQl$=g2xv;yv6L+)|(U@eupPozm!=C{Mne@gB zIIa-Yd-=`Hu?$({F(dJGZN+! ze59B87)-ucO}b@jGtrhoLFB$O#N~$GH*S^`C&#hZCP?GcayEZ{MGF3I;dj)vg(E>Sco|@b|Xk^jhj`DFelHfKrcX{3WtNnayxggXX&~xH^ zKHrcB=KEd5i91c=UrE&qOLm&eXXd4hq+}YuchUtbf#t3~xo0{8%EAzm7KuR}*KuW_-3^a6Suy_$zTVgeitsdb|YV|77fF<69z?IHSPZ9; z*6q40ibr0^lAo)pFR_EU(2bYGBSTR)l1_xeB3-Xn9_R?_GlaQ)__Z75{`qE%Z1fPa@PNVmSfI!K!YflZFJjU`YbG9Z3*p7hjaHMH*j;tnFiE!(Gumfgu+-d>xx~R^ zF3X+Av&@ER7-J?i0#q{?_1D2J{ULntz06F#z4|;JrR4-it_x){(4CS?wLjO^*IB1RWKI&;nz zg{qMkQy=%^7E9=&K*4{A0KdPy6^r;D!$vQE$oSMbVE(M`PbyX)V%aCp!(^Bob$=sd zdXvk)Z#vx9w{aL9t8veG^>WJBDeFUFRRc_YF@z}?Fs6W+y|X4)&O_a9>j<&*JR#1@NS75OGQjZ2MkJ$w!0wC*}E!e6r;{PPl9q;qWdf)LAbT zC`A8>I`fbosZFHq(+d{B?bOL)zz|U3m}WW;LP)S#)Y!@wq9w@ckpM8no$$pH@CDQV z6&S1mP8;zV!!NIIcjYFyWv)vi69QuajRzXiRpz#;RbC`Xzs5C>1n`%k)LDM*;QEmR zhft`R1+*5ic%f-86xp@=y+m)+W25v%#7iwr&`Hn7|Ca?If4ZJf+x9dnEhrrKdD&nE zTJ4QYNiXTye*dDwzD#Dlou8E4xp04BT`-fYhQwPl!yx>6hh zNlug@2WP9TUr=(Zt`KOXW?g?qpk~}$^&0#w15NgvcTOKHrt9GLq%zhV6x8h17`_>m z6A^bftsKd(47EN&$1+tpP~^hioT*7Atpu{dWS%lL71K(^!4Klnl)E&^27Ghr4~ z0|gSQP4R+hZb{ki?{2xurfr+&(}EV-DfjJNyIcLurq91@Zi)WXV4Oes0E7piC9jO@ zJ*1^a=P#y%O}~2c^KS(FU-NYTA#|{I z(nyEvrg32UN=YH~=k{+^^7|DeEi0Cows`Go&b&$mH#Mc-sX3YAvOGD9vGXg58pwaUaJ9pw`u$RRLhDlK70LwgTHA3?=HA9p61pQ`dHqj z!%A{{k*%$xJL@1kH=p6iq=!0MO2fF(e zBdo?C?Udo3tt0@uy}ONq?K zP4}%U#Ae--0;G(7{{9-C2)E(F@Se+X8KznY(HA@%?gqNleFY-Zdmg2{r~62z2XWA8 zB4t6HdsRxqxV#A))Oo1-1PzHdKp6H)|5Yz=Nq)+CvN7>++^8RZEpVisG=gXI!Q{ZUTFeqyS--43d~vb zB-Evwi7?iFdQBH0UsJiP|7-F^b6-fYcVF&8-|ocrbBWE3kHF1U<~NBgXCHCL0TUx` zUYByZ>hx4@a3`(iX{P~;f&HMX7IYAUNRA@FryQF8r-sJ&OJ#SZTp@MukYtkk_tF!F%u0*!VTZhUggTCU`)>thyW_76)G^A7Ff&=tBn6RXR~1G7e;VrEn7@-^xtqUJs_7R;yKcGfPeVSD&C%&(j9EB3c`5*ooZ)*%srwtg@kQ(R zYm`1~(zZSzpAiErPpB)tfADFO-CbOJm-q0r^i~iiG19mM`UtUa1J4Tub(8lk>JNcL z{XP2P z2=3*rm}lH-{v+YZ({-#GoId0o>P9bnE{&A(Fr18u9wFlD0ZzEwGxb-%V@{V(Jv%%h zs+`YNQR6zwgmtt}9j;wPHRluzgg`5L7rIKeCCW(YU@=nD+H6)kwu5gwxx-CT>_>@9 z$51>=2OQK(6YQOL1lo+_YWfmXONc?*#%Qlfz@k#OmQOqbc->^tdM3XbY~e`Gs{d}Z zfGxY@5$az_{$af@A?#m~d4a0F*y~K@^*~)$Bwean|ReKkl-Qygw+w@C2wg^?FiiR(TiuTx~vk%dg zy`Hl&QR}yg7skD4CMPw2N`{>n%XvE=_L?~iyw*C4*t#_!hTAY6c=&M)&W!p}K^&DQ zD$n1xjlL|nju|!ie6oy{sZrNlEOh{P{#oz4BsIb-0C6#V9egIG>GQ!*_Uh{!+jLnw zg;gZ}k6b5s_1QZf7w{31qCTjB&ZVKu#xoNlQ;s<4TYYtAn6Y%;d@`CaXjGAJbe}4D zP-rdMsyWq?OSSO{va6J2^ZJ+0$<+w9-q*{4g)KJHj@^TZEs?b-hc|Wx`$R2iW34lH z39qZ@)ywq&-A45&l@7%XSZa)Q^fU91Uo!_0I~m$d2`5AHEMdZNBJ4LxLY`lQc5FP| z@y-ZgP0ktZn27o7Hq3;9mN`JS=p9U!ep4(;9llw;sJk52YZ>lT5urF`8E!E?|HiyD zI)G*~6yw^y2-dKOw74LRMH64z=8RmzUmuJ#OUffD0MH3&}TYoIb)vZ-7RCZTPe7Q^tf369Oq?hj5jy{Lj1|ByT(q{zw#g`oq}}Jw{=htH z8IAKuuDfaCFpwN@>aoe`Kq{Yag; zr{2-=wpA$Z=X`am$n-h~lv*=zzOjrYkC94vl!98jrDwp7$>%tYw7-F7c(p_ROC4=i zbl41EeS?n1`0M$-R7Owd7ypmW+X@TA)puUktK9}U=U3(HBITT}m(5n|m5f_luz zs<$=^o6>Ci>>5j8Y#%wJ;2x121Dtu+8|RdEuD`-aaitdWEi7Lb^`?`@5?S-n1#k_2$$yEM*Rm z`>mp)yzSpo9P7e$t3XXx2?jz$vGUvy!zMZf%T@o8ylKBZ{}FUgOFE|y?sN=6ebbjA zWD}!U;ZUz2XgX3cyCoH(oL4wA)6Y0Llu(sVI2W)^+=uZh-n`ct_vHcZsmZx5;1e?; zlu11-6IH`K8%l6r71;(fyu*!kPKMte&YKyCv%rgs-23!jx^y*RHYBsezEIX%NritT z&0=|wJN%$=rODL*E@m|*U^M+>_j+6sp9!R>U7fqORjv5xqB#8*LaMn|_=gK2ZAJ$V zXZm^;>x1d1y*FD2HKj@Yfexl=-&`ig@A~LPW`tj6hAD4Ie)fOHonXDQCr`L7Y)U

        =xxKFgjx#U%sca+vfZeU8|eyJu&YEGfkiFN$Vhcu9A+SzD}K6H3zXJg>Vvzmk?2W0oCdH*Yso}nbiyjht+zR5a$UPl}6Z>67n5I;Fz>4iY@aHH%Lk4{^=Nj>k^j;rfVaqOuM zf7nfQuCrJCE@i+bL5QCG);3|q%YjEe`zO-=uEn)=HN*f`QZR|xaL`IPu3}BH@rALT z#G{CU*0*MWJhlnb5_!xfzucGmC)F2YaVFu$X}`c@Cjj&}Z!yh12k5@?E4?+;!?RW_ zw|%?orWDZCgod3bEZ;IDIQch@>)&Rl6?{swqj}0*YK>-iPn*frROcGumStf7udI)a zq=!9KQ4oo^=){_@fhu0rDzh3XgJxayTh;Os=W5$y4sWG zraiZ>TK4)M`pmg*C3&|e;>BwZIm%7=mOMbk;84PT_Xg3Dm^Yeh(0Af4Gt94wBdZ3F z{GL1Lb5evV+nTH!LPuIBp%}1U=t#GmUM-^#1)+}~xR9&?A5OpZ9v7=Q#^1PtXl%^& z?u*6PA2EY8SQI7<6pX?^(E$cD1y}(~9&scG>^Za6;uufAJ7giSH1})=n9*JLSjxyh z7g`5@&y9Ofg}2-I<7x!S_QbNk-f!3I+a0_^+@-bTu3#4oSm+*f0m_SP zp8M)*6!c00MaO1Z58MB}*E_^;irXSky%j(1_b=R+^{;ya{D*Gi)7MRY*C1vPLR7JE z$G>xO1+Y>)vntK_wzi^oX!PWAXm>fhfc&7C=j`aioDqMRd>Enj`Iq7X9&Ao-fQa=Hs-Lyiu5h=^bD8wZT!i#7 z5;t@CCkstmw{po-m%@}GLMBI2l&1ALN7#gnm6C66fe*ko4a5}-X!&fG2 z3LrMB^SF=mk0S?p6|}Dfq3wZR7l2(D)OtcUbfuU=f@M1<4-wh-Ko%wT!o4|| z@EqRz`*dodL;I(!3#aE~DF!-pE>7AJ1pip`9`UNZ#vk-z*<`P?JB5`>>>x+bf>i`O z$!AgSu86x8Zr;#)pX6 z)rY3Kv8y}d&q5`#B*43Y0s`C)<9*cdpZw@r<8w@OTBwC!bCP<>qCc) zVJm;vC3|l9yT1W?HM8g=*A1{)(&b&tg7)xTuqQQ(Mn8e z!iV8HuRYVfLpXf~I{g&3_Vbj6-1Xy?dxLy8ND1@~IT@}L=_$OuT8!l1?A{f5yJ4eF z2EYVq8AP!a5KlWRmmkIGd3;a9V|jy=; zA6T^cT>$G5JAObnT?27R^WxTt&99Iu2v4_0xhQ5Qbe5A!iMa8s7>>m&)p+0 z?&`osQWWgBua0fqS3XbwJv*q2t#Y3+j%{>fk zOH8a9m_DoJi`~$T%+kXv&sxydBX=fl0hjWGs#O!d$9=am2dl#VHk@&lpj6x-z>y^a1dZ#{*hNxCC$s6LcJnVYAX zY=-l@;~?88&>YF{In&zs2XX=uOYYit$@XLB_gTsGL{bVk;fP-F9<9)^~&2YopfIH zkrNz7TS1g=$s%YN82DvIEJuhN+kVpP++{8#)a4Z^o+fqrg9{dEE!2AwEH#8wWF^sB zMx#H^@Ri_0#gQ&J*V7Y^4}hjnX75UADd+#}ejqR{LPK1e9-xVe@1PH``F7;Os7XF6 zfkr#CsqcD$dg8oLa-H_z`Afm_XDK1F zVA#s_TaA5agGT^&0{1TLfYdo?OTe^2xV=i7(tHWao0f-(UM2$wv2Ch^T_4LTvSCl4 z*smk$=lDmJ`VNoJ$k*;IVel+sngUT4hl7R~cgKa7_#Rxq(8KDgBX>v0wds%q@-0pb zck~k>n^ND{5&ppA}-JOf|`E=<~E?URcE1P4N zswNs|z2Q=L$wjYdUyL$YS578IPUFvL1^`w`!apuuHxW#Rz+6b%+V)>uno&v#ucl|O zG9dllDyw;X^JH!5a}Sb`wb1)L z0Dg`Co-V1w|L^Iti|7YlH+-{xH1%7u_a&`0)3BtO^IF7~nVio*koh7pyl54djaZ z`52B|xoB*=x*lj*%rJu(sGOZvb#s!|uDz`zlBvG#t?qlniS#~u`MtZw{-gVou8O%_ zsG!T#^v5R0>OYIAKjLJ`Gre22wRAcIy7Ov@U#{_NF?X zK+1xNqCohEWQ|Xs3&fP8Ak-hf63LgrzyAEanQYAag69i!I)O=&IFUSjOiM+SAsF}8 zR%^RmFxTZ*t>cFUDV@;L;zFwS#&NF!<)&51JxfU_UL`U*JXaHLlI|Ka6u$Q^`+;`Q zm0djbbD;Cut)_%wNgH`>{HD56>fYPx&V!i*;aU8)#gz8 znhWw$W*k3TSA zJwV&8`)-M@%knWq5eW{dOw8ECN+R99mtrjcXa29LDuowRxG%}HTH^or-bBE9_NS&! z+|~AdZSijY%>=EJ*){5>Ix@3DK%`*_I{k7`5}z;cV&s(;K$+C7Hl1f+p=n+$j;k~> zxM1=}`y$igh{e)Y0^K|;ntsbdviJ>y?BWIp83Ae&HlATJy&~PV+m7jUh_P9w9qwzE zx|1_*WH+~U`?AK~EcD4A=Z1sLqY60pAp~?S;y!XM*VNOKkTrIb2Vv*+u9l0a|9x_1 zv&hfMyGMN365Rtiq))6Zty+jCq))C$f%^4QRUTkD!GxW?!oEwPXSYZaAJ?A`NY~Lu zEbSk-^CjdH`%;CcWp+TSZzsftiQDZT9`8upf{CVt{FYruMR2zr zV$l11WTU#}o+qX=5r6KgqQBmJ{NY(IQy#FzZOuj~wShi*V=po$CLN^z7#{Q5x#o20 zFYYv8AjFtFP`K>FYrt-D)$3GUmIjU13xt~3xv{puYpwFW7(j2ifJleoP7cw8VeA>w zo{SxIy&DD`07e#bPF!>T1i#?Q(dpvDNua}=#DclE^R&ul+c3@Pl!$D&pVhO2rfPv- z*8+t065Ql9yX4MBLe1oFmX_b~j>yy26tK%4{(7E&TwqN9m>qRoT9ao`<&9>(`&{n9 zgykpE-un?(+X*Tq&F+e71@Ak{t+9&U#rD8xl;v zO?7$H1G_GkG!Fl+yisOG|3sR5v_86c+5FutI~&^WAEu*eXKznQ{=-Ri%QhS0xul^5U4K|OhEa7OKaRGvwn z_r!NVTfMVg^-srbBz2{eq-X+$RUb9)&Y&N^+h1>UPx1JvGx9pz+$YVcd`NK7bZ7Q#}bKj}{{?b{(IP*#Pu-ymM5faAMo?s~jUpaUNUpbS^V z<{x@&?mcBHzc*`!?p!HdHKtSl*K966^m}&bQyLZ+qsw_`FpqsMX7J!TfWe*EfAZ>A z=d=w_|DX@X-||j5=zlc)OJKk6^oX$R(30uVZvO$}ELlNBPWJf2iFZX~(qC$XN||*9 z!b-(=Ue`Ri>(=qc?RN_k4kp1;Q899WlNcajP)~m2I*Rv93NYF z^+xoz^q`6FKh$*tub<+d|CSi8o2tiW;>!YJWzT;|LH>&PwhLS?Ck>km!xO8$psZf( zz3FuZg)FPydHs0%Vxk2nx)nvi0xtL$Sm%lGI ze>f^%cB6abY!yVl6Ma%V<{J8^0QG?;bVq9eGc_Fsq5V((6R@q8no;ANh@*yw+Fp@|vp zdZB{PBDqhM`YG>B?i&sKzutB7_$bip#`5Oe1(`g9@qgVG6uCwd$k9Cgkym<)Yzy}I z9u@5!t95<&e7!k%ty(?2W8(zMyaxKQvhc;(-D3q}X7es6gEMEE)fnziu0>lur)C&= z{}SSCAk=Wg2nu|mDWwL&f6+JUxWPfFK+!v3>=@5}N~KHVF?{5fX3`%=3uSFa8g zN|;d!nG3&DZQZgjXu9@S) zYbspFsV46!#2HlEgCVyTol7=Nb8d|_E$D-fU9fz2=6qe529UtWJ6;c~FQh*kq0Kzo zz1F1XN5hDo2dwq@T1|&*U@|ri+z(bl7YDW=Y}T?S@veF?rcKeeM>oGb+hYcRpyjg+ z>!oiV(ZiTO!nJsguA=A1M4}tSfuJs6bJ?KjyH^~TUe4Ip&%V(#T7s7ef(?MCvv+`q zQ2HiTM-~uHH1l9MRD>V8=Ue$4jsv%?QC-Bqqz_c0$Rk07BVW-!`AVx9&0ji9ZmHR+dgs4&eLZd2QBE1?3b6+mzt^_@&2?Gd zIkGR#*z>NWrpNXQ`OA~?*_wg;fX!Qfvc7i9)ksU12xaqg^hU7y%l6EoXi4JD z#)E$rme8|Nn4fQqLOV($3#!L8Y40PUJ^7ElCU8B$Hv}q zUg;&E52Wr*L(V??>a_pn8QIHB8K=uI%5OFQGN1G+eVX|TLh5H6d+8WP6c^Zb_T?BP zwxErgFJv(aJlz9NDkbyf^dS`<&TM}Rf6dHQ>w@zBpZDKYp=7*!kd z^6ulYj8ORonQ(;BLw_iJgUcP51xCFX0&r6=ADM_!Byh06DL+%FUd3J3Fktyt6k#a$ zc@0$@D~VUMi5c`IYT?C{BO9JHpxid!b41Svd3ZU2%0>q(N4{SuPq(~;xD(p@Rl;Wookj3!a(TIhTaXQaA-gh z-QZcez!!sUos&VED{B&Lt+W^`3%^5WfhZ4X#J8Wt&? zc48=GFm}01VMs{_Ti@fAh6s?3OW9C3WY*Z$)UtEPu6(qukbYUyqr!y`N0FSZdsBD) zUHamBg-6@mu3r#yVBUG6W%{-+R^&LsU1k&v`7=h^TVvU*1rG1pc)WS)t*QY3M%9Ku zgzlu`^JrlwfDQdoDj)wf={tWX|=CW@8tnp4Am> z>pB;qzijSPefOz5eR^kG(>1GwH?#4GW#b7%>R^EptypNbbUq*YZfVUZ#qVQQPw<~N zO@2{i<2ktAI~n$L%={!<`%uJUd=8)FXIY@Phe_BwGvtQ_rUCuc~9 zQz!o8oK54p%>Gqj{B3Xgiub6>FTu+Pn7 z(9SK<{{hfzHF*eyiN&&u#ebO}?i%{Pz-yh=Y^eFEa*m}&7Od?eqa!N|o5gXNQEQp4NDj;uGCv+Km{!{*%#viBNmk;M>WuJJl3i&^cI@;eu^bO+k8hQ( zT{n_D;{aSrPQ4+Txcb9Xsgzo1&1N+d;z5)%X!jGPiukSklY%x6-!~*)_6#})Ngui= zH9gt)DZQ=g#VfWe<07Kc=v^gb95p{G*wu*({-Ffub~Xdl83hm0ObVud{kdlAT`-_J zft_*)Y46_ZsY*t@Jv+3x$$X2^WGzTCN-ol^kX9Ox_&ciEL73Or;dsbUVE;;aOd5ur zDfo->_dlGqql)40wUisW5yHPcx@P7{9;X%6`SFh)nLG}O$Uav|qY7A(s*HNQqHK2b zVcKjgjT>0uYzQ9Y+0%(yHKEl$E5WLJM<2t5Y;T)J-tFiqq{f;;7Xn)M8sM2H*R^&W znCE2HGJbrJ!5G0e=$F31=;HH_Vm3!qr6L{X>A z_Znp0iC(2W_#x}S_`Ul3(KvTaC1H7d1Eme;r@YN zcok+Km9#Pc#HCtVh*3787F z_)QzU>leVhgquib!0AzMTQQPAt9@JK**|iNzs1u~#~9?q(qsWfB+J;Ip?}ES zLE`ZDrWud4TWze>DDaTLy#?I>@7GONLIc^0nY+Z%lWqE>gy*wS@^3#ICjXFb&%Y)& zH1rK1Wwf*`adD9LJ848Dit3!)OL0I->J9aIZIEBZzxf?C#UhS9OB#lmZnf%NErwJP@OL4wF6p$-o$&W*Caep64m9jQp2tDo+1!j}Idd+Ua1mz_ z&BDRFa}4p4-1WVFdxnqrh9v4-{+KB=0I@XY!GHlRILFlFvN|F1r6J1ahwur<{lUCk}@YOd;9G^6yJN0Xt+Lzhe zi!X0;?)Xf~H!YzZv0`(+w+acgTCEVQ6UimS-Uyk~Z-TYzQ0gfIeF-dJ4rr}Y2G7Sh zhX-R{x%58pS?@=|JsQ#_m)DLPZwVt@QM*gWTYGi~8EdpmB1jz~_uT@00m9=8wnrB7 z^d<)9SK{2Bvfd8tgo6UlE(N@6SerTi1gtDkvryeE?`EHChutw5n|O$-BMY5w3|dr zby1zcE~T{Ws`!E7q)o(o=zBqd@A}W99N%kh4#>LAeN{CEPvm?-g`&3q96a`VP6nLg?4mMn{k=Q5j_@M-C{QK%wZO{T!%{t{_>?xg{sCAdL7LPY-WJJ3D@AENDnI-MyK&XVK zOisXaV&dbTG$0p4A>o7&oF3p+Y;3|Ne*b_MUO0+&q!Wu8cXNDw(8AQSED9Bosb3?f>4JC~73foVAe%!$wf zlvN->oaYftc2AH@$-0f~iz5i}-32InWGjh0CwMu{z-5iA{|i?iPp7(pMi-Xp57x*6 zmjZ(3u6YBpR)T_e_N#uT9BlPDCnl6zO7LiWrD{yVRa))Hrt+u1pd(XiKz1HWvLaut05D?X&%(ENmPu!qo%1x>EhgQM zy)MOBGWcDFbgR* z6V%jBatQ6$zT|cI%xK&Z?aQlIgPN8UFW*_XkwP-mwz>G|6`rQ%#VEV6OA?h2(JXoJ zi;q7vcX>u%h_B5dLazjgEroa1kz<=a|L8Nho-y5s&>EKfx)CI?t&fnMfw60MaD;;~ z1n2y$>FpQ!4Qm+DDyZ4A3-hwtOklE$us$YQ{c84S#*e6o-#+ij8k%NDSX8(0R)|@Q6qNCa>^^kwp0bXc_6#x z)kx`F*Iq0WouK1{R*18tRhF_6b(Hap46vJ)6INVh9?dPjF@r-i@qh36So13(Nr799 zSKRGk8;GdJ(eR|#Rtl)B{et~cX=Ju6*R+o*{&)C4bKo+s5u)KiZ4+ww`$#jR&0-TSffV2 zppWsEBJ^_0>iUa3QzunrrZOb*sQhN-UA079sNMEhVQDBx(X;Y(O)52s7!M&CxxtnY zO6L9_S{4dl-HRE44%hNRcoiYUQxev@n>_ujeH+?mui2T5L@wMpLk6>`i*q;xeP?Tw zLp#CE0NvTrhD+tjt9xAXowm>ir;hf$q7~WJN?ILYgD6<+08X=zi?6tnK@bX^%y@G>; znoiAb%SGJ#)-NNsr=Y_lhYqfNJN&F0Mawa6rh;9YvPP{Q7Dq>+?D$f^x$0{^sG#%+ z&H!x$HzB?oh) zEb^jS(UI;l&RG!qsCQnPQLBa|WO4t&@Gzwa?@mc|@U;->E#WlMfk#=cXY~!>?=1|* zA~bJh8P$?p?l#maV_$7Mf*jr0JSd<1Ew!J6F!i~0k@*oet>yUpO;K)PmV2*zgBlw}*#Wf`DXn_Ce z2N_QXkR7jE&_|lHY$%2jNMX?~oxH;z7+LdeSKK;~IpSs`=VC(AdZ1ZzHo-RZ<)0rA ze=uQl1~v=TVZFKfZfm*WrN?}c5O7}}kypAqY`;AETk?P~G;W2Zwx+W=J0Y{|K#{F4 zyFY7CGvJi?H4!}mo!=6T{NV@}zOrHvZmd3|dmi;MI`W2N;+AF}_)GudWzhocQxY(C zxSJji_KJ zZj~-E_*2XcCOL6FM)|V=KYgU?r>ysOr`znMLmicL^#ahZa4Pos&-XX@kXW9J;CBYG zfUnYb-r|QKw>Mm3HO$7^AFEMRpWRzK-*BTC(&;R(5}&I7Yj(_TB^%^7L6w?a`R2`M zG+Z<<17u%b_eAvVH+hwPrmpP{{Irk_-{g+})vLPuUOBs8fKyGs_X2VbdpLM~IEM4V z>qpEm-ygLcbvpKap;Gn6Grx(LP zw7_f1CRYbZZuWOn7!V;{&x%pH6>U*#ql?LfFAHJVsMgToT%|K9-d6wArn-_2-QJ*h z|EH0O#?46pY%=oS+CCE4iwI2yz+&|Fjvk>LA|7bu5FH!NKZXB%t0`axQ2@v+i5-H# z+1p#Afw5aA2-eQl41!JNexWg1QS9y2IUEEIqZ086;U>gFJ=m=<1Ss2yr-kGZIwsM? z>H_QYAvI-fK>f`UesLEUN6gk8kO!pmJJZ*$slw{2xQAi;`-#qX3)VY(8yG7LJ%-yw7{*hsHZ!uR`rDh@>f6nSlB zh3cDEet-ql>qGCi5_roL9HTMeI#2U*h>j4hAW+&#no(2z>Ez~e1*7kB_H7IWq3o_( z_qv2yxXoxiR>n?Q3c|xM2a|K(lOW$L;7ab092q&hP3GN9?eudo$#!SabR>55MLmMs z0CMVu`!wy`gBNv9MyNKOyE`<^TY zR9id&9zqsSf-X%2$ut#7l|dH`d+aO#OEI%)R!GJBbL0w`UP)s>dy~Myb9`|WKKluE z|3}ei1JxN1#J`Udap@(|IP1&{JE3AF{`H$O|NN_fgve~A1o0*EFV8rjO1o|jbP*Rm zxEZfUGD=Gz8YhtJxoH_HPuRun+#ev-3&$6AVhp7ePWo$9O%vv#LM9M#GnYO2L~jgV z&#UNyik9PirSNdsY2PT!dmt1oNnW?!1pe@19b345J0u>kRcQC)b?lUIA~nJLW;p1F zUD$=~PEPHZene7t6gO9qa4x z`-9HJw5VsF84mwz{{B6sniZUh=W1(bG@7yhj1XA1nb5V7wJ!eGwEpG|b!3I_s zR3BrVHpCyuO362LxSdo^@j`T8;?}W;@FSGf(vg{fyJZl97qM`<)>=G#J02c4!G z)J|;B2$0H&!45I+6f(=kSe`LATY8FRM)z_RmGV#1O?>>L2s!k^=NXS{y>CsK7|TSK z`xZ%yd)OH`ymsn+d)gHA?GCn^G`M{yMM0;K6Z&hCr?T!kFDcaOL0wPbn!+mj)TZ`~ zL9|l}v{9zRW{A|jeUQ@RCxQH!l}yf^bpmZ)@6;>$qULRu9<0x)E$y@Gxe%kzut*;Z z{;I`wT|5`nL#3^AUHT`*oysqgA+lq@Q$Sv zdV;g?^7#+&c&~wbriDHev6uK&HY;wsMG#K5uc#0-38lXG-#b(uFuLC0*&F3BV4#{~ zp_#0uI9#9Ve+otH_xMeGt#RBSKumhrCY0TA4x*k zje1@nWX(&@X;2he65}+rbbg>_%KjJ{uX7W*Sif$yCUUJ))_C9uR7d$!a`JkcY4+K6 znb&n~84`XSm!?d3B@^p{WIlh|_))@|>-8|4#<`q$s_`s!G7g)4Z5*jRMYjpAsud9` z4Tg0+zkerOmgu|HGX2;v+(klu>L%<)X=~%YnY#zIxsu8(1Jcz&EvsKwMGJX%$jWEA@4K5mGiheyBp ztQlSQa4E&Q<@VnOXHvp>8j`)iD9bhX+We0@(sJR;1kz;hbEAa|;>8<6$3Ytdqf<(X z7I48Oe`N{Ax5?2c!Y>EJCd3A{G6!?ca(I>jEIQH!bllRB(DZB^Y;vDAaYiu8ILlay z(^}|{w$#r*tz(4FdIYPU8pEV%#d1=9^yFt3WwS5iZ{(fOe0JOpj zsT_3~63UpAI#y-&&kJ^qDIYNYH(O$>((+|v6h#&ET*(d1_u;!~ZN^gQpye$KK?)mJ zSO-P69%5)bYBW871q6G#QAQRK zdAapV9ZY%=jB)87N%B>M;cAPylMQa;amknkIQ}^&N5air$h8dbwjPTWOu= zXwJ(Dk&Enj+ZL=ImGwvu^(*#P$Co|xBDdIw4TOTY^TZn59GWA%3vLVL&H-#-0jHWy zf*F}(j_2r4`L1##~xWp3pj+1D)Z{4v~v2>}0?uWIV# zEv7k$F{Yla|y$5RTKu+)GeFM@VZ=P10%CmGNj;b)Vc zFrRM>8e~0L(^yPf#*TxCMdNvfei@@Ho2tZLDObBIoC9Vo$HZ^Uo#9&Wo1k>ss=VZX6y&6MuswsQ}L%$H-d53a(D`e_%JuWl4 zq)_)J1sFho8L!D9hC|p*LXA)Q)DH5F0C!e%T~)+`8$xjP^m}@7i7MOugC!Tqztvm^ zM@&SX*I!jzf6%utLo+@F?%3nZQPNgT_v(kn8oYFP`MhJ_^>ZiSh6Zs-sACFBH!}b5 zrdUxsBXi&-qcm$w{3ye>f4o4C?~RUexy}>c`ZK$wM}gbb#IMY#H!nVVm1iA$??kUv zM;v{KNk0#2mYp8a#}9rv-D|fDlBi~eG&z{k4mk%`F%h0LNnl6HsR#t?z989Kak~Hk zej+1$#@G9q%Q)w_??-6ue2&EZrOqw}HAHM??HjS}Y*n(Jz-bbAtvUMo@$jDISP`Vt zj_lI4lWAa0bt`*xw1|}g-=R_55AGN)R{kXmu z^xT`XQZFtPAo5ADBly@+iVusQqch`^zSI3?e?;enAV24g{%EKYw5M}A_IbK#jam83 z5mhKK&Lt>IO!|S4`>uWC5xm=WCx$ori2Kuh)`w#-mj))ntP|$scA(uzqsK_XsTq~@ zh#(ng-3?|*bsj$*Z%w6z`?tTk^k`;wQr}du z&PnO1I$=B@lj`-&Ml-CS^SQ89ZW^uzXSpMRLHx9-BUsNA$|8(nWS3vGjWTXDjH4B6 zoEy~HXaM%3>zEeA@5(C%^)0{=0h4}u zYTrC`(9u@hTTatDUCh)sxK?{W+T~!nwNRW!iHof-wOc5Ty8jo<|G^fX-g3?aQC2(W zL(>Keuy#!#dQ2h^0AQ0aY|0iCG2hycdcAui_<+IF8N}|NIZX_8Ym4g~%h^I>811bP zcIykF8Eo}g%wWo^jI4wF$ynIoaQGJOsM<`CJHLrF@bm0x3D&keU@rqEF2bE;z6}u3QO&(zMwlRuK z>`zTusD4d?xv&jvK5TB$d%4nkp~dfCgz}8KKh&i)uX_bAiwarmBAa`Ct!jtZz68Re z#JudK|HIaKMm4#1-CD(7Kt+0qih?LrdP`IURGJ8g(u)d6lioriq983;P&xsT-la>4 z)F3VNARqw(LU(TXOPE|vzokfMN!an+W}YG-Dxhglcg7C0^nk1)h)i84 znI9_CL_CUmlooQ0xhM8GxKr5Y6Yl9aNgW|tX&iT~qI9pe_HFT+D&D&6c}A}d#;R{x z@xoTJV!3ms0911(e~BF?CjM-`Z5OH^HwWAC^}7WuN3;;O4ietl?Z+{dx}x0+og3jN z(X;eUb@B^I1e_ji$MJ__QDI&s%|y5IJ+P(x4bNSm)O?_(c?;Hd|5$O3GE;*+Ajf~? zbL3m-!Ve{};)C{Nr{0yI0Qv;O&Qry+5|aAi41bdTd?QbZ;naKf<=}>dG^4P@WH64< zTmnryKN3{Ha(|I|!&dG)1P}0yvk{~XO(T@vShQdI(SeD{?~&~oiKlC_x@o^RqoxG% zS>QL<12&~Ir8Vc9G~yPET{qQ*_c^6$cIZqjbekHlE=f|$%|^j z9~>pToZiu%h0DHX5m0bZCK)Ho1!WMCQ3+7i*5>8)*j+WqqxcWRn}0SSv+aZI9hvRz zfe3Ip`fEd7c>Whnz^Adz_Rw%SqO0XJZ67Q$vdiX*=+V5N9p4W+J^1-`EuGiF_(*(u zVy_#pWbf|SIbL8Z;>vHn-sSknYrjU{*qp}*swy&wCvd@@5$nDEsrfVOChICfK-gK9 zf<(k&>X)2?VP00v)ZLvB4VD5iXSPId1)xLhwZcuD;1jHfLv&o5dmuA@4x^Ubd!GC= zsrF5@MSN$%t*c^p=9@$h2h82iRvWr)$Mew8Xi&3bD zfVGpwn!<#!yi5di!?~1`2aub6@Ip%7I`_)33dV1V7L=!+H^Yp$N#yVQd*e|1!GJWc z;~3;1Ww#%}x&XPLuGr1lDju)+SiEk*Y`>nQ*NNx$M6{&Jp(hCc;AH?O^-w%SBnmWF z!MFb;^DAavqZf5?{m*-r7--A!ro>3K;)@D(l?1ftSggI3{AI z%l3=YBMR~O7vLs8W3w`5E=ucD>yc|@9_*Qh@9sbQcfj^7^QH*_0cthBbVr^gOVSUu z4942Usg1=~!B>~!gb#vgIw>i<{gGR()v{zm z=AFm~WfE#e9TscWuZ~e~fL=jLe*d!VNA45Ha>Ps=MU#m`$Z^<%R4QgIu~V zE)eOdm1dXh0J2hpo2aIdOV20595jG1*L-hkuaeYuG$3OAQNhADFx?^DxARBQ9JW?U zM7jUh@_zu7mwdI-jH?KqRaFBKgm-TKf;kKS!#XVpD+?8o!|c)uoA-D7TK6_wTy7sG zfJK=3yMujp5c>8el(mz<#livD0x*k#(zD5ZfT@YYeS{)*2 zR|l-I6-Zb}J>G(C6JSwvTsSeyWn+KxRZa$n!*Ti-xZ;F0H?o@SK0dfJ&^aFZc%?ws zTAwXR{5yl7WjXOaYpQ!C5>*%&x>t_=W5~4)M;Go3<+of{s1NAq5)-5)Sh+Boqxg=# z)tWK3o3xd^`UCGs-AdJ?etA-we)YG$1G};b9pCJTYM{s|B3`U{U)o0lC}I%zU+A&_ zYm#@D7WpHDQn-b%S&fln<{0JtEtB(LsNH}9)bWk`*Gm6N`@#Q8`=Mp~oVSA*#}h*j zcsR|6<9E;#*G+vS8tcC$pXuH>q_(5yxh!)vZRf642D=;jSK9Y-=4(3#=?e-nsAKDP zY|-9Z)c{$gYAtsBs)9 zye?xLA8a%;@x{ao&oO)vm9d3_=g<8-hXa%=<>Ugr3uNcxXcvG``tqDU_=Y7s=@_t1 zX7kM#uOJbs7SwwuM0g}z;+aJh3&1$`zPRBLvS%?EJi zv%?h9BZiSO@LCV zbWL-)NG+4}1&3D2!Qk-ekPOS20^TbIFOhKi1oJlbMYVLUA%s}yg^G516;X3nTRb=? z=XX(%fFtZe{BZ;Np{|6j=mNnoZZmJnN3D4$ zIX_{|bLUf|Uclzm#22IBf3m!g9_Kg|27rIw8>{P6;u>CrA3MJ+&dFV*pVN`1%J`O& z#K_XDJS{H%>(tZj;4DMJTLp?rW?_q3>53`kmVthVHdq!TY_D#;K;of?)A(;7ZapT6A%Lcj4UpIxLB)y5yXa!FC(+5G? z@Y>KWr3GT#9_mv$v2!+!dH>CxES zZ#Z@tginEBgWls(RAcY%8~T$ofBe*R)?2I&PnanzxZ&bNE1Ex6MqcJpg3fJYpt=!| zlCp8%Omz8;H&qg);QHqt^oxUzVZfumCvueZTfIZEUw#;5O@YM)k4*_|==3PTv)JXRFy>%k@1d zq^q+P>jC8fCLoaNKYn>fZ1TgxOb1bM=KP>wul}IryOtLEuh2@_kr}D2g{3k$mSZ@f z-y+wIgarqC3eQRE&MzcoBMvS$C)d5-SAJyqc*8u#JDtsOlqZNCV5r33lB%F!WS$O$ zfYB~&#AYg?5ZS*Q;BxNW@mlp;No?IeN&&_exm!BcomZOTHllT(3G#QjO0WOt=94## z2Ok!tbu9cHny?4D88S$m(n_nS5$$D=NwiSM1bW+!+#{sr=qTU#r6xGwj;ZGEKUWey zqdcW@!-4fVR817CDta9XnZ1ZoG9~J1x$i8Sy(g&y*oEZYCusg;e_Ws-PXjUJg(W}; z`Olvo$eDrblu2VQm$I3B9?fUvJ#emkHj}@U78zib7uXZjAhTl-w&l5K%4YJ9D=qap zu&#o*ri_fb|7fd-CUz^yaVD^P2z1BmkUqA|zIR!Oa$%7sWN(jbc9}ugx_t&Sp%@~) zT_f2SodQ0GuAqFH%%pVT?$d80Cf?^GB|V?DrDNESr*v6r{yI>{WQP&21z60WdG{K6 zY@DmL9aeTs^ERh6+aq09zMG0H#fx(#><@Y(t`i$o$UKy(6M3IX z*mn)=tMK5%yLP`8?Txfv?#CW{AD{H!NdmSL-`x>dv-j zfbH?AGS?mSKp0V?D9ceCzjcPr*U69jS_yKZEP_3q6{?p@SK6hvI@+1v!NnM5_#hR> zsm$$v?n&0z7in*u61Jb^O4)?S_iAWEpI@21P7G=(+la zm}tCqY%fxw6lu>IipNgJpKMuNN*nmT*~iD&Va3_K?Q(Q$a^-r)zw9CyCI9*A+Qr?( zoD`4oDBqKSi4zKae7u5z*2pW717D+LlT*EpWiHBNZlsF*B0OEHywUV6>eyuXW63A` z)9aT*+8h?td;@Ms59qv%+1=&fP~m#npHjq*u$DR)o)X-5gXk>0I)F>&)z1LBrn?v# zh3Qt=_+AxdZq?n6&lU|tMxENhNP#pDF2mCIhbx z6!hs#(<7+LrVr1y6363ACrH({^K)m?uSi>Z^|ny*^ciu#t`{sK|CAtnO8l?vijwbH ze7|%89%V?>mYf>Q9&l1s3Vd9SZK~MvW?y2juoBh0kUE^PD}U`-^B;{bam9!IE1P4lI8){n)t-4VRwpduUjf3WM9cZS zq3w4C<$oohl;^jL?)<)DG(Lv!GDsrbZEn|eW>(adMd_EwtnmC$T|5;v7w4(%`Sh^? z^qE6~u6wm7AZ@>OLZT=zfT3-I>sJ0~)Gyk&YjT8P-7qozE#eH>&sOtlD#-{-mHX*^ zM^YxcG}H$1AqvKezH*8)lxv_Pl_vABC3B z?xA;a>_Q$5Wh~Ain7cbsAO`7U5HV%*cT^pl-)N^ru@(W)pj`}W(SnP`{?2NF-+J9+ z{!qD$WYORJnLt%?$Hppn$#6ni!gs}kR(CFOsq5+$1;*OsmxBM%kSG3aHpuMN$o1&s z9c8y@QKh@mIKlXfyZNg2P#q%V?iUKkQ+1iXeR<~G zG5w~E)z_!ci#3J#ji#&Xga+m{%1BZk;c@m^Bl^D|J>sB;sf%mK zr(b6L`%?Xn8wE6d6F5Q%{-l`~5j?GN7fU(E@|Dm`TNoomVsGro73cwXXV^QGhOAU! zB;c*<5^Idjr&kB{T-w2Gd5^9Bb7FRdvcY06jaqc=j{Az^UmJt4eV!0n3RB4eCCU4} zrb*_L1>==>_UDaa?_~WFM!sT$O@ph=D-UG#qzX&X-@GJPa=gXW@Uu!Z`6ya88wCo3Vqv)o!wtp%Ho$5OjaHVSp_PHqS7OCiPrD)hYU_Cz(iH0PPP|0!glBmW?lA0hd&ss`d$1VCT&^ZzP=!-$ z#S3fzuo*rNjtYG#Ce`v@5&wZoz5P-B;KPXvJA)bF(51t+V;kO#ns#aCm0U{_>JAnb zG<2qnay|j^M)Q0<;X*ZpVKlKAIS%TWfjE8Dm;HmGsv4yoEg{ISQx50gxIHbIgkZ<9 zc5L$xzh!a`F%i-4g+nWbu>Q0R+vT4CGhIG1)snO6XNd$N4(`WsI+(tB0nvX~%|SMC z7_4bTR>*{izqz<|Kbga3_I?WWh~Uag7aOg)$H_v!e#cN&*4^DTsIzl&7;NP<0eA{` z_>Ri>>zm`n?IdxeeF^BX6eWQWoXQeoq+muWK*lW}5fgW}Ngr;;-B!W%tM0wsr(RX#<8OQWVKDnnt)C(w8?5=O%!F0Z5*0Dl8}K(3q;{Ck zkn3^h4wapaH+ZtIjG$-_0i9cf+yCsnx9}OvcGEpH7h0R4*`h4Kum(+U2cJeNkdM4J zZ3$p5kQ{-~CFbu8dMEwDj0-vGs4l&qaxuAb;amEvXE)F%{M=X)_MJgRARx-q!F_Bd zakdETGq~_h)9#hgZ?4sxKm(c3?9)H<`hB1CI3T8&D znCVpEFY~3#3gN#m!Htf}x^R|YS2vfWC2p@lIX$DX#E43=Drv6?DFTQNGg5gexwN zO9vVkgXXr@W+n1xhIg*Wtc!s`jQ19{wD?z8pKx> z@Bfu`3>TG2v6Tr>Uv+NC^JWYqj%{F3cdBbqxK1$a=QpQ}Q5yaJp+`?1#Z7ZAu~~Dz zP|W%!H8Pt$%mk=%3#dw;WOHw^k3V9IvXoNY-UOZ|+bwF?j^o*`3xV7b2L|cuRRr$< zF@+-R5rf)&0iC6!P@{ZEeNmc*hjZ*pFNJ9x%U-axJKMfuxCH~Jp6n7^h~05s@Y&iu z8tO)xOjFD*O=&MX?C@a)hx9sC*sf0y6Wpa{xb5a<$Ac@ zVy5@>^_W+xh+N1gE=@AD^8Pjxxwy&|BocdTtJj12Miz!>-O<|y8@jo#u4!J-Y8Mw4 zYtOq!dNB9nqqPDx&%5u6;fW76X@F;J7m(__pVb0i-{-te4cI-oCeP3?a>(=$_)jms zi@x`{0{ewgFhC;w32x^Vt?x7fsp-`bk7(1ehM>J10BAJG6dig=hp#3H2)dUqxPK|kh4QC zn07n#*(y#2g3uYc!!3nRdHRGYAdCH(BH)j{dB13Yc*m(zNWwn-&Iiv96wPtvlG?As zOVChe3gYDN%%rQ`j{(kydNa?pyLWp4nqkHCtpszg!(B|BBV<%VTa zxDH&DH#%1rGe^7qZ9uTLgbWJDGf@*uMqPYl@1dz-t;5BCuD?j$=*EZ+BB1@Dk%I_5^e8n zmW6kqeJ6?I@pHZ-LYjvqmWM&gl*wR_H*wkAl~TY$0-F~`uEFKXxkG^ko(KKKuY|DDBQ#Nga6 zJeGPaU^0#FW&0ksfV?};J%C?QoR6aTe3`TrUNMjtO*{iLz7m@79Gu6_RKeCrC*Mc%}r z<)vu@Zq|C){9E3-96$GN*P!N!T0J|TEvNb~9Qe|4j4~kJqaM37MHZ?~ojc&9&Rusn5&1w;>#!s|6zf!xy-gDfFNv0MgC7divDf3y`8qZ*%!6j(6RwM z7{-!$VU22Nz5c{HDT_M3pn95j(IUHDA8c~;`nJhIUqceppfA8CJ@7vI!xXCbfpZEP zy1{X{+02&2Rw~L1i3%;JdW7@(X;oG~jz146AQ*mOy!ekrG_&ErBo|Lwiws{aS^CqC znI-~9IEy0uOaSke)yq=b9LS!P04L+F8ELX3+NFMuy7X){OhP3;jCxk^f<1zg$+s`M z+`j^{#xiqnoMD-VnnR#LP3wuoNm^D9E2L6^9BkwIS24j}Zvm`Sb!kknF}8oiXxqP9 z`)n%DAVgfWseJBxKO{1sw7E}-sDfjaZNkaoSBmM4w>OA^BN zByWJ;uFh z#H#cq+1L3Xaz)4KiECH1D8Yqe%)S$s>dBZ6D^9a;=-s2?M+D&%3Df`^MOgjuhAu6-i*dFWLZN)VQjIFQqD;z6v#T;L za<(`dK~TV#J6?Nzf9qI&_R`wP;4$b~Pfv{ts;OB0YEY(CxG9M(v$QDz66&ypTg46s z)Xv;4P@QhCc+UE#B!4di&ujSbMidXXJ(y#ZPpje4^MAT{5O+MXQ@ey`?LApG9`}%B z0|!?UUa8Z#d&)4pEib06^J@r#SiG7)fBBX}!I%^RIPj}>4bj7`F<|mW^Lp79c6S_d zA+>qVP^l%{O#2+yU9|iy>C3|j*)cyeRaJB(6Gl^;!*h$${UKSyx*_5H@Is^XL9bEn z3-Y>jzNnh_C)eBsYgOO#oU8UfgoF|-T)SsSE_saxy;(_ndFxlOu)V-@|4*Wo=Sp_x zFL)lf(0Qft3KtLet6dlut>@b37~S}>U;amH5}o$RVV_(GFR#7nJ-7QoSEkj>NKcY+ zeM5WF-H39wy)A*ETvR=jK(9as^{&4TZkw$iw*9kwzL0vXu39pnwd01!@=(Z$o!N6o zd;RW+!8~F_?&`?XMvPreRH%O<*P2{~pguwax%=5wN%Q6rqVPSgZJF%dDu{5T?AU>% znBzFZmLJ&`n-5sk;6gaCtDiXc&^slP?k*hWVgtAVb*aABe-3(-GJF7-9^dk;Wzj)J zUOq=rj@~h(2R8EomS7(>ru-P2W6$_@ys`E_eNB|}%9flqBBjV~ zl8<%8mLS1MF)oX_l(_W6>BW)TibK)?97>4DH9#QP&=t$7MH_<5|HCaV_ zuWsDNHi+L4PCc8o`$#nh zG5S_^@*JFS!Gm0i5x1uXYTlQ;l(E_~vS0jx`e|9R(9GBIG|Atsog2bO$Q*9d;w@d0 zTc=ONygzj6aBw7Jq1^uad3eFLMFl0}Svhaxmn`(_5xZwm!hKG|F+6V;2!o?P8;Mx^ z28^I8WmJ-$6IJ&Ro%G`?aEi^O%lsFU9`zq4UBdY78r#jXHwUw^z-BCI_NrIw+>@B? zOK|G{D~1}lH7#L)i##iF7RgBbRYY_G{S+i-%QG8yeVgzP^%;BGaly(!sWZtmR=+(oWz@A!k4K?F@% zzf4hA)2~y#l)ba)}c+UxaS!xzY-z-%pu$Ogr=sAi04-9iC>P}Yg?`a zQFh}==U+@ItX;a;8o}qK-vr#XP!`VH|e}UzRW3M&iy`puW=wXZ41I@&Ab+1mg zbkq*(Rm6knX$bZPK3o9@O@J&nXe}F2EgKTt#Hi45Bd)&73)=MJDwa6|szxaDJ$^Wduw{O2uHQ(_&-RpzCG-gOW>ieT&C^ zct|;`zyVuk(?@gLjZaZv(z!4EKf&s1i}q#uO8bjra!J^C<`vb+!0?Y21630vO@De}^E=_zfNWY2nQFucl$2Zv99miU_Z%Lf&V?jgsa zU$g)jvsG++o#r^_ag~^jLAy^9wW3#G`Aqy)k)7PSSAnZdY>=Xnvb1RL5`p@LLw6U30%N7kZV$f~b3K&uJ5(u!ToxQct{ z&vE1%i4+2-Xq~=1SKTsyz7lOUT|j=SKX_8yMhLf9|7C3n9B+eMz^Wn9A!SLVe#A)} zcM#Q`qyiN%UYSCFcor7HxBO{Djz_CB>9_c6)R39^rydc*sDWHPv5ol5VsylyznXXt zw|MDq5Bg6H^j5V^jt#Qie&EXwepqc8yjJ#oe#5io6ytLFw5ZSqf2v+#`i@IzI&25r z^XqozkQl4{Q?LVKKo@Hy_QLbkLg)EX^|_bmk6TOK3a%ZaYmz|GM75L4yc^sUI8+EaHOW>Yu=+O< zueEKR(X>nNkJ@gL3cz7Uz&$hXX#R?(8>Vq+NzrORylf?7fz9d91|uR%7vDNJvuau8 z#u%P{^flyBgmQrOU)DQpe4$UX`tPB}ZVC3Z;=c))eLR~jedSIUMtV&M`@*7^iq}7{ z{XJum&LG_O^ndROz;NvK5{c_-W)D2(He~V=e zt?58?Nfd*_d{iK=Gh6Fs$&J?o`@X|89vYxbXtj12VQECuL?F~ z%{8KN(cVFrRdDU*s`1XE&Z*4e_1!!+CWS__&%7>YroJh?td{p(rZM&IWe%Ma0_D!b zygOTuWum`Q(npLq7i_#i^~=Y1PyRms?XNJ;_W2qlrq!$2Z67!(pv-NemR zAQk#KXk!?duV$v@5nEHM!a9uP?R&k8UT)T3hpQie)W#9!!2{nu&)=iNLi==mHLc*M z^a{X41j ziA_#KGr;$abbGR#U4(C2wKv-uLSFu^^w6?c{Byg#<>9_; zg%=Z-Y*mnU-lK*A(sgiwktm)ktEAJZqOZrxH;$p+%2Mt74zKOJVm8Z$rWX>mJ-!`bHY~#1f~2;jON5s%g&l|%=<>#wpSX6#$LOF|gu`kMqWv{q??sixE`CaG;m&nvQ|a`vm$Eud7*MftdbUys$v!{M36gB{I;%A!x+NznWh|tDw=TM`k`5COqzB)Y*qM6d6{#7gr*#3D1P@4Jw@;B`1oi`>RKL@7jIFCU(F(EQ1*AwfK)w5@F}&8Z3TL(^d#KRGS| zH^7Aq=QthrUl3v@?0$OMfr|$ockkmdu^0Nmt6AK@;+D%Zo~(jb{b?W=9#TCE`tnHb zn-n|qDDTMy&@-;!wAuF+OLMmnWW-6LVbo;Qb3gv_aGrXj-6i--t;~TVeGnLLlo$2D znU|D`yz*UVHS$#SAS2fr(-D-FfM{SHTV*_(25JLyJz0a zFJA+Y%eQc?bNMy?Z2i$I)7GQ95sb8qUoR3(J>Vf=DTHA6%zf99)GwuYJ~0sNNRjMmCK-%7Dutb5a3zJ6%n)MHt{Ph9EIExcsn zF&c)j4VGNOhK)wxFXp2{no>W0WS<)4J5~f?Tg22%IA`!=K`{-m!bOnAyZy52NAi^< zziC=^xMNvN#ZxAMHX)5!2x|HuPRY*};>Zc1oTar)aBW>y_Hx2>MJ2$`vecMvT35!5 zD=`Zj`t%D>0IehN3>WU+7{+^a;XM&AkF7$7mhaRlVyFT4CG<5DN!4sGr%2al#8r&9 z%tyAtf#g!&SU19~fPMl2#uHCOHGur+92S(bbtm)L+ORoPD1iY$J0lg?=Ur-YhN4g^ z+|db3D-SvrgS*>m(u9u7_t?zYt}}r<5&rl$D`%!&YqrgbgR9r31nFx1jz867$z+Sg z*Hq_Um`*4GPegndezvBXkF-rF`2hs1hX5<4mxFh^z-8?6GBW_?j9e=k>ms@pMCC0c zqDp6}^moLl5RI0rn95l0QSOlv1#i0?&h3byH*RE8Bb7;%N2{DDtuqlG9~Hh0l_9_4 z@r|@W*Md%7uY4u5rBJq|(P{AZGkoUL2Bnto^C#X<*Nwt5*Y3mRGo$sI1Deu7Iie7d z_;G#`_NIcoYdPqZi;WSZHTxPvT3G!9nu+dUPbNa>RV?<~Du1~$Hl&&%SZthN(Z%U| zE%Pc=P1x;puOA&NHIF2Xd#Ce$s2h=j5s4yAuo0swarC!@rWL4@>ae-P^RT!xct-OT zGpqQAoBUkcj}e-{XzY8c!cCW(&NR1293Ll(spb&bN0(ia9_LFGMXP#`S9Ai0M5Boz zU%%zyB_o@+b}FC(EF2VVt9apb^?$$ja6+AtkE*)wlS50~fII0GdHT=|qi?Hm^u6baFJagLL<%@S6j z(ATiehVcNl=K(N-7&$s7bWHviQqADxweDo8ArvCcwSP?tzQLI?Fu8%7$ zmBg)e7^hmpoF3AfUdZ+b2t(HLaD3Ezy{~>Ewv@~KbU>$m&F=4vcYJ9(sXR@xv;>;c z?n4eEU1u)6dpbG1P~sdQ3D;He9-S_RwLj`SQx~PT#SfU9^(7wIxw$0UdUADFs*a(* zOqT647|C5!OA72wz^-on`3ljj?0|U-pMYo8@;!fE>jqAp?=)0cl0uNHj2=ft!_%Ii zdYv33Zk>f48H(J0yIu)Cn7b$&gp*Gw%TenZPKbVDy@;FNemB`*{8p7GXtTB;-pbRu zfSjv!OYgg`KJrvDS^2d_=$>-w)pS6D_iIa5gMm4V9ng_FzT2mwb-8ZFsKLa{%CStF zV23!}efdiACgaf6NlFc_7*uEB*kFq)?|!1REc}5?H%QBzIOfwoDhoOSXMC48icca> zIP{V0DoM7VsXn0jN9-E)-%&i~2uUELs`rkm^0S{V=+9iJU^9jV1@_mh$6kzzFT1f` zSP%5pnHAJ1dl15yZQJ(PkPnmf%MAknZY*hWGlOixxh@csetqqn-Z+IvMK4M??tWDr zMmE01t~G+SPQw_dRv3;eTrE4tBmw^r0M%V?%sPFWGXwdHEgIhc%C`zFBB{8J08RGy zZ^AouUE_s?BYSI43+cEE7q`xA=nL1?rxV)CvX5z40iMmG9$KaQ=^9Y1_DZreX5*7B zHW;p?xV1u@X6rM>VZ^}iVWgeinX9B^Pj;uxmdTO9qY8fdjsGdvnnq|^3UveBZbUZX zK!5m`4`b7R?0UNK0Oa=zEA0KY=LSxBLvh?5pb(xqU1a~u4QN&CN7a*RhuwQ@opz;E z-%T};EDFqpF~3Ky+?r8IqHTf0O2)2zSZuk6KK2~E6I|ES-5yUL-zwTlY7;!d*J$zk z42)38%2?;wt-U&{kXRD-rJ5Llex}aVfO-OR-HQlN%n=|YdxhNdY{oyBk<4B)69{=5 zohCo$_Aa6;Xp{PQDa9fg+>HC~?S-aU6z;*Kh^$`LBp* zSp7agt|1p__}T$lcT7O0FIp z;=bkCHqZ;KogMygR(I=wtj?*CBe^bL1W@W-lTt_YR7lwDpE~hFFQLG^r77w{n03px zGs4QRBR@O($hdrsZk>0Lt6l2_nJ*rWcPmjhuewDFxQR|cEBr~Fx(EO_(N$KO~0eDQrjidM&* z`nJn`*x9Hh-p}KgEH(^oGB|oa(71ID-ZXq5n(#j9`I8vCuRJ?Jsls;Ikr_8fJeN+e zj@6H)xjePwKF?{`iK&2J_z9Zp_vVM^?OtyDx~xK9yq$jK^Q^Y={4!YavHp--uyg@M zsG92fNTuwxT0$CDzAN>!LGDM$FQ^OfgE7{v<mP-*j>sTcK zx=^TV7F{(J7ZJfKJL0*dbB$AnfZZ?XXzdC>%TqewTepDN65>&z^njx`$-KLXnpCN6 zI7}qUwT7J@k%SE}Cb@9i59PcE9Pwe|2SF;#; z!dmaY9$j{S=ZttSc7})2izPL7!rvaE zjJ-bN3Y)^CZ;s@na@vE&7bl}SIi+?K41UxSR%rY<3)u=v)X!Wyir(%Sx?* zZJ0^NIzgw%4bmA+GP<0yFP~l)H83wc##U}i*a^@VcBH2W|2j)ZOsOQbF4te>J+kfT z7m)0fsDI_wkwMR1nvsAkdNH$*54Q*E`IJ=UqSD%#<`B`^3`OCvKVAQ$w=z?c?xrfcwuwDr-9v_vgI8civ447L`sYr3(5qB-$&wd_5;iB<5ohB; zeRjiVSRVj)Ynp5$zHKe|U%;w%2*&es9V)!UpHvqfh8TD2DE-px^ZUz5lk}3QXPTzJ z&GMz)A-R4Rjfs5xt;6igqFzN7sj@s+uDbSmy$v;32VUN&wzdcuJ(o{9Qo7Y)&ZO_T ze^iA_y8e>|04Goqr#*`ozo+2y#Uk|Ba6=j6M~FuqDeb=?Vfx61+6$~dSwNwQe^W?0 zdhSUT{XtVTIwa~bue*)2d(bCNR-cX0kCs7=cX}pIi!ue^gaug6H5o}E)rv?=#_TW`UBWgYe>1V0DD1yT{om7v@?L2DC z*K{UtaTs34p=NwB?^$>6+WxK^M;n_2O4y9pmC+{8StlFlrZgz(YRlW=e7$b@J0c&S z@mqFoYh$)l*U74Ji1(eBldIhS(D-1^p?K%^P3PeJ_xk{Yb5f8ar;fBCT{^Ou<0_t` zgw0rm2EhdXD`<8oRC)rfbVyF*a>qb7bX*%|-YaD7==ub@P^GoZr`GhQ`Ba=}<))rZ zma)gT)`JfY8rOVg_=|oV)baS$`qtN&*!at5;IrLAoAUTpXIAeE8JmgX_vua*U!-#tVL}( zchPK^@2peRg>5G&O6sQ#L{n(ty8v``D(m#fREA;MMsQ-HixuUoU-v(>{lCfCdsTI4 z>C3yoms39OG(0*qbhdeP#?KqnoRJhw=Csy{`x*#&pyp zTh}$a#pq!C@<@_wwy>z90+J@~h1K`oJu8f4olP_x#mTahKeFf#lxWDE4a%G68&}X+ z5z=?9hHL8~zyFPXa^A9xV8lJw`NEd1$d06IV7)uV<}B$V9^n2nmP0O1kKT^yt+s0)_9}& zYW?06a$|c+i=%F!$(NF8YPHwUvI}Z;5O%NdRKGeq2ECT{9|e}4p2$eqaoC-6R{>5L zUwQuNEgR#VUWI$?R8r5p-f%P3OsFBgVzgCHJ_MUXPAb?cx)UtCS?Q)9O`$3@5T6yQ znydoN$CAr_f^|79tL(&rKo#tQNzMcy>eJpHDcE3v}TRVcIWCp@cZ(K5jvBejMh+ee*Q{hzz&c+0GuQ^Aj!` zx%EkoTLb;mwkL7KK zfr6d)66W=33g8iSRR5KB@EC{>mwqf1xn%-5^Nx6o?mH3)A?Nprh>w;oM6~1~7P9#x z?=P4B0a-BFH-EH{yW?v}43`cu)*KTSlyCQ~=ce14+nV4JY0~(Sn!D8~R9v8a;i4x0 zZ_}n%z8dxS11YKG)=f~_^i!o8Z7ePioEBHaA1P^92jwN#2A2dz;Vs68+DAY^(P2w& zmv)0#xzGv6Nl=;=TI|)6W}5*7FM|7R8Y+?N`F8A$hu%W>A(bA*GY={=@{F@f5HhI8 z`l_GNLD$O2MZ(iWU<32@b$odmDgKxN+5Vu*#C*PeXMGJvO@tD(!>tADkuCpT6H*jg zxn`(3>=?X?HI;8mw1c!vWUNWRR7wa5Vk$U?lUnbteLLqD@#{S$r~o8TOg5fvG5i+I zO?^1^H74*w51Y@GqP!V${ZY00t>^sr3 zKY%5dg?GTeVnp(!QH_rQcDWeAx9+wqff#2n*+8<@#Wb>n8bPU9WutAzcn%fp2t73g zB-Yrl%y-vi*AjNcp*>#qD`|C*uXfn!a%V=ER%kIn6-QqTnJkMsV~5GFnn6)kz*fcR zx3c&~Y&(IZI8|iFhH}$o)}fYcbn5UxSq%%0h3S-criDMG{MbVVMk?S5?wzKK z8kw01o#3XZkRS$;rox~+iAAv0NhEP-lb;SVeb4~TLjtj zRepUG*I(M1DR!5kSNqEq@Cm_Z2PpTiPpQ6i1TQg;(DY}@DQ6LWz*!4mz40y;C_7SfaIxnA?8An_*Cg)h_Q5M5PHYi(cd z`b*3FFV->L)vy{j+f!X;>&viS6ff4aF3NlveRi! zHH`hn5VL0xwuA|k(upJFh@lK_yY_&W?ZGdl18?AFC!l2gHZ`gnL3mf}hEW_Z<}RDi zIAg%uqp}{6w)N8{c(jx)VCykeJ{SWM$}eQ{f4JiWVKxdUyz>V6ghFy93#wDr;+z$K zmxGVNfT>*kG2?4m5=J9a>_DKY&M@`5vzj27Z~Ag}I1HIAi<>38Ymvr6`?#oeV_*B> zMpU0{R~>}v4Jgti%DF(VjIWo9COjI~XnMfnf4c%|8~PzdLI#-!cW%3l^gMAWUxCN| zAG+Q%s;PGC0=2w?(jo%ViHb-EY0`;^fE1-D0#YI%O{5D_6A=NC5|t_~6al4*^cs4Q z7CJ}^5IRIiLJKL}=zGrje%yP<^Di0MV`rE3%r)0sb8&o49Bsk)o`^vioFGzZ!bP78 zZw5W&A9H7hyOkdiAboSB=w~{^<@gv7$h+OP`b2U8P5n)R#`wq;HGO#T|EY()iU%_N zuX(~RWR)}(^6*F_auDfBW*mMN^)#v5k_o7loS>lOYL%^Wi*~Hh)>){QG@r}qUtgM@ z^7}8fe?>+22gMxS+6G1|B9s%G0(kA8sd&9?$(Ee&iG~!-uzQL>LrpSlPL_8r^Wt1q z`UY-KK>xwB8%pXzYM!NOB)c5hsGI&;_x9lR)8MP9(hMmYsK0?4>N{UCL#{m@luMj` z))&rqmQW*#@q<{c!g=fK>Tei^yaz{8qr;6C3$hMzcA^k(_N`40fH4#!*{FzHHOmI?z|`tSII}^~#L4*1{ET@IflYC`=83E35A~s|?TCc4Fb7JM5vrhIyt<@{;TfruMuYkPya6deZnL^Lh+aKBKJML<> zxj$gm4jH{cG20HZf_cZ+xEkpc&>yB-5bW3r2QFGrxcs0iZeI6-nE~3od>QePfj?@~ zsoLk-v=S_K}y65~I%+8PaXEcJJ5F{9r2 zl-9oEr!o|C%B(z9&h0oJO{wvE!fm<*qVEzTbWJ8yE1~dg{P~;!N{oDa4K}c)2duU% z9YiZ|e;AF;c~X5%XS>moYGDY<>>c=!!mV0n5u(cYgaXX+ok-nyw$Qnl)6B;2kNda4 zoRxpZ7FQelfBwNLSy8CTd`BBRaZ&;%uHrdTo%!Hz$}VkS6LHX_;D5#d`d_`2pKW)o zW~ff^t@;02i2bjg4hEmIAW&2^Tkdo!Bs6o}AIXm1pAJ8gK6&kY5ZXkCq)&9kP3SdA zah8`58s2LqF7JiEikT=$eY9`4?scS0wmS)J(o1N`&f}T-N&&{qp-2^f^!C7mt(OIs zx@#m!W9O!Q>cjtWpET)eq@R7pRY5AvE0bmkl1Z~s{=>o6MKSEo#~#X+7+4>Yug)19 z${v2$aYOd#|IB(I&*Eoxlg;ub*IE9}fc0D6J0$B=N`AUpC5)NQdel;bAc@}iVS8u{ zfNe|!JpR)ZjZ+&Ftp0x+F=q~2IpYof8ZrOPy#F;~zHfe|;H=z|g@fN0OHana2|{JI-z=<9(d=k>Ok=)D2GPUJC)c*YljWj&_p5a0K8yuf|KOeN z-@hn|(G)=Ps3_z7*jv@bG@Kk$LPKeTP{Ce?Y^cPU{>*hg!jrfEyDc8wTZ!i1zea)E zEIJy(t))FoYW-BDyUf+7r^~#sO5k3!1D589rcsTYRz4zIQPeVUg2*;Zut!LsbRBZe zx3`W)0MWFl8+9xc^Lpn>1yq7n@T1`jzhEr@v6OO0KDavlIYmoKCZ4A^piSYMO!}Zm z{<8O`9*fgA*|@|fCuY6(vh9o5p0}^on(yxq$gW!%vFATBglt`J4)_{(-NkwId(?@* z^;D{r{IgGrJYOPe^`cx=YK|539}M;(xf`S|e3JA!wWzc2bV26Jr>`pvA{l)k!!_bdY`O>+fq6D;C(cK+@w*8pJ z56WD5Z5R)j-g0`L(azFxd9!uICFD)fRNML+ZP2-Z*rnS~<`7HgXF#FdiW+!2-qYHN zfFc9J;t~7DrI;QTk>PfbHufL&*xRlKX@0zTEkn3vmW7>y9^)m=NM2Y;qu2Y&M~r13YW&!;aNrttwLbrbwOpx48Z!0# zCI6`B(dcImB`m+`3g{=B5!JCIP8Ij<3sM9Z1w9^MKcx}VtM_#f;sR^+X`C0z*D^69%NvQebK4$XymMAjWMpYb=6LuTOI(FZtAkR* zJpo@EAoZz64*G~CYzrch&d`P{dw4B=aJ_tZp>1upozGp*ie*Gg|4Q7Qx5Hu3H@R$( zFXFv+U2Sx9x6_d+{q)$<^?s`8arhj3^mGEYYUoh)S`Ar9)O%ORuW6$fSZ=?Y|ATB_ zg&H*=7t06r9Eq>)12qD$()r``IXEEv?TzH36YDAem*+Svx@oGTC_#IQBF%mV3>qM~ zsG|N&Z(#{INs}AHe5gyh?$V$BIwUvJ9FnCwYTS5S#f)gy#jMWus2Y=}!v1zxA=)3Kt>O51_z!wT|xHUixC?tv|hXm6GzW?28}=QP%%l9TiJ2 zBIz0ojFB0#m5mCMp8rYmy7SRNg=Vjraj>Mq(cRKHMP)~vr#{SEupHE23!G$pLEJ`r zZ@*M#IeY|=0wVvm-^F7BzaDy;#5{)4%oKzQ-XLj}exld$+z;m#QH)_mx2ny=j=n_5Jr4F#c%PK4qsPs#;!b7K?marDfMF6`Gnp*yc)DYGX9E*Uet zxaCuTdivR8nvU~?Jmvj@s0h{Les_dfJU?FT{^?aodM@{?3l5zap1OibnumudVy)M) zcA)mxNj(L82c{%jRswMDeCeIor%Q6*|4pggy^O}#tT<+D2AIV{R~& zC+>S^ggy+3qW-yhxBLDGN1;43@gjtk$R}m%{Tss}YNJ6+2S9b@!NVhZv=8CFvrWKi z3>68bZXAjjPe@Lio0D74?J7)k4x^u1rCtG51zZeiT#>sjlef_qn=pAL%p>*U*0hCFbrGFP*Z7~f*TbNMt1!|l3 zw1wktH6_l7)oylL@eI4gbvMNmTh+*yeAhgoCnXG_@4$QuoVnq|7U@P zcFpb`BXG-Grg1o7~G^R-y-C#w(tR;~YIn)}x= ztodD%l)F-o#bbzCIv*iQ$G#ZCg3Bcs-C`3bpWl;y*z*x@u5Qk-qyxz zAkQAmcg1)%!E$NCi9~8?mHkT&Zx3CMUW^nTy=I7;JlNB?U)M9)=0xhFcI-^{9lmQA z{L@tC8iyhmKD-|jP*=yvGTDF?o$Bs0DM zpxG4vf4m2-V65`2+e4EGBv^Z_-an{d3qBmAWPjk8tn0cs8PmBKx94ws^@-y(d&i(^{O1a+oC zJ2>jIG`Aw+dSZl|pPZg;3n;WqDbHrX5)9mkyDU5*h}?<@;H&)o40~3KVuvI!kj?Dm zO3d6=$cHc9d{A={`WYW*EQ_#A;q~5uQ0QFIlGFF;aCO}dSE*oaG{rDIz%TKIXW>@m z-v%m~eY9E`Uq|794-hq`vd!q+0vk|_*l}17p&18KcaiAACPK8`+mt^^Ly6|mV4 zdt}&Y(AI5SfY&*>c(07}Ju$hDn{dX9oG@vOiJ*3BcG!A&%`hgln+bTYcQZEDhC~aV za(r;~Ry0a7?(&1jI`At7h@wQ>j|E!t`@W&ELO-Jfea43%b1TDj&D^FWRp88|aM>9P z6#lH?1ItU}+9Cop+2<8l-2n{m~pl^f|mP`AmqpHH zDW0FpTG6aVxUoj?=>h8$DB(m+i2KqjXg92aCZEt)GKP#v6CT+bdi`#wM#pOb)mPnS z5Dm~ww1w@FYL6#3VBfJ8cT#y+Ht^}XuD`{;gVQOA@2AfDe9P~Db}8=7GuhBjKMlcq zCm{VB(FK6)yHX-PXp1I#xzW%@zd@W5>&Gl^Gt1hY(!tI6^)kHLq5$>1ZudY*$;h3@ z_yLi6t=)hP+ySxlCxLg{Pe1}I9to5|Cm&RvAiExH^_=u01dzK`R6kNbl!r+*|HW2u zo@fDR&Z?)J==jo{7_DFh$OQO_s_6HzzSgmek~VhD=tV24ew21}prj4WZ9?e|ooJ3zLZQ$kx2XB!aGQdD)B70*Oz30NA- z3?+~C7Y_Dj1|1`FUoC4J9Ry81Y5DZ5sZ0Jj2iuMc*z4yAJfg?EQQ$4l3d~cc=1lFL zh7b#I9zMUsnW0Q~^u5O*MC)p~g0FRP4}1&zP!aenaA8Zb+%w}rIT&Z)mXmG=$);`& z3sTat26RqT|A9$m+uy!TUYUze9vSfLe1?j@P}Dh)g)XHQ3{Fx*j;Q#rp{pSNiPZ3p z3d9deq#QKb;H?dbK$ZY?yPt3hFV&og3p?FUozUn5a>%hf*nhPGfwjzN(-h0WP@GF= zKr58}B(uhZDvPkAYIhC>pauj;+Q8WhEj#E6-D@Rv&>rLQ*h7NX0(Ci5gZbxDW)L;pE7~)8c z??*0fg?#gV3W1~1WqZp&Bi0Hka24?`|KKDU^cgD61!<62(sH~oy6tu|cn<%lV`)QpO*i4aZ!?On{m$->Hzj^D^|h*FM~Pj0J}CGbp*6RRJe*2W#{=x5(T~ zDVE_W4*hxRUHQ(v6`@deQ+sUdsq|1i@DgAWp}YAInG)jdacCiyK>4ZrSieOeoi=ZF zyBN+X*|dAl{R>5A%NqS~GQEMhQ)nk>v}GQtK3>t7Io5?#A3%0OD)?dbEcIoDWr|ow z%eA0kM7#7|!s}so)BXUlo)De6P46Y^g`SJVGEa7092jdAx^DGUWav#$SbP7IvZKnO z+6~mSRFbx8ch`~Cb>`7cj{K6016|3+y7z57x(jv2Wih{mSf__(GMNdC6kf8)_!6ym zZhj&>FL`Zqk(u8<2gMn4*6QImQRSwus|;ajMm3`1kl3T1<(MvRSHx1!8ie@4x_f|E zd}nt@QsWr`n@%6sQQi{r4KQPzMNnvBnpA$^5Yj+pwNO4dI+QlILUGNU9G!D`RWoxg zCA^htIiHpG8zW5^LoK+VkKAU}7b2J<5$-E;wj;Y%h=o|(5PmzLEW4+C+6so}r#{aT zmpLa64qOrPz$tqMuQO`go7Cxlpq?2D?Rbr1A42v3w@_c_Mo@z-SX(v_!RnQjm*0AM zV0=`nW1OGHhe3BpXMT4&w)QB8g;FHEFl=-X%=VC_NS*10{umQp)XUwQloc6&OQU@J z*UwkaUjdLKA#$@1{K%IKihJ;#S?Y;PQ#MQ8h3PoPe#rq_p_0h}O=ZN@*53reH%;84 z!NjP?l5=Qxun4j>Nc{;5Og(5;@}-9eEL);B#`I&=#~L-dX~F~X(z_Y=QA0-Z_1oZ* zj*o~w{K1{kPseuF2X^=DOi$5kyOk1-e#9bJzHY`iMUHzuz6ad^Fe*u545e`^QS39V zbk(MP+@3=U8H=UkM&K0l^AQyJv+)M+!t6#|O15DtzJP9Z`IrH5K)!o^VP)SC%*SMuod?^**oT~^S>@PrumC!Qy zOWHC2T?@*xty?#O8Oven1pz7BHQa<;K9Br`OX6juMGJs7CYMjt-HF(o3J_9X=rX8o zYS9WUCw})cd9FTx;&>u^5}<@eFL-1Sd-vhe{yNi;8PZhtpU|gTaDX~e8mnwq9W#_W zvHebw_p|O!7-Z_FE$fuiW!Ka$_yQk3=$`7R3hnos)?jwsN!J;EM)iF}+$nxFAoFw&)}Qe&vD-1@h)FH<&4ZqZuzt%IEGu@{Xt z!F@*4`=d$3usQ}pOA@lZ46`6I1=-Gve{nsNc=!fe_zfeMwTF{)kQdab4T60$nWY7p zDM*@%21d`BPQ!=p=5=*3J)5Q?aC@R}g}_oXcFzAg_%N!=Nc8t!UDTkqcqn+QAUPzWK&(%is3QrPRI{DWtBchaFOer?axkiK*4U1|K=F&bOYyVHET)L5Ddb|%vf^eME1Ldi zV&t>pJfHL3t{|x~!YS!4-hGUcsBWW(xp9`rGJRSF2xIjyT;9B?fDf)w!>`i>)!vcUZA{Eyml9v;zy*jk#)T!u>spJw^3OU|Oqo&H*s z(>wF(HzR$fSC+LFuU(tV?qX-@(PYhUe$AY6If*813pAaQ0bf4mVfLuwG@&K>ey9)x*K0GU$Pu8 z7-|^kcD2rm$S-{f*M8ugqHv1(S(>?X>|C#4sKcu+u9-_>*OGuf4azptl{V-WWEp1! z%jE_qzN#v7Ng{vbcdwjbfl}E5TOu1iF;8XFQ+`inUO1oU)J;cy`p=vfRW3qGMC+S@ z-6^i?=GXM8j>2^IxV%auki@Hxy(;62AZ)A%Msw#Loat`U1V9Q?$c}LaMK*i|+g&gL zMD!gwsO8&I#QP7HAA*-ECAT=`pB5|5_?(Lw5pdGDx?N23 zjgXRw@<2f$e2}(+?J}$*^;W^m-b~eo)kHet{;=AH(pAlMZZ}WR9~0R1zxc*_<%gByx88aAEy{sMLjZpYEv>ACKV#p~+BfpSMFFy&e!21$s=T?0=WW;ZwHNE@FId&pK}!yt+IF?j}erUoDARuCFaU z&aNGWt-ie05HceC^K9X8eUlT_9 z$0&B>&kwO3bMGcdipli!K}3tt80N$&uB+TWW&+hW0{dqWe~I(y~z>)zz} zJ-YLuFLmFWk{8lYCsAbOv$kz{v`K6Jk6vm)rr|nrAg@)BI_=RVIXA&& z<@Gy|_j{*cW_5Ru98pc2Tdo@Z=`Hgp?E^IdT11?zw;e-Vba(c`nlZ%wqU=kwX*E?v zKU3FVP@0eWWvQK&MNBp8<`g|=Jw$0{F$cYmTpZHPZx)0Zudn(K1!$XSa@%SumVT}V zHd6}U|B4?f!KNM@gv>6RIY7l{sCo7|kx#KM}*gOV)Z=Wm|Z?T|>BO#9`yPPqioCs@c(FZ&s@7A$1 z^mFS0F}(cJIJ58I&^GWph*0wjWV*nY*=UQC%W+JRpifbe$}5` zp33E~<0tEeIi+o{a)#w0Qd2~>caqV`o^Em{d=t_pS~iB#hsBjNkbSJcqSrTbX@8$G zsaDe@Xkp*?Sp_g*0Ky-A-^Q#H!57^9`4`hXey#9ns;4(T+djS;*S!g@xz8Z|t&bc_ zyg%r6_@pUq(%Rbo=~qy-2d8$v%53hdu}RVXgIGDgBNKFY<({9(n*XGIXr;o05>c|* zn(#b;g&?bKe}I^$vD5j%G*ETIV*}X_H1yLic=EHWEyKF z&AodYSUvdP5G&m5ahjw=T&lmib2A7bck+13-!SyfO_t4R2zYo$RrXKFO`V2fP~kyc zT$!1FL$INW+|BOOe)*Pt_&PfamdgPS(L8Fw%{3+XVBz{uw?Bf+z_6kQq!E~U9S zPlGVQ`Htc>Q^LM}xR$_I))&W$-YS|ntY4y9uy+S4_!;qCe8^2p;dR>%I>xBH39^vO zc#A=M#)$P0ICnrhQ^1Y*M}H&k&FYix1jurd0RK|bj-auvXLjf;fDkHeKd&dapG1ej z%6&qP^_;G>5(v;2=W%1dTlJ*pzb^F zPU+Y!K16a%sX}=Tp&0Nbx1;3n(bHh*4|+i95u{@3h>!%VGgBQ?S7D^k2AEw=;ezaj zGw(h5jXYDbQs)M!nJpkym6^8Nsfs@3sykB}km&jQ@A}^s> zHyxX=JkJ0Yb%^sqrXbWKPc`=Wtd`%jUw}@?fxd{ef0h2eapB;ioZ#@)2-^>ejmH}d zBvFMf)^3dKyC{qfj-7ff>k>TVtua|voFn%>x%R*=#4MX&?l@l^PKYn@PUhxkz zSJi-+w&PXi7F?3UrT1V2B=GnQuqKem5SFvSnCYq{%_SnfeTrX8k80-cm9FH)z0df{ zlu+Vv)>oiLMBND6^9Q8%HBxc%BeT=IpsW$ywb8)osM<;46(GYDYEzxh0EjF;re~+; z1H-xnZW!gGs6#TB?aP6DbbJMju_EdxT*&w663gXw!#Z-=Srtx6-^Vm;(L?SjJA2OtWhtRQyApZ2f(X5aG044`X>;>9L%~i1<8Q& zoTx9wz$V_#wR&ewY8XuL_>)$MB^be$hVm%HEOcL~TelKaK^mQV-tQfzHoh-Tm{jVV zqy7myfU>;mx1(g=07v;TFJY;|iV@1;2SDsCKl!MwWCRUCm5jz^TsRT-mvz$EyzQ^A z_FXuhsXwIijykf#SytfY0%&h1U7J+0)A9ZAmq5Iqmf7VWH1_d^Pe;CLg(e1qcHZlz z0f4`>Kpc8EvOC{E?ZAbP;5;J;XmcuQ;$V`biHh2NHDKNs5aT*5 zreRm&cLwJzG4X@dvjxbuW#DR__L!L$d$^o#a2a@IBxVx03j@d!7@dnfR04HC!Jk{2 zLwSSh4sbsG;_E|ag*m4aLLZzgqsaZZFqziqRUan}{bzWP2)Jc__2n8ag@#&wPke0d zhbu*f5voDJ3OKM2+=s$D@DW~tKz(qe(6#H=4?G>;nr9H1Ybn6Xxy;1=6RuRa^b+Tm z@3-ZSYO>mS(mWjSjhFec4*?Ucj$>QxaOGc7JUS-x-rf5wu*Y@SxP2+!fO7Wn$s>hl zx$pCt`huRuLY|p_g0aSjPn>klrklRTgxM$B&-sLWZRm@VSuwD~s6f0VUaWS?0r`Nw zYGX{ZzsL)+IUCqhpHBWjT170XQ-&FoAL4Q^8TXT_&M>&ezh<-pUNzbtJiC9_Z@kih zYn@Z+4kVNkm0o3ypG|(`sS0kA4+QenxG6dbIK59Uv*+s-HURZ6l?BC;rXc zeL<+~iEkGlLXE%cIR_?~v6lRlc@$D$Z6<XvOhrzc5ug(?Mokcu}EsriM(Dfi=`GPlrXn2l#| z5HR5YDNU1K#>U5TXa`uca0_vK_T-blY=}?Ux8k~l10P?;8!w#G-tQJ?2)_94+~-f3 z{F8YeaFCm_Xv)O9Szc>>S++1CFWi0aAk2L z>8y=Kr5UOU=4m0XYRq6IJ(LQRe>bP;`iuz!I0S+o8MW-wC`i=gAwTrKvMccWv#b$ng`!?}-DuNO8H7 z-2zZ|+^rMo{U&6;P`wPI5(Rz?U8G+vN{yGlZm#*RzB(3?9u2C@^i3T6Y2@PZQZ;UEPYL@HA`(^aP;Zm?etCQZsOzFC zxE=Jc43o6<&&lEOn5%aT5P8aY$=0glVehH3zHw`Q0zjq|yI@yYES-(VX=MJHa+( z5$$#OPG5z?7ulLGL$JIkyC#-?XCncDCCzK|s*4@{d#HwH)hdX?ac97haUbt&o?T!Z zNtT>7t&Xj1F_`jq0A4xi$vK zZZgu^c=At;5k`_OMAYc1MVHLsmu@Q6A4{657a?mryL=$NL#v|cTzevOpr^E&b!+*Z z@{Qaa4bb&|%A+dy9%ePnrbl)&YT?$O{5625_cRYC#0vZ;`vW`-_RQE2f+WN1;>g2H z#Mv$N0-eB~|KaCM?m8Q~-dk>?+2(d~ae6k#!)`5sjov2f6!44P@1QWB&2p}&nn@aU z=PzEJrAZP~qyajc72%ZxIGZE%`-k{leWQ=)qC%<5n=>6LUcxlhRM34a97p@#a3eZ3 zIbGI2ksKI_NkC!=NI}CLS5YZ}K%K)x!^zNCeyp+fKP9paSylz91dAQZ%KmwZ)NtoYAAhmyS(3-xNp7G7qLKMJw}YV-nPP zWozKUG=L~O*t~SPKI_N@adDt|IOo0MuQ;EDJOb_QW7)mTma4_^FnphHC^aK4H*Ay9 zaTRlOGfq!*(%~_;Tkb{Oh`r(q-ocvMqS$vqoLvEm5s>NpCJ~wVjoHijGUJ@jGhF23 ztj)}`;T75;Sf!)Rmx3a3=P{B;n!gm=ZC1EC7`xi29o}((hJ0kmcRuOxjuRf}`xs+# zR*`Jl6liO$c1o<^{`*@rqS(huKJhR}OW?=zD8y+HP=#jgVvn?gXcPz{l* zVu>hR6ZaBG(nOPR3U$Lbs@O?GOvHAagFmcyPYQucwOP}Pr|F3QR?~X z%C%qK7Z)qef60X4YP~w+UQJy^Cw7}G3&T{mk1G@J>#H$6Cl=0gT&fYwin>jGepUf()e1K1kMK~=cv*|CK`h0TB0 zyJ$>&MTuxpbjGLecX8} zw?qUhuaypy=2_0r55-(m8P4wCfLrl=98bJ^qTmNs=dbLkF`7knbiNx|gm!j$G5{Gu4!HvR=l-vA^ifp=7#r zn80P^%%-DnF`ZYRw6799u9og6xuQG#cc_>9>;}Em)pZrY@`T+f?91}- zwTp9L*Zoy^-pB>_kehG8&wItwUT%aP2eh8a%{wEv8k&KZs3MQfU0+XtsZKkGu6?Hr zalh<<_ai_|uzmCxQIWbQ8kI?U731_b6>WgW!tN%uebPOy?krg#$E$%?ory@ycU2EK zanf!}!Ed%(H}{=kv@_@hF^QIJD_3!$deAh&vIbm$C10jCKt}S`!t)sOV=J@lXL3mQ zDnR;#_dqU_#w(?Bw2*ppfZ9MMev{xNsG+{SlqZ1LeJgu3Fe&RUoKIY~4SseqDvnM? z?A%5WSLZe+Pj&}3%%P;h(H$sdZX>-M_yFKpk!%_tY@rgXIC9iG{0;i z$4Jz#zCT-yjO!knvU=LrDj|qJO1PXDmOgt6u@TJBGU(m~+7z5vgC%(_oO!8y+HW>L%unrW$yUQHm0vyI+Q3~m5GJZ5 z_eO4T$qv>J*Dx2@OBcXA<4DC;49<=tsww?|7s}W8USVth>r*;TqvWip0}_k-#HG(l%OXO2}0*L6xe__5U3<*D`xl2C zmXcoCs6AmcP-0z9KH>9eQA6Cj%UW=UVCi_4Pq;aRb?3-rbh)+yYL^nS=-ZS0wg6UdJF?OGQ^($2(Gol0r&t9c#t5-#pa0(AF;e80tPR@~}S5-X3DUIvKT9`(w+1`(n z0M*is81Sw~^O~S@X&h(%CCPBMd*S!Je|LR^O-Of!mDuT3HVf5W`=_zA3yT^qa`bK` zevGtw>n6(cW!lcwkhy+Qjay8qZ-MS}v#OX+IrBfU^xbFS@6mhTU`BQ8dFLw}6W6c* z*9%|=Hmc+Lc)%T#?7V+ILcb!UAmyl8EpyYGQ^102oC$EoS(K^QKIS5gx@7B0CTFO>^mp<+YYL({WUrDVYwCLX+*{LazE4Tt+QENg2n$=#(gFI zE|_yrw}%*_@g)1-*98mrHlAEXfMfqHL=WKVs4d^j-wxMY`5UqSg2+EWQts?w+UcEk zUFx4*pp4Vq)hLBts5Nk+7>@>umxdSE`f!tq&{A(2yJVKcMScUyrokI z$|fn)x$MyyDG`o;6`SJ>hWDZl?=Kx&=HX%>g`=+k0Mqt9-E!L&mFCX;6LVgCJ&i!8 z9Dc;8{s&q^mG(>OX?jh{0F~D{ZAA{c^f~O`mF|4Xpz=WYW(g@jdludq)0n5!_pAI* zU2NdM=H%N$Tl@+YqYgb4#{V|uHIf3L!(bod=*4NLv!Rp9tE?5m%a z0ani@2AR~ZowXI!rG>t~DuR&igqoyL@iDfC8b&V{^-l?{N3G}_xt58jd4~v?dh0yq zGXP0GZG?3+wP!PWc-Ug=mpb-py`P4DcA~_ALDDFXyWC4pCx?x*@0Djxazxvx8I*T< zIH&#D@Fz&Wque%zX|DI7o95^pH79ig)S!D3o)nsn(q-Y9UNuMUBC z4T*J?KDsGVyW){rZr9U%sRoiWDVO=#=jcVb{~%0;JKw?+Hk>8-sOBi?LbOdt=k_hj zmSaXbN3m|)MZ%>}QA=>!bqPqzSU?|`=oE1_G>3VoktnpPsTTB(?iQL;qfr_uj(7yb z;k^){40#rwSM1_AZ+?XX2Ny#YQ)AfK5e6Fb1g0;zQ@tvim?QhJouE?};gxnAp6}Fd zPu8My&a4E3TU0`cJb;#5HZGvU#Lb9|rLGx+S=_q~ZQG|cW{{_`igy#~9~VubZu2gh zDPpC*8T6~75C&r%!#=x=)7*vF5d?DKXu))y5%les|AFx<+R*i#Jo1&K?PRh_)->qe zR4SSnMOR^~5U5i<6xCMIYC}Evr4`JOJ4XK?!s2w}+0aWHN8*N=kNR%gyi-oKIl0t) z=R`d6=(1CAPY>z~ifZy@lcO%yuR;I#G1azh(shTQKf=9+x5Ow8aCNn#3x7_x*7&pd z?eg8i>2Fwv7u;n;)jjEBK+XtU4p#(nzvxqsh|Sl@DZnv@HI!Ys8mO^+z#Zkgc#5D0 zkvwR8ym0$Wgb6Qu>^VK$6OCHvKTZixd`P`hf!PO{-6ckK2DjtN*NyPDoJIQ*sQ4YW zG$OY+bZr~_4R)+>)&8v)nw#`9fW65M=}gJzpCSB%In8AF0EUXE??PT!jHCPdppH33 z?)0;#x_EI6^9XZ>!l&S^(81 z&eSP@Kc5@xklsOv>x2|3qPcL**(YnOc^EY|G@<(NCilKGt!yf&wgs-GNx<8_KkTh( zQBmEzY1K=UT<$$4MpyIjsQ@=eWhphsf8c6;|Aal#M#h_|=`*Z{hWdTg`uE$5HC4SE zSAVb3pRpi^zG=No$s{7%=cOA}HzRYlU&$dB*Xv*rlhAVbuM=N547$G)77h3WIN`3J z1MwuJNwt9(JfJ&mdCVut1fcBxPTvPGfy1(?tBR((EZk@RW?536;yo+9XrUzwx(ROp z=5Hx)=MEvan8VJD-j(D%2p~qf&*NK|{|Ri4I5e?c!DHWe^mAKiTU z0NV-=B4dtH&o^ySLEQ!&pHKEI3T^jd4l~HSsfVLe;0*I+1hlpA-H;!kHN@pO3=!3; zE_G7*+kF;BT8c?2G!MXCzCuc}*`f%;XtoFCYV4qc!hoHdZ&<+>3l^0xW!` zhP-m7|Li4_uufFC`jRi8o8vXesXc@Xw(O?MgcTwsj^1XWj5J0_Z-hI~`raZvGU!K$ zDGkR^FQGa)@oj4PAv%yulrQldU?2sn1&#%NXMjrEZuT8}M%W=8S%G2^0~trA8k;XA zL{%C5730Z2$wnNNCrXsQ7n4U2CV{o6Nw(=KyOwIhj+^H0Yv3y6)rlimtBWb-7haks zMxec@l9WnHKM3|w`-Ul-%)L;7_VFdQY$evPB^&->vln)iz9ez)3_wxsK2g0GOnhYm z$}kI2>M%aYReN?FEb)K{%YywCSEksund$7axI+5{FmA4H`mD zeZr=Y-(Jl=2hpxKi3%;zp%AVVpF8*=%Uk1ZHh_C(}2A%-z^uYu%t@BitA($@Otw>Otvm zep}juQ=(dBYf9<3A9GZFdR^4@i%y?1U8vl)j-j1;H|Gwtsbj`!mXCOV4cB-<2&ECE z;CC#_B9YA0MXgvA=25KRI3bop@lJ2R8OTr78_#tScP`m-s`C|+^B3I>U2nzXfcJWr z+k~ty-NB5U!Z7?};jpqHA}Nn(pI4wML$KF??vC=drafB-0^#%{ezt>ZmY2A@P|vcl z52kkxP2%Go>?ja%@1>_h&$eOq<0$c94fOlkecdCr2@cc2aDpD|pfBcPcO$kj7peAJ z#*rs^{NORWJmiseltP}eQ54|}(U@=O!OWwbPcmDg!DbZ*w;~hY(uNnR-%3?XcIfxq ztVOe?L$#glk*U9z=9YZrmH$}{ik@-FO+U^(d)_myWd%jE49yw!)zeRSRZ%K+?l@Vsk&(!25v?&IhijpV%N zA|KPc`oG%PnKyIfX}ZJ0*CQUU;T*Xk+Y`t!yI?aQOFt*Jn~>ZzgU!!j8@bh_T^ifI z3>U@nIp+8>NXiwsg*yDA4L?iet}A4+eq#JDwozH@{%pJCOr%+=sMU7tQf~}?a7Pd- zDkwp=n<7@T2EqST;?T_HP%b+j@iejKAKLmqs>eUd$B=%eBS?<}b?{K?7uC5x$Yx%s z3j{TBO+fytXv|tV*oy`z6Q0lm?=H58L2#C4E_Z$omC_e_n?$WyYW#x*C*DAlWCSWL zpn1gO**}@Hq_^N1G=Mn!O-{B~>%U~F21}f>%z-dr=iH3Z!!o7{+d3y&b@|V-a(o?p zC3NtMg!U}Li_v$d_|u_Dp4z`z{xrzpp$AVOWOcv}%F;vW3>5~3Kw_k)7hz%_OcOJ^ zLnTNb4r464NS(ulvv6Ep&qw@44)VSAU_Jawtxzv+DjHTl@4nkQR(^38Vt zp_@rx1%Z0bw7Lq(l}0Dis^61>K_l3tr-_`fZ_P9=ck=g<5 zs?5GU9=BcJQuy3>&kHoUwY4H(1~^A!^M+Ylx}`X51OdMQ@tF8+8AXZSLpqMiVo;0$ z!le6n@TV{NyK!>4KxG!C^%K!V6-`51Wmm204O8-x=SLv^i0I^Q_<9_kBj>H){z!h9sx z2idB;m;FJbYmgr~NY11h99*ty2|S;tB}k|A(*lj%u>&x_(teR1g%Um)HSmA|O2hQBXk; zLFq(5ib|ChS|TbS9TcVa4oVROloF(cYUoG}5PA3LYX0HJ-(>HhZj<_hAv)demDQ;dSEoxjb`{-cKq? z4ZwmqJAt^d4yLvgsr(53tpoIu6lHXVN2pmR5iYJ{qSmrH8)Pqh# zi`<2`a$Ce_jjR1SnE{zeB4uRf^47fQACkS-9t+ePLMGyl^V283-5y&KF#7t<6eR4Yl?>QXF#r>F8V2P zn!e{dlCK=@B?R(g+Lv4Jzcel|*mpLdgS#O=oCThWy5iDqtYAA^%vO@#$kX`fT}?XY z>5QCb5tuQ7xzkZsU#3grJMhCfhUK)f$WAatYB6r;~)X(O7o^IBucLE`rDhx_E zuEep7#K;gnf{Z>4U81Pv9kcmhU(glNQ0nlV8VXPDe{W$>eF;)XU}0yj(vjMz04+$# ztQwo#@{Dh`qsj=MI9sVEGr@Ao$@iMN*zIVa+6D7RJLcLE_PgQWj=Eb>E%5grG1kPh zwTT7(b1c<$aU87|_xnRO?gwPWh0m6+Z#q}S8E`#s|FZIx=sSK zOxOGKcTJ1j^MBP?`p#d?21U5mvmF0bW*755Aioo%wowQ_xpG zG6U@!(J(KtE{kkw{E$J?;A)IrYFi>C)Q!BA=QYF7w@fu7OxrTrF`}4{^v{kqul_}{BmXbN-uTox-3i&3iz4W z>n;VBDg=9)UKO!JC`s?@s`+Z;p3w=3VK1A+n+tE?2 zo`h6d50b|T@?Z+o{_z`T4ByYt$Y34}uQsyvpNt^kgZ4zgKD-3a%Cee_PcLLgcTsS6 zCUdF5Y{Z}6ROVrc1n6)~I@q6ujosGGf1VcG9yHV=j~ z4t4qTrR!H*Z^xYr9YTGmm~h68wZbRg3?MxKKnnoabopsMNZ+POY5e8EMExj+pvg;z z3VoWtTL4(7`2`Q%-M5cb*KjZFU9Q4NP{;-2adUL8B=q;-`)?Vk4g)_sZiJ1_%PVK? z8Lq_3S6k$_#SC_reteF3)d?a^ksO>>VY1#0&sBMD-GO(zzcgbJ&nH(o$ z$6u|VxnFdQr}k6qI`TU9BI2z}GNlhDl<M{NskE+ zTT{g1bA{L?8x2Aj)0!Dzy5`A-eo#+@TCEi5Yud!yF402z#hAMa3p1S`M`g#V%idpt zkBQN*OOH<6Im@*qVz^jIYp%}YYw&;T)h=oN3la+T3utnVRv}#SymO9v$93FGjdl?g z@X~57@hqo~>jSXNvA&(EEqA%o4d47GQ1<9EUssegJ04sv?e~}yLRXGFUNz2BKBtB3 zLyF$?I2Bz{H{7^ehEhNsZvXMyxb1YP+E}zcZxcMx9$|repjP_k(OC5)c9qX{Fw}sJ zA%t=Xe=WH82wI}RP+gE%#lC-QX39kt0R;*#J6AO6Zmy7`Wzo1FuGnNEm zTC}FHlAu!edNuYygUU7suu-4sT-}lL-G-$ZD0Aw=YMP$!=j#S$K6`H{M4zHL^^!L3 zmuwil?Y-Z`yIK*w~zgQtcct~7pt-ua2$^e-8 z)ij_c{`;sMWrOi3??CXQFyCO@|FE4kLS%5Ns~Yv9$tL>Y_Q<+Cye@hpa5B_zI`q#- z_nKSnw|0tLec9U1|y{4M*qhBr8%Ak$SEn*8`wmEBB zby^&4Vz^Fw1M|04^n-qb@TIOssQmw{l;+qqQ3~UrNsJ4L4kxB3W|^w%VJLHppJgcj zdq$Bqw4`G`J%DEbI-PbK;fCy_zlYDp@Yfe{l-OqIT)6yk-^&-&lacROcl#y*o_M3z zWxZhdA?=#(;Z>$Q@RT{z6{-AdUKw?C-||Zpd%Ciq-`$%3H`Y0F$@$TP0p+vWr0v9U zpe?W6w%0ubwDOc?x1T?2Dq)Bw|MeJ9fQ_rMk~_hz)M~m$CPr;KP5bx@E(vnE87L6lkn`K@;|M$AAcl1#WKikqcl9N5WxrYn<(W&m6f} zKouR3eUyHvR+&iRn%PTuWx;ViLpygqy@f)bhONR>^R@gA+L8sR9-9VHTf#caLd}!h zw>s3z)4v9~zuO0AC67C&k4D^d?=OE5tpdk^`Q}s#n4Yf9LQt2WThI=?P6QB zH1YcD6qgCd^1U2vz*Z9fSrCEia6$ zF2{Bfui?nM^R}NKG9PsB#3`Qdtm0fH@`Q?XS5IzjUPOHuVM-vNQu2 z-gD=&pTH0Gk)xcKiey^s5h#{V7ax()r?9^KzU9!mqN7C(RVWQ$DwHimy={xC!A$!~ zf6NxF_&LB7If+gnIQTQS+{DwC_huhCmw|(Ct)0z>lUezh4w*t{dZbOFaP8h*T1Cs$ z$Gz+lb$PzavWLDyOMh}|TR6^pw-8Po`(sVD2bCI2xfqx467;UZzsouz7f~kPh{+u6 zv{z%_v=Npu#CrN|)tM`e=rVtl$O9CNB{*#Z2VE3DS_Q8>bH4*sA@hCM2Kkbiecr_Q z*WtB8CCvlnPfI^tGsRr07#?#XTwN=P)L~hot|=?7VVe4pOo5&`hBbSj4aGwixsVab z$HP?aAe7CO)6qjT#}m18vg98~4>3PQx={$-1X4IByLjR=?sFJ)93LSQUs#OZanh$a zxJ@B$>51G9m}Q1|>8Pk?c|bN5qrRd=9%6%N9&}&&7$UGjCyE0^3sHUQ+IWzbrSy<=n>Ri*+12nV;C$ki4{I+`ghyCvueJ6OP=68gIQ2i><_Lkgh z9B(OeIp4*ngcpcb=Vw243NX3MwP9GM=#zUuV`qU7>)#Qo6`$WSLU<4@5+98apQH<8 zs*%%-??~vXmho>b%J=%Dk7abpgivA)MiM_NDgIp(#xWxV|AgN6B$jtEZ41;lwn*Pb zwM?X1y7OacoKrD}X>$Z|noWlA&0k)LnemdgNlzyGxJBr#drWpud+1SYJ-EPS+{T)- zaH(%#7f>{Er2NG~lMb*@_XarNaDsHlRJ02UMpIb#kh48V_JsG!wC=qmjN1eQ*PKV= z101DsGCt#EFULUP(ET{#@G2XA@Vzu6kW!x*)>4AGdn1O+0p3CmIK7|j6y0#e6^FBS zq+L>*~R#qr-=$ z!%7-*T^ICGqm9)qUXaePC~{ao4>}&S>cx1oiUu!CWN6P{pE=g5IBy#ebfspF&USiR zsj(#!d{g2!Ip$r<_X$@Ls2J376RB1>P)*R%)f%Fh`)C>f2Iq0|U6dmUVa5PJU0 z;Q|RFD^ng|i#bC$bsi@a8XQ&BcvOPqmv56=pXp4IL_C?T)&4`s7x%sDGiM|*c^+T0`k^CU979?l(y!!Y z>3pN_`9^oXUmnUi!)!+EVRGPH>qIc6Vc?-6!@n4FtaQ%9F3(n46ys#NJ(hCi63$;Z z!VRv{)XatTOQ2$)a;vsc;#IRu4`!dzgKd{WPnz!8wrhaST}~-dJtF=_XJf;-0CGfn zPwlPBj}EqTI&KOJ&R;v+zPxMC53s2s7H@V?FVt_JaR z@(iEc7P{qggQCZ6tJ3bJD|LdmoF~sXjUn3^U$#<_@$wlciS~7o*4~e~J0kWdgkZEA zLHHz$$%sR}IePJ|4DqJ<5?|}5W3u72hmH>FXOY;}Ed0wKTJjOfqfZ{}8=0ip1(BXU zt7U#lS6ZzOyB09?+)_vN7QQ#!^!d2}t*z7dKAoCR*=!Yc^}8u^bV_Y52f-_>AE6=h zqUmYP533!u5Xq+1GMaF|$r8nD>F1uQtD5UG$jq)h+|(7wmcF(aQ@$o}YRzTesEQrJ z`%@V#mkAP|cYoll{|j^~Ym>aZ~R!w}(wRcXsC&{X*L@YYZ+&|6 z%at0SeMlqj|1FFuS?TiAzwG}@ z{iOfLOq6!G^xWj=5RH9Pxs5xR^65LZzzQ2UDEhmHak?iJKiNrXr0ViP3i2C8+lwXw zHSU{ocD-=rf9#$T?bWnbP(Xmo@25GvRhU9tz9TfT0?!~8Mlv)5FR4_*RT%#G=2C0E zPL>)V$z%gH_>c{3_cZsPTNnyx2miRq^(VFHjv)b?1YyG*)n^CN%0GUe-)w?_n<#jr z*YGJqFkN|nH7AwTffb^?Y^u3x6UZZWbV%4W+h2z59=+mR6oJ$^kPb)0HtN@^d9>W< z(svVJyuBXT$IdE-;$e-EwO2#&=qze)0LCJs>Wvu{9$LWD`BQk&ToW{@*#>Um>Hbwo zyJ>cx$OhnFQP}%oTb}0`KY>VhVpHwv*{(b2yJM><-xTZVGV27ae zS)>DSbqa1tT%rN^^@Od1%uY>p1}QH=(!{?FfQla1+jq)e@*PdF_Z{8J(xwDEZm9_l zY8Rxk@MjCe|7HdiEw-dDH(v=+e_oTh&iCz!+_e$H9V^Xu8}~JB4My4`=-(1cHBeQ3 z;kS4;I`|B$ZE4(hETJ5t^>1$oFG_Js^hL5tTjUDbvfjVc3_FW_XyvAm&w7+E=Xp_U z(3}lpwzl^&%0^P+`(7^Y4vS z1+Fhn2ug|0cd#f$MkU|FzTf&{eZtJ`Idm<__{a$MHR^)veax4H-IHIu6Xu^QtUz0T zSq$#SF+9QdiBt7x#hn(j@t0LD-|ids$`(t&<$LZ|NxjlDYMaYeXpOPT60%*jt=5D- z#A+2mRLutZE-8^8?r!D*xG$P6YL$1)RXjYCm*?$@*kPQM*-mNwHzApn&D?meZ_qE6Lb>C|jg`S=`3dM4{w@D`+vVMdzL)jw3uN6NIlD)ze=di0Z zX9S9P)6k>?#-0U9#SRGBuDoSR+{{at^W4gRco$k+U*ULrX8$Rtu15nG?v{2t(-(*}-yV2<@QZmNUcGBV@_)?lej2A_Mv*bxHeBV3t8)V|sIRw?*+=*7fMtuLpOpjO z{^Rk30di_wjk6z6z#$}@>hjkE+n)!p@lz5PM{h3nokHy6Dyf7&-fv0)!WFQ-cll*0 zTFvxe+RNe9mjASk#1gpMfph7Zovcm8LFpu9tHliyGo+OJfx@R^8wgx*QCR7Txbd(Z zC&;me%N!z}T2t6Mv8{D-Nfl&5;^u%bHe+1%5c_M8G7|*%1eNU>=S#>2ZL^ zO9f~&H6Y~E<;WXE-LtJZ6{s&X&0ugc9u94!Mn|H?Ywb+DW`_~O%CjY zfvMD@Z@S@%<09l3-a`RLO7kIFGu2Pe==f}#rn3mj4hNW!B8;W=p^d-?JW@6a<2OUH z_omn`i_-nWTzlH<8bwnR_4U?t-i*aH{d6^1CD^G_e;uvnYSU&D(+98a+Oph{E}5f( zSH9w#8uwx2*?+wlA$|z%Zb5wcPzp(>hb(mCY1w>KhDWymjGj53mL^p6IIgnbq8S;*yH=G6iNjV z54Cuyl;*vOA=ZJawF!!N|DLLfiV0|QAfixbJHOh|1iWKj*XcLq45Xo~qa;PK z$`1d;)}V6x5&Ow3Ul;7M-2`844uxHcRM=@dBsP?&TQj%>2ZcSqxgOH!p*Y{ZGg6uR z0B#DMv%P|?zXVK6Ii}7KeP0l9X-&X-)L}K?C;Xj`8p`&)7J@hBCxSb1l-7083|sKQ zVN0K15%_0-z`B@kO8NopBSDhd@35t`n~-j3`^Z5gOIbGCmU^&GF4S9GIzfO-YN+G;glRxbCdY6(5Z!vuiW~YNnkp%sg%{ zNJt9~zD3leE)VcU$FT+8eEL)WhiYpItte5y+)kD&;h3@qx4-;hb8pu(TS>JWHI}l+ zhLgJ#VcJhk6z4m9T7)a()_i;389z3!)+i_wETP6zTl2 zO<2ylZL~IeRJ5C;Z&fl1_q@&!(&p!zOX=zkcUp)^z7qY@cbem z{jTZDs6XD}eFTlDli)qZe~ymDYETFgJ819qP*k4{vRYv}|M$daM!2o3@a3Al{k8~W zvFJq>?FU~9;+8`CkG8=c&yS0N{Y|m9=6Z(d(H4IGUnbgRtkfEF4e$+}byK!tqm}QEj7MZ> zSbGJ@BA*gV_uO9Zum)OMB+xu=Ml@#c^9Nd;n&%i7Fg|%K!DEk!;=uD=xLY9QGqr8? z$9mavjh(8w@9S$^rwc(Dy%1#Su2m=T;mFlB9pnX5Ly13Lt|h$WrrSly&D`G1)d?J@xW zuD#v)fKstgSZe9v@H?Ljm7r|yQpzx?bUcKP+pS^j77PM7xqfp%V({oW(i$2NO)UiYw2f~tMYP_VlOt;vC%I8myHzmF=hb)+Rdql= zfb`VFx12+r5S5JfBzsa%2JGMDsS&`0|KgbfKI#XepTx0y;q@jCN4G_Ibnc8GdCY&y z)c`KA`#&<~8{ZZMMyTcOebRL=7HHtphPY~>>MZD1zj?7_0EyUF$(<+#1GRV^O~Q+X~_3F z5_Jn@!>%lxy?dM#sS8UythTme5t=_6N*X>@t@?UavXw9W_EP`r^w%rIiQL9%BPcon zd^}3)P&_Ci0YUPpm)HnM|6}_ijbkB2?2!!PwTU6fmEKfyom#cEIC>IwdMGEe%;k8VVvpLS6J)^*YlgBIQEY}WO&&)Or#yfkbYNWf@S;dmQ0aouC#yKUA_>t!D% zSW_;R&kaHpj`dRcIe4oKWuj&ClYC`T|CBV0cOo|yXCf0*w#6V0POw-CGJz8p{Nd*I6* z9Huaxc;@OSRTJr=s_gUP9MM+BFb4*(PO;vi055=Tq>q zG3QuKpe0z{R8ke$q&$qFn%A)1r2p2sAl^ApUS&CEW5rSnv7rB@-`AMb zcUqKIH;e!(s2;maZk5gOFx&IL18yH%ge79FOL?qLjfYLi1D^NOulIbeWIk3+LP&g} zZ8jq)JVDRkElCLT9%S_@JdLs&wsjq3!Y(&iP0^Pv#)6np0ZTCLv2E7|NDl&f$FK(4 ztq<=#G)V1P(zlcf;)}ksx8yM~F@Sz~jyh*%ML`rmH!r~v4-l{QYy3Rb^QLsWCD z4v^6F)lo*|aIWXsm1S^(E7 zce%d;EGfjiL{K^4TK(M*ADH@Cus760$Tt{PZucTZJfqM+eGBfP5n;$K8U_qsCzbC@ z>|Clhh6?ejZ0M9-eS%BasNPlzOsw5Qjur5y$9{L8PKz0VL+Q~MVy=I>*>1j5(~(fp z%eb3}SnojoZX^rv29_cXxdTRIg*mIf((Xm_4F6zrdmf1Mjn_e!SahT53wsCl=kK-b zp&!Z$+II^e2z%Nk_(t{6^p4$LF|vrES+OyO{34APSS02O)cy_@+(8|Yy_$nF2j2pR zyd13gWlXuqqOnw2jWlcwnM{J+VBF;Gd}gE6cM?Z@^hxDf?S-qp#;-XO7K^9ePEkM5 zS*i8uyygwmRa!%)>t(cW_D;v1{swx;_{R1bGWMTLfb!o4ZLGiY|F=+48N(SKag+C^ ze#tRY`DIJKnPvMGYY$AfYzOGXHs~x*w&|~B?sMX-0zcCYqTQ8#skgs+8JWPZ;E4Vx@#YG?08n!T!9D8w06 z>*-#1?1+|S|E-J^2(z=mpWsVl6@mG@F*kcMRU1v7oKR<_oQY~de!rL>nwNidrfuKP zHs}MG>0a1Ia>%u=!Ox=FOmh*+U2QDSi`hu=Tnj4EN|IfrF($h8ax?;BUVlcQJGp-!sYn~!ho&_K5KeAvAL8V?=tXZy*!1Wj zwttoZr!#5!#_~Vdo#DJb(a_pr^g{FT3mty4rYmn&##iwv=zImQEc?;>ExPJoZtg{<#{D7&Sg%MI`#` z!*MpIM;4XrU!2CUXS>`<7Yd3J0ec&WWH8N`yEHO zp;T{)WJauAqiR7g%-Gu}2(V`8+BwuTUcD==ntDc8zC{4vN@p5gY)Ca}Icb=Y^uTgH ztA#r*kO`+0VY~N7$fn!Z2p@47NbtLLro}pgd4W7(TiOuEGHUF9=Qm6`ilwXjXO;n{ z@7M&mdzfV?j&!4P?+lIBDkW$kFiB?uW3GSbgUCiUcyJNAm%D`qut+ z3D**%@V^@6Uzk=&3T8Dgc03R|1%2?S2(f2qC zptLy&0*=W*?v3-36?&DSl{5|DHz4vWl>D30mFzM1Z_qyqY zHG1RZPDS}fG5~upNRJ$HE$l`yVl=wS-;6yvA1Kss#4j}Y`F~=ZfV25u&=%|P+Fu&+ z%@HBcD?WwYTpo|3!IGfIwprrX}m|+JG9m3kcQ+eDA@`cVZ0H9h}}VMAaO1X7IIz;M+_Tr zf^b?Jht~?=wiOe(dF4KB!~L1~L=9-K8mD$r0(X#gP^mTw3OY!c`eCeQG+R4*y6fD> zdxnn*%U!oL)BglKiXVV~{2Z4_2+3|uk>e%jpWl(sde9k>ux_9MeU;~)Rd4_GTk{=} z)wXO8lk=E}?6%l9sC=i}T^}4}n7QIg-m{>D>@EF+IjbXnMrS`FsEB@l_-E%8n|p@~ z4~W!4&i52A5-$OJzlINe&5q5gvZ@~E5@h{CmuAX zAIAE)pZ#V@dUlM`8Hn{LG_xNPDLu|&Z*hXB?vtwpEl!{ zlD`4f%rGgsB1khTEjP*0QS5YtBS=}fLyC-knmi?^xN}M>WU)$;3 znl{mIvGeMzQ~NzyQ2#L&WqqEXt??omn@1P2+_Dm`TPliV{2NGhNQX8_N&6R6rTQA^ z!T-!SWJ3&hbd>I@@1~F1HQt1T zL&FauC{Q|amj^U6v#IdZMm7dy}%wgoeCji+oKs=y3ZAmAmyB+rz zyY&+UR`QEJZl(JUVX_QwMJn$^H}wh-iCSu2foO!Bj2Y9=Wc(9vn5fKXXA5(epWF*G z$oT7TMMjEP?x9iLp&|N0+Akrcf0G}0;!}jv)nSNZ?`#2|Fhaz*)yVK?Xm7eecPpO? zz%cj3jBg)aR)7WW?@g^t)NqiqQx!PINoiJC6f4n|`LCe7%LV64E&@5>~Gq8k9)EVWnCad2*4a>%T@l#QSH{p-rItJ(yIW0n3hBuwY}!z zYX%qs1ONEhsAr!_!956?JTf?=Bfc)J__Cs;G>ppt{yNJGn2*vvf{s>D* z?zqGobH~p)$_RQNZV4^6M{t2o7)Xb&VISFbJN)oWBE}l4bo`(l=UI3RfA`q6E)DWo z0p$JczLC`^+Pb>_XJEc5ViCgbF&0L39{k(YP`(F=a@81fj!P|n=>&15oMoU9tr-H&&F6h&J+>{NqZzE>DpH8}%|7S$*UQ&b#e zjvK+6{55SdpnW;eSxTF|>SD`q=~~g+1Ao(zf1+5U{J7Y8Za(d2_IPMJd}3ez%arkI z@A(MBs8i_&057}jh}yjq+P9!LpHX==9oH69;@yL?WMO9t6(=UnmYq$W;Fl&PU@f~) zcQZ5c978o2IM8jx>EK7Kuf2Ep{VZm`Bxv>}KCnRBa;Ng|s)NJ`v1MGGV%Um>{lUGI z%pqHfb@~R3W~C-i;DVd|2?zO@!`D~3f%e>XHz+Cm3O6EUQF_m2MFKkmzmcwbc?S&nswoaUk32qK@F{3c92lmEDy z2lOI|vFugTo%q3EnOVPP7-Y2Dc&VV0gu6X!C6hp=5( z?sLjni}%%ReaXgzeu&~#NJT!gyzs-tNP4_CYoIZSkCuWTShHp9=J>y}L}t64+eZ`f zgsIki(@w~{LLgDX7=(O*#wN%&LyV~WaK)yT^M|PZu}pKtcAewSxD@cKx+94TooWz7 z-JU>b_34`t-YQQQwdxukKgy&2XbTm$gA{%&+{yl#_c~59-gq1RC%~V=`*WE?`uLOE zOmg1QX4M^%yGh0?pR>CrRTaxzD?7hXC2q_FDV@CeuVW#7m-@ydlIq4aJ$>OFAhsN2 z{aZCfz5VI^Bg0m80{Ef+EtoAC&kf6X%^eJ*!94#!AOEkMgOXC z5{ewsd}@>aBgNMq#P$C@TPXS+E9aVdVq@}G0ro{E?F)H%aQSmiBj(#fh8!~PC4u1S zEGpcWBcOVvl-z_|ivEDDcenkx+^Kw`~kc zo!eS7sXqo!hSGONXxzJJx6W5g8DeAbJdMWfIgetB;t-=d2|MeZif2q2A6$)oyPbN` zSnO3)R~=YzKlDc+N+hCfk#j`$)dhn#i8=eF>vn#PWuy1_rfsSOHc&1OFHW_CZehE# zlz3V|YRIOwm?mihv2ZLbI?K8X59Cbf6p*@KIu~LmLIJQ9tk5-7AqbTAV+ruiUvGSS z{642|VbjgDM(tF5yKDai)Tlc^r5B;x3bXn3RDR?{o@!H3RbnaLz8SHRYR~+2 z8y;bH@!2<|Z+H}zRBsDxl48v}Dw(I$MjLTgB0Kolr#@)dznMMvg~imjKpmcAMSXKv zQ!kw}?Uxg8#Ow9Du*^rpr-q|8i=W*6PQr-YWh-x0eyUlQHi=u&mK@?k^%+32yZKP@{y++Y*yQP=UU zgts84S4QiSi!biAAlAlo2H$fwbgc9O{>Gkl# zX;4xBdl1p`kj* z{>v|0&z5eWfZa*V2Cjh!NN=MV?n!lUyoen~%RmxkkDF(VYMe~WU@_X}agIN;c#Hy> zR>DrlN>A&%l3tCcB6FS+XR}>;=}9Ndem0Eg3bGcRy@8j(|60MmD#yLPBBp1|1lS-& z|2(gPoJGk|;&-wxn6`^h_C*yL4Zo2YevBIk8z139k>Zkcp%!%p%Vw7qAR0*p!J0<{ z#uqK1CJ=(>%0GR1=!l`9Fw956(S&PmdkbB%*&Euj`AtngOe-E z;Ov;OD*>^;{@3VRdmeJzN$#lN+Z5&Ql%Kr}^mN_+{Zhd(c%8ow=y|U1^P|v-(buP# z8u2t_q_aAL7|rJ2zRj$flN9Hvp8M@evRQyHaBi76$P6ocdH%SVI9v{w}d4*dc(MF@=PAkco`IR1c zuO%37F<^in*tiZW`nH?j`hBD0qrIc^tP}UdPgpSRN3nBz(qo*$=(;GkcY7R!-gd;b zxR9X1znbXxERMpae|-)u9|B)E7P0Hwp5pqO_yu~AXuzS)SkV@aq~m5|5cHjGhrshJ zpcpb=Ka{pj+%`ti3q!LZU~1m7G$Ep2ZZrscN6w%c`%5aV>X`Zc$*RMzu}mtb@UNfS zIA0izkTJ-~h?4x~mV%9q{WGK?z=bCJ2Un=sPE0i~raTG6KahlGoyi~uXD5Zl4&O(1 zy!Aq_X{^eO8|+u#;7tf6CM(C|m=R`KveBu56SyVV?}b4o$DQ)48KW)Q z@3^`YIJb4?2a|fQ?CFiBz!q^|iI&1Ye$W3>ErPd&KOwar9}{-pxdE{X?y!0B}1p%KB1=E`Yx_ld6gY1#WmS<`c} z)dRkh`@-Z*P^EKu!mvKX%GuuN@a@!9fdFQ~CBmG@aHzzUK28WOErB4oloyX9{G2DK ztY-42-x{=9gshcW*b{G2hSPuE@qnBEq3R9RQ5w$WZ7uWRk zLZ{USr+=MUg?*485Xgq57SVCOt}1s(f)`)$d$Q{9rA&XU zP}WlQJMl9(W6hxiffsitV-8>8HXE`H4sn|SHlUaTY<>9?7w=-a|2I&7;fC2ngsP3l z^_M~Lebo;b{wp7lPpk;Q6U&NN28hmL`)zaG$MeaJk5u6om89G%a&AJnkPiiYnTs$C znN9rijw%M{)XLgM;PY_Xnbe}=8I`|hjAlPkUb}=9-M%^NefWm2kz|IUW_or# zBRtJ2VMI;B=j9KG$fA5st!_HGI-OP;=FchrDSjC`r|m7A{8ZgRH!Ll{ZKu-ksI&8T`?^PxrxscGB_ zh;8P2KI=S??m8lcAFQTUsLfWVWwoHqddk&jw6GV!ensbQb)S%o7vt$MO;2*&WmV#B zxYeedfDz4T?$esT>i@~+dLuBs(&Dn7vdvty;3fFwF?(O}`KBmN{kdJuX=|i`#IxS9 z*c-Rys?CnQ#6RqaeTI<1WFsZcob$Eu z{B+T^Gy2>jKYRYJBnN-{ApNQfJx84UqSaQ#(A~Y5+hn4IYEZf%CQYQqBwSZR*AZBG z{H(1j7@0y2Q)f{`v1pC~(l&jfrEJ#?FHKN8P7X+K9!$O}Bv;q>!q z+81wWBXGWr&o$2K>lVosZ!~$_cl_mAHCG-L7pqwz<|g)OQx~Hs8se{Wd7}Rq|B)l= zhwkJDG6dNPLszV-$+egB+xwGMb#s(v*JFgu{|{U59o5v{ZGRu-C>~G{5$QxlML-Nl zZ;4ny1VKRoX`x8(y@fvDU z&EBx~{C?(KYiTCNd$?-|aHtpN02S$lZ=`6pMgb3^wbKNI+vP(if!+4!T3K9}*|M@E zZk{lpzk9xLGA8hMtxTha-zTQsH=@hQueXLHr{W96ziUQ z-?$4$m^Qi`I!SI9LmIAz<+6X(n}{8}2kKZ^T$dgO$8I634&>?C1dsZHz^Z=)2@Vq5 zVhleEpuZec74mIF?V;k!WHBtOuR#9eCuw-k!N`sC;`E5X7bNp?oWI!zVmf4F-#B2D zK|)&dH)R(cn%GlUBvilo^gl=Y1LF;;8x_=RW(SBpAMNpHMD=Rbju$O4OVn4b|9(gk z&mNG~cmH9FY>XKm^iX>0k z;loy)w&Z!}FLqAouLDC^+1(y+u|jMEg7$31AK=3-)W71OfX;Ib5O~6uIpR_U?j4XX zJ6;BOn`45CnHz~k{w2RFf74ka>9ga|x>Nz<-aX$uB2_D7-=GJq0 z1&2@E(^}?X1FcK>Y|^s;mXtf}MMWN8gW@yWQacl55rb;9H!v z6s?|lKO?%=%so8LMk$w*aq-7vsv>cjvU!T3W7U=YF2%xB<0%@?^D)ke%0 z^-Jd^cMWFvbn;~fwx{Q$MR$>PCZOoNKl>10hU=!>86HRk^#gSv`o}{RZRNBly1bC z2M>Ng=dK@c%#yAo&$0G3$l*F910Rv5Fu-3!!29r!7Iu>I(J_+QVYM^NX>YFd{#WPj zb7FWAv1WzvP~Y_sugpj6_1axqsS4qQD}tRzLrCbYmHR5+Ia=)_RX~IB7h7ufHGJP& z3BZ~S^{a!U>J4_#5A^8t4+WW!~u0ouqvM zK0B(_Wj}tu8WskpQ*>Hy(06y^#{eg7&8hd~o#_kssSe1R=4$IN;C=eBu)J&iXYq3q zf7Q3xyWvrHFG7HM@g^{;wYAA0AiJ zzhK4*vu?`WoQc`AdooV4xbK>Y_xEcvD;!E7-@ec8Xoz)<2^fCOnTkrG<8M8v`fGQ zVBR!xaE70Eh8QtYOajqhLX zf~r2q3FNmm72})w=1f8d*#x`-Du0bxUnywPbF_b)^#`33*HEm#J@A zKA-mNw(A|qoa}PpXdijimc8AOOgqwhp;t#&TB1H?x=a>=VfMpBO|v{4vTAQFtzbYb z*H;kE=Iz#c{>cCpIsU7yDsIf>keAdS61U?*crqWvujJZFrh;kneh%vT_v6KFT~Y>2 z(0K2H0@I(eA-EGtw%@g-`sRl(R|sFz@p9=s<)EykHM-aI#_e!ZsRG)2OF6E>zP)a9 zF980kE@JoO>U;Npzzf;}x_q(%=%7yqp;-xaGt)b_bh=iLzt9HM@W}98xMS2cd;_nB zWmkH6&6>QG)Q=H+1Sw=NSLhQzvim~|YqbrqY^{1x-(!pZggI>O=(T$kybZ6$X$iab zIk59+!F!jxrbj{kBWkh0UE9Iu%LsNMP3!^bay0Ge#@WUG`?M~gnp2R{-3J9dL8}+I zbJkZ(y(q?QQqE0zUn6#(3LmIiD%x9I-b?iKzHjDSLkN*>OOW*;-Ba`2dzuA3PkTCd87>UVI}^BtxEeu z38bJ(v^4eePVe=};$|Px7eq8v+nj%LLI8Ws4R8KlGvRbjwrl4Kl@kQ^>!(?&>Lzf6 z!FfW=b7yyqOOJk7^HZ4}0Q6yQXfTuZhAm{KGTv9+e-1t6BHby2Er%f)=A97VAp}OZ?-@W4I3U zKBqkTJrvaTsusAl@LH_4c7NyS>}%YSlEuX5S-mGT=72?ItM!Q+Amx*sO>>mD5mCSm z*#k)5;X70OUe|R?5w=1X=1w3KHu=+o{JODlWtt|7e}*pAJ(gW|KBH{WGPtx)Q`$r? zcGNxdE0iKPFUTAGP(?_SZY?8=TM8x&s3nxZ^bQMhAMFM0juFh*q*nfgUFr=l>er@$ zBu#po6u+NYERX)V=K`p|tuC?EF<6H1;2Cvbrad!?MS%D-0@@#D4SM><8h(U24D)T9 zoW_gi>=vA#cq?GYH<)G!&M-DL&9XAZo@k7;2Tww;0q;rFwkZ zX_>_xC9Q-JsW0r8wYKFlJmzKufeSEoe$(4ow~)2$SS`TLXT>9}UaqJnNC)C?*pN@c zVMFL~h*0Qqjy(;J#eX+|@a-&yZS*xbFC^UUG-j#>L%fIFg}C zB|38G4vcq7HZ;b)VqojMmvZiI&of8-<*H?&6?pn5&u`r4?qq~t@n>PvB7L81?PXVaad}-f$AHeH z%cZa$&#cG&YK3U=GGghK@a*qn%u7M^8?VV=U1GTC#Igvkk=qp8THSGQ5t0a~ix%uu zE`Evbc6sYOS;BZdmPyX$6i@^hc!i%3qEvD3390E8N<2j=&-I|zz41zpQRW%|TwxvA zebW6(%MJL14@lK~dOi4FllUC<9)bP^YI=IJ`pE8}zlLI7zTh2MQcF6WhD#CEwqg~g^ezW#edq=9u?3JAq-s!+ zVyR2bm%AmhYl{Au7dE~1**lKfA`_f`Fx=4r*gq6e?fX$FVH#`gLQ+FBQH{~xDFf{6?DPR3R+X}jlZn1}A_F>m zy6|PeM*JL!*ifW&!F~#52{@XUZ>4r&Ym43OQrGVyUHq40N1*zLae=F8Zc2O2s=vl< zA5oY?PUlBgxi+GI#yZuOVQM^FkOM|Pr*=33`T9UyNMZs^gxFmI0&0HfzZk|RitMm)& zF!PMCh$sB~pqLgQ_9UqCFUPxvsYw#ZN3M1zmn;IA)Aytmf1}bpcYydJ$9>RUexo4&!m1X$#q-t5mEpbaXX9=Pp8v#t zW370QJ)gJp`iV}3`-On#N+yxsE(xq^FMfv>Ef0G;wjZi`$uC)%iFO5Pxq#`YnKtx!q`Ws-BZz$`T0ZfQ0%E<_M+-S5xLx&xKB;4dDa_o_wO>_kH6REgc-o)vo zHr7f|+w!U{XfS&g&D8bwd@ifG09GzT=We8IlU>5YL5rWvm7P3|mec-*Us9*yY^7h- zmr1ze?dUOXKc1ldzZckJY<0~Ng_gzx?TYq<1w%z0z99vjb`qKPsxooK;}kC$z)b)q zLLkhGTC+IJwEopq<(BGMNPmr%do^YQ9@t`p7~ZeBRO?f-P_)OP?<9SJ@@>?*cF7m z)0OAHIfSIoW_MyH12!;81NW#uCy%p;|DnP%=o2;A!#}V>lBmFsWhM%3P1OG%ZT4Rx zJneK!fpwp|>l2fI!`Q^`(e@9!(ZZq35ZvkJTb(`z6ALO)LDE^EzPxWv9$wTC*rj8p z*0Woz_bzZCaGRKkJ+r4^$aFVKe(UHM>`>xoiqRwoOz-STZ2I3?f$7Ftm$Z_=kadnp zCHl8L6HBs`544+{Q^DGuIehqFC$1t)=6(9-v2A=>rF=c^P8t3)BZl^eR72rhbLOvF z>7|sabT76}5=u)BAW`FNK=P7Q98!SvqSs%=1b~tbEx?_MJcpJ`n(*|4YS$qQjeRn9 z0i-m`AAMr9&tUq+Z#{ZDFSE#G@?UwZ1jdE9qBqNU;kzq4)+z-x>v~HJO8&lJM{}x#IsU{IO8{_}dXTv}pbD~>put)?~99efbzId2r zEpeSiN%hV2-oy`&2aP|r-ygSda+7YxN~plGyHyJQWAoQH_l18c24(7-1ipYN9uR^8 z&OOU=PiAepHXzOJNYYUKn@XpVrWlJoQrI8Dk5dzCL|_Jy4U{eR9U&UBbtGh{_2-Id z9S9i}PKfetjSnhlan+#LWr=-&$nS8WyK1bBw?4z!f2-9kFLBKNfGGKTyuDhN^#u{g zeZfFsTg^?Xo>0bHaED8N=W!^De{^@1MkY)BWgCHd|2ExLOACMeKcz1@Z#=5Lu4BR* z<*a_^<_845B0lez`we9a7xlVrJC&ymS8o73d!P@Pondybd!(wCnvSrBvfu7G^aFbn5tGbpvzikV} zz09=I;em8A(2=#%9_EsRB>_i}dNUE+HOlm{P{Y-DWZofQjM;HNhmZQBO(VE?`Psgz zZp#{bZ*c!+QUAf{vci_ve^&B~1qW0Cb-tFeV;=Z@4@b?pGfDv5Nmwsj2$CGhBRB&`r zTOph!??bwfX63K>8%MB+rc$dL?GH@!a&^Qtqn~W8m=RjPI4FkdrLj&@Y#)7S8NE(y zds(a4rK4W1!>oMtMJ!}SLLh*R7A5k5V=qdt^x2|BPu!0v!TKJicz>(tz02%42azKu zg+{A(TukxS!|#&c!=t~=j{&%=+`lF-d;Z`+Ue@4-KVz5Mul?zULcZGU*hCJT5(!g@ zBHuh#Q;}d|2TVcsuIrz7CcOBkC1OC{%$k)x9uDp*f6QgLVpsTvODJJb`CRC6sHN0#_iQLAI9H9zt;z?k$ESuGv#(u9KCdsa#IMk4#$ElYi<)V|prr^`+i zi|sUl_OZS|tjZ6K0WjmSm@k}3K}TGiV}*!qEtSrQbV@lvEJ5-8n#AL_QSk9l5%QD( zF$z6w%ZT*OKER*IoD!rShm;>UrTbt?638k%Qc3NamGswX5avF|o=Rq&e zDaBPIjhK(ZY?34OzwJ2WhF+;%@R-AI7m7_^Jy9iUC*&B4_7K?)DD8X6e5V!jNjXM> z%;3KNNUMK+=7M%lMBt1#boWdCR6-lgD~wt+n=ABeo6`rew;`#;QWm=*U`Se0*mB;0 zh-Ym~ZL7sLJUBI}0$T3y1U+yFJ)UOqDhvmo{^|lBm5+yyF;LZ#Zc|-maad0M^1R1I zWunOBG2~50s(BrVKY#cE=t~~so?&ecy=Ggsu8(?{Q|m=IAx%?J5?d47=wwtkmSBzAG;Q~G3QgFiqJ-!Y?*e@&Gei(E* z`kTnHA*tHvN-g0^utyzg0~Aa<-d!6`4$Imv8MK&W4h~;to_Ro5lOG27WKo_ez#Kwb zZhkR-iVLwd+pcE|099vb{PF(!3ejkpU|FLmOu7NdhbOXN^98AIe*d=^(08a^qGm@W zZ+N0*FiTtb(!C9jO^M;AmkcJ~SHAVdl9904&LkKG5&uUXX>?id}i5=P2Kkp~D zVh4Usl6lbqHTtNTVEkW%f-bUkT%7QgYvZ>9QTek>Ts>Ypkf#6Q2VR6I#kJcq{7}eR zJKr%7l6N%&!5-%>Up9hdT~rpl-h>(x1JJOT26~;*zIRuhlF1Or-nMRylSW)ewAUdcU)5* z#;dHN$qXN}Hm;zS%CTSGns~G3CGazJpj3~96xEF}^7t_dBpD-mqe_~rLesf!kO)wh zjyG?$=+GZsA9(#b#V^g|KHqCcB_^wFv`KV%u&(O`4y@Dn&{P zCyK0>+Wx1FFqMZ#yc|K*m=m_F?9Y)+k}v*JQCX)5U~EPRZaPIXHdOS+!=~?-4iaz| z3&y^`6w)*5S}Xt`TC1LQrgh06YnOd`&1a^f<%>?@+I?d<7G2hxqU^lG0gas+A)$Z^ zS?)3VC6D@zHu7){<+=1nM~+I{iC3p;>r%+s`ffmA<{SM#!`BVAJ*iCRO1 zXw;snlj}E&#f#Pnr=lc)4yLD8@IATF)~A@~^e?I!52sCN_3Sp-+Mh=_XV%pInzslR z;o9w-dR*{t@Bt?eT3tJ310b%gxNYE{|68f&Wpy04DGdIT%3!sBj5t|yLaW<4m-Uu? zkF$5>Z(~S+!6E;T)`gDMa={aUcnZsW0ONc3YO-LU)yl1^Lumb0iUD~UqSLw#I*DJV z_mPQ&msuj2`6{=fvW5Wi6X0Ol1UhbgZJ5?g3s$)U{zMFp zjuN4g0;fdD-|Dv9ELWTYX4%)J=S=BNjLp8j?S+iOP>U^PxP9lP1+|69tB7?(xYkccfZ#5^vch($@so`G)-qgkB=m*dlL<3HN{ zm_@^AuS=kzF5ZZ$rAo|hOx?G#*M_}ius`KYTd)`IIeleX*iX#yNk|hQKdw}t{l_J`{z?OqFlkNoj4!}q(Uj9(YBM>+2PlF zD=$!ZE{}Su=0sF;U_`8wbcn&8Lf;=N9FAvw7_3>)EpjAJ0T_c-@T+Im2R-;RNy4(& zk>D>v$BbeIb&Hj$DXOMh4E~qgrq?pQnC8ZU{T&vY*`YJW3u;OChA`i^3jKB6%>`-b zdVJzOOs__@jZd~5l#t4!VRTAA-@i>%?p{{ph|0ALfPniUN318_?sz;76HjeaNiaEL z!#F5T`_HC`#9d`4X6tqHgv$uFO1&Msp(r?|_t7%{o_PG|*5MuKo91xCFw;Z)W9G9vI>)kT3z!3vnwLmWXg)Ru#^7|p-3fNT-iafH3 zrSX%Fp_87Vwltz3SSOAaG@ zRHIC2CD4c$NriUZe&*&x2(#|!ueyGmmgy3+Ymiq7CV1q2rNl`aKcmQ2M%jJYoxj{R zpx}NBcN-~F&KQ5CxbC$Hp5;IOVTY=KL+!|6r)5xwlYarbYkS=ygM~KNIMQ zMmQc)<~egOk5cIb#Ow<|h4Psr~@tV8`IPb=fd zaUkQR!i;h_R2>!0|8ly)%Qz0h_v>s)Y);Qt4^PaMU z?)7jPC=Eg^`2%XxmPBm%Wv%dC}OU>(Uraw~Zq_xSD1;;Mg9S;6^eHiJ6;XT)n4ZD~wfdR(IaU?YT!u z#eXIx8*!ufThbp(({%}BWb%V*4%RQuVP?2f1N>5YTEUBk$l*hPPi*LpO2+&6OUkZ~ zF&}Gq#z7Gpuo-Ck*6Db(fcoQC9FXqdCp;z%<6fCs4Wk^G<|CBz zye$V{vxgS$g;67}2P?h)`^JYm;II8#qStDkXjf(b#6`IGOt8IFwm+{7-ygXjr_z=X zO_QjxNbmlfXp6$_+cX0MPX`1NKuO`(i|dLH zEb#kGyz;|kYC|UM2g%&tR;%pu*U)M2 ztRFku`IdX0*7^@#r%r`?HBTiC+l}a^uPCF=_B!KOc?8(TsVY;p&p$L(q}vurE*DmR+H7i7JDE2oGxemZbJ;I-c6#UQ zO&`Ch-hy6{QnsPyiEQ|GMv-OE*de7tt3|VKYV|p_YDTtr+80=m4)~dQ&g`B`y5u0x z#kk}xJi6Vgu)V(7CnF^2w~Kyg@Fx0{?c>USb?Bb}F0%mY#1wBROPxy09}Menwob@9 z06issCO7`~+)KDK`i;vGIZaVdcFK*HxvVB^*Y;x^_? zc2V6^4iB7G{RIv)6?{?<$v`0@1JuB zIe}0x`E1T8+l*Wlh7s*JRF*EidwE;jEL@w|XYQ7x$He%aAOI1v~V8PsUsB`rRp*v)J1?z6Q`?XYw46a@PGvYrhcaEhrI z*nbYl46^R+1l&^9?V=iWN+nu}yL^dV4bSHTaT1hzIrHMT?$lRr?Wizdg?rhak=0HG3MF`e(bGxCve%`2aq!> z!0&)9D>0MJCx3H2Zo#|EQZl zNw?EG&g$nD9trV15k3AT)kE~)5lFOtSK`|hPA~!WP@*MT?)WPwPJ8U&(}mt!Za)8% z;#uLW&M-oRX~nw&IR#$BStC+z7o`9=?cUSs|Ch127v;n2Rc2}h6H=!?{>dU2>0_TY z^#|7!Oej;7UM{g0JQ zg_ffs31^GhED;+^9W5holET8KjXR#i9$z)knq22iaja1a3G-klm!Wpo5)eLTr`o2gG+Tfw zE?Y}`W-8{*)0-Yphr9gP^KFo-WdLkZSa67^Ppi;ZMM!(O@9Ba``sF@4>;zjagndH4LyX`8Aad%rWbI=^BB zZv>iaYSH3lzZ)1V*XyDgGV~jOfvtbjmw2@Mtw;t^n6j}lm>?8NU{w;QVUEz$_XHX$? zp1W9#SeA9tSw4cC!^eLZqI7=0(go4GGEQX+9JwwwxQv$qBoVGWmL9P?cj$H#V|?Z zhV2_VbUHWH3_LC0zpwb2l9p;NAVS!8Kzcw2CEB|v# zI{kHJ#KzYjnR=bQ&(&gNPRxoK-tna~WG7=lK0$7l^M$RHWK@T-DMRh06g>o)FzWYK zIqnXT!F<%brVk*@w#mk6c3Prmbqt1}?=;Zj}?r4{_P5)=+4_%bI=Go?Fjvn_ZBz253s8$Y_C%Z0Dv4wvoIXS9u zTpzOzmvGf_f_ZJ$>FD=XPw0BQ*G@Q}0o*pLeSSBkVRTlX!Lu2Z2mAKZjwtwF_L_?G zYxUU;*t`=vO>dYc3ln6Gr|R9J1(AVJmx{aN3)ZTZ(VE4&^QYNzzHKZPKtm?>XLfER zSeR6jlDTQ@WJ0Y@{sX&xn_8O|ow)O82VMZXU`p#PA^* zevH8m^+eChF5mOD^75qlcneM4mMOIyJa^yM{}Y=~Sy=T@l8gDnl@ni@d)h~nZceM* znD}1*Vp2&v=zs_Pi|SqJEZXo$unOFWGhE$S`n1hjfR_ z2WgY)G4s@D4LD$tEYeZ;D}R=@ON_>2|BjTKf#W4I>#){+riy*h?W3@q)_B@63XDJ8NK%_Hp=f#k3m|CK-o z(@wRLs=(%&KuJwPv|&@Dh2qvYGu^MJs<8MjdG>;$oO2*htzaDzV*MoUoXWf8GYMM) zhEZP93S)^i@W7dJTzBRHhdZ<`W^P0P?Ndc={f2db|IK@l;M^&Q7d62-*pmFTSuc?%siytF>$Dl6;ReT}! z-$!oYY%Wfvzl6qj<;SW;LDW_bin02k#jXp(2V-P`>AaJ{3$CL-IH&&n<5hLL(bN#e zb>GZZQLBf9R-#AYAFqlCvq~SC#y4F-+hLYoz!BpfpBY9vV@%t_aivLv-%$jG|I=eu zhhaG?brYwFwF+f5wiqoXTOx2h1l1T3}ja$U7ciXb9!sC9qTUWI`eJk@KyR$VBZmDE8|yWqgwGl7h65(8k$i3 z;P7OQp9*8^KC@XG9|PZI_`WHy!c?rU1b^8s=*{{tgRbhfrV)w`dxMnk`!>lkn!`$9 ziGZX35@A=f6B&-3&;0t_eurCQ9cCTJ;*8|ENpY`zi_d4}{v;Gf*u)j0#m4WUm;oz+ z0hvpL7KMlW$7rdQD>C$xG6`wz?iC%XS2J2pEouDy34FK$W#juA#l2o-!0u%9yA@6| z7^b-5yZCe9zlj3&z|EZ54)fGNl*`0_-hW5YzbP=e@Us8vZxnP?M&x(#ugCWuUiSvY|TkG?*nu{IWxCEapO3zy!MC_EL{WlZhwc zNl6Q-`SG$pBGBn~h*xF#KB47XW?&?_V$PuJ@@`Khb_oYtarcWK>K}@e+QjoEYy?m4 z`aE3z3%UW2+3@+s7Q|TA(LD%{k~^o(?=a(=6^B;gBhAWTFSh)NLt*JS08GAKUHXQ* zbLV1Y$%0F7_r1d5(!}J{{!S*7cf5|190rTpTj%AeFGrrtOHNUlL{_H%57Ptf9Sjzq2|DSs7ovc)H40BkC*<$fjHZN zk-ybnv6OoSP|jod82IjCBt>_QPZv2w(u2$E^5bS(Jst-iNj_Q@R&1|s>>LoD8&{eT@%tBw8 zBtPFq9iaHm)QOTaogCXXiL_@hmnzG8K5N0CctJ&r^t$;(=3dUawF;pxGz*Q2XUjd? zyxLve(A>8p(nnFpPnjKz>3n>+Y&MqKLr7N$xNDAh`)YdmNny~jH1-tErZd_Oe%ITe z;wTeXvbWka3udXkA)-JkB{HLcQn!}Xdh;&KyN)FHmcqMGn1d3E)%0Fux?#8Yavs5c&5y8`VIhQxpTqY%CF?)|FkjaQQIqSQ&e zrA)%&cMkSgW!rl7DHGhz+O*R_9x6iRo2zAGT(ly)v?I`6?sSNe=R;7zo?+9@(IT@? zER*pvFDn?Tb2+_$-M?V72la2lm4PiZw#!4(**8&#y6Vi}SqQT>Xjel1#_cXNkoDF| zpQO1IeN2*&X!$(DfFM7`@Q{-N8sUkpXARQJ%BIr3znB^GLmQN7!sd)zwu^=Mis%B8W8{vNd!?rChSXl~-a3D?o8+TgH_98dCR zp&g1`Mxuw~Yw?YCqPo(<+A*-pdi6Z8L48lN_2z02OKfZ)&+)U?Yd8679|_zYmhZfQ z5w?ge~C%_FBflzYA64R?_dNy^a=UV=r)3MHz*tW%&Ai^C>jZ#r5bnRehC~6}4mTF3_Ue zslUYRTk!{}I#1vDncvK4{+edN2?q9}89g0utWTw{SDUkMKiDUj3D%)k!7nl~DnwBB zggmUc*#TKEW4=!T31g#5=j4iRCuxEE#|~M=jdsEos_b)suIcXXmV7~6k=&k2#S+Jn zeg+K0ZBq?T1o#)<9dWuB+#ok5L!ISO?*D^XU1XnLvGT(lb(xc$r@#5wYdi_!@`G#h z-p(^!Z+%iRBBFD=0X(i8ntV9kd%vYU@FePe8L#OYxOd8?Fg#7DyUb6^{vI&YnuNUN zlk4uvJX@;K3NKRBZk~Mbeo@wu3%I`WmQSnJCBMl``Q1PlxX5b;$k#bzDRAVh3L{KG zD8VIseyAcL@99uG+M@w^A3#x93n^b1J0%?L650=!w6~aiv{6Hjf}-B4x6}^Ezu$3L zt)p%O_F0D{f4ZtCUh4Z!dKHnQ7=wJ8o3%9c{1XM>Yd~Dg-jaW23COml)w0!F6kILs zoV0g&(iwvsjD9KI4GpL*ddz47=IgzKLaZ$kQ4~>au`>$T+w4k9I2O1>J6?v`0zeO8LbD!v!uzmj1Ys zTQS6~OuAbg=rE;(KUY13QN05=uFFC5CuVPo9>0t1I$Gi`$a`3j?l!j{cc6Er{+n8Y9YzOjtY9UQd0waDe5#PAkKZv3g6IE!9Erx?U$a+|e?2Y-X z-eKZ4eiV8VIsFptR|NlotzC8-6*_P*!KMDXorPPwX5WkP6+nmgUn3YXz>o}HN@Y+S*2IQDp&&-g-$RWp-zR__gkYfCRL=mjyZTYCfY&vNL$c!zlAfqNfxpXjoJ1etIoI zvG+?UQW!NJJ_JR+s35J}+-Syp{Sus{gkUgNYwZsiDUsroVng?85f7Oud62F&>mh#; zSqbeV>EqW}115MnH)+P!Pp4d(I)(Fk5((Qp100(qz2$6vmx)&`dy!LXyBqc9YUclp zIC#uuk)c=(`ZuiV6A&e?DuOs>wl#M*?ZaULRsVGMH{B)%JVOjm|1PbJ10m843K$8h zLWBON59#-?gg*^g&)x);y+*SL(nb4yK#QbLsyb^XK2X26FnaD(evVW4E{^YQ0(j%z zbTTmT^0Bnx{;Vo=gb}1#w6r(dpKpkbMQR2MmNm2f>Jr#STf|1jQI*oj14J@uw(lpY zQ)3qJDXUKRV%&pig1-PG0k-fNVy8X5@%{FWPk?H@c*`XK!|nQyhJ)RVPnW|AE9tCJ zEs-wkl>f2&v;0o&5k!q=aHpO1*H+|2~*Dkw2B1{?=R;$|D{u%drH2B zOuBRuo7Ht*wLI31ezhw2N(UxSXYA;@{a^1+JIv}>ZF~;h8^lsVY*lwE*o1~tfF~L0 z0)`&1iwb;IQ}x@|!O8f*x|z*Tt4ba0)3Ilq2wh%|B|85r7KOOR8TP-SQtE%Z$+Ecx z-W$P2npm3uULqt7I6uWXbql{$;xJ4^OUBr`e-9 z7HFrn$vgmEzD8KOfJi`Yj1w8%@(f%Qf+Q!IF7_2*_l$FdC2i!QJ;Cx80bQBFNnozV zEQe}4+Tq>91JC+D(Y?P zL+caw7|GQ+%eNgTnu$;>e6PI)e zv0|xQ#0k7LxWy!@k+sDoX1lBV^d2waEZzHR+s{aq>S*{;r=b7)4_te!4t4EOw4*aU z7zB?lIF1u`G9D7iM4dy6%Gsj-3ClDWOVnJk> z(4paeir(v*lXl(?u`U$kIX+(7xb92POH1t2>FuEk6S|1r5SN=umb}FXj|ljV5H1v@ zTnHtbxaL^we-Vc1P|SMe7Y8v^YgK*rm2Fu0<6u4Z4`E(1R1NH) zypD3uj;~AWuhO7HJC&g`V$!k4Rw+#(HH$Z|2MV9+WSc$`@sL)h51Mp{-m~Vj+hCUd zo0mC_qxRzWLh(8u7Q;Y?+nBYRA^-u1MiF@gXc{uXW4|kJ9v&;V$Ysr&57i@2VH-`da1sB4_xiG=Mo8)hI1&-Ne{HYI;Hv2z2 zjzFsvijGwB5oSqyw1201F~+(NmWZ%s@HYJwJ1B3os+X~FJv3pocfgDN&@kIw(!~3ULexJ&V+7EGR69`|K_P}3lEf6bJ?DK%DIh*#+k=bIikPGcYam- zX0l2f;1=8fZK&A`eONXQbIn?veF8cwF)DD6=$G#CF2mCGWX7ig_eMHl@fxS0!%r>x z5h5WWdD}s>z~BGoy{atCS6+6XOmak6{>3l{1kQ9vmNc@IK>c$UhSPOFRCP51ieDGb zoyRazv|H}o6dWeqa87CmOg#{6kD&u;f1z z4TQRxZKioi0`&jK*qg^g*}wnaS9M8OLM1yhl}kjn?7}2PNo0wTktLF449PZTlA>%= z_9By1_I+O`S;jtD$G#25&KQFkX56Q`uFv=L``*9D{kR|JKlA5o=leX4*YSEik1q#6 zoLE_$Zzsx1Ab|F!B5N;FHm&1URZ9(2*kt_KUxjkILbcbBmiVpfBS|kd%cI^|i*PdP zrP~MQNjGHWY<)iESLNj%$Q3z%{xAVQ_s`3<(mP9O{?Y$UMp4mM)P+A7Zp}us<@I$aet2dJ`OAmLUE8X!RNLNo z^o7f}4A%dBD)3-LX@PT}*rUXrQ|Sl%y??aNrdUSgAAV8g7x2Yfa2Y}`>}=?ti|j-6 z8u6ykv{3mruXSv5?z-i6aQxzXXZI&#aNjbJWf;8i)UJuJur|mEm&7TF8OQDdF z9#1~jJ0tIR@tHT(*H@IC_Z{D;Vi$v~c@>CrQ5$JkWRi&x2p@tpx}<*p(uqj599mqh zV`xi7cN#}!@6}tPi-e_l|DqkIwT=X66_txADO%On##uWfbIJiVGW1P-FWI{u>~E1! zKcSrR2Y!#Rm_%RoNjZ(BjDAalptGHuU9g0RwK6Qo2T>h#a^?Sw2_b@VqvKDT=osR|i2o*22JTDCZ`;TAxw;2@@Q3?|dtFDk#U zuZd~vziFzL<8xc0dQrZM^ADAH`S%O=mXIqj19JI;gkz3*+v-B(Qv|+p&yPe2*dR^( z(@)-%^;hFf~mQ?HoT-Ft|A&;BT4WZ|T zMw^fCz#L^n8DXOLcjiAEpwM$)#4R7&HxOVz0V(nc26e7Qm&fQkKwckpnHd);rqEcriv zg3MU2V_h(W-l4hqI8)N8!=pAY-}}##(r;B@1-7L`p%lm%D$XMo$Iu&EU*mc#KeJ9_ zc?^2d1wJ;veGbPQ*dNd6L6^mu$6-0PXa%|Ema`!Kr4{Zg(!B!tG*&8YPpf>ZQL2vA z_x1mHHwZBI`XV;&&M(>a7mbSn&Q<>gwf>)(qU7w#QA%8hv>RLc<_m(<5t2}HjLjr{ z)ik!ax2}2+BV1tKCZ+waRRQp;P92jS!+fwlWeXc^*zq%PYw`+G=i#Ys31ju{@Y6WALV9?Pw$_{I2zIdcJ^u%ZFQ<;#}Q|N znNxSzpeuB6Vzn7zm(s^HL3;LvoCDoA)0Mqc_bwjcn{iZ+ciHYq38#=;Aki?Sf)=yo z9jqfr&hFrQ{4DWZ>K|wWcPCl$OY6;2jp;-}nh^KJ>%x|wywumg7ebj4d6PG$EbzM) zfATVBu|`j(S$A@y6amJtO zf|RGd$RbW2#+>c79QM|3q|xa zY$96B#KM6)Fk7yE^7RjG=g$#~bQzczYq6J0T85>CzEz2n}yv-M6|kF=l8xXMnu#(NmX$;s=ws;3`O* z5zY{S7i-fxX2oaQCX=+Ers3qUJK13_C|;#%U_XE07kwDKn6Qicps8b-y=C}NA0r@> zRFkapR*S zUAa8EF~?$+87gNh)=d!lGZ)alM;R=Rp^v#x8_Kw3nFsFqjYNteu7MVT>=$pA;_%Q; zS;xMx8bkacM6qg=2@sp6kekVrCQ-E_3CyO8kj?VBE0f!I8B4hIXEmk|%9LAMafqgN z+K3S4l_EZ4La(lvj4XN}`r6s3*`>&Q_I7f^43h+uMc(VC!u=ne2Ywqzv$g+Y9Q7qk z=K~#Je0`>>hdhT+dr7+-dk(*DU=&?w7rh-)xBlL189Xt>Og$7yNl^M8PR`vJvBQx3 z>376Vx=rP6M9II4>b-`$2Q2X3uIr`kK1He|m-7O3?0O9{AV;?&V^CNjPm*W>_|z*Q z@|gaRornZdD1*U9zwNi#k)X|Vq>?Z9=xj}sC+Qx^Jv)&R{+Dh1@)yZ-pr1|}Cm0rO zHk&i-2{(4)>N))f9ebLHZ^+wJvk4o!T`;l z64WEum=K&cSOf4HE}pUTYw#7vP1z_uCc^vdQIw#-zqgY3k{=;}LveZRSvwG@SMiZQ zP(*k9X=Z@;{~k(^sRmJr|C-Jou(yn(gbsQ?H)9% zdW6C}jDh1;;Vg0WEe+No>KC-cpK6Vs{Sfu#BlunF?hC2g^T#A> ze;NtA$vP#H3R8Y*f4bSL?O5aYJiZM+Be633eAf$wKO(u^`_|Vlb&?xzdR%Wt$u7hu zDnC|#EV$+CETR5(*8uZPtNmFCe$gJnY{J6`>+JB!;1c zCC`o2<8Y#Lk%Ha%XNJ1iND-T`tATgJ&8W36?z=y5o*QC}boDw%aWZP{K7J0yv^c$) zQWveu@xl9KVNYz4H&ir&F6X&j{#Kikjth7sYAzjEC$kYyc&ryur84d zy4t0A%cN;5u9WG>km5X}O0hvoMn{bz#eqk;o7Jb{tOh6PWm3=+SYsco@#)ZyxowH` zBRk{ODt|-_?y=mfAwS5XH;;e%^KcUQjNu(va{|N`N4(d#bLK18sX4NPrmz83BuZji z=DxWmDL7R|>uzlhHJ_2CQ5oS_E9HtzU+84yrIr=)$PkH+w5I#Qp96}uP?&&+X~UF7 zhXf0w0IS7xHy5AUbXX}d^hou~VSd|BHp3-mEc*h%O?QL60+!TRw-1KR0oee=_3~Gv z-|ojlg5VNxWf=j5!CbkH-CaPbm0+mjsk1S2q7_>AmBIyN<7S^VWH)bTqkP}s&s(ooJU}?7N7EE#Emg@UnCyDJ;w5~gdKpCy$xg4` ztRxXBJThO;Jckb;h2OSUfqwaN@M^I%s5f`#U;N*R@glQ--KD>x_M8Z8z}5hW z(SyKeN>%?)rH4MgoOT%cO1Set2>3-Dh)jDOFN9K<$i^$i;%|L642Fi~^P^?Qm-DIF z8~opn+^N3-O_*Ljz2;@M%j(5Ey+z95)kfN=yabm?BIQCuQ_~Kc-+TVP1ys;4osjN` zYC7-p=iY}ak4m>KD~71&|B7%#-DB#jTU%V*!nCeYZ&zFGRGObepcX2hA)yS7bi|*T zK<|LoaPfQ)&&(bzCVWU}%^lO7NcIk}UNf$wj=%Utb2T5i*BiK?e#`2srR9tK)LuKG z^4GxE_@QLh-5#F)=fnk>kuDZ5DTzLLR6 z7p5e2Km3djagbNr7~>3s)}NC47-Kl@+;RVIa|0sg5`63C9cZMLb^R0}QP5#9UxhZU zq{_IKeV39DdsQTOKt^t>5f*;GT;|(!#{G^~>{6Wfxpx4aF}KxN4J6lxaW(U zSV^ml^3Sf`yW!r-RKI(%Q*GTG6|?W3O*xHweL%iBOZ(<5Mw?cISjDRB!2G6HWr8Cj zM9({413PbVYHX0}(}%1dQk)bbzKu-^wvWF|ns|85yuqffAXVplz3b*_ePvSGibvq_ zcE^h6O1mOD$51+QSJt&9a=ay;k}*20S)GNc*TVPu%oUoKPiOfKVtjYJhw~4&M?9`@ zKUXl$6+>h%mrwS)50dMg@vz))^?1xKQ*_Gh(?Z5d(KJ^AUDvsw_*J3E{`|Eey(c-9 zg8CG>S1CeDjK=AX0_ydzk#BBAU0i>mq_M<#X11U=gM7UKdNd3(DV99_v_?kH{(Ebv z|2($Pc^y|JakunXZhyFM^bobIfcA0PoZ&gmeujPLtz-G*&Q+ZZZ|wDG;#czxIMhQd zLQ}dwRIbg69HcgZ{9ni>#@LE2mdqPv7ze(D<!%L!ap5%v0YB*;};+K6teVz18U_MOo zAJf*bI~)%O5q~2?sOk!u;%Q%+5C6D*7jW<#q&gGT+=;u(I0%DXG|Xx_~@Y3$GCE5PO9L1_)O{p^B|Hz_0%c*?RB8yL56&#(DeaO2mW8?aOuO|F?aV_fy>UnNwayw5vql9Q(@(jc~+( z96kK($me7e#wJOIbDtS%$hkke&m=bNQ!)G0DKK*zqP_>-U&5;Ik@iH0<8adOCoYTqmZ>(6)OMRAh*R49L90Y$yOI(OgVk)=o|^N{$I}Hz_fNg7kVYD-+s_kJq5w;dh!= z(44P9Zr|rztFadDbhRKs;6nVnq#)U_uUIj3#E{5oJ=zKs)^zh5JL zX4!NsYi~;>YPlj(C4rf(UP!$<45I%FX$9!)v*MZdk?I4FT- zAnAphz?-T#rX00uKRK*5-rfuOS{`cgehLh!eu1pa zh?d$}BJ70(g1Y?jgXgc*<(3V#$DG5(C_qeOj@)`6asfs(V1#$Ln9{?N)VuMQR9Ws9 z_Yw;bsy46NX>AkAWAy^-Rmk!HgB0pW&l=K8GiQp%ag{;$g$>mvgH5h_KbQ-`l=|v0 zi);)sn+=K1mc(LE`C#`YWdn$YCdq9@2<8%iaC^kYbwo)6bqyj*iKdllqUkDIm&66V z3LY)pO}Q!W>BS`TN14l5r3s;?6q^ep!4uqZ-h%01SE6~9uhU2_{y*Cg<9nrPR(uUF)W zuPR1ZpHJxX9m<7gU0W?TE8n#9T2=XJfoSmOmQe9HC-cQCAULh~X!gHeQj9i>`nht? z+}|EKEHU-+qnkvU?-u3GDX}C z1tS?d>)8hf;v)*o_saZonb2sw#&I58_s2O&z@X%f}LkA+nZi7m0_pmvmy2a^V z#UWu-lB@QM+x<6{MoJ!er#qbdpAkMTrK*%wGht1&{Lz0A6pVt_fYO&yUX=MSi;I=P zL|cL=RrESV0ClSfqNKu|O=4;E&z(o%2GwWMx5)m^ET%RIJ3Z37Jruo4?tB09lcm-i z>Evvb=fYxY+&%vchdt$6^x-zEv?onuRg;ouZwrk*a$wRX-54e7!=5;2fG0BHl%$#x z2$<^ub}Vi$c-%oIAnsnPeO0;5YexebUhc&|ZML7Z<4nK^!enPWBx+<0xsOKejLisi zsw_D)ED_dUqoqx8=01zOe%b=Q_y5q_Kcnn-Qo6(^u$>b8SJ+`nv` z*Ob}!spn}I^;e5!3a-38x}YeuR4sS(AJ3?R+La?U6%gV6FyyHFv9ya(?;?D^9^0>l zI^I_&acvyR)=unUbAQ}RmYO=8#)q-Gxia5!U-j;Y%UEa1!_IWOz1?~zSGfV3%>^EW zY}s0+_UEI2VO_-`4u7YSlCS7=@14|>7Mge#E?^k1$jwoZczn0jfS)dz$RGBdA{AR$ znu?W3Sj_-aEKg({5zEt@$c0rFmXcH;pfp)Am*5oNC4>QQ?SAJ)<+jhGiy1%Du!TNp zE^_13haj`JY7ib`j0ev}KucVPQB%u7S}gQ4`p6RobE^5;15Vk<5k%I77&%OjdqtwG zG<*UkL`A1meD$cR->a3#$5hM;hhxX2VY3IC5ZYs`wY&^Tu-E^ODO7VXMjZwBZvc@?b{eJ~I0J{? zuaDYv)V{A-w0YyrY%M&3qBRM?XHL$AUkwrV2y-d`;~0n9qoRaFnOY!P6yL7fv=>N- zIkz*mNF(3Jbc3jnAZ{6(zSDp`G-q!_2BFDVQV;w?^N}I#0hbMud>fBXlo|NWv7-^I z*6e%NP{-*=45xp*AXmaPY0ovVu0r36nswj^?cPcN5U7s|_Dag!VIPFE+UctVNBrLq zj~pE!)}46R4MAu=2uPL+ZUcdORS$#4G>}D*y`~Ul>b^{kz~+%^RUa%$JZNb5h^Ug_ zn#2U}7A-r}UM=V`sTT;HbBtO|#gIml%`s|V$jp^Y^h9$m+PtH6(Av0;H+lC8#l}sv zj>eSvcpqE^#OUKUSW1WJ@X58WAR@KHxv|}M7x}>8P_15<<$$B%!^Qg8>AHF+YqqI+ z;Z2BU9ct%OXeyW%ZRI1r)o`-SJ$SS?cIlMDz%!eHLV6~1=vKbqmW_$Qavx!vvffmB zgeh+Mb`%k_<00&pvCR|J4xk8)XMUz}?(tANC#n8gd%6gd($zXBm2M>r44%2S6h;BE zK5N$_JhiF@Gzy#CbIAq_v_es}HxJ<%J`ZNSM$d!j7(l6a6JoV7cld1*O+Hy;9k{{! z`Mt7V$_#!4_y=bl?qs)Z%A{a0PYtLf2=fedBRJ3cawPXI2W08$ z>#<+L*){vfROG=F>m(q6ve@^3N2!8U%)Vzrw@uJBpnwtIz)rdRbUKrbV9p)NQ{r8@ zJwZoKvCG1%nhvW^^N^Z0w6N^K)klSU0tV=!SBTW50&9kp?8l$3G@4+oQlY6fBnJ{u z`|+7rk_lhz$V%>vbZ`8=1WF^BaD324oDHIuNIGJrvg9KT()`%=0FESWD$foYJ+u{@ zmIUl+rLxZmdy?xp1Y>DxA~vBdTQgCh2jkyUCQA)hYAk-W#GD1zOwE$kX9SP@aeVe) zbV5NDYHzL_yAyEnt|-VsR=9lXJp`x|$8;A{VRZ{F+gms7ldK2+%O^UJ``^TCR$v%* zqA1DeYm;poqNwqw5M|PCs~C}_7rozL&>!>O<1TNSR? z*n4@i{a(1->0Or7Ei2NJQ&A$>rjW0fPW>q%T4dMdn4;I4BA-=q|6?+jt4o8Ur$d9B zw8N@`v3tiC8~2|c!J?Wisi$trN4%O#=(_vKPlQEnSOps?HR?f`%hptAkhw-#7vtRU zFh1I(1>TlQQn_%GXlw}o2E)Zd&tuKLk03;Zhb5dV^GK$lOX#D)SNJOb$t46GDBg4V-SYR>}_ zss_$RT)+kHP#drx)e9z8TEe^+h(k-u%p_BIKe8|BSTst)kWh=Owj?>iq$a*H<00%9 zcDZ$5-V5l9ne&UDH(n0Jf`SI*;1%2`5GmuV+KsyJ149L3&wRrJzpLTXyL=vitqC5p z1%&wzyKK|#7DbM-qJg03r(IelW5xh6?Sj`e%|x-kLxK<6b;91JeVVp{E_6VjVRSm@ za63YTXm`v+pT#OnA%Bm>><^^0;zb;9l*Y%kLd6OAJy)jchlvc$Jgz`9U|H+W5&{mk zkzZfc9JPBdz6!alTj#JQ{1a0z;$ zU;T}+-f*J#we)%Zh7^2endAKb;R$U#?i)I>2>m|=y^f&gX#<%eh<|#0%?Dd*os55M zqkGNUTD7{n%XSCq!2jBLb&yB8G(?85?$+m3v=B~2ZEaMgcj>8q>UrAvBEObc7nww* zy&V-}gq`DDKy-r%UdW&MXExe@cHa=*s90WFCCL$%RIu$f-`ttp+R${!#J(D;%*Apg z38gv<;Qqu;LEbxz_Mvx6?_yZTS-o#;)bY?}ctv$3YF^8?Q*?lpT0dw!oE zeT)bARR;n5|1yN89X9=hF?aqrgM^+G*Uh{8SOIbuo&h`sNK!PRx^;pDI zS;qECY|mT%(;Cd}pPug?Ke=Lm5#5?(Qg%h|mE=8#Lgy28-}KTlIp;o?n9&AgX1gz9 z!rPmc367S_kjY@SCNuZv@6uOTT$=EY)GR|@jU$Z5lUeNVubfHGJ)xr{!hfcTu#b3L z)~nA`srmG)^{2$Jn)8^BAoa`GLk;1a^h>b2iR(AC>$Ic{Zv^Us&hl)Yi+XiogqCvG zrx@&70)K-&_NVO)F3}5?jgAoGV<^GRcHXg^Q_2PE3keLWWC^BU839kdj$Ov&=^}O6ZTr72S<}#We1(P+`)mVm`>;BC73C_9T=fgt-A#(sbD!gDr{~>}!nY zaB9jU;|;9LSg&N30x+p4Y5yYwc2R3QcVJ(xT}iE9K^E$zWU_WvrKB$?(?jau!?%#- zF1vA6QLX6-FQCnYdZ)R1PVPX({$n51?MF*anyM_H{XQ=0iX9mF;e4DO-`?adtNoT~ z5n)&6YaokAd@`kH(>eP{PxJ4mf?w-yV?83+KRAExUE_HyVh@#bJXO{FfF^yP%zp** z-IIHy@)P|>DWw_yP;MUE$hLnt-hDpwkl>F*;qUE7S&?76B)@?+T6}_awu4|bcRTO# zi=9uN3|0OcW4u|mBS>EqX7CO#w>{f{8(gdgtpfSsfr&4-AKdI0KyrV$qS=4tI8yJg z3cA>r)4U#i@^$)2fwXjge%3SKTW;m!lZBzZ(RGi$;E%{ErQTmvS@Chj&hWv1%1z&j zh~w6gTND$`ko$NfZGyG~hX1LPIHdN4f^ zUo70R2fe+jvIR_Pl`a6QlmJUQ!N^2z#)0 zida&d4~(aCVbw8R019BtK-jyy&l=n~gt0&a=V6Yk4 zx5C#tfjE6a5pO2PiEt02MMIv}bm8b-YriD{;Pmbvl&Fx4Qq0@f1?Z>~HgWj}bkw#k z@}?iKKZTn-RFS<09C3+r6EbqA-*IYd)k;CW0>gJ;BcjDVD4!NHw^M5^Y)8LDnOBIi z`o~^c)zzG@YtNQJjYjREKOB8D1m+o*<5O_ObZ5q`lwo2$udB6K)nol2R z3qKKZ=zXfyo+jJf=ewTqM2(>JGd7tR-na8PA?|71NihFg?zOiF-Wr}7cB>`iiLl}} zG?3Bvj`fx#(Ubtgg={?G8R7S)F^zz^B)u!s3Mrf9rKw0|ipy0ykAGPJFcH2H~A5-@x3U095;)SryvJNt$TqG5sM4D}OL^b27e_@a0 zZ6p<}Joqfd!Q}B0vI4xu=iQ%uB)vh#LAbIJy3TQG(iHRi^}6MJ$tAlz ziHRB8Mhmw|E&*D%EZM%`182K=l4V&xwI{%hWe8FyYLv_nQa?`cor%!N9;Mxf)whKs z*;X0yM7BYOga+BGzAFU?J9TpU(B|xh#~S9$1#V?$(*g$fqy)S|&TgV^GuvlP-gJq?y^LujaX9LyYii32TZco;rhe54l! z2P~#src95b3PmMt?FQ}_v#-A+?9NV{YN+Ox-yGG6W(?5m&~wi%;#0!)d8aRPz#Akr z8DoIzXeWOfRk$N5Y6RLlB> z;G{NY)8m>PtL-If_hrZk*_+Evt8kkP@P$Gxh*ff-8UR-H*{ zA~#!?3HH2M!Tl8}olQHnMY=#dDxcq$TS-qP8}&OVTWSoJD-qJj?urZ2w|{b=}y!#6N?LE)38!f*sd>g zJdx5U^xlhcZuM;6Y26u`eWKdAR`2q+V%`XXNxrw{4`3s#4M-mBU*imc{jg*A^m;%5 zGE4)^IYTu`*PW3=XY&nr_07+N($Lm1nu#`Fk zT>$om*$&y?+cex?Nl(Wz+tbqxF&nz~eiAFrp~06oz?(|I8jxr66U-G(^0Rp`DtFiI z>AM)#uX1>5m-^Oo51$D2FdW(Pq0#3EU>Wz&_TpyxZko|6Avc*-36$_M(b_7%q|MrS zN%$XAYK-RihvM?)(WEuiQd0T<)Vev}w}G0Lu?egF2FMY_$l;- z%gG|$J4rf+iZ#1iUX_KK&<{@pa7eRw++rS14YavX^L4a6t8MM2Vw7rZ0JB3G_|;_$ zi865JII;r!C38kb!QCKQ778j)@(^6iC36Hg&9w6vtKCM$JA=>?3gf77#x-L)i_)Np8r}lLTN4<lHUA%RZch{1S z*7q7T5(}+O*(AQW7vWS~9ID|OZysU3?4cFG_B4E)U!T9?I}c+9b6J`p{gAVmO5od9 zzh7t0x>M?&AZO1*%&kaZTAoEKg7(VD&}RGk<5$~l(KH!8x8CtG#MgY}PRj~~o8BpG zEwtYTdOo=cIeJu28!})nCAlWXP^mx9c`1d!Dit+CiZE2)94**=AVkijc2VIen!3FVdUJ$nK%17?Y*5H0vwIJ3jTc2DBUOKFkXwqdrlYIiUA&aO z=A0yS{C-LE;20X_rTL)6M{!(q!rf5xO;G~*KHwiXBkxKi4-G>(mc;Yv`N0Xx_XysI zH`$2k#G@x*eM@qSsU9MCzbyfdd*qo8$H&ebqY-cH<(EA#9dT*S!RO!~plXyh*)ghK zjGe9lPWHz_=+Z-){1XybnJsuu`h@2n-Abc?D0`s?ZaEJ;{&~D~Y}KLLVGjHEG|5#^ z^j&nKQK8!WTJGiXN#F#_L!oGEy1T&iaC5?`r=|a_={B|6cQKoXRTcUg8{ED#OHEhe zXz4 z2ImVd-@qK1$F0VUPGdf|Z59p8@CB9SA8B0LanH1wxfyzTe_Ixb&(sy{jPdWI>*wf| zQqMgxo5Pn6gsu!-R)*gNit*ZlhC!g1<02`*YxD9$@#r-w_8yeF4euf?0TrscJNI3! zjUA~HMQmht4H3DQ&JEMXwuw0&DYe1grqbG|B2DtZ`@kzs z$o%3d>>rV*A*Ni&r^)96nRQ>Z>@$VSXQ^OvWeT@oM<3f)7Ugud=C_)hp_x}*lE_x# z0vm4z7G6WTzh8>6!`Z#%b-m?i44LiLuTI&vog@DD~M>X$c zA?!F)GR#ejp~CI;6+u_X=CkM*R_CEp3I1wK35-hF+uOBKCTMj`z=ZEcz92)mf3Yy~ zUg0sxe0=f%eyLdL`S@WahB>Zrg-3AG%7ar`KY25M&7w<;SHjM6ZBuXR#)+Y`8gD*%f<8Ikq)XrKuBPyp<~==z+bl7YjZyny;RY ztSh6p9|##8w^~#r=irSGb)qL2z&aO~;+I_Y9X#_DC-8gkKEM_=jX47;cXaC#bOqY= zEl|IS)dxqp>$Xl^!a+j+(R{@okkAi#%;#W!Q7pex?Dh%Kfxk9F^3!`d_HCVtF+z_S?;_XS1 zRQjoMxt!Vze&X7C@vG$%y4_#9G-t#cSuWnIB?S+Tohsjt4xrz@LXK<-m)dJ_dVoK5 zQ*uWf`S$w*g&e=`rZ+E0lW!h|)(79H)cJ~H%AW7}viRlh>G0Fr=Y0Tehr|AF(1YlY z%TvlXgPrHZs$OQAp2(<{(nYWxVG-oYwTeAez54NTNZ0b|$a4`p<>hzBjYV_G?0o6= zpdal=EN%xLWwhkmqcOxz|LU&yGFv%5@}eNdZdRy;2H4Ya#-}TN&gs34{l`p0Gy$HM zv7t-+JpN6XVABV)+rIz=^&-eS(+mC&aOz`tn$9))WvngQr+w!-lxogr)s*64DkC-% zQbV)8xq>}5e66ijxrTh!A^XY*dUCqm@6Wif&THkvrzhgqg;=?|hLEM5 z+pwrPz6ebrPxvx*DoxX0dov%|$JyH${L=zFQ`kRq`+IxgpFH-J27D^s4*t%Ye(V4y z@7ZH@=K;pMrAfI*OxIbX15JeZC_{dt*zA`)(nl8jEubLx6cMmaqzQy$%XTV4JET{Zqg@+nAQX_g2JtIk0K4H~Sp(u0RzcuW2(g7ck|U z*PnSA;x%0B;h!{7d^qmXI4m>Km^xr7_@Ut>lmNd{6WK4+OxEn(fwAR-5OO zJL<3dMK?gZ1pP|=D9nJ-)8dwOCwtDx^*(V*&%=FVa8=G*`J()x?wj3UcwF#E$|dA2 z)+5bY3(JPb%xzZokd48Um*b_nesG!I^#-;uOZ|4FT8CYHa$R#Ko<@B{QM&Nrxxus=eFrWj=iI=UiPJ}YzlXB@J({+$K)J$r0l`ko zBo)$`AkGM0FtMZYQGF zXg4hP6vD173ZxuGy{Wb)E@dh_lDULQ^)=Ku{nyfwCP-oVT+^pPFioVHgD;DxFVUb? zY)!#VsLiGLnxt|rE2SmgIK@jvN+F;V6_qvFUCn_c09}|nG7S*pQ>*j_v<*c@P3$Y?=VvUl;VRe> z-n?8#f1Jz#=9~z%48_wy^<6fg6Cd8Obt7kwH=<3_6CXM?m;P2MDP6wC1D8wh8&dlR2>&RZC3E)7 zFH3MJL1atVuIcYVV4o6j(?S#MG?q&5!2UTC+va1^$#RcD7-~hCoQX2A_FxqpZYyT9 z1QiFf{}Gj?oHfgxx8Zha&1v`EFV(fk8Q2bW3n(Y~qU4Fjvo0MMf(LNkk-v?o<>s?4 z%gA4f=&*VUW$G!g9Lh3qlYrIxg5CAI%$p+qw?V%a^-caa^xGSom0g>+IEjeq&*))R zP-A!Kl6tO_c&Ng?F1w{B&{LqC19M7F@4!BI&mr_51iOf-5?gc$cumUI&Hf!Wg=Qe8 zjh<6C9xXX%xLB|5i4-#M-Am)}0fMcI@XMN~BafF>og@e}+?~w>X80uHX7vQ%hiWKu zS*(dfUyrjmae`RDZ5{_X?)c;#9$Iq`2?0W-fqPHXoufv`OW&7`T`0@3-zq3Tt=9EL zZMx?gbSa4Iw$QQp-q+%zT9Qpwuu#^N37p_3)NVP}pKiXtg-f4cRNFups}uY5g^Ab9 z?1}xI%@GteJKbif^QZa=;jIE~)TN^Dr~eioZM>@+a%nr**rk`G2(S2=aIrg**@4-F zFa3BD=)RYApx4%%ppxG|>0T`ScCW74bUhh>;s_Z4(SZ)-N6Bw`eS5qaph6JTZyWd% z*nn_3mUI1?3<*Qm+GTN&>;K51y3JoarR)BYL!r&s6KySJ+?VDL^m0)^??#@;dW&zi z25L-T)gZ2E=Xe`NP=bRxWf+swtNArT+ychgu+2y-92#z*kqi>~sfA#;VkM)A@w7#0 zMlx_)K9@dc^EuaMx7d{ci$lpjtJ&Ju&v7R@vZ%oIzv*TNqwNieQMWSV(!YwbuzMyE zR3|4TP@c)NF>*$bQFw)CUA0W3`YtMZ!aM$M~&Q5pzC>)?X=Ggqn ztNYA1UhT6M##tK!d$ROLPs)+XPLJyu`QG|PkGzsfykATHd2gx@1xA}#cqZdhl2b_> zv3*t>v&RDdDxtpW-xScj+i~y3vRb9z;j;J&zr!a3jR*}bJ+Ij=O$X`jt8%|#WhSCN zKg`|X)4^}}`1OVeA!p~rE#aT;v+Hp7vu)il*iCwo z8RI_mF5Y?ImA?@fEw$y3T1--ruX|}d@rAaYPw^S$PC|wFBRAHK`@HfET=qwZyC?Q@ z>r@N78u|UpP*Ltp;9foZLv9WeOC;6?I>S35Fqz1mH=XOw`fopk+AO8FML`VFoR~m8| z7_R)Jaw9OCMQ2G|g%s(_8@|l)5$FA6sf3x$@HgTnh5ccC)`x=1G@GB<4I+K7OsnhY zZBEwOd}rhJ40!YT6!IAPIOF!#TN&($C=I#us|EQ_1SYPUZ~f?+t$E!*w4zisrIf2V z=fl`L(wSDU*UwT~XtuBQ%sm(THjY=Hajm!74L^xX*nMC8ig@9cnIP*QAhM!nL!U&Ql~l67#jJ~$bP(COygBoD<+$ZoPj+V|@v1qwKWysztD5M% z74|4Qa&moE(oRk7ie}8NhUs@McbSCWHxZCK{}PCP<{m(<@(v?rKEnG8=mOqDKy6q4 z*?g;W1xGuGqGtSuL>lg)wsaZ5(E%mY|0+12R`LS>=BU7siyFzm4yL7ol}(}s_>R*) zApwAXQent+^@z52>;$*zW9ioAQ&CYVx&$;OOAU-~r~>*{4F)x)&S9F!S}ROH0x8O- zJW1vyU~8G>>%w48mR_{UbwAMhqvZ*Kbjd}eOk^dq@%39}0BT397ff?bPRL~P*3Jh4 zm1sWh)QdXk)-hEQm>@aYgM^4aM!8fPp>fGpRDe{vrHlf~zTa1Q*7NPK0KlmO{S5=< z>0}^QYb#m=a94(@1>1*kffp?|snfHo{1+dhkKXYEU1=o8C7x4e*AV7CCga1>2)_<8 zOkG~ezU96B+;^PKV0h_H=k{VygrnbQ0`S=B!=YTzcTUGT2bgaDB+WKkud?f>l6u}m0q zn(;#V%>)bqwozzERQnop9S31BrUmYNJfZA(5%uGv^flL(%Aj)~S2zFp-9`8Uc(IIk z(eLxvmPv|uV`2g_`M%iki%%~h>ra7J<*QEQ+d4Q1OdaI|Y@=q?;NVTWeS3ZIQlWme zK>B&r+^GD#RYTp<&)@{%ACJTA{}z_n69s!eux*v#IWk~w-F;IzOO5SGq=Uv6hmLC} z(tR06!)BUPN)c2W?E7Wz!?3j<(MO-M$#7ok zv3Z4qmi#}Iy?Hd$|Nr-&RN9bKWUUmk@B7jwNs(kZC|DNn^1GnPZyQsqmFv16Wa0(6(8~vD*H13S$o)lEBLM<{g;t-xO$*;#sg?Z z4lRauxo>GBkq6GKpAWiUKf?U84(uEysKo)RZ16$C@(hE67A3WR6>>7v1eQjHwZv}> z_(mrR)>pI(koabDNYlGf%7P7Wep4$Z;t2%vg*_tMN}A?qzi%1$$Be#HPGswy}im^6L@%t{HJN(%#Nf4IhnD0^eD zk+b1oCx{48Nnj2w)7^K?_m($0!LwA;wS8+e=U8&!N-RZcZu^Mnb?BKi!ag7>RM(Gd zqU@OXcr$=PNRZgIWEj();ltS)+s-VNpoVkz-t~(*ZQ#e4!6Edm0L49>{(NxTwdaD;Zt%m+H`Fv@D_J<-nT+6ojxi~&4 zdSV_I4zc}(M;2w?z1xi{(}g=(b#6KEoag$k8+O;=Ea4~WcDt> zKC=ngWiKZYUC25RQ`(aEB zGQ@E>zomTJ>2r3IqU#J_odrCvK8>ASQ?Q1-@uGkL0ggY&m}+{&xUEje4;y#cjECaa=eqiXp~$A^|EoCyE@w-edtHoZ@Phv_+pBd;p)58?swbmQhy>n8JN_Tl_8NWW!aD!0Df$9Jp3BDsXfpiZB$N2jgx0?*Lb z##cW*Uky~urS%#U(~z!9-{*NtnhX}A!P(oXZ)o+#mL>dFBVQ~6g3z(v89xNc4?jeb zyr;^0p1?*A`sEWs1m%*XM%IPI(tMw#5d^PTI;mXm*9@s7&vR zYX*l$EYrs8{@!8-w8q=MH={))b7?_0iPnGCqJZz6r`Es~s51LKh@glL50JfD0V?}S z7|_5j^1m;zMMsG!h1LRCTY|Q6UtP=(^9P&$dAAZo=BVc}tJJT`H(?x~*x7HusO$8_ zV1u+d==pXc+z_n6jzbWn{I=QKJq;R(lkV6*i0F|kYbtAbo8)eU@g7nBP4osXg@D6> zn+n>dNU^~3#+F(_emCr`;k0+JaBAG>iKHZc?x>%gQA( z0*In-jxamb@}mv|bJXuX8eWIt~VnTvaVK1A2XdzjA+nKrTLr z&VE@w6Epvx5y@lchJZ1c9|%I)UqjD|hJq`=&o zB^cPRT>%9^D$3W#{w_N?MoU8cUX zr5o?hrgRmjWLz=B05Gig@gPH<4VO^lVSQOyz*I(=h%*;JS3g7yXnlTrNy@{Ojt;Hu z9q1qEJG)`7_*$_275O#lOLmjf?Mnxht6Fo`O+(L%qPZO!=b4$=OYq4=b$BOo-4Z|0khkgfB0NUHG; zM}^ivsSyg;E2EEksws@kOwY#|KBViR+8MXklq%a-&0my%e<9!$y8@w7R=y|7= zEx!64qE8m;NzGGP-_uiWY3>rUoWEXLV{|2`j$mfpxB3n|zcTJPL_ey!kzl-a+BimH z^_*~K_i$eyoDNYTq|thKJc@P2I4~P6ua`yi-qJXP;I`41z$*EAO!u;$rOa0uIT(9K zJ&ER=pw0z| z-W7^8nFD|Lp2h+?%&o-W=8U=oi=4=8*JqMv_#lR=$#eUNCNAWArrh0I6=*GzdA}xf z(AB&)gK@(OHmW4JGw`^<6BTky+|@f&ifC_=KW+B;8Qp_=xLA=eS&dvydxT?U6o@)?Qgn=Le&4v0ChE2I`L$J`aJD=+hvT#<-c6y_R-qwIh&O~W>*2SGR40X|X6K%_`OyEvh ziQlrcO?2V0>}&cw){M@+dGDn>&Irf=-F8?a+fzHfSh|b_6yN_t`pNYs?fiYH6@S1J zPC;(vRQ!WN7Hf@{>@P^du4d>8w8F0sqg~%t0^dhY-=9E{SA6*(+qr?foWj3}Xo7QI zvbZ_{s4m)v1-k*!i7#t7pLqyF&e<4$es3zC<_%bB1hPH`(X8fNgT_C;7y^PeTO*Vf z_4vPBzG-87dh_6om8uOc79G{{`eO%!bc0(3JewF!-n02VKyfj3t61uQ2meAa)MPza zAk?yaC7QxjR6L$dt)_$$pk7~!Bi*uRH&mBx_in!iKD#z4l$`v$SA8%U8RnyjX zpTCV<%lY{GOq5|#kv5RsLJ+b#ZyFP7`AxdAALk0a0HjH%3T|ZQw=|jA-LV^+Ef@L9 zF0E{)U}GN&T-uFJemdLlF+c3_=NgZbOIJXztPoE1J0-f*n(c0%oFw`KPc#J3jxVIT+4PVJ?0)tLZcn)w?4cUQdA@2>c+y{|x5 z{6^b=_?oSj2~<7vK~UY_YQgNAM}k$vvk?=_xb-xY7)<|OHl7MheESGLQ6~L^2XOPG z#_eyrj2roIii~G2d2>!P-~U`;H=B1IMN>cWp#Zsf|Cflz7_RNaCv7a7g_HWmeuH2W zneQZ4?(+4)cq!GA?@72gLxcH=t#lOG_+66oz_W(=RNMDfYo-A|SMxW%Gis-Sa6eSp zD!9$zr8ixp)ndcB+@7>e9RYf<%zhmWPp!{5yLZ$<%g6umi{Qc8`|xUq{zn^#$Vp`1 zbFuz;U^gh;8+a_!^P)2i^;LTgX(4jtR~k(Wr5#hsRnCz`^R=9d03_z3-_$SRYR>0; zZe|By$z5^MIB`U_Gg?kSUPX@maJC~OkdD6p)iR;h}kLD2> zgyROkM?5H)lxaEn^POq7fyf>fw;`aP=t<060e@-lyQ)TxF4?#vN6U zQ@-du@L#tv6rsTttyHRJ&?f&HkNgw@`MrD(r0*@>_C9M<^BcakItJCLBaa%ET`~1< z8A8(dvq<^nt(`gCRgTml&kS2ZukW>`u+A9o&+e+AViW|V-_Nso&-9qZ3Dv!O!?o?N0+I$Z&@H} zH45g`u{~xD0n~PAi0?+{LrI$Hw)@+-r-&hDh`Y&Z>7=Iv;p5|DFF+0Nx%SMrbGp4a zJ9eEhy|-#FcaVh8S9hi&zmi_O7&bzJ6H6nW0$G`!wVFE%1H$rn)5Bc#jRnxnheGotw0X_8s5%!2p1H0P|CzT!=*cz_2A)=>c5ge&ZVu%)1uZpchidgp4ZJeE;)BMdW@)PF`3T4ZPeJGHcU4ApteQ`%kTN)Iz z9B31UtBopek%U`q+jyY=*}oz({3nfgcy6|er!Ljse8{7_)NA;ee00=xv9}1PkWWm3 zrAs-wJgpVL@7@2;bC#Rcqm3X5fN%Vdir1UX<}v1Rc8Br2raxyVCNqK8>w?UDahN!k zCKw-ZZwGL>4>xE{m6Gn}P6#>HX8h@GhxK`(i3Ir}2+)qx4s>XC0Q;JM3^xCtpu?{_ zxi2vne`tP^Jy)Wc)~6o0cKG?#dLV#cpu+i@37=uSG|btuQ4J zu{l5}=Q-Q?UIRYlWsJP6KP@64zo?vRrnQB|v`$tFQ`xTk=U}%4jyySU#XSH00e*j1 z9WZEWuSn3eno8OKPi1cmHv;Qo1+Otd{K?Gd5~*rT8$PEvcFl3~{xsygniOiUNszI% z6X~je%a%Cj8O+Nb*r#*zXQ)quy#>|`7X7h3+(Fn`sBQ#2Kl4UiS5CT7A4!WWgb-)X?)Ef*8C~92jy)(nsqR1 zd$+|WmxRxqUXY*V3W$YG)tth~v^(;#Q)m27a(L?&v@+Y-$)-=dcRaw#bA7JSH1I0> z%5{cDE1T?d zUdXVpQXQ%mrGjjo{mKrW^$|kW)y=*#K34e#QK3X;_jr1LJ~xx=~A0CUYHhz`|Y$C4ILizY^_ ztQypDAX!hLwk$1GLCWhJ2fP-*U5qlC(Is2K=Z3Z;zVhALM~!-%-XR{VrMESC9#3h= zU9s61o=uAO(L?2UKU6pGJ6Uz$gjL{?=4ZxQXRdipy_+0nr?7h-SD7i^uKIXxlK#^F zvCoR#=h4XBv9~y80i@pvDM|)y1jXh#Y6GpeBWkw^VL6O3oAgSf7iZTN7|jAC{$(`V zi{`4&yEK;MIMCz)J{IS0uZBK!fp1|(Eh`K^(?QDJ!*fdzgcuvCezjT~+b@Ck8t5u2 zG9FP5re+)PC02nRllOoij@eX_cd~vh7Y$&fRW&CVKWdgxY_?Af#|5@``#|xA6Hk0| zhaLWOq*uAOfB=*uYi~GQ0+kkS!HITnnlnE#7G;635L8-o_tNSluiJF7++R=;aOI@_ z1vMiF6~V1)DUvLwp53&W<9E8mpA`}V*v4h?Y^^vC!RsS{a#T|=EHohWR=M_`#Y9PgBUFJ$=*B$PCxbU&~h^G)h%PUv24ui8WWI zDSdq;J}zBh>%lEQi&8{^^BDufC2H#wG&j0pt#dy=9I$c!;RL;YbAqscIKg|WhimGz zhJcwa)avk&J;Lvnbh?A?wBI(6d}*)b2ev{^WX|o%3Y=YQLGyk#Rz-%j=%4wrX{u%5 zC5;8HvUw_#m!9E8o`?sxaOR%=fPOaQG=zIK9}MTx2qI9dfj8N|p%Dzx3imZvK#3~?EZ3a9ztbsK01!sEA^-W*0V%TTKM>y&O{w7~LN1&!dJ4WN zK1bGIBHc@aY1c}+JJpYq*K6u-`T0X@)(lUB-uu$_z^J1sZC0-RL+hnDi_q~^O)5Cb zCbV|@)ikP)YWucX!3gZUD9w$aIyKn${k2BO*hN zJoie;bw+uzPFj5G6&mQ6WDwWR2;^#W4g8Nv+mm(*bMTSBk4GnUjsJ3sFg_{YYPwK; z*I%U-uuVU(w;6HX4G`-B%5{|Xe!fx;zo^{mK{&Nhh7XN5KaoGW>LpWFF@Rus>5}7M zRr3m{)ah>0_@lAi{i``kTC3Om2~%D8s@~DJl{FCN(??@0LAMdXV8&@JaKh}-9oN0X zFv{oqJEg44%ZhnZee2f{UmvAk_7@I6vpKv_dO?Z(*=>m=a8Z$;74CJ~6PeH>s6}i7 zADrl46{V9sy6Ph+AknTUMjA>I_2VoPIC=?|^YXl%RghF>QnCpJZ7{EGZ@)^HVA6V?eTH?5$`}|8-aSur60Y>xmaB9Toitz-kcZP%v0|7 z;AAC%a`HP6Yh+~hHk}@~Ci$XOwp*HD^QC-tO|xEjj_cS|rGP5Va{^MkE$iQFu;&TPbn4R6HAfE_eXL|r-_ z{Wfn*tSdT+l=v7e{O5m_qtnE0zY`FzAtR^v4Vl#OWuhUw-9aJ z2pl%l)RR&Bp!N15cmgmhn9wBsZHM)Oy7A97g|jc^QNVCCt}`F_kd4cV&GqNsG8K;B+YcNV0SqWx|3Dxor~WTEZ$c3Q zj)uDI!+Ddx->ClwoL2$ifajFQ0L?qU4U`$E+g5v4#;!-yreX2S7GBD!)#W&G@+$rB411xoipPuB5W&c(xvF#{HSb!#S&T{JO zI|Y>ym%!gxo;K-otLxhxBXD$6^-y`?=`SC2%~?4wa90ay(+-JHz`w#{3|zDcGWG%E z*@@JOgq%tFOqZXU2?6%EpEc-f?UK4KO2n9(z9~wfo%W>>W^J(BEf!>5hYUx(8}B?_ zKRz^*&VSI-*)+Jo&H6Ky5tNcJvuhfuw>V4*Q_&S)nRaC$_?(k_>#(8q`R?1A8{khe4u{=0>feWXW<+X4DREG&{FdKNRE)xB zo~lMpEGTlT{}{Hu(L{MU>`7fLP6o}bO`p@4DH5nZ%(4LD`0%(RPCL_HLb^}kJY|Cm z_C}E>vrV~>)cgQC(ycKKs_(N|;`My9P<^4r}mZChT2Mga@EPch_HHTIs-FGQ~wKa zAj9q+EBxDmJPsU0W6NQ%LD$c_2YjnVDwJC4wR0}v5_6>1YU0H$Bn&5e&1>BWaeo%gjFl)l_ z0a{rNfrXut@N#xQhNbPa7`Al-S_b+pN?lFm-jGk=bCZf&iwQuWC0ouGa{-mN?CMg+f+!xq zcu{uSdL|rar(+5A zPlKcYHzBa7k-ixen2@TvR8$72llKl|{w@ID7uN8O8jDp>xH&trrV|B(^-||NI=`3g z4SPli9j@(e9fGK*d8`2wlMt?~;Y+qGhgkj}+06euLrzp)8m+kaFG06-f5>*?KRXdZ zHr&;||1_?8)$ETtyBig}bcXOcIc4Rt+NS69i73C4n^nWV9z_03r~E1cRq#`-qa#3> z-=Rrp+c<452ft(0ssS7TtNp>D1jw&(?O1Vw*Of7>R?KLT75pGo)OAqgr*HI#J-^Gq zDtc%#v+0(dmKM8sO~veWhR;*f=zUlMi?LLdEoJ;Y=Ub9EDQLG#Pfa@mv}c&+&8Q>P zzfnna0w_AymGD#Y-Np=)^pSv1-v1j!=NMjiTfho%L^5^E##*|aID2ZKt3xQi<1uUj zEjRqNi#LaynIWaI8B7NtI?I}_GHa?uNOexYS$UDhPj~<79F9B_G6Z;r?f>H$Ch*;Q zL$O3(2D)kVtXORBn4gh7HB#1A9P}ct!AZcWRpP1TDRF1ESe4TfTvyIjCpDv4f(EoR zuK_~ld5Y}$o(N5iDg|`B=uBe~FXjB57m+L<{AL1NR>5z%X8F<2I0K3$l#QC3T*f&h^nzEa`=z#3J-!1lS-lTHpVPEamHUgi|IcTx^Owu7{ z7_T~*%xHI5D!&Y=mtSsSIIASP^sH2!N+H~GH%vv6eXkM1KoYBDe9}A zEY(f@+6X6Mj&Le63P9*c;G%Ndadhpo*6TC^?-%mL)A$JLA$90t$b`89Rkr78AU@De zFSPmRO5`~!{es@BItF#aiX~yDpRQE$D+Dl&H9F9^ZPW->8gFt;>M~laB)lh0>XcJ+ z`Addao9uEdS||7BzL^B(9{(b$lR>D*^{zZwGgSa&$S zJn~N#!2gBR4V3>u>W=&uQg>bO{?4YlzctZv#OHo4Y;zM0`8kFDCgDL7UhvdE zw)_HW%|h9-FspjuNpPb5lE!EFiErtCRKZ!_Nrw|Ct2+-r2+4znFTyJ*Z?Mt3AMxc6 z%?5m8fGvtkG&|I`y#HhrBalRlO7$R<`sPOCX=Sr<%70sl z*X7Rk*hbonxC589eN*mBz(*}0=_kD>3IJsLN8pKZ!-s<9$Vz^H;go;0K10f`Yu^;l z)=(g(vR38qp&7cLT2lutp}u>lcYzU3Xol5K z*D2{Ym6uwmK>GTZY88WJh(1|#Kzs!trvJ&!F{m|7`p-kA?A7Sz1)~Rwi~Gc|P5T0y z!!+FC$Uh`pVnf@`4|3*u?MePbAdVLf119n=iOVr5r_rfg{s%>>!hc-EzZ&)lzPV*p zIB=KoAZxEDFoQ{=BV$~6zV*#?D~mU`tkO~6Ty42S2`r}b90lqRj-`t}RoTY0z9`s8f3FTS+Hev)t)R6r{(pL_mm_e;rl|R>2 z9H7kgd=e(h)onc`7jow}s4OF%%1>JWUB!sdVhwUJ8O&K_EPH>`pkt{|B0 zS&guphT4qQ#Q@^k%A|8M>y43TjJ++_ZB2enH*kmz`yNYwMNvEZsq~J18m6~ogwQ^_ z={F|0+1szNrR<^oDTe%wZIxkgteKiAe~;oyE7B+ob!d+2>gy4lHoReY}#^QJ4?* z9j_R4DKuQ@o#^G}VY28s81*WuF{B1yAfMDvx?z}X>a%sKzpkmcfw`1}Q@2Bpm0yL& zz6 zhOBAx)yeE6H%`RaSI0nw-~(DY*v#4!q(?P;v>w)C$BCf{&=g zV8)NbrE+(X%MVb&oD6%b##d{kC>cUVKEgz(NC%#igs$=B#kP;EWcr_%OmkpCG7@Gbd`S!(ke z;4I;9O-BKiQ#0ma0wrLVx@8N*s;KfA8!#b}PTtmYt`l;66hxvKEoee{V60GLyBZpA z*m-0((OfXAHgP|COPsBoTL~@wg!gu&i0Ktk_&8u>M*AuV2G;yqv>D#N<8MD*@DrX= zhjM~8NgM=DuZOSH=^4MOw6lpOJ2myP8)3^+Pq$@^E8H z4Co55E(jAg`S26XSQc##=e%(g(8HeJN(bdq`52aU-cXNT^Hp{MLeU+N9YlDXMWxPd z^fuI+;d3ajf8Ij)a1~JEf>Mle$xf;b81(xN#HRYw#F5w?Dw=AZdb>t3eYmYC_;W2? z`U;ZZ1mrJr>dtL;FuM#jhmA}@R1(hiMuF;gsjEBM^pAax$c}j5evk9%WFbZDK+snK z%6w`}pfp?|;cBJ!pN{GuKu7hqyb!iyziI2Y2Vs9ZdGbf@hcJguzybTr;qqu+d9?p} zsmR-Ii=gCaR+;%!d;3E(iwC+f!H*-$7DP121@!MXtb0I7!452iJvxijgG?)>oSU;( zg822;e;s2%lILghcw6>PBDgmgHzHCCDB94L>&MvhYVa4G-9>wEDhN6Xj)4!ncqzi9 z=&+7-&X%n&`6v~`UK*Yl7_J-3$&T52IdkNEvISh%Tx&C=e;#IhLb~(U(AJZsq~48G zTFns^zs*TI-W~4pxz)*G8A3F@!bl2xd4-^nF#-_=;p>h`oypr! zXiXLKgl_rIl8vv9e#D5+7EM*MJs7h^eF#W@V>=<6Ns15|wbYjDuCio|KhH}!0akt~r?XxT- z{s~ryz`P&Z=7un2b<$ZG8TcVoMp)8)Y zOhGL4!3CiL*YV?V@PpGVTJL#MpXTPW#riIgDCSsyYkfm(l2@Hp;_~W{;q_{R_<(K` zEpCYWFT!|&Qt=PvJ^3tv1Sp@q>1SGnC=BI*6>)tn-A0nITy|>N zB3(1M;-%5bPKWlvf6?>U@q=1WDv-^p>Xb<^qfaaDB+_ThYM>g!%i6|L#_dAzLxqNX z))`CS@Pcjru*EBtq&n?(f5X;~xgY#%ufol8?sT7Ue)NJ{8q;jcZYG1?AZ=q~0=KY1}_=usx z(Vx`1I{oj8q=@n7_+%P2bWXLGfR`-9>(1zVy5heUn^cT%yAPN18(YZ~Y`wQOOW7qJ zm~kDB*hsX^{TLzW3m<4zenQrd%J?nT0g9OfKVqL}D0r)A``ZKTME{yNT(<8S5N`;3 z>Nf0fcI1=Wx^Y3yxeX34BG1YA3X);%>te`4oQAUYEx{-2$3=ou*>4D_-+Iw+wt7KQ zpV#Gj#|!Qhk8gJ70r3n)fz@Cn_qyOf65G12guwI+*WK|?$xe|7`oj+Dd;COT64PAf zYd?;Xk_dUqLU-&g%UNbsgkd9$M&9h#rgh-67<9a$A&k7JKXGq^>~-WTN8je#z|8|C zTJ6k?ZV{Ij$@3d7{!OFp;1;e6rdvPi@?-2D7;1s)7Bac62ANI}ot2eS?jXX-{MRLL zx0EHYw-qfiuyW8Y#_MD6OnL&v-Wu-4x-}=MB-vriYcDl$;K0qC>AeOnYHx9i@3{C1_gap# zU~|}8Hm{^WGr$YHSsxdqtBZt1OG`v7Mihll9b@sI=@f(>8I+}EtVYKh-+$cTBE9ms z;@3Pl7Cf_xM8|zTO_W=et1aU^nIyS7FDPg=%dCS^Y z`Y1C6I(1Y>JFmqo$p%(h&!Z<9Bu)Mc8!mrP_))aH2X6;x{yOSKym<$6%&9lB{HxD1 z{SBb1t1<&hh2-l*!PbS?^cR=*B&mv<|8|F#&87tH81N?q6QGerhUnU za#dul#4EP_!4|qy>rF-Gh20^}guZi1SW4=Ph!Z(uPtv8EooZxDum2Pym@n}MHZBFa zox(e|S9~hZ^rv2iaIl=gQcl@!I>0_=8YC+7;XW}j$T`8)Ow+24RVz(^m}9+Br*Ke2{9*FVlgHi{Bhu05{`D_RtN}TVuk% z(TaUGOT6|nlENu3Xpgzf6Xy{8JL%8Q<}E2rWqT;FqdcdIOWoD_&p)IQEz;!?Fsb&KmWB{Rv7%>fMnOA(1IuG< zbQon)H&i~U=VbHcpvOeUH$AT#C%wa4xSBP_Cepz%0j^x!_><4`v}v0fFF)7|W=EO; zS!Gblh6THZD;XKUyoFl0(SxE==N1tbq4cfAMFeAmyojkvCs_o%p`?4or#(CsuIcvR z7+Z*ZHVs%U?6nB^wu)oq@fEVbrd%F2>(6HxlQ?x`9GQcUCt4YsW+YLMM+LNcgAN~# zCVd}_p3DAq;&CfJl5brIZJjlzThXwt8I4{^GAFjdfEsi z2h2@&WVM6QMP(A3^MIuo6!uJnHJ5#I_wB9KR~~-dpm@|3;Q=oI+TGuhko>p}+biTZ zROx!Z`9!-IVhW>7VQ53%!{iUu=5J4x`_N?&g9Y*e_b<-T_x%k};lor&kJ5@S&6Hd$ zQ`r&(+3GGjP#x{xSbaoyhkIhcSG!*fw%@xvh!sFB-A}`v^L@pJQyGHmxu{ZA7;EuY zjwgXS6UCIDltphO*2hHpxxugO5=-?k9jff8Ep%tN@(L@(7@E0?_4>%Y>qRyJA_$g8 zJ^tTOd%p=hY81=&7NOFSZSf1)sfoIli4F7~_*~$R;!R0q?2CaACuooM0&B16a_QW` z{=3uG@OXQsl(%`(?nz9buMe{W>@r4*C&8bR*G#W*E5b0>W4%OJ%%ZxwMQ$C*yV#>M zw>=x~Y%gFCF_;h*>dkrV=jJI{WX>$tR>m7jJ4+;L!EOP8pzk8+d{o99mOj4-Vr++H z!2^U8P~QdFc{?OJ$Q{cQe;ou}F9kgnJa`-w@zdj2;4gOOqFJGFx5o#)LINkHH?9{5 z5h{A5G^Z-h!|8PxWo+MJcm4&x&GjRwC7ZD1jRAJ;=42J%h8s=yG;mNvcaeGHMBp$) z))KNxhVesfA*lo%TWc&uxSM##*C%49pthh-Krr9D_G{rP0=MFU{j$9ZJ>kRO z>37aXcS5Y%C5M8%Ya&n<%56MF^RIobqh`=ywVj=CVj(~+hEwAD&TZxI<5o4YmpcbZ zCEL{k_zUIvk5n+F{>M^Rf)7ToG~aLpRmH$`&bkjdzC+A*Y-3#`tp3*mPRM7rKmEIBAyIh781@igwdQSR%e;=){P&6!M7GPJta z+yNQmv4Uxv6uQ{rhkWoOH(Ef*)_YUss{5Eaf|L+YdFbgJLuWcC_vy$DLkoX^L zsb{JH4Gm$BVB9&82sl~&N1zm9I=rlZs=VVFx@1h50sRmr^fPhODtU*?#^fFB3hxKb zqZGJ6r(fH{U8j%W9-ya|vC`k)kI;e5@Fecu>bBVQrCh;c7c2)+bmC+`W2WwK#@Bg^ZdQ1d~fMkd-5=<1>x7yhvN!A%2Xu0}gFAWG(p{|~f9>7Db#uZ*=G8H`ZyEv^N_4S^yp|Pkv&Enn zfu@CchMWhI;X^l}^QZ_snX0Lqmc$vkAXn`9ixGayp$q6OUq6wnb_uZ2JW; z_`(i{A+9qnZ68kD{9yT@l}DQ$kMAWs<4IJdI7OOpoK0!V%=5^HSH7IqWr+1elG)>sE4;)W z?c7oBk(-zm)sdjjm__Z~ENCV6WOumfA z%$gXanV=SnXl$=3t%+TmqFzq*s5_sfIi%lFJ`b}3J3b!elAu^RKPwVsdb?PWe}Gk& zIoRH+_sH=WZ@ARplzdT>dZ|b-2j&#^sR!m_VCfb5n}{$#krQDQ2!cg@Uz#9Zq5%&0 zIZ4-Ca`ir~g9JLhMkfYyZZJDG`d^Z&pzyAHY6znwrJ&iDg+o^$kCliCsIp56v^f@8 zr9Duavd_Ex?xNs@ zzV=O z=U(tPTeuN>2=6Po$^8nZj9(*&+cM`F%~Gv-Xt`LM(}v3t#RrQ8Jd&mtW$=+=4XcwY z)v1Daok=^vr)}n}=5|`N*u2T(Oo@-#ZpHilHXQL5;3J>b0RBnJTB2^YZl~UTPDYR^ zW9mDw(zHe-qn_RjY$cGaCg#>E2ejjLG1T_KFY>}t;4EI8rjOC?Y^Vyv6;@WS`rG5L92Ti7?)8D;$HdB_5~N`fDW%+}@ZVyM{4`q@>D z!XsM+wABhHhI)szlP>lQE6?vF(2^1Ql1hMq7h;#_4_uWE)B1b%&9DA67Z3wrZ?Nbh zwyVE1=w4KQGxDL=jG z&_=H%YC^SaN30P0{*=^tTcn0+9S_k^e}3_lh-)K&<7*N?iAm z;_^m+W}vSJ=u@2;xgkYLAyrtj-~TQe2Y_=$;T$4Sryc`g`Ve|*}IH0}x#4t{Ec$^OW;Ou))e;oza znGx1%J}eLhDM0=s>Mfz?4b3LF)TD%RJvVRf$J)`1TIISl_Eo*)K6?G{{^bs!f7$=I z!+$&cuYQAND|b|i;lAM(jvhM66x6yZ3sar?N> zmU|@BVFYt&xbah6vz;pY>XK){CrC=>5w2+CRRJzhrpi&bmicm_a~goer-@_oJWG=g zPt||ndrY=R=6(%AYtd{62DoW9F`#s40vqZ(BNYS-q?8JT&kvLKf_t`}vfqltgt$(P zEC&h|i*}zrW&4F)Q(CFYpE2qFa@=_@sUgcoz%tcAJXvKTm%0cRl zX~(wB786dOAz~eTxi$7kyHNy7^rI`U2CN*4&OYo`Hp-DPI1yu8J~MAf>U8784ZA84 zx5z8I2wfsaTuB#!+pqQX#3l1t>FAX#BaNzV?2TfrQrnySrM=6zSz_fYu_+VA6X^_e zOnE)|(^&Z4xxj2Bq3U`f3ue~iJe;-GyS9o!KXJ%{y>`#khZ1PJBgAakU1OZm*Ue>W z9yB3Q4jV7h?&!ict@l5Dn%1vtYBdZe{iSgByNxZxHn?`RD0F_{K4kid1^lm{?qv%L zqC=&zMW`rt@cA;nfk0!_c=d60Tkps;jkcLhg3T=7>-x9$!DAUTlK>j;7sKNG;>hQP zPIR5`Y(zf74N~a&!)+dheStS$pl3h&TO#MZjycugd>mZ25`I+GRKF;P@|;}qxSB48 z++Ny!wRp3M4%J^9(!mfbFkUj=zPYq6WzE>8PpnEcY*~+KAI~l?t=0`KiFQsEvhAcp z^ixD+r!S)`IMsBYwNqN7Lu*5PyL)0@eFID7{n7r449!5d+KLf+2yw(vC&q_w*-z>+!aUK@0bn_4B_NJA8Avfe{BqALS z9Si!?lz}S%nA?S-vqp@-UBR9iXzPL~?VBu=f)N<__ z2*o!*e~YnA$jZVp*3Yo(FtRxnJtjrF!)8k_Hx_{e86ls~Vs%kkw(9UjLpSqzs+v8?r3J^O+^o@ds+Y;|%Gn{V=%*3x%ZUYqZD+oTZ5+1|0doo&)c!KaU!q6?ut zJ^1zaPK+Fct3-zaI&NXEXunGD-(crLGp4c`H_aW(|Ljjo)=X-5APP&elabZG9a9=PX??%H-xp@AHQ@mx-I>wi;k>X|IJXx^lv~X8WN991eINhsjR@%n6OLNR%>HS=53t%K}LFEX>+5j@qv zXF97QOR5kLNg0ZX%^c6aZlGp(ze-qB@xMcVECpe4=_4+t}4CrumWk z5Q(o3DYSD@CZ#R5H()!w+3$;Y%WpjJlJ4g-*^*PoahnvGgIx9$nv3}F*S==168qvA zq9Z!kRPY4c^R~;779~NFPygU*aXc^L1RG?x2=(kODh<^II}}nrtNU(NFz>F_X-W2~ zuXWB_VXr=hmcC3rjD8!V5yejJdCE=WVBfC!T>qq5(&8NHq&q$Jr?+hv8?o$N*_`Mp zwBub`Z#3-L9&aA?t~zB3+=YGzipqKZ@W$ZA>+|TCkGgG+V%y5sd*g-^oIVH@CM*~R z7OY)~Vi;k$ZB?>IKe|ISpsEA!%U^zKrk2jZb!IGQTTkdDGcuRpb#rpUdbfvm6V@KO zI0B;usV;lihi6{tJH~Q?x-L{LdOia4)_W8e=>>4fwU<;P~zn*E117^y7<4do*ugbSRl=5PfxaFOUT2uTti1h+sT=U65sNNGG4|2uBGI4C zA?qsaU6tSXhB4N@e?euyrsTV}>s12(i?;I&Yij$}y^4T}fQVA0L`4NeI!I4cL=+ID zC`wfk0cldDghVzfQlo-&5~X)kx|D$Q8ahaT2mwNe5E9aJqkHfFIrlvG-f#C=pHtUd zbB^&F?>k05JBt1-fRRYX+m2{bs!Q@8NSDz%sJn|5Wddtb)CX&l2s0Z+)>$eH%AoFc zLYX_eEIM{%ggs3e31k3R_}&o~1%cFMU|7VJl`YoF+6eUBI+DKFJA$Y(?X3N}*;972 z9@6*mSKH1hmFMM=T$OV<64q=JCoj6JaJp<>nRUqOz4@mZHuE%Bl6^n%|L(bGY6*+P zGhBnbHmjm~E)V`W21X8u<5u_vXgui-C8NVo zuDFq0ePon9xdfLR(x%BvT$}8)&c_a4?$8R-d=Ay*@j}0e9y-kMVpPFA=u(?~(% zV9uZO8I`5c4iOiFg!*{JFGH`{a%0SY3@$%?+0c5<=?t7Xw;zjoq|m=(yhew- zpfYmO5AWO_`rcJ4W&s2Tv34~;5HQu;JDNGy%FW2qAPEyz7d1{Kh*4D3Yv08gG_^e7 zhCkwi+*X=Qk1U_CAh+JycOXtZezwR&aX1l@qy5)wT!gyIs-P*zDUS}9m|D#v zvA=E$jla3~sWWhpOfulgSBHlJ?9ZP1N+h|aJpNWXB`9`>M22!j@a3_xB(mcF&N1FPP1K!0Rox zkFCZD;FuTr&zv8K?sE3{I*aN{Q?{q!y867+`mw@i=f5Yo(qABR#J>~X<*Xof9}+-x z%GwBPZ=^mSI?6Eso!jq{$zCwMh@im-iG)n5Jtp5V47|6?F;VF6i^G7yAyf?U%`)o& zd!=U19%tCvqpqC*74V!_F!sjz(r|NR$<*4^>_eWwEwC-+C(kbL!3wp9#(KO|Cnf z%iulg;l1tHw|$qa&Y#`?;?qtCK9tc!e~<4YSF1LwEO--hO?K(-kI>3h-W?I+-eFKQ z=2QTpj50%Dw@#5R+{5CkP6nU*{#0=il0stEK?c!?TkfjM#uK4^rIaDU-d`J$g$xZz z|0loJx?Wnds$4^u?737aQy}bBB-2}dFBnOMxOpH=LZNn`<*w#r)Jy9gb+ZtrsZxT` zc~sdFPCOrhBN*HH%#W!C-Wf23GBD?08!k7*olC=f~%0?Ab+mR z$eJEifAT`pYTeKotAaK14})22UE!%c>(jO5RTXt>gQEwF)4!Mw8o|XRYab49jz^zw zV9g)np|w2H2-k=^E5PQ@b(4%NCa_h2EaCUevew%5Mz%UppMNE-H)F>!!SV4BZ*b` z>&ch#?d=;Meyx1|cjUMF>liWiOQx2@>MO)o~uH+jA$r>5NZ6S?b%Cbh5UKn03b){V`#dGe6OU>zn5 z7r`js5k(yz;KsE{doeN%dv!Aw;!vO6I{8Cx>~$&elYavFxgZJyn%mOWvY^v95oT*| ztE=%oz#IjmRnSs)Ix7}1ALT1#ioBMsJ-C*x9P*UfL;k`%q;};9i4+QXC~E>;ytKdW z;qup%YUO>UM@x_yB8AEA58pU@yy0L8TfIH&?RoRFid}HISmlw~GCXMhTJ0I2v*q~@V$!nRrlTg}K$a((Q zVJBr>p#JSc9XF3wD1*;kx!s5A;?|rof~X^3iLGgg(}HYb6O>O!bIMg3#3A2m{590s zrg#$8T&Gf6S!0jZdELr9*D#OJxf5PaO~;#I)~a>7&Gf!xmOB7^mnPLWbHPKcc7ruvpEsw-8S1F+_%7Z^d z!-78XW{8SVx}r2XT1EJuJnV}s2Llrozl>34345;_L2i3%eAax^a7P~$QUM5rZ@|2{ z;ef3zWq^&OllCP5rY*&T9NI!8n@P%(Uo=4P!}2@!!hLUO+xh-fqF!iH4AYCGbO-%1 zbhPi8L(V1g$5SZSX`N0QbXe>Yi#T(J$=pabV+(0#FBXp+&RG4W_~bn68cuO`dCQp| z79IJCg|Dw~g$8FwDi9AYy`Z*guW2VJyRHC=hLP9s7cX2AyC@HG|5jFsc=7{4ALe>G zk8pKkmBJ!%(U_TcPWk@FD3Jn;ef>nD=)E6wgw(H}eQa!whTg5nqqRf`mTE$}# z-=BcV+3YU;fGxJLZg*x*TJLB-%mZiUrNesvNfS%C$A5(tC;5uGfi{c8gh5}BucCK_ z>zurN85cs=wJB{1pG}-sQ6(#si64PZ{SGLAX8vbF00anb3N8&pLAAxDO!!oji;ySx z=voT$X(Tk=0V08WrHR1w^7s8^LD(ZMwLU6qN3*xZ#YkiS1ctTq%H;~~dlxP@0SqBG zAYQ@XAm}5{ragM&Lq$uH%daYi#+WKW=EgjsMAlv!{2zv~0QoWV?JKE)Fj+piMGXEq@8c!l82aA>aGvEuBS$ zp7NKN49+OnL-OYCuy1Q^cfdP!sQ4QS?qSD|^E=AMqa7F)2gsB8xXW{)2It7LM`&kw z)7YQeWdaF0`~BIke&P!-$nBE{7-TO6LNf3hpp%OJRf-Ma)##uuj74U!ykpiG%+6$9 z?EqRn&NDBTRq*Mhto%mJOkOF@?N#+Gx=bT?7Cjh9+jL|P`MCJDDgT9yguU4l8I|#; ziEYon-S-kTMjR~if9i2EMu11{fPv3rk)n~YiP}$LZ8gMRH#>27ntJp`^L6RZBVwA3 zR`H{P!iTl z^^wa5;LLW#x!%`plnXCwDc1wvp<<@*-_BsqH(CaGG7O2`XXmnlqdLxa9GdiXKN8B+ z9snl^z$zYLRQH+qgk@Q3+8x!yb?>PT@Sn<+00yrct6uWD_ROozk|7-zM&6xpk}Z2m zE7h>3g%_*`qit97(*QWn%#H{T9xT|CjwnJ&+Sb%8c>_?&f%LlJ#f>xSjwNp!DAOAX zd8gEdpqr?t8KDDO@ZH=U?&=QoScZUFe z({!s4ZDGFQOphNS>ha}!c#H!t_IF&5t+}zgkSC_dGtPXl1S-3X?IYfL@DcMU(&y|k zV%vwuw_-jYD)-T9D^PQ+d}Iidz!qtCqQ%W-F4;GVbu{WtaDUXMrItCz9;N9tp>!a> z=J!Sig2J=FPt8>1;V?Lnk)G(3xnaE`c%N@g?lQ?Q$+a&KAJq${O+aXG^$4N7QVWnG zf1qv&OZbNtv3p=*xX)ZiG@prTl z+FqNVPi~|)Uut7QP~OehfNIW8V|JHdl3)Qj_tbwSw0tl4?y7-oW$_<~#s|;91`|7O<>sfWDe`ek0w`J}j zDj8P;w7&{jC*{1_THv;SH@t!j5}|VIxl&$)tgU0%z1>;GvsY-gjZq|`aidlmVc-K! z_h-H)3bzI*j9@fQo0-Ob2}5l$v@ko0IlVN_BwV;Bx$Ou5*Su|Mydzl8MCWdXSAlow z>f)e>#RFGh7;y>4=?o4SeeKBk2nk z(Y&^omW~|LWkdpY=9W`#b*;kuf`JGZ8-g>7TS8Ri&qba-N=84vZH7V$RZCdXTy$L- z_xc(W(XE>!>a0*E`>)l6h2)go+N0aHkVe@3*ZD(2Ree1#Kng=(h3YiDf5W$k_@nDm z91iUp{r|ZeH8lAJAaQka1P+yFYja4wL4)tZy`?!E9!;FxWFKp!)={R|-tGxxGJqV2 z3~tB*X%JIvDnkQ%I7N5gv3I$oxi`S!@nt$4*A9cgW3SOS1XN~M+{jA;|F12=eB{pD7+3GiXVm=w}>!kcfm6gE_m zfAL>eIIBq#|P1{F-9s$=L+XYV)+p&c_AfV*P10pw23TYQ_5(9$#|03w$`# zo)$Z?ToTsy^}?;jD#?*b`!$%d`gVf0V%~#t)P?fOI-rh*2Mxq;ef~CRWrZtNY*YNVZApi zmglwQA%4ia`r*yf+oc+CnZWbVSN6yo*$>>Lp{`~bpQQ_y;`a%d|J;#(q0DQRKdSZm zwTg9X$lw?5q~O&V#}2`E?;5#a^*n7g)5@_xnrmnuKRYwzX=@sHaUE;a;u$g0wwdi;Bd4VzfZ!`-W@@ol4X zhF@P_o16Edjyzlq|C(^F`NjQ1C(}|$29FFAV7wpI5~!zQ9nFuh4n+!zy2sxD+Lnxe zi%BbS3~>3`FW&YZ^&F%WY-zHrm(}hd@(M7^Pi+f+%XjzL#18s*;?_CJ6ouMaWjav9 zQc3J!gZ8NBij(9&&y%KQU1lWt>4&$3CSeZ{JUL5`P+{`tX;#YJd)vz1H}_)JU(BEC zR6%@B!|MrSASEkXlwJ{yZ0jRqq@#OuXK_`(Z2P}Ag`{Kis*<~>C~y6}7aDPnZys34 zR5{{maR`_t>N`D`Ue`e6)808QDuWG*jl|-Q6}s&Z>NID!?V7kX%{uTp`AS)b0Ba~C zL^@74PC@YNvUt

        s){%M3NX*LqWlW=Xu^71!M2Rb4yN@>9P^Jw=# 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 #w3ZHRS@N{wP~@l;|~E6J#3> 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

        eZ*$5Y_OqTWxu~Xf4PQTmC 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�z$6PLs z;N&v);8v~&;Vzj8?ym}b6#%<$<0jbflMnu_R#$iF3gcajtHQz z|0-(9^XD2J6XVL7WN1mgq#G@Gq|SOcNDGmI4<>T3uG0f%79S(-<2s1M5C=8TUgI_k zdh>#kbb>7;Jy8n&?KGMtaTViI(?!p}pVuj(>RQ$L^qS2Yak%|koorXAr48nzivauL zWIjy;?G@iAoO_*KeTh?rH$iLV?W1TWT1&{~yZu-%@v@LK zQM=HQTwViN5IaNVYtdVMpP+A7P#{0@jK3=Xu4AeeZ?CRX{hi5>rA=?oBJWf1Xb=X7 z^o94B$bj9^E(rzGOf=}t!T0nY3YPupOQGA5yB#980$Kd5y8XR}q4Qpc4HB#}YVu`= z=DPAycdE=c49USPHzVL%jCi8pNwlPe`pbP?4mg)ADP{ZAqmRAfoZEDu*o2iwU3(Srg^?D@OPf6x}u6@X{ z8&QCcsX7@JQG!f@pM%#W3bc`m1fZidWmhC%6;3B75J91Q>xIQO}it zr;ayZ{n41=D>RE=X5KRA+?D<&XlRL(Jco(2=T6*JpG!N~`X zTPi7hf)&gqGl!A-i(2`7rwkz(ddq8ZLL!%c$~{h$Ez5OFJ%E5|MCV;D#mpTh!jk75 z9l`$gQ9ATqvOjjgtNy9eRfoEckpKc5rYYYfy81A*?fX`@8SHT_lel{_fB&7B@XZ|s z6|P48T%$;Lc6$b`L=-+k<>P{sUwg_Gm@xi`B-#xmaPBXuwr|arlB}Yj(&-KTu_u?8Z>EUXrjnIbt>gr>~ zgbddzlokX8(tR~*F=@0;(gQ1`Z$r6b`+C%)f*R@{Ic9nG-Rr3=oMM41Q7=cFf7Cz* z#&no&aMRqNfpvtX(92RjHS0cHP*EPk4a#SgQ#Y;Wwg7wWQG7Wk+ty7zA*`(-EHwXY z>5Yb)!a_uu?_@fAChg4DIqiDeis7v~1GRnQ{U~d>CG4FH`!R?%@)5dzGSqHIk(=yR z7?NIB|I?41?=U3zD>O>qVpa{Pd@6S4WP3a$>NVr9;6;`4@0_byD1KLjTAEjUX48>m z!;yJ+y{2xV``r?K=-l=Z{lZxntV=Bx=isSRxu;QSKC85huay z{YN%sT3oa?DcA;c>F+gWr#l!JTi^3`=>I7D{f&0a7ZdQn$qt6qKRa^+`>baO3SccX z2n!7XtM>I_RR<q!do(q@RewOEpzaYfB71#dWS~Qyo>qEDP+zgnh%Yb! z28(jz3SgsOnw}<>YTI;_p@(T%IpGNqTMhr_uLea{v@OB+pa@xbi3E3KK9qB*F!x8? zjz7l0r!M{#V2tK?JC1&{$Dzh41!Flzq$JY-bD@>vGp4F1YBe&+<^B{toekkGZ3EFo z!rJwHxh^DUg!@nq2XS3$^a}K%-gl#KIk^0anjO%@*%;m8T}4Ov*}54=(xF*U3!P?t ziQdE-Z*CvpA1+ED+&gkVS*A?e|$CDy2>d6*?Q8QXF4YSumTLDBEsf$IM7>2*H&69Ils!}2rCIg3vzr=*;&F#0MX z#g+6}4GPM9Mt0JaFP3Dp+*C6zwkhoEBCI)4hWyrEx*;MD*qf1fDduvW5U3pWp8UEXituBMcE|3k!K95^`5bf5{z@Bfcrp;`OazZU%9K)nu`F#T!oUwJOr@T9Np=M=2o3=txV?ogiOT&o&3L(qkHQlB7IZYG!6 z4L9c3Pdv5sX%k;t>>+N4BEcTN8lbFyRv3C+5Bg97+J4%*!`%PT*3_12RN*9YD~x5w zzc>da<3NrnM>JjV?+X55=58@9TjoH0g+-amhT^h@Es$FO<9uYw0N%*Xa~ z8{xFMxK&08`5HkDotz&C*i?_$yZ+K&f=AIN%Testg-r7L%GX!C@xnOj%H1uUKHS-g zvze@G;~r3%oynb{S#8=-iB?$f{8cx$t0N0$JscP6xNBn0zTmCno=uRsXoZD~k0X~9 z)jf9S)m@6w)jBf|?ic9sLEy8;lvIdy=)=>kkSmy|h5QGeW+QBnG3*rwY_4+*%pH*| z764{gQW&Z;U%4<&C9z`2Hyy87LVpQawDH`=@^9-GBx;*}hbA&-sTU*GHjDf*e7JYF z5VOYzf0pV{jlo`ajFk3fW}zM_Ul^eYfCU}p&HNe&IZ;$M>t2KRP1$hU(em^}__s^^ zimy-A`@E#DFBK`8=e;dMji||aW>NT72>Vtm-g2^)(E#VC~$Uy93U+S!mU$S=X}O|l5epR3<-MeI*XF%_a3jfUYY17H+D(R z#C3Upv)h-8Vkytbmd$wcXt*EhyWJ4mO8nW?^m<4?g^P2l2H0_pT)GUacQ0d!BeF!E zJQ=<2b@93U)=)$JD;Mc;{#Lz^`$}zJWDYCLdY+P)SnMYlQqDe4HooR!FI8ApzaR9KUg%zX<|{6vNsth|*<;Kfb{1wU`d5~`-e&Rp9U%3;15X#4 zHDHk2zHhJv`T|`u>2W?F_085Y!{rn7_|3h@pE9)0aE9oK)!Iddkn4oh25z3iwD{hW z$36~z6cf&~fNV%+C>kN1J)DP$DG5!mRH~h~&oQgB?@#Lo+4#aBNV^))-fJf87Ph8= z$dTc#S>woWtnZg9N(Irza)4fKjTa(0w@+OV@&1SLJ}0kSUNuF z{DnLIyGPRR_Ydv#l`M1Q8~50ES#0_CUyy~3FU)O;@y&!L&xE?MrcSC=DeWnMp}8wzEKOcs13;(QPt2^j!NbV33u6b!ya~P=3OcIcPfGvFGjFi zKeJgXm&eUF>fwn=K4Cn4qGXQ?^3QxKXFoJfF@FJ#C$>5+s$05&IdL=5Lu<*~7>!8H zMtO5^hltz};JwM4n%2to+>FqnX)$E2d5Y$7dA|O=Q>ImxQMnm=7*DSh^r)y7NJdMf zn*~W;*!#`Yu=vXYHt`YwY~J}mk#Vj4dGveqDCEbiy$tiOh{iv0x@y{ug6ib$(j9}jdr&(n|wV_d~2metTb)%zG~V~_S`j$1FDn? zAVOQXS9`kS!xhN==f=CMZvD0`etP=^)u0u36^kJa)QMZnsi?I5yCw8*o?~+X z2!AMrKE3ekni)I7l?D8#hW!Y6p>qQsgTJ5m^NjU$?*h%(wS2h~5f9&r*A)U6L96&Dvcx5BfqhylDct=qo_vUIwXs?9p z?>w%5kJ$a!Fnv4<2-hdo$iLYZ{5F8QAAWm1C}~*sBjWdtLVxpb51wY-`!~zxe>uee z_Jy~3VBAaAJ6z5F<-&f0Api4M&G)A~KKgIJ>;K>%D)#|fo{mA|rkB4jiU0g^{^d)9 zY|Z{Zv*mfa>j+gs?sK*Lzqp{^?xpgXINaae`~O!1`*7D0Dz{D|;lyv>@h?O2pTBzK z#f-%N#m9fYdc$2uCpve4F!fk}|BnCj`||&jsn@c*&*3rK|@ZDy?2_ljY0fz3O;_1FjCfZHn6BiDc*`kURuRUVa`J5~W>;Uf7c_c{v-5hn@4{SvxaM5`N z#DajIauB|sL37hWxc+n6wI^yKxx?g}wdB$R9c2f;$CVL?MVYXluU)E_9x!yZe2V?c zIPmZ0b!hPCuF$Vg*Q0-b-J!uxRO)@UW-#6qC5X1Kf~jnI32*}K2ki))1lAVfToYC8 zW=ge{L|vR&G6fz+td*JlXwZ!fy(M zUoPOE#^&A(@Xr?Uuw?tE=l|)?w|9a-fw4cCFC@AdplXJoGA6f>mFl>hy)PF5{QWQ0 z+k0Lh_H+ODZ_;E^9sM6KfWIH+gJ)bX?heSkT8D&xb=-Xz{Ml#pBTK=#AjGltOsTAnb~{-e`iEf%Gl?FdkxvJlpVP0~J@D!fzs;QIDcIUmRP zpN%+|e#abKZpn}nKF>LEu&?xvs>40BbGK8u6Q%^3gWH@=lr3Fv#+S@G7#G@RI}OB5 zRyYk8S1Dzt$-TVXxoSP|6IyODX^pS5i9m-;#z_{(NubpF1f{FJhi~EIA|0TD`sI4U z@teU__@5@9eBR9uGO;c?fCA^G%6IXD#S*x_YWwD^IroyEVsnh~gB4EG-&iOEwCSf` z$%%4RqC<+<=xPsLa|2EMZW0?o8m_^?YA(b{+C40Dz%H-qSN{0%__16-Wu;E2B*LsX z&>O(Et$MlS*QX?MNf;r7=`g9_xM)SYu{&s+af>xC+XEG_cx5Fg+ShOJ%>V+g*DSTV zm*i|J+m8o`U5`y$(NWdV4TnoB9Rq(s*nhW49FcwloSPuGq#`x0|MI?|6vld^iS1ap zhgO%+1%uRR+BsS{(OAHsF*oZ;BLg#Q*7`!%K)6iC1J9MC3gMd8&ViEBf?FP7v#D9e z{Lf~iYAqtN&%fmW^03LRk3>uI5M#LmZfR@GAZO;0cS7-?nv^L5nJaGq+Ckgp~jQ?a|c#UzE_Qn0EMo# z4Vz+%{*7>vDoR7gngiWeEw8^v?Z-v*z`=bUe_t!vkvBwA5SH+ zhKpObhe0_LwmL!$xn6V}K(-Cd7Z0=wO^&+nE!6b3L?`u8j~u>Xv+7XbxMa}f-yV$% zu!9-9peZj4lu<(4Q#}}XfBsw!W{bx!BU?pi)c>kWD<8X45N5WmLICN{e|!<3%2DCa zqehtIn(ghvL~r$-58T;oGMp2pfFB1d04_6qxpr2@lPG~e`npv8#7f}E;{v*Q+w`SC z(!I|H4Gz!;dQdx}T2vBcY1pP@wxc2x;@^<(!7FqgU;|H!pSH;+0ZI>acQeeVt{H(c zogBosuWXy3?W}GnOa--vFO8iq8+%X}=)exHr+#zBIPf&IM~};q*9WMy6?L~KggEHM z4Vi4qs`OMZauva#4&7Zo!#gXeoCD2w&xK@AGm$K}&=V>hpe>O&)J)G;&M>|neYHf; zO3I}#?X$g(=`sya&)&56)a4r#rJ#LkOkWrVxqCfdNhQIe#5VZwg)iP2A~E97WIe(_ zT_mI&r6e= zd)4k>+OZ8{AidptX>htxOkl9ehE2W9zE4Dl9}Rqb`;kb&59Ka+LC^%zJjoy?oVc41 zdPId#`VZAr^R?-W_y6~{PYIzc4Sbzx-}w=qt=pyVs}NA|K}dD1lf$6MJxt-})N94V zd7qaUn=tO9?~JH|ZD9Nh=aKD_!Tzwsw=gH^F0KVi7_C{6u&<2y2<-9vZHS-ZOsqPY zSmE!~HStK2{F2+TIoy;yRZ`F|8t7fJQ+K|r9?%O~sy*#(0G>5ukIh}uZ)q8xdDaPh zMG6f)>PtaIR1C@!UY;j|U#w?Bt|d<$7ugfA!Z^G6x$tQZs&Z@I#$&O{ZBq04kPYm# zi&g$f-eGykdUq@jgh78#K<6EJp2ai2qDMUUKT|C9+l?%2>1-1AQ!+&4fNe0I|-=0p8sdVd08Xg(KduTVI5&6yVIL{e|VtO3y1FPefd4>s7;Oe zMpv+;>HO32g)=vW{hZ_UGLK-w<3HVpJ*MW0Yq>#dRtxTP)sKR5Gi^B%<2bIbp~>r< z=;yA4cD}RyQF77)Z3vQNntAmF-E8A=TO!Sa+jX>V)`4X3o?gQGcOlDPn}e(Lp(8I8 z6P7OEe#@6{GMikX2J~nL01);6 zp)+?>_XI5-^#zn2#T&VlnRWe$@L(&i5D9x-aj0}h%doS~2>qgWcfM~4OZm48a5X{Z zPQ5JG(y%J>F-R7h2KHnmY7fj~GnmZI%{c;2?F)DPFt^xF9n-rcma9{fG3-jTg?wjY z0hO(JlnU(O!Y4IoEZ0Oh`wgW9m0@u7-^Gu5(x;2W%E-xY%NyHeM6M~g{m_kC_7^i9yO z56t}(8=*X3Vr=ZRK;*?q{@F-!PhCSloJs zDP@qice#6kN;h=91Z0zMNisFkx&Hpr)6_jr1t%;J7gNRP>*l_LrIdt?WVDjY4m z2)#c`GT?Ero3hmjZoeUTzXVFZ5AWD*&QB_9?|gAp*1vw>d)AZfCxP@3&0XjX>wWh5 zOnaQcGpd)k`(dcQag}`@2z0-e*U)jEf&HUDH>~y0W{GJuSdDY4Q+vz$^$|-Es<;sCGS}WcEqJ+(XuH!yBmaU(X#4kt?OrmOuB98a) zan%ID7C3&o!)t!q1L|d4CORWp3`*#fN#<|Y(LS2|cs70;2BKmB-{8 zOeLoJF+y7SSo znwvQ+$CBd6?bO=IQK)CHFIf9Z*Ar5SL(m+Gyg6)Q7&gr+ll!);K!Koj~w(L zpL2b?e=T$#6Ba+lkamhW@p>~kk68RVwE1amu--+>5C#_r;%d(>`wC(mo}aH2?t;sR z$}#XpG}6(OhHL&HQGuH;kRhfvqP1HY@kvY1!Yq`SRgwNP+1}*0I*qw1s=+d5fs;-e zxe(FEU@LO*_G_qx%89Vme~A1QJm+X?$9LF4p236QsQ`28MxA!ULh~&mQW{r)sOPSoaEn>K&tw2Eq7pi&&vk{F3=2$Qqa-Eq@xV+Jal%bs)o>Gc? zxM$WEM_Rs_h*y99DYnwGh9L%n<~&D!_R)zmf>=`@?AJ@Ggpc*%7;Csi!5idht#1Z-Mr4v!vtZypWO{L5q4K&(T(2pauuK5>s9=Z^_(z{wf#jTm!C~cJPC6A?Is+1iO zv-^!qd3mxJX5RKNRqyHaAyeaB@LyVVtQs~8Ti-R6cHFVJI=)P#D3Z@2Yiko3qlMne zEhhAsK59XT*iMZcxnqQ~DID36#Y~!1%0hJn4zMeb1CnR2L%C(?BT#OB_&44*8&BB+ z1?X8gIT+_u!Sdv0{8M99j7)n6Pk?(e1?sDPv5gXGTu?DXVI> z0yL`s3h?sj&}&VvK|pVOYIn?xQD0EF)In09mvPXTaV3})F*7Cfc$a!F%&^qr zOYl6ryDfz}h18Ul$uwCj13D;qT_J}Vtx^j>!M z-Vx&q3!Z9;nN!lQJIA$B;5POnPY`thXN-~wfTdW05IMtZbnp+M<)I>Y!x@hbgaG5) z!1bD%#tVl+nN@T6J=80^wm(wk597b;hqB?<6_Vq3^3#m7F=SnIO|a96Uvs;ExAOk+ zTxf~%1p2OgLP4yuk1lv^TF@Y_5mRAv=>NGD)U zsEbW8PO>LdLG^@@#opjxs23DZh@k9jF87p6Ke!^=g-BVYCUu@W?%WGm%e3#Y4p`zA zYRh_s=eI*0yt*mlP^Fqh?As;$3 zDO9Y~Y_@u~S`=?8;l@}BL2LyxSZw>g?v}QPr9Vb~L27Jy+T|Bq9(lGh8)I*D7PU)u z0yc@bCre>*wVY4;O4KkHy#_yx{GKR zmLcBn+q~~fqZFG$)T>bSaAvb%FG|xTessUGbjI+{ShhW@91t zWq2Sc2*2#O)aKt+rAVEU23~)&p|Z%%Rb$E>%m@8u?UmLh>dTNxvyXF4ov0hx#PuQl z_(_!G!fek+5BQibB}t*^N6)6iloA>Qo&#cr=2_v~+p{k0-x@pp$l3c|9C_0BQ)zTv zTgSE{O%ti%*FiX$PUE3F>TN6r?yTI-t@cl``amwrhDXnoMa?Cotg5fJJ-E2MGqfJK zyz<82gb_trHxf>IHlN@07MBug1)|f0&q)QVaxtBhNZY9K8`6NIsdWwST}krq${=mD zAW{g^OYCZDvr(S%erzq_yfdLzw$*8xyr?ZIC2$=%i|WnA;PdhY>yoVe$ZI5glkR$b zZd|=_C|j(`9;4>&64z)NUw$9P%a&tw^HqI(=yCb?tWAFWRoXp@?K^Na+C2f|ik%51 z7W&p^0VPzmQh6SoC@>R00PZ47Pa zoZ%T4O!i*t>|-k6oX|D6dmX(SYr)wS9w|{9uzJ~+R5W6qovdoUiviXj^8_J}m4G6> zM26h2BRcR+)7Y{5L$>$$ixR8Wlyj*1DoZSn9BHxVzYyA=Ma>&uHKOx9E%37eUgg}T zUY5{uP>5D{)Jd$^z6jIAWiC`O%Y~E=LVZ{)9P=mz6{DQT(@zUtzLZ%76DdAfGE>dm zV^gz7WgJn>>1hHY&e0gbnw$6tmwHKALvxdHW=3hQ@_UueQ-$Izm-Qtkc2jMko@^>1 zDkj{MYut!N%Xm_WY7d7`FhVT#u|2Tu#hE`;MP;8b)S@@$DfA-5HbIH@qy$xvq_hUe z))7eGdF-9ObR9VutHl00T*pol7(O$vwR=Y`>bB?WQ#u$Wg>wie`q35hYFwC|)sDao zs}c9fs6#?i))S{Wzq1#3K+o~{P8`bkF=_7yxzwADt7dpFY z#TH-@=8imT>5%B!!kFI60`~S~JZ!|;%hdLc)8;EIdGYlm9J`U36xcq~VQh<<%dvT$ zH8^>79}Xp(|F%q!BWIF{ z@!F(NWTxK%f`M#-+OYV8pbRF+;||lNh~Y9$#DPFrk8qYz{nAtUbziEzogq24xmH4p zd7pcGL~Cb|j9m`W{g}DAsxJ{P_#_(_Qo#b^y}x#tD<*k4K)=(Zpw%Z+Yn7CDJ%6GG z#o8L|S@Rp-v1$38AR9CYrEGKNzE4(s*5HyRNs;l=YI3!39SRzqI)V4{(pyzG08 znPq{+r~lkEM;Q=vnXfw)e#9O6#hZFJ_= zR{5z{@*#RJcH}SFpYytRwaH85m=o1e6T2o;(+20(vC;aKck)YC{LkgJvD;i1w_;}1 zQ2TLVza5!rm&C{|+`>M_N$jf#(vSOnCB~TszPE!81*>zBp*qs{_NGmg`iUq@&ZX5W zzM#?xq8=6{`z&mUnSdhOSK=>#ZUt$Kz_8ayc%sM~>CYW)3RO5`heS?F^txnD z6G;++a%6N-S1D9j*{12L_%Ekq%|8#ZU{erD5RS; zmS5(d0zKAoN@-=-;L)s*2=7sr!Qd!>5h*|ld)LF<_I_V7mHV<3FSvav0MS0p4g4aQ zJ+xxi%=%03hG?3^y0%HAvgl&-07PQyvwpK^)u_l_Gug`KzeI`OpCjEU8$ElfV;TCHAaO86!2h#B06(rvEtbaLkDIqY2&;~ei zW#OpHX5K5V=xPR9n zv!lmK$4svM90E6^)f2`k*5~iB9=EPoy7lZzOmkzWky^5Mor|h*xsvax46Z5W>kSr5 z8{i*J`?XVm4ue&0)>Ce>bZ%e0{0vO@g{6Vm4)`lX#%9>-4K*~={Hk7_aSWUHBt=A%Do$-&we)`t(D?R;{~^;y z`sr@z>AFa$^DH+O%jR<=rLK4T+dn_-;Hi_XowaEuM z3%w|P-=Qn_B{I+c2X)`h!@R&zzC+wVt8w-?rw-S=Z4YEE2epPVxp^&k>{!_hEuug< zwBzh>UppgLj(*O9zJ3&TBg5h2II0}wxaWUkZ=s{oisjZ z1SFI_j84YiM9Dpga^+rUtI3FfmG{$!a4o%UK>}oNtR2hfsF8%jm&Q*o8M^*k=2%$A z5XWqI!JZi>cp`^+YR8X8f!|Z;^5geh=ZkX!a}EcZmCA21bLl2Orf`+f_gQL!Ks{*uUrOqvfj;Z44}p;-PeP(PIbc% z08`!C;eaMCa`PyCNcWJ`{hpGn9G3PA_H}-qDAQStjhA}uUAmYHGV&78^63+Kw@1<6bgptd+UK8Lyn}@3cuF-3dbEr zb7zTjLpRwHud>d?kKWhWa;x!d+j0KnH5sV=3RaP(HC~f~;I5<=zpGEZ;MMnGPL21G z7bwd@s5<&kKj0p;lG$+WKSaX6fR4n9md+B~zbT#m_P&S&2{FYIKLD z9EM(*)VoAZQ`xu5EmAa(oDL0vK6b(+ZMj-(b#O{T*N1MjKhn%|9yUUB9ndcmAI_Kr zMYY$NwG=nUNI)koT1mOZnMIfsEX!{9Znz!Ho9fh%!e37-s{`rK7$!AMZYnOCcCbN#qM`AEZ{?_J_7SynC_|cCK4wL+GqiFBXAx`AM_z5Z6<=`@dh`}P(6$XyR`^pR@^yLr_o6&?dXj_ zpTWT4$1aUpwo}&?hMO`1h9K<8n%jLSMW^KdA0*F#n#6l4pqJV(XJA#UI zq<2I_N@!B01VjWZ2uklwdPwNKL_k65H332>QUZh|^Z)_!Zf5400iEM1&-?xQ-hYAY z>~imQuX?R(IlB0`e!p{(0-7%kv@AX>JoW&pUtKR`J7f!Fr5SL!ddrk6SPMc8Sz%|# z_-xAPj`RXjRkl=sf?X~}qQ3*av1VB9JI`NNWwbh{jjMw2x@nglBW_1BQ==t!+FIA* zj(ukbjh`ntnNgtq4Fn+^hNW7|p!xiWL@SQeThF#?zXf8i;Y6=>6}1PL_r(b*Y@b(r z%pjeAC8Qf1u?9Ygj&9=j09&k{50=K<>3uyS{~E!CuSwV@n$+Ki}o=@Ol z(~rASYz8!IzQJ?()TDO;owUp1yw-gwJ`FAN<06||*6MH=bhO}l+aqA!nCe)=a%T}# zv{DFYwpkmY>a}aitK6y19pQajXE5E62^n_pI*Nd+(UT^7KVSUbOGR4%OfcCDdAHkX zy<%J}j||D=YU-&e z^%57emsX>Xok2PCh6sb3M+%*@fG**pJD9F-lAWVYiKa8|RAAzH!S5`i*8@5G^G%97 zuByh)abq?{wZFp&frkPyl}V-qFkkzosG+vJD#BWRfX8P5rn0koUGIi_>4;=ij8ybV z!FISu8{-8?+Kk1R5{Y;7^8qBp=~4yT4WM5Ov^?!QmE}-3ew6INYzRzQ{!^R8@zrkW z^RxZgJ#$5X!YGxlOuuwLr*@uI&OtQ;4ujjy$Q_&jJUjFFSTCM_L^?^TSUs{5n6tF` zrm{a*$F;xy^yyN_N#5km@hl;HsRBi=rH$oomCYi-B!NpEa(H$8BSAH#GK(Bru|cMS!!&Ly%*Ct+XATQI%~k=7CG z{ipvSY3hqRBwRHjR=Ny{$a2O7wTQ)5?YbN1m6f!GXrLm5#sU?9SHUb^nZ9`ZT;g0D zPu-Z2=G02L@oF~!%>%d!WzQgbLpfjMUVg#Fdu}{=B4gS;)dTa=%`8V-F)f6$~osy z8JAnE(`-2h$E&BM-K4FLo#rV7O#WbStg|_bwh`kj2*zT;d?}H-B0q~FE;GNzC4CAv zFq`D}cAHeZacukp$k9946N)chss4oQY%?<=HLiw{Ojaqo~TxlYn~5Wh)GB@>dX63lU(>Ah6m*vof5Zq`t*O}xTkhSgWoL013f}ru^8#rs zUQS=ENvEArV~C{NZTOyl%X?-MncM)73Q805_jKBHF>c zH)BQjMkf1yX0CN5tj9WctUt5e&>W+8%;Fy zk5ZjoPC8(Yf;(L2|tYLN^vEiogX*1Ms1953fT@DO!Qq@U)n6pdS6QsxjLg|~`iYa*KLDiAk};(7U;vizqy&5RJL_&U#*Y43G43wi zO`s=f06@7Yld5gf%uflb)lVAwI=-vjX+jD0YFq1ASV{W!uSmk}u&0H9Kx@mLoVFHe zTM1hRh&51L6C5ZK>|+KRrb$Ur&>!MDO0f#w>I)}r$hZ`NucdE)jE9w3ns}q5xZCMh z=SUMRksxR(37syY0{6Oh9GXcW&f1RFYgYmUOS|?dKZ=`paRx}yuY+euCP2|-yEgZ` zkEvq$H<{#Co1?)T5DlE}E7ET6Lh3k_v^v`+I>KuoDI0z?E?IQ_u@~Cd%MN+*l~4ZO zk=Or0k^KBAs0aYT%WdGF;`i!eb^JkBqYI(}0WBI7X+X#AaUIu4M|#^c@v#pd1J1fs zOv>HdvzZtCUQjRM;vML;-oG`~8JeBtpI$efN$~YSz0>)4uT-|0rhZEHSWiN#^b8vS zxM6#fh~sSB_57;wyxRynqaz>x$tu#IEeZaCRV3t?&&$jf_oFbDH#eAJml-fA7ed5n zUe(FdeIb3APXH1cQ;X-7-S8HYrio(NIYZt+8yT1{Poz0mO6jd9}ds3GkL03bgFo~<(Eh~t5LEUYS9q`GycYR>Vae0j(Y=Eb<>;2EA% zl52}+yshx1)S8McuS}|aKF&H{)Hkdq7whNO&tMFJYSp)p=o`y&7FGe1@TY#uijgPD zmg8uc6X?i|C#{vv=Z`!Ne=gg~YOJm!{W@+$&Sq}Ck^5=Z?W2dWmBL%Y{N5aLhR#kZ ziKl0rZ)(ewxp(sLyhME=NcT&hF?EE^a%lIu<3PrDu}W_A#VRm{!>;OD<*IgPUck!9 z!we2!plX(x>=M^daXFB#aJiHD5wnoJlXN^Z30si%z<;x_lk7|W5~_ip1KR!P*Vs%j z5KD)_IM7ix2xqYYtuAzf;JZPLKm`Q=Knq1Lbiup8qj#|!Kne}}_%VbWtP!ML)B{Sb zP#HDfakKQqu)ud=u2*U(wY^oR91-U@NTa1%uO%L^)!JCc)y z&lP?$w`t`NAzQv&m7?KMckLOGI`A3e2*fk(Gn-v(s}L}rbAC1sfUeGGQLSL7g1+qx z7Me#(E_^MPQihJr3hEl^O|lV@PcOb|ddIYG%%dM=YR1#Q!x-1m^v}5L2xFBl00rP0 zxLSoRTir>PJ{C?Zq)Sh#{!2G}le`!Tk_yn1V4 z+8u8uhIvb&pvE6*+Vf8D`KJ??-j|zOe(1~uX8L*2QkcFy+Lrs>1=t+Qeh8pq9({|( zSm*Q6Z$b`;&${V|%1Nq9elG#hYj^SpJ1Rc!|5GR5YY^%ILa7OhK1FA&DDj(A#$!ggk7z^PVk9v zWY$*MW$u^{F(nTs`Odz3iA|5oS=L2Oa(<1}=E7o+Ajk4A-*$YktmH+vZCq;L%AMjk zxl7nFCv%}LCk|Jk%P|OnY0S%oN)nvN0m#>*R}C|F71A!_fyvRgLAf=dQlcHFyjA)# z+hgqhCxpzuF2ZI|)j1Yhj2Q%43${U)9mA93odu&VL4ymys;)3!IN>G<=}Rs~JChk; zM_tB4SXWGXy_BFH%U5&UA{Q+tIO;&s+f!^f_>Pl9Pss7QooQxYBkiR;9jM~#j@!wt zY~Bg${B!@LqMl?tV|M*ODs2F^`EUJoIFTn zItG*=and_3<=f8ly=7bq^2~3e^9lw@;w)A;G2@g!-&-d01el#`i3qRV`;$*6E;s_o zE^k3$<2iG++Q;FU-VXtNwH6Q;&0}i8Te}u@eLVvK@v;@s zm@z^t+690bGWnWD@!&pf`40M$jN;Dku--zj592q{<2jN4dFbwt#5G z6RBK?J^}SdB@Z_*eo~GW~d^Y(5BKx6td<&p+!YpR|e}k`IL? z{)6Fm`Roh9d9VC=z^Fb-Ny?=6HCy&M{v zHAOIGRHRzt9ne>}Y7&1=zIBWaQZbhz*D2Lbc)%JzOd8V^Lc2<&bc$F4)rA(4hZrqnpN+i=s6R}u(30UGbSZf#oo;DB*Ji57*v_J@8gLFWNc z|J{CvWuk7|HpU@hYcyOW#F>g=vKz+X zQL)R2u8CMrkp3x}gt3PH&7AMDTwZ9`>wIV9UO4?D+Onn(s#qZH27ec=(Bh>)0Ja0S_-1PdYH4oI&(w-w z+HND@4x!3jk=TEcYH;hm|5_hcEwe?7GqT8x&J@mXo86dZyrh?rJkv&^ zp;c?I>Z}70eks+2--?5fe3m{*39UA&sT`Qvg|7S70o~E5GNt`%c7{WPmI)f)iz~*x zq6bU~=dp(nNA)3d;1anYAlpAcgjIdz)Vj7+bq#2x|IA^m%A6!MbB8<>GcKn4Ac!Hf zf)lr-dn_?In8hvA%M|83DmoqGYaZ2G`Sk4JBIB{(UdnaL+4Sq0;EWB7spsYK>%w+D zUA|2@&Xj*d?y|>Eeyo$SV7g=BMxOuA7_?m>6yc^gb$q9rm|vCzdz0~^)&{6cE%c{^ zZw{#-jh{qvo`$pK?n2RUzJ8oGemLrL<8SkqjD+3GzUDGR@jt|}qk zz8;Vw?C=f1&3d|mNPN$9b!Yh*>}24F4$S<-tdaiPa17{PyzC8a;{s!q{+kxu*d|!R zGJTVgeRSw~lgYZ1(3A_K0ESygG@#6T%I1s!>daOCTigKU&k>LHN`ElpsAB^_tVZ+E zC+4;DSvr%tA!V|=yhGg=BNH_gLL z56Y<)vW>WuYUi5>+G@-LnRD0Ocy!EE+!F@CgkW$rg8j}iRAGWrfoKSjQuVM})*n1) z@IR_zb#FH!| zedw(K;O;-@oY*`Ax96LM&zjB5*Z3wd{<-AU^G~U~V>{ek4QdrkDv-NDixXz?Gu{D^ zkIRkE01AdUwkE;g5Vom6a|B_A*_K}xK63}?Ve_EOQ zQTWLlJgwL+LrD(z_+!_7ak3+>d5^wqDW)xQ7z{agZ6j+3iUga14w7%c6mEuN*e`5Y z^#mS(KyB5Fk6FUGe5z9QOxchmwY+nTUhEg>O&x>maG8?Q8UL$nA1s{i_6}Gd#(0^RsI{Lx2 z!Nodgoak4OJmUheV>CYV@Fmh>qVejOa}vGiz|}p@>sK8=+YKQ73KrqrizwfZObEGi zKzH(Ce?HmH?-Fqd~5SWGQQM0g!K;2|V>v|jY&|Hz|b z%&~tFaa4TVL&UKMbEv*{aBztvH)wiYe*eSld3c%M{9JZ9H=umC_Uks_3#T*cf&&Vg znVS=9^CPi*E|sGP=zv7{9>w^RiT<5$S(t!!>UX`9+&{1r(^2_3z|kOp1`5=C-YXcq z@=duFRoKg^mHiNt?A|o)=wWo|?_BS~bD48M^^oU~mfP$7xAb1`A<(n5Hf=~~KFvu3 zp3P3(?F1N*xVLXj_9plK;EPuPHA9FEU&#K;sAnGB#n2Cx*o=pE&sQ|<)&)S_HP*4` z!xR5(5Bd*_%QCdf4T2*z5AHufP=rjUeWJ4TpG;xycb<0TqK8pL49%#&j}rP19{LyJ z1T5XPT@ID08y$=PtF!F6#sxdzgw~pcm;RnC?0+@8xv_|HARD%H8uRRL$np=b<)44P zF9S|klB0LxFG$Xw2mJY^v=}~@aq)QTEb;p)>94Ks{oNA|Y32RB4g6modJ3Qtud4YX zdj9{`OG)45Cb#3%*-N_nzgbSdrPCLV8;^Wv`M)OFJ-`VQg&4W^=-8h#t$#tJWIT=m zO3LABNYKwBy1U{f43#;5mJ8tL8?gpXt{VS_@dusz*TiOzkfziCQGax~Nb`P>`tkdm z3ut$RGlf43@t?ZX*b&C;XD>~JqkqUSzQ21AXbYU*B|!ulvw)O*Dxww?#4T1Gme{h= zIXzHUOWJBe53d6A(JH^PPEFdEQvgjet3%}=3s)N?w-d9@o_qh}ds`>Zqp6;qLRK~{ z(kndYynpb@l{+VDi6*v=^+E$pwZ!w{Po116v^Xj&6%yhH3KJ_(Ps^WD>3*U#;scI4 zEPL=l9~bBFa-v1lV87yh*@p+n%M#VS9isfp@sWI?9>jkynyNdX%(6%f(>wc?$4^t{ z;b2Q?SP@sd$?UuXCK+JwNX(gC+C*vAEa@7%r3SO|w^bYWD{dyycr2qsapPWuiVztW zZT0#8jG+7TPwdC9cwr{E%FzDw{w+2}b)xG%A%ACq-edYp`N1T(E|KTti4#|E-@bjz zN`~+pteTMsH1RG~4&U`bNl^{{`77?bPWJ9>`u-)y9~cx;qz)^-p;x$l`@^X-(T5Xw zq+kJCtMp{MQ2Bd-RP)Zc^Lg3X$Dz-lc3Yw!ry%(l#W}oWSe<+Fm9h|VZF_(0-%}k- z!M`!4F}HmjlimMD_zf>yxuU1K%2y&)zsg*$=;M)C+lnK20B*_%QCYeT?A=aRxD%l%GEN z-gc-+?pARTZXkqpXb6<+bQ0;;7<5;|4Ic$2(JMYxcwHML^m?&}o3p3C)!nsoWIHS} z@v>QJYS{3_Z|B>-wF~==+*ljwWD>n|=~9C?RV=hEk7dw2mldM#=vYwl>Rl8pF}&S# ziZDD>U>(mQbf&zcEypgX@*XV>sJ9SZbp^G$wB+PPsQM6doVUCE1zg8Le78H0_BPjBE{H;Db1T33{+XiThKJHz*hE8QXiLU!LrOFK|+qoHNy z;V6#5=kCcS=*f7|V;0|v1aDb?_gFKTlMp@hw=|s}zxs2Tm2>M|l@@z#?ZOdmy%Jfe zL?Oc^